Browse Source

update UI to use themeSwitcher

Hamidreza Ghavami 1 year ago
parent
commit
0e266b88f0

+ 7 - 7
web/html/common/prompt_modal.html

@@ -1,7 +1,7 @@
 {{define "promptModal"}}
 <a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
          :closable="true" @ok="promptModal.ok" :mask-closable="false"
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'>
     <a-input id="prompt-modal-input" :type="promptModal.type"
              v-model="promptModal.value"
@@ -36,12 +36,12 @@
         },
         confirm() {},
         open({
-                title='',
-                type='text',
-                value='',
-                okText='{{ i18n "sure"}}',
-                confirm=() => {},
-            }) {
+            title = '',
+            type = 'text',
+            value = '',
+            okText = '{{ i18n "sure"}}',
+            confirm = () => {},
+        }) {
             this.title = title;
             this.type = type;
             this.value = value;

+ 5 - 3
web/html/common/qrcode_modal.html

@@ -1,10 +1,12 @@
 {{define "qrcodeModal"}}
 <a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
          :closable="true"
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :footer="null"
          width="300px">
-    <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >{{ i18n "pages.inbounds.clickOnQRcode" }}</a-tag>
+    <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;">
+        {{ i18n "pages.inbounds.clickOnQRcode" }}
+    </a-tag>
     <canvas @click="copyToClipboard()" id="qrCode" style="width: 100%; height: 100%;"></canvas>
 </a-modal>
 
@@ -19,7 +21,7 @@
         qrcode: null,
         clipboard: null,
         visible: false,
-        show: function (title='', content='', dbInbound=new DBInbound(), copyText='') {
+        show: function (title = '', content = '', dbInbound = new DBInbound(), copyText = '') {
             this.title = title;
             this.content = content;
             this.dbInbound = dbInbound;

+ 2 - 2
web/html/common/text_modal.html

@@ -1,7 +1,7 @@
 {{define "textModal"}}
 <a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title"
          :closable="true" ok-text='{{ i18n "copy" }}' cancel-text='{{ i18n "close" }}'
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}">
     <a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" type="primary" style="margin-bottom: 10px;"
               :href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)"
@@ -21,7 +21,7 @@
         qrcode: null,
         clipboard: null,
         visible: false,
-        show: function (title='', content='', fileName='') {
+        show: function (title = '', content = '', fileName = '') {
             this.title = title;
             this.content = content;
             this.fileName = fileName;

+ 37 - 31
web/html/xui/client_bulk_modal.html

@@ -1,11 +1,11 @@
 {{define "clientsBulkModal"}}
 <a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title" @ok="clientsBulkModal.ok"
          :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
     <a-form layout="inline">
         <a-form-item label='{{ i18n "pages.client.method" }}'>
-            <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+            <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :dropdown-class-name="themeSwitcher.darkCardClass">
                 <a-select-option :value="0">Random</a-select-option>
                 <a-select-option :value="1">Random+Prefix</a-select-option>
                 <a-select-option :value="2">Random+Prefix+Num</a-select-option>
@@ -71,20 +71,20 @@
         </a-form-item>
         <br>
         <a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
-            <a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+            <a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
                 <a-select-option value="">{{ i18n "none" }}</a-select-option>
                 <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
             </a-select>
         </a-form-item>
         <a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline">
-            <a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+            <a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
                 <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
                 <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
             </a-select>
         </a-form-item>
         <a-form-item>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+                <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                 <a-tooltip>
                     <template slot="title">
                         0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -104,7 +104,7 @@
         </a-form-item>
         <a-form-item v-else>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+                <span>{{ i18n "pages.inbounds.expireDate" }}</span>
                 <a-tooltip>
                     <template slot="title">
                         <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -113,7 +113,7 @@
                 </a-tooltip>
             </span>
             <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                           :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                           :dropdown-class-name="themeSwitcher.darkCardClass"
                            v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
         </a-form-item>
     </a-form>
@@ -143,37 +143,42 @@
         delayedStart: false,
         ok() {
             clients = [];
-            method=clientsBulkModal.emailMethod;
-            if(method>1){
-                start=clientsBulkModal.firstNum;
-                end=clientsBulkModal.lastNum + 1;
+            method = clientsBulkModal.emailMethod;
+            if (method > 1) {
+                start = clientsBulkModal.firstNum;
+                end = clientsBulkModal.lastNum + 1;
             } else {
-                start=0;
-                end=clientsBulkModal.quantity;
+                start = 0;
+                end = clientsBulkModal.quantity;
             }
-            prefix = (method>0 && clientsBulkModal.emailPrefix.length>0) ? clientsBulkModal.emailPrefix : "";
-            useNum=(method>1);
-            postfix = (method>2 && clientsBulkModal.emailPostfix.length>0) ? clientsBulkModal.emailPostfix : "";
+            prefix = (method > 0 && clientsBulkModal.emailPrefix.length > 0) ? clientsBulkModal.emailPrefix : "";
+            useNum = (method > 1);
+            postfix = (method > 2 && clientsBulkModal.emailPostfix.length > 0) ? clientsBulkModal.emailPostfix : "";
             for (let i = start; i < end; i++) {
                 newClient = clientsBulkModal.newClient(clientsBulkModal.dbInbound.protocol);
-                if(method==4) newClient.email = "";
+                if (method == 4) newClient.email = "";
                 newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix;
                 newClient.subId = clientsBulkModal.subId;
                 newClient.tgId = clientsBulkModal.tgId;
                 newClient.limitIp = clientsBulkModal.limitIp;
                 newClient._totalGB = clientsBulkModal.totalGB;
                 newClient._expiryTime = clientsBulkModal.expiryTime;
-                if(clientsBulkModal.inbound.canEnableTlsFlow()){
+                if (clientsBulkModal.inbound.canEnableTlsFlow()) {
                     newClient.flow = clientsBulkModal.flow;
                 }
-                if(clientsBulkModal.inbound.xtls){
+                if (clientsBulkModal.inbound.xtls) {
                     newClient.flow = clientsBulkModal.flow;
                 }
                 clients.push(newClient);
             }
             ObjectUtil.execute(clientsBulkModal.confirm, clients, clientsBulkModal.dbInbound.id);
         },
-        show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
+        show({
+            title = '',
+            okText = '{{ i18n "sure" }}',
+            dbInbound = null,
+            confirm = (inbound, dbInbound) => { }
+        }) {
             this.visible = true;
             this.title = title;
             this.okText = okText;
@@ -181,21 +186,21 @@
             this.quantity = 1;
             this.totalGB = 0;
             this.expiryTime = 0;
-            this.emailMethod= 0;
-            this.limitIp= 0;
-            this.firstNum= 1;
-            this.lastNum= 1;
-            this.emailPrefix= "";
-            this.emailPostfix= "";
-            this.subId= "";
-            this.tgId= "";
-            this.flow= "";
+            this.emailMethod = 0;
+            this.limitIp = 0;
+            this.firstNum = 1;
+            this.lastNum = 1;
+            this.emailPrefix = "";
+            this.emailPostfix = "";
+            this.subId = "";
+            this.tgId = "";
+            this.flow = "";
             this.dbInbound = new DBInbound(dbInbound);
             this.inbound = dbInbound.toInbound();
             this.delayedStart = false;
         },
         getClients(protocol, clientSettings) {
-            switch(protocol){
+            switch (protocol) {
                 case Protocols.VMESS: return clientSettings.vmesses;
                 case Protocols.VLESS: return clientSettings.vlesses;
                 case Protocols.TROJAN: return clientSettings.trojans;
@@ -230,10 +235,11 @@
             get delayedExpireDays() {
                 return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0;
             },
-            set delayedExpireDays(days){
+            set delayedExpireDays(days) {
                 this.clientsBulkModal.expiryTime = -86400000 * days;
             },
         },
     });
+
 </script>
 {{end}}

+ 23 - 22
web/html/xui/client_modal.html

@@ -1,7 +1,7 @@
 {{define "clientsModal"}}
 <a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"
          :confirm-loading="clientModal.confirmLoading" :closable="true" :mask-closable="false"
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
     {{template "form/client"}}
 </a-modal>
@@ -23,13 +23,13 @@
         isExpired: false,
         delayedStart: false,
         ok() {
-            if(clientModal.isEdit){
+            if (clientModal.isEdit) {
                 ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
             } else {
                 ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
             }
         },
-        show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=()=>{}, isEdit=false  }) {
+        show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
             this.visible = true;
             this.title = title;
             this.okText = okText;
@@ -40,11 +40,11 @@
             this.index = index === null ? this.clients.length : index;
             this.isExpired = isEdit ? this.inbound.isExpiry(this.index) : false;
             this.delayedStart = false;
-            if (isEdit){
-                if (this.clients[index].expiryTime < 0){
+            if (isEdit) {
+                if (this.clients[index].expiryTime < 0) {
                     this.delayedStart = true;
                 }
-                this.oldClientId = this.getClientId(dbInbound.protocol,clients[index]);
+                this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
             } else {
                 this.addClient(this.inbound.protocol, this.clients);
             }
@@ -52,7 +52,7 @@
             this.confirm = confirm;
         },
         getClients(protocol, clientSettings) {
-            switch(protocol){
+            switch (protocol) {
                 case Protocols.VMESS: return clientSettings.vmesses;
                 case Protocols.VLESS: return clientSettings.vlesses;
                 case Protocols.TROJAN: return clientSettings.trojans;
@@ -61,7 +61,7 @@
             }
         },
         getClientId(protocol, client) {
-            switch(protocol){
+            switch (protocol) {
                 case Protocols.TROJAN: return client.password;
                 case Protocols.SHADOWSOCKS: return client.email;
                 default: return client.id;
@@ -103,24 +103,24 @@
                 return this.clientModal.isEdit;
             },
             get isTrafficExhausted() {
-                if(!clientStats) return false
-                if(clientStats.total <= 0) return false
-                if(clientStats.up + clientStats.down < clientStats.total) return false
+                if (!clientStats) return false
+                if (clientStats.total <= 0) return false
+                if (clientStats.up + clientStats.down < clientStats.total) return false
                 return true
             },
             get isExpiry() {
                 return this.clientModal.isExpired
             },
             get statsColor() {
-                if(!clientStats) return 'blue'
-                if(clientStats.total <= 0) return 'blue'
-                else if(clientStats.total > 0 && (clientStats.down+clientStats.up) < clientStats.total) return 'cyan'
+                if (!clientStats) return 'blue'
+                if (clientStats.total <= 0) return 'blue'
+                else if (clientStats.total > 0 && (clientStats.down + clientStats.up) < clientStats.total) return 'cyan'
                 else return 'red'
             },
             get delayedExpireDays() {
                 return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
             },
-            set delayedExpireDays(days){
+            set delayedExpireDays(days) {
                 this.client.expiryTime = -86400000 * days;
             },
         },
@@ -129,13 +129,13 @@
                 var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
                 var string = '';
                 var len = 6 + Math.floor(Math.random() * 5);
-                for(var ii=0; ii<len; ii++){
+                for (var ii = 0; ii < len; ii++) {
                     string += chars[Math.floor(Math.random() * chars.length)];
                 }
                 client.email = string;
             },
-            async getDBClientIps(email,event) {
-                const msg = await HttpUtil.post('/xui/inbound/clientIps/'+ email);
+            async getDBClientIps(email, event) {
+                const msg = await HttpUtil.post('/xui/inbound/clientIps/' + email);
                 if (!msg.success) {
                     return;
                 }
@@ -149,22 +149,22 @@
                 }
             },
             async clearDBClientIps(email) {
-                const msg = await HttpUtil.post('/xui/inbound/clearClientIps/'+ email);
+                const msg = await HttpUtil.post('/xui/inbound/clearClientIps/' + email);
                 if (!msg.success) {
                     return;
                 }
                 document.getElementById("clientIPs").value = ""
             },
-            resetClientTraffic(email,dbInboundId,iconElement) {
+            resetClientTraffic(email, dbInboundId, iconElement) {
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.resetTraffic"}}',
                     content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: async () => {
                         iconElement.disabled = true;
-                        const msg = await HttpUtil.postWithModal('/xui/inbound/' + dbInboundId + '/resetClientTraffic/'+ email);
+                        const msg = await HttpUtil.postWithModal('/xui/inbound/' + dbInboundId + '/resetClientTraffic/' + email);
                         if (msg.success) {
                             this.clientModal.clientStats.up = 0;
                             this.clientModal.clientStats.down = 0;
@@ -175,5 +175,6 @@
             },
         },
     });
+
 </script>
 {{end}}

+ 6 - 6
web/html/xui/common_sider.html

@@ -23,14 +23,14 @@
 
 
 {{define "commonSider"}}
-<a-layout-sider :theme="siderDrawer.theme" id="sider" collapsible breakpoint="md" collapsed-width="0">
-    <a-menu :theme="siderDrawer.theme" mode="inline" selected-keys="">
+<a-layout-sider :theme="themeSwitcher.currentTheme" id="sider" collapsible breakpoint="md" collapsed-width="0">
+    <a-menu :theme="themeSwitcher.currentTheme" mode="inline" selected-keys="">
         <a-menu-item mode="inline">
             <a-icon type="bg-colors"></a-icon>
             <theme-switch></theme-switch>
         </a-menu-item>
     </a-menu>
-    <a-menu :theme="siderDrawer.theme" mode="inline" :selected-keys="['{{ .request_uri }}']"
+    <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']"
             @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
         {{template "menuItems" .}}
     </a-menu>
@@ -38,18 +38,18 @@
 <a-drawer id="sider-drawer" placement="left" :closable="false"
           @close="siderDrawer.close()"
           :visible="siderDrawer.visible"
-          :wrap-class-name="siderDrawer.isDarkTheme ? 'ant-drawer-dark' : ''"
+          :wrap-class-name="themeSwitcher.darkDrawerClass"
           :wrap-style="{ padding: 0 }">
     <div class="drawer-handle" @click="siderDrawer.change()" slot="handle">
         <a-icon :type="siderDrawer.visible ? 'close' : 'menu-fold'"></a-icon>
     </div>
-    <a-menu :theme="siderDrawer.theme" mode="inline" selected-keys="">
+    <a-menu :theme="themeSwitcher.currentTheme" mode="inline" selected-keys="">
         <a-menu-item mode="inline">
             <a-icon type="bg-colors"></a-icon>
             <theme-switch></theme-switch>
         </a-menu-item>
     </a-menu>
-    <a-menu :theme="siderDrawer.theme" mode="inline" :selected-keys="['{{ .request_uri }}']"
+    <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']"
         @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
         {{template "menuItems" .}}
     </a-menu>

+ 21 - 13
web/html/xui/form/client.html

@@ -1,7 +1,9 @@
 {{define "form/client"}}
 <a-form layout="inline" v-if="client">
     <template v-if="isEdit">
-    <a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
+        <a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">
+            Account is (Expired|Traffic Ended) And Disabled
+        </a-tag>
     </template>
     <a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
         <a-switch v-model="client.enable"></a-switch>
@@ -17,11 +19,12 @@
                 <a-icon type="sync" @click="getNewEmail(client)"></a-icon>
             </a-tooltip>
         </span>
-        <a-input v-model.trim="client.email" style="width: 200px;" ></a-input>
+        <a-input v-model.trim="client.email" style="width: 200px;"></a-input>
     </a-form-item>
     <a-form-item label="Password" v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
-        <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
-        <a-input v-model.trim="client.password" style="width: 300px;" ></a-input>
+        <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"
+                @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
+        <a-input v-model.trim="client.password" style="width: 300px;"></a-input>
     </a-form-item>
     <br>
     <a-form-item label='{{ i18n "additional" }} ID' v-if="inbound.protocol === Protocols.VMESS">
@@ -85,26 +88,29 @@
 			</a-tooltip>
 		</span>
 		<a-form layout="block">
-			<a-textarea id="clientIPs" readonly @click="getDBClientIps(client.email,$event)" placeholder="Click To Get IPs"  :auto-size="{ minRows: 2, maxRows: 10 }">
+			<a-textarea id="clientIPs" readonly 
+                        @click="getDBClientIps(client.email,$event)"
+                        placeholder="Click To Get IPs"
+                        :auto-size="{ minRows: 2, maxRows: 10 }">
 			</a-textarea>
 		</a-form>
 	</a-form-item>
     <br>
     <a-form-item v-if="inbound.xtls" label="Flow">
-        <a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="">{{ i18n "none" }}</a-select-option>
             <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
     <a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow">
-        <a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
             <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
     <a-form-item>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+            <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
             <a-tooltip>
                 <template slot="title">
                     0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -112,9 +118,10 @@
                 <a-icon type="question-circle" theme="filled"></a-icon>
             </a-tooltip>
         </span>
-        <a-input-number v-model="client._totalGB":min="0"></a-input-number>
+        <a-input-number v-model="client._totalGB" :min="0"></a-input-number>
         <template v-if="isEdit && clientStats">
-            <br><span> {{ i18n "usage" }}:</span>
+            <br>
+            <span> {{ i18n "usage" }}:</span>
             <a-tag :color="statsColor">
                 [[ sizeFormat(clientStats.up) ]] / 
                 [[ sizeFormat(clientStats.down) ]]
@@ -122,7 +129,8 @@
             </a-tag>
             <a-tooltip>
                 <template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
-                <a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)" v-if="client.email.length > 0"></a-icon>
+                <a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
+                        v-if="client.email.length > 0"></a-icon>
             </a-tooltip>
         </template>        
     </a-form-item>
@@ -136,7 +144,7 @@
     </a-form-item>
     <a-form-item v-else>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+            <span>{{ i18n "pages.inbounds.expireDate" }}</span>
             <a-tooltip>
                 <template slot="title">
                     <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -145,7 +153,7 @@
             </a-tooltip>
         </span>
         <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                       :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                       :dropdown-class-name="themeSwitcher.darkCardClass"
                        v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
         <a-tag color="red" v-if="isExpiry">Expired</a-tag>
     </a-form-item>

+ 4 - 4
web/html/xui/form/inbound.html

@@ -8,7 +8,7 @@
         <a-switch v-model="dbInbound.enable"></a-switch>
     </a-form-item>
     <a-form-item label='{{ i18n "protocol" }}'>
-        <a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
         </a-select>
     </a-form-item>
@@ -31,7 +31,7 @@
     <br>
     <a-form-item>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+            <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
             <a-tooltip>
                 <template slot="title">
                     0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -43,7 +43,7 @@
     </a-form-item>
     <a-form-item>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+            <span>{{ i18n "pages.inbounds.expireDate" }}</span>
             <a-tooltip>
                 <template slot="title">
                     <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -52,7 +52,7 @@
             </a-tooltip>
         </span>
         <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                       :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                       :dropdown-class-name="themeSwitcher.darkCardClass"
                        v-model="dbInbound._expiryTime" style="width: 300px;"></a-date-picker>
     </a-form-item>
 </a-form>

+ 1 - 1
web/html/xui/form/protocol/dokodemo.html

@@ -8,7 +8,7 @@
     </a-form-item>
     <br>
     <a-form-item label='{{ i18n "pages.inbounds.network"}}'>
-        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="tcp,udp">TCP+UDP</a-select-option>
             <a-select-option value="tcp">TCP</a-select-option>
             <a-select-option value="udp">UDP</a-select-option>

+ 5 - 5
web/html/xui/form/protocol/shadowsocks.html

@@ -57,7 +57,7 @@
             <br>
             <a-form-item>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+                    <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                     <a-tooltip>
                         <template slot="title">
                             0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -77,7 +77,7 @@
             </a-form-item>
             <a-form-item v-else>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+                    <span>{{ i18n "pages.inbounds.expireDate" }}</span>
                     <a-tooltip>
                         <template slot="title">
                             <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -86,7 +86,7 @@
                     </a-tooltip>
                 </span>
                 <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                                :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                                :dropdown-class-name="themeSwitcher.darkCardClass"
                                 v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
             </a-form-item>
         </a-collapse-panel>
@@ -106,7 +106,7 @@
 </a-form>
 <a-form layout="inline">
     <a-form-item label='{{ i18n "encryption" }}'>
-        <a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
         </a-select>
     </a-form-item>
@@ -114,7 +114,7 @@
         <a-input v-model.trim="inbound.settings.password" style="width: 250px;"></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.network" }}'>
-        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="tcp,udp">TCP+UDP</a-select-option>
             <a-select-option value="tcp">TCP</a-select-option>
             <a-select-option value="udp">UDP</a-select-option>

+ 1 - 2
web/html/xui/form/protocol/socks.html

@@ -17,8 +17,7 @@
     <a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'>
         <a-switch v-model="inbound.settings.udp"></a-switch>
     </a-form-item>
-    <a-form-item v-if="inbound.settings.udp"
-                 label="IP">
+    <a-form-item v-if="inbound.settings.udp" label="IP">
         <a-input v-model.trim="inbound.settings.ip"></a-input>
     </a-form-item>
 </a-form>

+ 5 - 6
web/html/xui/form/protocol/trojan.html

@@ -55,14 +55,14 @@
             </a-form-item>
             <br>
             <a-form-item v-if="inbound.xtls" label="Flow">
-                <a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                <a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="themeSwitcher.darkCardClass">
                     <a-select-option value="">{{ i18n "none" }}</a-select-option>
                     <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
                 </a-select>
             </a-form-item>
             <a-form-item>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+                    <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                     <a-tooltip>
                         <template slot="title">
                             0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -82,7 +82,7 @@
             </a-form-item>
             <a-form-item v-else>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+                    <span>{{ i18n "pages.inbounds.expireDate" }}</span>
                     <a-tooltip>
                         <template slot="title">
                             <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -91,7 +91,7 @@
                     </a-tooltip>
                 </span>
                 <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                                :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                                :dropdown-class-name="themeSwitcher.darkCardClass"
                                 v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
             </a-form-item>
         </a-collapse-panel>
@@ -113,8 +113,7 @@
     <a-form layout="inline">
         <a-form-item label="Fallbacks">
             <a-row>
-                <a-button type="primary" size="small"
-                        @click="inbound.settings.addTrojanFallback()">
+                <a-button type="primary" size="small" @click="inbound.settings.addTrojanFallback()">
                     +
                 </a-button>
             </a-row>

+ 7 - 8
web/html/xui/form/protocol/vless.html

@@ -15,7 +15,7 @@
                 <a-input v-model.trim="client.email" style="width: 150px;"></a-input>
             </a-form-item>
             <a-form-item label="ID">
-                <a-input v-model.trim="client.id"  style="width: 300px;"></a-input>
+                <a-input v-model.trim="client.id" style="width: 300px;"></a-input>
             </a-form-item>
             <a-form-item v-if="client.email">
                 <span slot="label">
@@ -55,20 +55,20 @@
             </a-form-item>
             <br>
             <a-form-item v-if="inbound.xtls" label="Flow">
-                <a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                <a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
                     <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
                     <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
                 </a-select>
             </a-form-item>
             <a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow">
-                <a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                <a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="themeSwitcher.darkCardClass">
                     <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
                     <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
                 </a-select>
             </a-form-item>
             <a-form-item>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+                    <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                     <a-tooltip>
                         <template slot="title">
                             0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -88,7 +88,7 @@
             </a-form-item>
             <a-form-item v-else>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+                    <span>{{ i18n "pages.inbounds.expireDate" }}</span>
                     <a-tooltip>
                         <template slot="title">
                             <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -97,7 +97,7 @@
                     </a-tooltip>
                 </span>
                 <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                               :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                               :dropdown-class-name="themeSwitcher.darkCardClass"
                                v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
             </a-form-item>
         </a-collapse-panel>     
@@ -119,8 +119,7 @@
     <a-form layout="inline">
         <a-form-item label="Fallbacks">
             <a-row>
-                <a-button type="primary" size="small"
-                        @click="inbound.settings.addFallback()">
+                <a-button type="primary" size="small" @click="inbound.settings.addFallback()">
                     +
                 </a-button>
             </a-row>

+ 3 - 3
web/html/xui/form/protocol/vmess.html

@@ -61,7 +61,7 @@
             <br>
             <a-form-item>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
+                    <span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                     <a-tooltip>
                         <template slot="title">
                             0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -81,7 +81,7 @@
             </a-form-item>
             <a-form-item v-else>
                 <span slot="label">
-                    <span >{{ i18n "pages.inbounds.expireDate" }}</span>
+                    <span>{{ i18n "pages.inbounds.expireDate" }}</span>
                     <a-tooltip>
                         <template slot="title">
                             <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
@@ -90,7 +90,7 @@
                     </a-tooltip>
                 </span>
                 <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                                :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+                                :dropdown-class-name="themeSwitcher.darkCardClass"
                                 v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
             </a-form-item>
         </a-collapse-panel>     

+ 17 - 17
web/html/xui/form/sniffing.html

@@ -1,21 +1,21 @@
 {{define "form/sniffing"}}
 <a-form layout="inline">
-  <a-form-item>
-            <span slot="label">
-                Sniffing
-                <a-tooltip>
-                    <template slot="title">
-                        <span >{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
-                    </template>
-                    <a-icon type="question-circle" theme="filled"></a-icon>
-                </a-tooltip>
-            </span>
-    <a-switch v-model="inbound.sniffing.enabled"></a-switch>
-  </a-form-item>
-  <a-form-item>
-    <a-checkbox-group v-model="inbound.sniffing.destOverride" v-if="inbound.sniffing.enabled">
-      <a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
-    </a-checkbox-group>
-  </a-form-item>
+    <a-form-item>
+        <span slot="label">
+            Sniffing
+            <a-tooltip>
+                <template slot="title">
+                    <span >{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
+                </template>
+                <a-icon type="question-circle" theme="filled"></a-icon>
+            </a-tooltip>
+        </span>
+        <a-switch v-model="inbound.sniffing.enabled"></a-switch>
+    </a-form-item>
+    <a-form-item>
+        <a-checkbox-group v-model="inbound.sniffing.destOverride" v-if="inbound.sniffing.enabled">
+            <a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
+        </a-checkbox-group>
+    </a-form-item>
 </a-form>
 {{end}}

+ 1 - 1
web/html/xui/form/stream/stream_kcp.html

@@ -1,7 +1,7 @@
 {{define "form/streamKCP"}}
 <a-form layout="inline">
     <a-form-item label='{{ i18n "camouflage" }}'>
-        <a-select v-model="inbound.stream.kcp.type" style="width: 280px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.kcp.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="none">None (Not Camouflage)</a-select-option>
             <a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option>
             <a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option>

+ 2 - 2
web/html/xui/form/stream/stream_quic.html

@@ -1,7 +1,7 @@
 {{define "form/streamQUIC"}}
 <a-form layout="inline">
     <a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
-        <a-select v-model="inbound.stream.quic.security" style="width: 165px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.quic.security" style="width: 165px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="none">none</a-select-option>
             <a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
             <a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
@@ -11,7 +11,7 @@
         <a-input v-model.trim="inbound.stream.quic.key"></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "camouflage" }}'>
-        <a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="none">none (not camouflage)</a-select-option>
             <a-select-option value="srtp">srtp (camouflage video call)</a-select-option>
             <a-select-option value="utp">utp (camouflage BT download)</a-select-option>

+ 1 - 1
web/html/xui/form/stream/stream_settings.html

@@ -2,7 +2,7 @@
 <!-- select stream network -->
 <a-form layout="inline">
     <a-form-item label='{{ i18n "transmission" }}'>
-        <a-select v-model="inbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="tcp">TCP</a-select-option>
             <a-select-option value="kcp">KCP</a-select-option>
             <a-select-option value="ws">WS</a-select-option>

+ 6 - 12
web/html/xui/form/stream/stream_tcp.html

@@ -13,8 +13,7 @@
 </a-form>
 
 <!-- tcp request -->
-<a-form v-if="inbound.stream.tcp.type === 'http'"
-        layout="inline">
+<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
     <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
         <a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
     </a-form-item>
@@ -28,8 +27,7 @@
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
         <a-row>
-            <a-button size="small"
-                      @click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">
+            <a-button size="small" @click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">
                 +
             </a-button>
         </a-row>
@@ -39,8 +37,7 @@
             <a-input style="width: 50%" v-model.trim="header.value"
                      addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
                 <template slot="addonAfter">
-                    <a-button size="small"
-                              @click="inbound.stream.tcp.request.removeHeader(index)">
+                    <a-button size="small" @click="inbound.stream.tcp.request.removeHeader(index)">
                         -
                     </a-button>
                 </template>
@@ -50,8 +47,7 @@
 </a-form>
 
 <!-- tcp response -->
-<a-form v-if="inbound.stream.tcp.type === 'http'"
-        layout="inline">
+<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
     <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
         <a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
     </a-form-item>
@@ -63,8 +59,7 @@
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
         <a-row>
-            <a-button size="small"
-                      @click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">
+            <a-button size="small" @click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">
                 +
             </a-button>
         </a-row>
@@ -74,8 +69,7 @@
             <a-input style="width: 50%" v-model.trim="header.value"
                      addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
                 <template slot="addonAfter">
-                    <a-button size="small"
-                              @click="inbound.stream.tcp.response.removeHeader(index)">
+                    <a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">
                         -
                     </a-button>
                 </template>

+ 2 - 4
web/html/xui/form/stream/stream_ws.html

@@ -10,8 +10,7 @@
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
         <a-row>
-            <a-button size="small"
-                      @click="inbound.stream.ws.addHeader('Host', '')">
+            <a-button size="small" @click="inbound.stream.ws.addHeader('Host', '')">
                 +
             </a-button>
         </a-row>
@@ -21,8 +20,7 @@
             <a-input style="width: 50%" v-model.trim="header.value"
                      addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
                 <template slot="addonAfter">
-                    <a-button size="small"
-                              @click="inbound.stream.ws.removeHeader(index)">
+                    <a-button size="small" @click="inbound.stream.ws.removeHeader(index)">
                         -
                     </a-button>
                 </template>

+ 7 - 7
web/html/xui/form/tls_settings.html

@@ -37,18 +37,18 @@
         <a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
     </a-form-item>
     <a-form-item label="CipherSuites">
-        <a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value="">auto</a-select-option>
             <a-select-option v-for="key in TLS_CIPHER_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
     <a-form-item label="MinVersion">
-        <a-select v-model="inbound.stream.tls.minVersion" style="width: 60px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.tls.minVersion" style="width: 60px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
     <a-form-item label="MaxVersion">
-        <a-select v-model="inbound.stream.tls.maxVersion" style="width: 60px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+        <a-select v-model="inbound.stream.tls.maxVersion" style="width: 60px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
@@ -57,7 +57,7 @@
     </a-form-item>
     <a-form-item label="uTLS">
         <a-select v-model="inbound.stream.tls.settings.fingerprint"
-                  style="width: 170px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                  style="width: 170px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option value=''>None</a-select-option>
             <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
         </a-select>
@@ -145,9 +145,9 @@
     <a-form-item label="xVer">
         <a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number>
     </a-form-item>
-    <a-form-item label="uTLS" >
+    <a-form-item label="uTLS">
         <a-select v-model="inbound.stream.reality.settings.fingerprint" 
-                    style="width: 135px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                    style="width: 135px" :dropdown-class-name="themeSwitcher.darkCardClass">
             <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
         </a-select>
     </a-form-item>
@@ -169,7 +169,7 @@
     <a-form-item label="Public Key">
         <a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 300px"></a-input>
     </a-form-item>
-    <a-form-item >
+    <a-form-item>
         <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Key</a-button>
     </a-form-item>
 </a-form>

+ 8 - 4
web/html/xui/inbound_client_table.html

@@ -29,10 +29,12 @@
     <a-tag v-if="!isClientEnabled(record, client.email)" color="red">{{ i18n "depleted" }}</a-tag>
 </template>                                    
 <template slot="traffic" slot-scope="text, client">
-    <a-tag color="blue">[[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]</a-tag>
+    <a-tag color="blue">
+        [[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]
+    </a-tag>
     <template v-if="client._totalGB > 0">
-        <a-tag v-if="isTrafficExhausted(record, client.email)" color="red">[[client._totalGB]]GB</a-tag>
-        <a-tag v-else color="cyan">[[client._totalGB]]GB</a-tag>
+        <a-tag v-if="isTrafficExhausted(record, client.email)" color="red">[[client._totalGB]] GB</a-tag>
+        <a-tag v-else color="cyan">[[client._totalGB]] GB</a-tag>
     </template>
     <a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
 </template>                                    
@@ -42,7 +44,9 @@
             [[ DateUtil.formatMillis(client._expiryTime) ]]
         </a-tag>
     </template>
-    <a-tag v-else-if="client.expiryTime < 0" color="cyan">[[ client._expiryTime ]] {{ i18n "pages.client.days" }}</a-tag>
+    <a-tag v-else-if="client.expiryTime < 0" color="cyan">
+        [[ client._expiryTime ]] {{ i18n "pages.client.days" }}
+    </a-tag>
     <a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
 </template>
 {{end}}

+ 79 - 69
web/html/xui/inbound_info_modal.html

@@ -3,62 +3,66 @@
     v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}'
     :closable="true"
     :mask-closable="true"
-    :class="siderDrawer.isDarkTheme ? darkClass : ''"
+    :class="themeSwitcher.darkCardClass"
     :footer="null"
     width="600px"
     >
     <table style="margin-bottom: 10px; width: 100%;">
-        <tr><td>
-            <table>
-                <tr><td>{{ i18n "protocol" }}</td><td><a-tag color="green">[[ dbInbound.protocol ]]</a-tag></td></tr>
-                <tr><td>{{ i18n "pages.inbounds.address" }}</td><td><a-tag color="blue">[[ dbInbound.address ]]</a-tag></td></tr>
-                <tr><td>{{ i18n "pages.inbounds.port" }}</td><td><a-tag color="green">[[ dbInbound.port ]]</a-tag></td></tr>
-            </table>
-        </td>
-        <td v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
-            <table>
-                <tr>
-                    <td>{{ i18n "transmission" }}</td><td><a-tag color="green">[[ inbound.network ]]</a-tag></td>
-                </tr>
-            <template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
-                <tr v-if="inbound.host"><td>{{ i18n "host" }}</td><td><a-tag color="green">[[ inbound.host ]]</a-tag></td></tr>
-                <tr v-else><td>{{ i18n "host" }}</td><td><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
-            
-                <tr v-if="inbound.path"><td>{{ i18n "path" }}</td><td><a-tag color="green">[[ inbound.path ]]</a-tag></td></tr>
-                <tr v-else><td>{{ i18n "path" }}</td><td><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
-            </template>
-            
-            <template v-if="inbound.isQuic">
-                <tr><td>quic {{ i18n "encryption" }}</td><td><a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></td></tr>
-                <tr><td>quic {{ i18n "password" }}</td><td><a-tag color="green">[[ inbound.quicKey ]]</a-tag></td></tr>
-                <tr><td>quic {{ i18n "camouflage" }}</td><td><a-tag color="green">[[ inbound.quicType ]]</a-tag></td></tr>
-            </template>
-            
-            <template v-if="inbound.isKcp">
-                <tr><td>kcp {{ i18n "encryption" }}</td><td><a-tag color="green">[[ inbound.kcpType ]]</a-tag></td></tr>
-                <tr><td>kcp {{ i18n "password" }}</td><td><a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></td></tr>
-            </template>
-            
-            <template v-if="inbound.isGrpc">
-                <tr><td>grpc serviceName</td><td><a-tag color="green">[[ inbound.serviceName ]]</a-tag></td></tr>
-                <tr><td>grpc multiMode</td><td><a-tag color="green">[[ inbound.stream.grpc.multiMode ]]</a-tag></td></tr>
-            </template>
-            </table>
-        </td></tr>
-            <tr colspan="2" v-if="dbInbound.hasLink()">
-                <td v-if="inbound.tls">
-                    tls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
-                    tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
-                </td>
-                <td v-else-if="inbound.xtls">
-                    xtls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
-                    xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
-                </td>
-                <td v-else-if="inbound.reality">
-                    reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
-                    reality {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
-                </td>
-                <td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
+        <tr>
+            <td>
+                <table>
+                    <tr><td>{{ i18n "protocol" }}</td><td><a-tag color="green">[[ dbInbound.protocol ]]</a-tag></td></tr>
+                    <tr><td>{{ i18n "pages.inbounds.address" }}</td><td><a-tag color="blue">[[ dbInbound.address ]]</a-tag></td></tr>
+                    <tr><td>{{ i18n "pages.inbounds.port" }}</td><td><a-tag color="green">[[ dbInbound.port ]]</a-tag></td></tr>
+                </table>
+            </td>
+            <td v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
+                <table>
+                    <tr>
+                        <td>{{ i18n "transmission" }}</td><td><a-tag color="green">[[ inbound.network ]]</a-tag></td>
+                    </tr>
+
+                    <template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
+                        <tr v-if="inbound.host"><td>{{ i18n "host" }}</td><td><a-tag color="green">[[ inbound.host ]]</a-tag></td></tr>
+                        <tr v-else><td>{{ i18n "host" }}</td><td><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
+
+                        <tr v-if="inbound.path"><td>{{ i18n "path" }}</td><td><a-tag color="green">[[ inbound.path ]]</a-tag></td></tr>
+                        <tr v-else><td>{{ i18n "path" }}</td><td><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
+                    </template>
+
+                    <template v-if="inbound.isQuic">
+                        <tr><td>quic {{ i18n "encryption" }}</td><td><a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></td></tr>
+                        <tr><td>quic {{ i18n "password" }}</td><td><a-tag color="green">[[ inbound.quicKey ]]</a-tag></td></tr>
+                        <tr><td>quic {{ i18n "camouflage" }}</td><td><a-tag color="green">[[ inbound.quicType ]]</a-tag></td></tr>
+                    </template>
+
+                    <template v-if="inbound.isKcp">
+                        <tr><td>kcp {{ i18n "encryption" }}</td><td><a-tag color="green">[[ inbound.kcpType ]]</a-tag></td></tr>
+                        <tr><td>kcp {{ i18n "password" }}</td><td><a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></td></tr>
+                    </template>
+
+                    <template v-if="inbound.isGrpc">
+                        <tr><td>grpc serviceName</td><td><a-tag color="green">[[ inbound.serviceName ]]</a-tag></td></tr>
+                        <tr><td>grpc multiMode</td><td><a-tag color="green">[[ inbound.stream.grpc.multiMode ]]</a-tag></td></tr>
+                    </template>
+                </table>
+            </td>
+        </tr>
+        <tr colspan="2" v-if="dbInbound.hasLink()">
+            <td v-if="inbound.tls">
+                tls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
+                tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
+            </td>
+            <td v-else-if="inbound.xtls">
+                xtls: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
+                xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
+            </td>
+            <td v-else-if="inbound.reality">
+                reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
+                reality {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
+            </td>
+            <td v-else>
+                tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
             </td>
         </tr>
     </table>
@@ -124,7 +128,8 @@
                 <th>{{ i18n "encryption" }}</th>
                 <th>{{ i18n "password" }}</th>
                 <th>{{ i18n "pages.inbounds.network" }}</th>
-            </tr><tr>
+            </tr>
+            <tr>
                 <td><a-tag color="green">[[ inbound.settings.method ]]</a-tag></td>
                 <td><a-tag color="blue">[[ inbound.settings.password ]]</a-tag></td>
                 <td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
@@ -136,7 +141,8 @@
                 <th>{{ i18n "pages.inbounds.destinationPort" }}</th>
                 <th>{{ i18n "pages.inbounds.network" }}</th>
                 <th>FollowRedirect</th>
-            </tr><tr>
+            </tr>
+            <tr>
                 <td><a-tag color="green">[[ inbound.settings.address ]]</a-tag></td>
                 <td><a-tag color="blue">[[ inbound.settings.port ]]</a-tag></td>
                 <td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
@@ -149,15 +155,18 @@
                 <th>{{ i18n "password" }} Auth</th>
                 <th>{{ i18n "pages.inbounds.enable" }} udp</th>
                 <th>IP</th>
-            </tr><tr>
+            </tr>
+            <tr>
                 <td><a-tag color="green">[[ inbound.settings.auth ]]</a-tag></td>
                 <td><a-tag color="blue">[[ inbound.settings.udp]]</a-tag></td>
                 <td><a-tag color="green">[[ inbound.settings.ip ]]</a-tag></td>
-            </tr><tr v-if="inbound.settings.auth == 'password'">
+            </tr>
+            <tr v-if="inbound.settings.auth == 'password'">
                 <td> </td>
                 <td>{{ i18n "username" }}</td>
                 <td>{{ i18n "password" }}</td>
-            </tr><tr v-for="account,index in inbound.settings.accounts">
+            </tr>
+            <tr v-for="account,index in inbound.settings.accounts">
                 <td><a-tag color="green">[[ index ]]</a-tag></td>
                 <td><a-tag color="blue">[[ account.user ]]</a-tag></td>
                 <td><a-tag color="green">[[ account.pass ]]</a-tag></td>
@@ -169,7 +178,8 @@
                 <th> </th>
                 <th>{{ i18n "username" }}</th>
                 <th>{{ i18n "password" }}</th>
-            </tr><tr v-for="account,index in inbound.settings.accounts">
+            </tr>
+            <tr v-for="account,index in inbound.settings.accounts">
                 <td><a-tag color="green">[[ index ]]</a-tag></td>
                 <td><a-tag color="blue">[[ account.user ]]</a-tag></td>
                 <td><a-tag color="green">[[ account.pass ]]</a-tag></td>
@@ -184,6 +194,7 @@
     </div>
 </a-modal>
 <script>
+
     const infoModal = {
         visible: false,
         inbound: new Inbound(),
@@ -233,42 +244,41 @@
                 return this.infoModal.inbound;
             },
             get isActive() {
-                if(infoModal.clientStats){
+                if (infoModal.clientStats) {
                     return infoModal.clientStats.enable;
                 }
                 return infoModal.dbInbound.isEnable;
             },
             get isEnable() {
-                if(infoModal.clientSettings){
+                if (infoModal.clientSettings) {
                     return infoModal.clientSettings.enable;
                 }
                 return infoModal.dbInbound.isEnable;
             },
             get subBase() {
-                return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port:"") + basePath + "sub/";
+                return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "") + basePath + "sub/";
             },
             get tgBase() {
                 return "https://t.me/"
             },
         },
         methods: {
-            copyTextToClipboard(elmentId,content) {
+            copyTextToClipboard(elmentId, content) {
                 this.infoModal.clipboard = new ClipboardJS('#' + elmentId, {
-                        text: () => content,
-                    });
-                this.infoModal.clipboard.on('success', () => { 
+                    text: () => content,
+                });
+                this.infoModal.clipboard.on('success', () => {
                     app.$message.success('{{ i18n "copied" }}')
                     this.infoModal.clipboard.destroy();
                 });
             },
             statsColor(stats) {
-                if(!stats) return 'blue'
-                if(stats['total'] === 0) return 'blue'
-                else if(stats['total'] > 0 && (stats['down']+stats['up']) < stats['total']) return 'cyan'
+                if (!stats) return 'blue'
+                if (stats['total'] === 0) return 'blue'
+                else if (stats['total'] > 0 && (stats['down'] + stats['up']) < stats['total']) return 'cyan'
                 else return 'red'
             }
         },
-        
     });
 
 </script>

+ 8 - 8
web/html/xui/inbound_modal.html

@@ -1,7 +1,7 @@
 {{define "inboundModal"}}
 <a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title" @ok="inModal.ok"
          :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
-         :class="siderDrawer.isDarkTheme ? darkClass : ''"
+         :class="themeSwitcher.darkCardClass"
          :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
     {{template "form/inbound"}}
 </a-modal>
@@ -19,7 +19,7 @@
         ok() {
             ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
         },
-        show({ title='', okText='{{ i18n "sure" }}', inbound=null, dbInbound=null, confirm=(inbound, dbInbound)=>{}, isEdit=false  }) {
+        show({ title = '', okText = '{{ i18n "sure" }}', inbound = null, dbInbound = null, confirm = (inbound, dbInbound) => {}, isEdit = false }) {
             this.title = title;
             this.okText = okText;
             if (inbound) {
@@ -44,7 +44,7 @@
             inModal.confirmLoading = loading;
         },
         getClients(protocol, clientSettings) {
-            switch(protocol){
+            switch (protocol) {
                 case Protocols.VMESS: return clientSettings.vmesses;
                 case Protocols.VLESS: return clientSettings.vlesses;
                 case Protocols.TROJAN: return clientSettings.trojans;
@@ -87,7 +87,7 @@
             get delayedExpireDays() {
                 return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
             },
-            set delayedExpireDays(days){
+            set delayedExpireDays(days) {
                 this.client.expiryTime = -86400000 * days;
             },
         },
@@ -100,15 +100,15 @@
                     this.inModal.inbound.reality = false;
                 }
             },
-            setDefaultCertData(){
+            setDefaultCertData() {
                 inModal.inbound.stream.tls.certs[0].certFile = app.defaultCert;
                 inModal.inbound.stream.tls.certs[0].keyFile = app.defaultKey;
             },
-            setDefaultCertXtls(){
+            setDefaultCertXtls() {
                 inModal.inbound.stream.xtls.certs[0].certFile = app.defaultCert;
                 inModal.inbound.stream.xtls.certs[0].keyFile = app.defaultKey;
             },
-            async getNewX25519Cert(){
+            async getNewX25519Cert() {
                 inModal.loading(true);
                 const msg = await HttpUtil.post('/server/getNewX25519Cert');
                 inModal.loading(false);
@@ -122,7 +122,7 @@
                 var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
                 var string = '';
                 var len = 6 + Math.floor(Math.random() * 5);
-                for(var ii=0; ii<len; ii++){
+                for (var ii = 0; ii < len; ii++) {
                     string += chars[Math.floor(Math.random() * chars.length)];
                 }
                 client.email = string;

+ 71 - 67
web/html/xui/inbounds.html

@@ -12,10 +12,11 @@
         margin-top: 10px;
     }
 </style>
+
 <body>
 <a-layout id="app" v-cloak>
     {{ template "commonSider" . }}
-    <a-layout id="content-layout" :style="siderDrawer.isDarkTheme ? bgDarkStyle : ''">
+    <a-layout id="content-layout" :style="themeSwitcher.bgStyle">
         <a-layout-content>
             <a-spin :spinning="spinning" :delay="500" tip="loading">
                 <transition name="list" appear>
@@ -24,7 +25,7 @@
                     </a-tag>
                 </transition>
                 <transition name="list" appear>
-                    <a-card hoverable style="margin-bottom: 20px;" :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                    <a-card hoverable style="margin-bottom: 20px;" :class="themeSwitcher.darkCardClass">
                         <a-row>
                             <a-col :xs="24" :sm="24" :lg="12">
                                 {{ i18n "pages.inbounds.totalDownUp" }}:
@@ -41,19 +42,19 @@
                             <a-col :xs="24" :sm="24" :lg="12">
                                 {{ i18n "clients" }}:
                                 <a-tag color="green">[[ total.clients ]]</a-tag>
-                                <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.darkClass">
                                     <template slot="content">
                                         <p v-for="clientEmail in total.deactive">[[ clientEmail ]]</p>
                                     </template>
                                     <a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
                                 </a-popover>
-                                <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.darkClass">
                                     <template slot="content">
                                         <p v-for="clientEmail in total.depleted">[[ clientEmail ]]</p>
                                     </template>
                                     <a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag>
                                 </a-popover>
-                                <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.darkClass">
                                     <template slot="content">
                                         <p v-for="clientEmail in total.expiring">[[ clientEmail ]]</p>
                                     </template>
@@ -64,14 +65,14 @@
                     </a-card>
                 </transition>
                 <transition name="list" appear>
-                    <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                    <a-card hoverable :class="themeSwitcher.darkCardClass">
                         <div slot="title">
                             <a-row>
                                 <a-col :xs="24" :sm="24" :lg="12">
                                     <a-button type="primary" icon="plus" @click="openAddInbound">{{ i18n "pages.inbounds.addInbound" }}</a-button>
                                     <a-dropdown :trigger="['click']">
                                         <a-button type="primary" icon="menu">{{ i18n "pages.inbounds.generalActions" }}</a-button>
-                                        <a-menu slot="overlay" @click="a => generalActions(a)" :theme="siderDrawer.theme">
+                                        <a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme">
                                             <a-menu-item key="export">
                                                 <a-icon type="export"></a-icon>
                                                 {{ i18n "pages.inbounds.export" }}
@@ -96,7 +97,7 @@
                                               style="width: 65px;"
                                               v-if="isRefreshEnabled"
                                               @change="changeRefreshInterval"
-                                              :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                                              :dropdown-class-name="themeSwitcher.darkCardClass">
                                         <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
                                     </a-select>
                                     <a-icon type="sync" :spin="refreshing" @click="manualRefresh" style="margin: 0 5px;"></a-icon>
@@ -115,7 +116,7 @@
                                 <a-icon type="edit" style="font-size: 22px" @click="openEditInbound(dbInbound.id);"></a-icon>
                                 <a-dropdown :trigger="['click']">
                                     <a @click="e => e.preventDefault()">{{ i18n "pages.inbounds.operate" }}</a>
-                                    <a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="siderDrawer.theme">
+                                    <a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme">
                                         <a-menu-item key="edit">
                                             <a-icon type="edit"></a-icon>
                                             {{ i18n "edit" }}
@@ -174,19 +175,19 @@
                             <template slot="clients" slot-scope="text, dbInbound">
                                 <template v-if="clientCount[dbInbound.id]">
                                     <a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
-                                    <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                    <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.darkClass">
                                         <template slot="content">
                                             <p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
                                         </template>
                                         <a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
                                     </a-popover>
-                                    <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                    <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.darkClass">
                                         <template slot="content">
                                             <p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
                                         </template>
                                         <a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
                                     </a-popover>
-                                    <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="siderDrawer.isDarkTheme ? 'ant-dark' : ''">
+                                    <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.darkClass">
                                         <template slot="content">
                                             <p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
                                         </template>
@@ -244,6 +245,7 @@
     </a-layout>
 </a-layout>
 {{template "js" .}}
+{{template "component/themeSwitcher" .}}
 <script>
 
     const columns = [{
@@ -315,7 +317,7 @@
         delimiters: ['[[', ']]'],
         el: '#app',
         data: {
-            siderDrawer,
+            themeSwitcher,
             spinning: false,
             inbounds: [],
             dbInbounds: [],
@@ -331,7 +333,7 @@
             refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
         },
         methods: {
-            loading(spinning=true) {
+            loading(spinning = true) {
                 this.spinning = spinning;
             },
             async getDBInbounds() {
@@ -361,29 +363,29 @@
                     to_inbound = dbInbound.toInbound()
                     this.inbounds.push(to_inbound);
                     this.dbInbounds.push(dbInbound);
-                    if([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol) ){
-                        this.clientCount[inbound.id] = this.getClientCounts(inbound,to_inbound);
+                    if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol)) {
+                        this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
                     }
                 }
                 this.searchInbounds(this.searchKey);
             },
-            getClientCounts(dbInbound,inbound){
-                let clientCount = 0,active = [], deactive = [], depleted = [], expiring = [];
+            getClientCounts(dbInbound, inbound) {
+                let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [];
                 clients = this.getClients(dbInbound.protocol, inbound.settings);
                 clientStats = dbInbound.clientStats
                 now = new Date().getTime()
-                if(clients){
+                if (clients) {
                     clientCount = clients.length;
-                    if(dbInbound.enable){
+                    if (dbInbound.enable) {
                         clients.forEach(client => {
                             client.enable ? active.push(client.email) : deactive.push(client.email);
                         });
                         clientStats.forEach(client => {
-                            if(!client.enable) {
+                            if (!client.enable) {
                                 depleted.push(client.email);
                             } else {
-                                if ((client.expiryTime > 0 && (client.expiryTime-now < this.expireDiff)) ||
-                                (client.total > 0 && (client.total-(client.up+client.down) < this.trafficDiff ))) expiring.push(client.email);
+                                if ((client.expiryTime > 0 && (client.expiryTime - now < this.expireDiff)) ||
+                                    (client.total > 0 && (client.total - (client.up + client.down) < this.trafficDiff))) expiring.push(client.email);
                             }
                         });
                     } else {
@@ -409,10 +411,10 @@
                         if (ObjectUtil.deepSearch(inbound, key)) {
                             const newInbound = new DBInbound(inbound);
                             const inboundSettings = JSON.parse(inbound.settings);
-                            if (inboundSettings.hasOwnProperty('clients')){
+                            if (inboundSettings.hasOwnProperty('clients')) {
                                 const searchedSettings = { "clients": [] };
                                 inboundSettings.clients.forEach(client => {
-                                    if (ObjectUtil.deepSearch(client, key)){
+                                    if (ObjectUtil.deepSearch(client, key)) {
                                         searchedSettings.clients.push(client);
                                     }
                                 });
@@ -423,7 +425,7 @@
                     });
                 }
             },
-            generalActions(action){
+            generalActions(action) {
                 switch (action.key) {
                     case "export":
                         this.exportAllLinks();
@@ -476,7 +478,7 @@
                         break;
                 }
             },
-			openCloneInbound(dbInbound) {
+            openCloneInbound(dbInbound) {
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.cloneInbound"}} \"' + dbInbound.remark + '\"',
                     content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
@@ -621,21 +623,21 @@
                     isEdit: true
                 });
             },
-            findIndexOfClient(clients,client) {
+            findIndexOfClient(clients, client) {
                 firstKey = Object.keys(client)[0];
                 return clients.findIndex(c => c[firstKey] === client[firstKey]);
             },
             async addClient(clients, dbInboundId) {
                 const data = {
                     id: dbInboundId,
-                    settings: '{"clients": [' + clients.toString() +']}',
+                    settings: '{"clients": [' + clients.toString() + ']}',
                 };
                 await this.submit(`/xui/inbound/addClient`, data);
             },
             async updateClient(client, dbInboundId, clientId) {
                 const data = {
                     id: dbInboundId,
-                    settings: '{"clients": [' + client.toString() +']}',
+                    settings: '{"clients": [' + client.toString() + ']}',
                 };
                 await this.submit(`/xui/inbound/updateClient/${clientId}`, data);
             },
@@ -644,7 +646,7 @@
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.resetTraffic"}}',
                     content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => {
@@ -659,26 +661,26 @@
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.deleteInbound"}}',
                     content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "delete"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => this.submit('/xui/inbound/del/' + dbInboundId),
                 });
             },
-            delClient(dbInboundId,client) {
+            delClient(dbInboundId, client) {
                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
-                clientId = this.getClientId(dbInbound.protocol,client);
+                clientId = this.getClientId(dbInbound.protocol, client);
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.deleteInbound"}}',
                     content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "delete"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => this.submit(`/xui/inbound/${dbInboundId}/delClient/${clientId}`),
                 });
             },
             getClients(protocol, clientSettings) {
-                switch(protocol){
+                switch (protocol) {
                     case Protocols.VMESS: return clientSettings.vmesses;
                     case Protocols.VLESS: return clientSettings.vlesses;
                     case Protocols.TROJAN: return clientSettings.trojans;
@@ -687,7 +689,7 @@
                 }
             },
             getClientId(protocol, client) {
-                switch(protocol){
+                switch (protocol) {
                     case Protocols.TROJAN: return client.password;
                     case Protocols.SHADOWSOCKS: return client.email;
                     default: return client.id;
@@ -711,8 +713,8 @@
                 clients = this.getClients(dbInbound.protocol, inbound.settings);
                 index = this.findIndexOfClient(clients, client);
                 clients[index].enable = !clients[index].enable;
-                clientId = this.getClientId(dbInbound.protocol,clients[index]);
-                await this.updateClient(clients[index],dbInboundId, clientId);
+                clientId = this.getClientId(dbInbound.protocol, clients[index]);
+                await this.updateClient(clients[index], dbInboundId, clientId);
                 this.loading(false);
             },
             async submit(url, data) {
@@ -722,31 +724,31 @@
                 }
             },
             getInboundClients(dbInbound) {
-                if(dbInbound.protocol == Protocols.VLESS) {
+                if (dbInbound.protocol == Protocols.VLESS) {
                     return dbInbound.toInbound().settings.vlesses;
-                } else if(dbInbound.protocol == Protocols.VMESS) {
+                } else if (dbInbound.protocol == Protocols.VMESS) {
                     return dbInbound.toInbound().settings.vmesses;
-                } else if(dbInbound.protocol == Protocols.TROJAN) {
+                } else if (dbInbound.protocol == Protocols.TROJAN) {
                     return dbInbound.toInbound().settings.trojans;
-                } else if(dbInbound.protocol == Protocols.SHADOWSOCKS) {
+                } else if (dbInbound.protocol == Protocols.SHADOWSOCKS) {
                     return dbInbound.toInbound().settings.shadowsockses;
                 }
             },
-            resetClientTraffic(client,dbInboundId) {
+            resetClientTraffic(client, dbInboundId) {
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.resetTraffic"}}',
                     content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
-                    onOk: () => this.submit('/xui/inbound/' + dbInboundId + '/resetClientTraffic/'+ client.email),
+                    onOk: () => this.submit('/xui/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email),
                 })
             },
             resetAllTraffic() {
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.resetAllTrafficTitle"}}',
                     content: '{{ i18n "pages.inbounds.resetAllTrafficContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => this.submit('/xui/inbound/resetAllTraffics'),
@@ -754,9 +756,9 @@
             },
             resetAllClientTraffics(dbInboundId) {
                 this.$confirm({
-                    title: dbInboundId>0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficTitle"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficTitle"}}',
-                    content: dbInboundId>0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficContent"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    title: dbInboundId > 0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficTitle"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficTitle"}}',
+                    content: dbInboundId > 0 ? '{{ i18n "pages.inbounds.resetInboundClientTrafficContent"}}' : '{{ i18n "pages.inbounds.resetAllClientTrafficContent"}}',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => this.submit('/xui/inbound/resetAllClientTraffics/' + dbInboundId),
@@ -766,7 +768,7 @@
                 this.$confirm({
                     title: '{{ i18n "pages.inbounds.delDepletedClientsTitle"}}',
                     content: '{{ i18n "pages.inbounds.delDepletedClientsContent"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: () => this.submit('/xui/inbound/delDepletedClients/' + dbInboundId),
@@ -776,17 +778,17 @@
                 return dbInbound.toInbound().isExpiry(index)
             },
             getUpStats(dbInbound, email) {
-                if(email.length == 0) return 0
+                if (email.length == 0) return 0
                 clientStats = dbInbound.clientStats.find(stats => stats.email === email)
                 return clientStats ? clientStats.up : 0
             },
             getDownStats(dbInbound, email) {
-                if(email.length == 0) return 0
+                if (email.length == 0) return 0
                 clientStats = dbInbound.clientStats.find(stats => stats.email === email)
                 return clientStats ? clientStats.down : 0
             },
             isTrafficExhausted(dbInbound, email) {
-                if(email.length == 0) return false
+                if (email.length == 0) return false
                 clientStats = dbInbound.clientStats.find(stats => stats.email === email)
                 return clientStats ? clientStats.down + clientStats.up > clientStats.total : false
             },
@@ -794,28 +796,28 @@
                 clientStats = dbInbound.clientStats ? dbInbound.clientStats.find(stats => stats.email === email) : null
                 return clientStats ? clientStats['enable'] : true
             },
-            isRemovable(dbInbound_id){
+            isRemovable(dbInbound_id) {
                 return this.getInboundClients(this.dbInbounds.find(row => row.id === dbInbound_id)).length > 1
             },
             inboundLinks(dbInboundId) {
                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
-                txtModal.show('{{ i18n "pages.inbounds.export"}}',dbInbound.genInboundLinks,dbInbound.remark);
+                txtModal.show('{{ i18n "pages.inbounds.export"}}', dbInbound.genInboundLinks, dbInbound.remark);
             },
-			exportAllLinks() {
+            exportAllLinks() {
                 let copyText = '';
                 for (const dbInbound of this.dbInbounds) {
                     copyText += dbInbound.genInboundLinks
                 }
-                txtModal.show('{{ i18n "pages.inbounds.export"}}',copyText,'All-Inbounds');
+                txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText, 'All-Inbounds');
             },
             async startDataRefreshLoop() {
                 while (this.isRefreshEnabled) {
-                try {
-                    await this.getDBInbounds();
-                } catch (e) {
-                    console.error(e);
-                }
-                await PromiseUtil.sleep(this.refreshInterval);
+                    try {
+                        await this.getDBInbounds();
+                    } catch (e) {
+                        console.error(e);
+                    }
+                    await PromiseUtil.sleep(this.refreshInterval);
                 }
             },
             toggleRefresh() {
@@ -824,11 +826,11 @@
                     this.startDataRefreshLoop();
                 }
             },
-            changeRefreshInterval(){
+            changeRefreshInterval() {
                 localStorage.setItem("refreshInterval", this.refreshInterval);
             },
-            async manualRefresh(){
-                if(!this.refreshing){
+            async manualRefresh() {
+                if (!this.refreshing) {
                     this.spinning = true;
                     await this.getDBInbounds();
                     this.spinning = false;
@@ -876,6 +878,7 @@
             }
         },
     });
+
 </script>
 
 {{template "inboundModal"}}
@@ -885,5 +888,6 @@
 {{template "inboundInfoModal"}}
 {{template "clientsModal"}}
 {{template "clientsBulkModal"}}
+
 </body>
 </html>

+ 28 - 26
web/html/xui/index.html

@@ -13,32 +13,33 @@
     }
 
     .ant-card-dark h2 {
-        color: hsla(0,0%,100%,.65);
+        color: hsla(0, 0%, 100%, .65);
     }
 </style>
+
 <body>
 <a-layout id="app" v-cloak>
     {{ template "commonSider" . }}
-    <a-layout id="content-layout" :style="siderDrawer.isDarkTheme ? bgDarkStyle : ''">
+    <a-layout id="content-layout" :style="themeSwitcher.bgStyle">
         <a-layout-content>
             <a-spin :spinning="spinning" :delay="200" :tip="loadingTip"/>
             <transition name="list" appear>
                 <a-row>
-                    <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                    <a-card hoverable :class="themeSwitcher.darkCardClass">
                         <a-row>
                             <a-col :sm="24" :md="12">
                                 <a-row>
                                     <a-col :span="12" style="text-align: center">
                                         <a-progress type="dashboard" status="normal"
                                                     :stroke-color="status.cpu.color"
-                                                    :class="siderDrawer.isDarkTheme ? darkClass : ''"
+                                                    :class="themeSwitcher.darkCardClass"
                                                     :percent="status.cpu.percent"></a-progress>
                                         <div>CPU</div>
                                     </a-col>
                                     <a-col :span="12" style="text-align: center">
                                         <a-progress type="dashboard" status="normal"
                                                     :stroke-color="status.mem.color"
-                                                    :class="siderDrawer.isDarkTheme ? darkClass : ''"
+                                                    :class="themeSwitcher.darkCardClass"
                                                     :percent="status.mem.percent"></a-progress>
                                         <div>
                                             {{ i18n "pages.index.memory"}}: [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
@@ -51,7 +52,7 @@
                                     <a-col :span="12" style="text-align: center">
                                         <a-progress type="dashboard" status="normal"
                                                     :stroke-color="status.swap.color"
-                                                    :class="siderDrawer.isDarkTheme ? darkClass : ''"
+                                                    :class="themeSwitcher.darkCardClass"
                                                     :percent="status.swap.percent"></a-progress>
                                         <div>
                                             Swap: [[ sizeFormat(status.swap.current) ]] / [[ sizeFormat(status.swap.total) ]]
@@ -60,7 +61,7 @@
                                     <a-col :span="12" style="text-align: center">
                                         <a-progress type="dashboard" status="normal"
                                                     :stroke-color="status.disk.color"
-                                                    :class="siderDrawer.isDarkTheme ? darkClass : ''"
+                                                    :class="themeSwitcher.darkCardClass"
                                                     :percent="status.disk.percent"></a-progress>
                                         <div>
                                             {{ i18n "pages.index.hard"}}: [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
@@ -75,14 +76,14 @@
             <transition name="list" appear>
                 <a-row>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             3x-ui: <a href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a>
                             Xray: <a-tag color="green" style="cursor: pointer;" @click="openSelectV2rayVersion">v[[ status.xray.version ]]</a-tag>
                             Telegram: <a href="https://t.me/panel3xui" target="_blank"><a-tag color="green">@panel3xui</a-tag></a>
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             {{ i18n "pages.index.operationHours" }}:
                             <a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
                             <a-tooltip>
@@ -94,7 +95,7 @@
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             {{ i18n "pages.index.xrayStatus" }}:
                             <a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
                             <a-tooltip v-if="status.xray.state === State.Error">
@@ -109,7 +110,7 @@
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             {{ i18n "menu.link" }}:
                             <a-tag color="blue" style="cursor: pointer;" @click="openLogs(20)">{{ i18n "pages.index.logs" }}</a-tag>
                             <a-tag color="blue" style="cursor: pointer;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
@@ -117,12 +118,12 @@
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             {{ i18n "pages.index.systemLoad" }}: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             TCP / UDP {{ i18n "pages.index.connectionCount" }}: [[ status.tcpCount ]] / [[ status.udpCount ]]
                             <a-tooltip>
                                 <template slot="title">
@@ -133,7 +134,7 @@
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             <a-row>
                                 <a-col :span="12">
                                     <a-icon type="arrow-up"></a-icon>
@@ -159,7 +160,7 @@
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :md="12">
-                        <a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                        <a-card hoverable :class="themeSwitcher.darkCardClass">
                             <a-row>
                                 <a-col :span="12">
                                     <a-icon type="cloud-upload"></a-icon>
@@ -191,7 +192,7 @@
 
     <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}'
              :closable="true" @ok="() => versionModal.visible = false"
-             :class="siderDrawer.isDarkTheme ? darkClass : ''"
+             :class="themeSwitcher.darkCardClass"
              footer="">
         <h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
         <h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
@@ -205,7 +206,7 @@
 
     <a-modal id="log-modal" v-model="logModal.visible" title="X-UI logs"
              :closable="true" @ok="() => logModal.visible = false" @cancel="() => logModal.visible = false"
-             :class="siderDrawer.isDarkTheme ? darkClass : ''"
+             :class="themeSwitcher.darkCardClass"
              width="800px"
              footer="">
         <a-form layout="inline">
@@ -213,7 +214,7 @@
                 <a-select v-model="logModal.rows"
                 style="width: 80px"
                 @change="openLogs(logModal.rows)"
-                :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+                :dropdown-class-name="themeSwitcher.darkCardClass">
                     <a-select-option value="10">10</a-select-option>
                     <a-select-option value="20">20</a-select-option>
                     <a-select-option value="50">50</a-select-option>
@@ -235,7 +236,7 @@
     </a-modal>
 
     <a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
-            :closable="true" :class="siderDrawer.isDarkTheme ? darkClass : ''"
+            :closable="true" :class="themeSwitcher.darkCardClass"
             @ok="() => backupModal.hide()" @cancel="() => backupModal.hide()">
         <p style="color: inherit; font-size: 16px; padding: 4px 2px;">
             <a-icon type="warning" style="color: inherit; font-size: 20px;"></a-icon>
@@ -253,6 +254,7 @@
 
 </a-layout>
 {{template "js" .}}
+{{template "component/themeSwitcher" .}}
 {{template "textModal"}}
 <script>
 
@@ -295,13 +297,13 @@
             this.disk = new CurTotal(0, 0);
             this.loads = [0, 0, 0];
             this.mem = new CurTotal(0, 0);
-            this.netIO = {up: 0, down: 0};
-            this.netTraffic = {sent: 0, recv: 0};
+            this.netIO = { up: 0, down: 0 };
+            this.netTraffic = { sent: 0, recv: 0 };
             this.swap = new CurTotal(0, 0);
             this.tcpCount = 0;
             this.udpCount = 0;
             this.uptime = 0;
-            this.xray = {state: State.Stop, errorMsg: "", version: "", color: ""};
+            this.xray = { state: State.Stop, errorMsg: "", version: "", color: "" };
 
             if (data == null) {
                 return;
@@ -386,7 +388,7 @@
         delimiters: ['[[', ']]'],
         el: '#app',
         data: {
-            siderDrawer,
+            themeSwitcher,
             status: new Status(),
             versionModal,
             logModal,
@@ -422,7 +424,7 @@
                     title: '{{ i18n "pages.index.xraySwitchVersionDialog"}}',
                     content: '{{ i18n "pages.index.xraySwitchVersionDialogDesc"}}' + ` ${version}?`,
                     okText: '{{ i18n "confirm"}}',
-                    class: siderDrawer.isDarkTheme ? darkClass : '',
+                    class: themeSwitcher.darkCardClass,
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: async () => {
                         versionModal.hide();
@@ -448,9 +450,9 @@
                     return;
                 }
             },
-            async openLogs(rows){
+            async openLogs(rows) {
                 this.loading(true);
-                const msg = await HttpUtil.post('server/logs/'+rows);
+                const msg = await HttpUtil.post('server/logs/' + rows);
                 this.loading(false);
                 if (!msg.success) {
                     return;

+ 731 - 728
web/html/xui/settings.html

@@ -26,757 +26,760 @@
 </style>
 
 <body>
-    <a-layout id="app" v-cloak>
-        {{ template "commonSider" . }}
-        <a-layout id="content-layout" :style="siderDrawer.isDarkTheme ? bgDarkStyle : ''">
-            <a-layout-content>
-                <a-spin :spinning="spinning" :delay="500" tip="loading">
-                    <a-space direction="vertical">
-                        <a-space direction="horizontal">
-                            <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
-                            <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
-                        </a-space>
-                        <a-tabs default-active-key="1" :class="siderDrawer.isDarkTheme ? darkClass : ''" >
-                            <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings"}}'>
-                                <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model="allSetting.webPort" :min="0"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}'  v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}'  v-model="allSetting.expireDiff" :min="0"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}'  v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
-                                    <a-list-item>
-                                        <a-row style="padding: 20px">
-                                            <a-col :lg="24" :xl="12">
-                                                <a-list-item-meta title="Language" />
-                                            </a-col>
+<a-layout id="app" v-cloak>
+    {{ template "commonSider" . }}
+    <a-layout id="content-layout" :style="themeSwitcher.bgStyle">
+        <a-layout-content>
+            <a-spin :spinning="spinning" :delay="500" tip="loading">
+                <a-space direction="vertical">
+                    <a-space direction="horizontal">
+                        <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
+                        <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
+                    </a-space>
+                    <a-tabs default-active-key="1" :class="themeSwitcher.darkCardClass" >
+                        <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings"}}'>
+                            <a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
+                                <setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model="allSetting.webPort" :min="0"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
+                                <setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}' v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item>
+                                <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item>
+                                <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
+                                <a-list-item>
+                                    <a-row style="padding: 20px">
+                                        <a-col :lg="24" :xl="12">
+                                            <a-list-item-meta title="Language" />
+                                        </a-col>
 
-                                            <a-col :lg="24" :xl="12">
-                                                <template>
-                                                    <a-select
-                                                        ref="selectLang"
-                                                        v-model="lang"
-                                                        @change="setLang(lang)"
-                                                        :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
-                                                        style="width: 100%"
-                                                    >
-                                                        <a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs">
-                                                            <span role="img" aria-label="l.name" v-text="l.icon"></span>&nbsp;&nbsp;<span v-text="l.name"></span>
-                                                        </a-select-option>
-                                                    </a-select>
-                                                </template>
-                                            </a-col>
-                                        </a-row>
-                                    </a-list-item>
-                                </a-list>
-                            </a-tab-pane>
+                                        <a-col :lg="24" :xl="12">
+                                            <template>
+                                                <a-select
+                                                    ref="selectLang"
+                                                    v-model="lang"
+                                                    @change="setLang(lang)"
+                                                    :dropdown-class-name="themeSwitcher.darkCardClass"
+                                                    style="width: 100%"
+                                                >
+                                                    <a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs">
+                                                        <span role="img" aria-label="l.name" v-text="l.icon"></span>&nbsp;&nbsp;<span v-text="l.name"></span>
+                                                    </a-select-option>
+                                                </a-select>
+                                            </template>
+                                        </a-col>
+                                    </a-row>
+                                </a-list-item>
+                            </a-list>
+                        </a-tab-pane>
 
-                            <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;">
-                                <a-tabs default-active-key="sec-1" :class="siderDrawer.isDarkTheme ? darkClass : ''">
-                                    <a-tab-pane key="sec-1" tab='{{ i18n "pages.settings.security.admin"}}'>
-                                        <a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
-                                            <a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
-                                                <a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
-                                            </a-form-item>
-                                            <a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
-                                                <a-input type="password" v-model="user.oldPassword" style="max-width: 300px"></a-input>
-                                            </a-form-item>
-                                            <a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
-                                                <a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
-                                            </a-form-item>
-                                            <a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
-                                                <a-input type="password" v-model="user.newPassword" style="max-width: 300px"></a-input>
-                                            </a-form-item>
-                                            <a-form-item>
-                                                <a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
-                                            </a-form-item>
-                                        </a-form>
-                                    </a-tab-pane>
-                                    <a-tab-pane key="sec-2" tab='{{ i18n "pages.settings.security.secret"}}'>
-                                        <a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
-                                            <a-list-item style="padding: 20px">
-                                                <a-row>
-                                                    <a-col :lg="24" :xl="12">
-                                                        <a-list-item-meta title='{{ i18n "pages.settings.security.loginSecurity" }}' description='{{ i18n "pages.settings.security.loginSecurityDesc" }}'/>
-                                                    </a-col>
-                                                    <a-col :lg="24" :xl="12">
-                                                        <template>
-                                                            <a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
-                                                        </template>
-                                                    </a-col>
+                        <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;">
+                            <a-tabs default-active-key="sec-1" :class="themeSwitcher.darkCardClass">
+                                <a-tab-pane key="sec-1" tab='{{ i18n "pages.settings.security.admin"}}'>
+                                    <a-form :style="'padding: 20px;' + themeSwitcher.textStyle">
+                                        <a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
+                                            <a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
+                                        </a-form-item>
+                                        <a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
+                                            <a-input type="password" v-model="user.oldPassword" style="max-width: 300px"></a-input>
+                                        </a-form-item>
+                                        <a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
+                                            <a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
+                                        </a-form-item>
+                                        <a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
+                                            <a-input type="password" v-model="user.newPassword" style="max-width: 300px"></a-input>
+                                        </a-form-item>
+                                        <a-form-item>
+                                            <a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
+                                        </a-form-item>
+                                    </a-form>
+                                </a-tab-pane>
+                                <a-tab-pane key="sec-2" tab='{{ i18n "pages.settings.security.secret"}}'>
+                                    <a-form :style="'padding: 20px;' + themeSwitcher.textStyle">
+                                        <a-list-item style="padding: 20px">
+                                            <a-row>
+                                                <a-col :lg="24" :xl="12">
+                                                    <a-list-item-meta title='{{ i18n "pages.settings.security.loginSecurity" }}' description='{{ i18n "pages.settings.security.loginSecurityDesc" }}' />
+                                                </a-col>
+                                                <a-col :lg="24" :xl="12">
+                                                    <template>
+                                                        <a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
+                                                    </template>
+                                                </a-col>
+                                            </a-row>
+                                        </a-list-item>
+                                        <a-list-item style="padding: 20px">
+                                            <a-row>
+                                                <a-col :lg="24" :xl="12">
+                                                    <a-list-item-meta title='{{ i18n "pages.settings.security.secretToken" }}' description='{{ i18n "pages.settings.security.secretTokenDesc" }}' />
+                                                </a-col>
+                                                <a-col :lg="24" :xl="12">
+                                                    <svg 
+                                                        @click="getNewSecret"
+                                                        xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"><path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/><path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> 
+                                                    </svg>
+                                                    <template>
+                                                        <a-textarea type="text" id='token' :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
+                                                    </template>
+                                                </a-col>
+                                            </a-row>
+                                        </a-list-item>
+                                        <a-button type="primary" @click="updateSecret">{{ i18n "confirm" }}</a-button>
+                                    </a-form>
+                                </a-tab-pane>
+                            </a-tabs>
+                        </a-tab-pane>
+
+                        <a-tab-pane key="3" tab='{{ i18n "pages.settings.xrayConfiguration"}}'>
+                            <a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
+                                <a-divider style="padding: 20px;">{{ i18n "pages.settings.actions"}}</a-divider>
+                                <a-space direction="horizontal" style="padding: 0px 20px">
+                                    <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
+                                </a-space>
+                                <a-divider style="padding: 20px;">{{ i18n "pages.settings.templates.title"}} </a-divider>
+                                
+                                <a-tabs default-active-key="tpl-1" :class="themeSwitcher.darkCardClass" style="padding: 20px 20px;">
+                                    <a-tab-pane key="tpl-1" tab='{{ i18n "pages.settings.templates.basicTemplate"}}' style="padding-top: 20px;">
+                                        <a-collapse>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.generalConfigs"}}'>
+                                                <a-row :xs="24" :sm="24" :lg="12">
+                                                    <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                        <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                        {{ i18n "pages.settings.templates.generalConfigsDesc" }}
+                                                    </h2>
+                                                </a-row>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigTorrent"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTorrentDesc"}}' v-model="torrentSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPrivateIpDesc"}}' v-model="privateIpSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigAds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigAdsDesc"}}' v-model="AdsSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPorn"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPornDesc"}}' v-model="PornSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.countryConfigs"}}'>
+                                                <a-row :xs="24" :sm="24" :lg="12">
+                                                    <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                        <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                        {{ i18n "pages.settings.templates.countryConfigsDesc" }}
+                                                    </h2>
+                                                </a-row>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRIpDesc"}}' v-model="IRIpSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRDomainDesc"}}' v-model="IRDomainSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaIpDesc"}}' v-model="ChinaIpSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.ipv4Configs"}}'>
+                                                <a-row :xs="24" :sm="24" :lg="12">
+                                                    <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                        <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                        {{ i18n "pages.settings.templates.ipv4ConfigsDesc" }}
+                                                    </h2>
                                                 </a-row>
-                                            </a-list-item>
-                                            <a-list-item style="padding: 20px">
-                                                <a-row>
-                                                    <a-col :lg="24" :xl="12">
-                                                        <a-list-item-meta title='{{ i18n "pages.settings.security.secretToken" }}' description='{{ i18n "pages.settings.security.secretTokenDesc" }}'/>
-                                                    </a-col>
-                                                    <a-col :lg="24" :xl="12">
-                                                        <svg 
-                                                            @click="getNewSecret"
-                                                            xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> 
-                                                        </svg>
-                                                        <template>
-                                                            <a-textarea type="text" id='token' :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
-                                                        </template>
-                                                    </a-col>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
+                                            </a-collapse-panel>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.warpConfigs"}}'>
+                                                <a-row :xs="24" :sm="24" :lg="12">
+                                                    <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                        <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                        {{ i18n "pages.settings.templates.warpConfigsDesc" }}
+                                                    </h2>
                                                 </a-row>
-                                            </a-list-item>
-                                            <a-button type="primary" @click="updateSecret">{{ i18n "confirm" }}</a-button>
-                                        </a-form>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleWARPDesc"}}' v-model="GoogleWARPSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARPDesc"}}' v-model="OpenAIWARPSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixWARPDesc"}}' v-model="NetflixWARPSettings"></setting-list-item>
+                                                <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                        </a-collapse>
+                                    </a-tab-pane>
+                                    <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;">
+                                        <a-collapse>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}'>
+                                                <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}'>
+                                                <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOutboundsDesc"}}' v-model="outboundSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                            <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}'>
+                                                <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRoutingsDesc"}}' v-model="routingRuleSettings"></setting-list-item>
+                                            </a-collapse-panel>
+                                        </a-collapse>
+                                    </a-tab-pane>
+                                    <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;">
+                                        <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
                                     </a-tab-pane>
                                 </a-tabs>
-                            </a-tab-pane>
+                            </a-list>
+                        </a-tab-pane>
 
-                            <a-tab-pane key="3" tab='{{ i18n "pages.settings.xrayConfiguration"}}'>
-                                <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <a-divider style="padding: 20px;">{{ i18n "pages.settings.actions"}}</a-divider>
-                                    <a-space direction="horizontal" style="padding: 0px 20px">
-                                        <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
-                                    </a-space>
-                                    <a-divider style="padding: 20px;">{{ i18n "pages.settings.templates.title"}} </a-divider>
-                                    
-                                    <a-tabs default-active-key="tpl-1" :class="siderDrawer.isDarkTheme ? darkClass : ''" style="padding: 20px 20px;">
-                                        <a-tab-pane key="tpl-1" tab='{{ i18n "pages.settings.templates.basicTemplate"}}' style="padding-top: 20px;">
-                                            <a-collapse>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.generalConfigs"}}'>
-                                                    <a-row :xs="24" :sm="24" :lg="12">
-                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                            {{ i18n "pages.settings.templates.generalConfigsDesc" }}
-                                                        </h2>
-                                                    </a-row>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigTorrent"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTorrentDesc"}}'  v-model="torrentSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPrivateIpDesc"}}'  v-model="privateIpSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigAds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigAdsDesc"}}'  v-model="AdsSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPorn"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPornDesc"}}'  v-model="PornSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.countryConfigs"}}'>
-                                                    <a-row :xs="24" :sm="24" :lg="12">
-                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                            {{ i18n "pages.settings.templates.countryConfigsDesc" }}
-                                                        </h2>
-                                                    </a-row>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRIpDesc"}}'  v-model="IRIpSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRDomainDesc"}}'  v-model="IRDomainSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaIpDesc"}}'  v-model="ChinaIpSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaDomainDesc"}}'  v-model="ChinaDomainSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaIpDesc"}}'  v-model="RussiaIpSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaDomainDesc"}}'  v-model="RussiaDomainSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.ipv4Configs"}}'>
-                                                    <a-row :xs="24" :sm="24" :lg="12">
-                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                            {{ i18n "pages.settings.templates.ipv4ConfigsDesc" }}
-                                                        </h2>
-                                                    </a-row>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4Desc"}}'  v-model="GoogleIPv4Settings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4Desc"}}'  v-model="NetflixIPv4Settings"></setting-list-item>
-                                                </a-collapse-panel>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.warpConfigs"}}'>
-                                                    <a-row :xs="24" :sm="24" :lg="12">
-                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                            {{ i18n "pages.settings.templates.warpConfigsDesc" }}
-                                                        </h2>
-                                                    </a-row>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleWARPDesc"}}'  v-model="GoogleWARPSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARPDesc"}}'  v-model="OpenAIWARPSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixWARPDesc"}}'  v-model="NetflixWARPSettings"></setting-list-item>
-                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARPDesc"}}'  v-model="SpotifyWARPSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                            </a-collapse>
-                                        </a-tab-pane>
-                                        <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;">
-                                            <a-collapse>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}'>
-                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}'>
-                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOutboundsDesc"}}' v-model="outboundSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}'>
-                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRoutingsDesc"}}' v-model="routingRuleSettings"></setting-list-item>
-                                                </a-collapse-panel>
-                                            </a-collapse>
-                                        </a-tab-pane>
-                                        <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;">
-                                            <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
-                                        </a-tab-pane>
-                                    </a-tabs>
-                                </a-list>
-                            </a-tab-pane>
-
-                            <a-tab-pane key="4" tab='{{ i18n "pages.settings.TGBotSettings"}}'>
-                                <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.telegramBotEnable" }}' desc='{{ i18n "pages.settings.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramToken"}}' desc='{{ i18n "pages.settings.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramChatId"}}' desc='{{ i18n "pages.settings.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramNotifyTime"}}' desc='{{ i18n "pages.settings.telegramNotifyTimeDesc"}}' v-model="allSetting.tgRunTime"></setting-list-item>
-                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.tgNotifyBackup" }}' desc='{{ i18n "pages.settings.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.settings.tgNotifyCpu" }}' desc='{{ i18n "pages.settings.tgNotifyCpuDesc" }}' v-model="allSetting.tgCpu" :min="0" :max="100"></setting-list-item>
-                                </a-list>
-                            </a-tab-pane>
-                        </a-tabs>
-                    </a-space>
-                </a-spin>
-            </a-layout-content>
-        </a-layout>
+                        <a-tab-pane key="4" tab='{{ i18n "pages.settings.TGBotSettings"}}'>
+                            <a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
+                                <setting-list-item type="switch" title='{{ i18n "pages.settings.telegramBotEnable" }}' desc='{{ i18n "pages.settings.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.telegramToken"}}' desc='{{ i18n "pages.settings.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.telegramChatId"}}' desc='{{ i18n "pages.settings.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
+                                <setting-list-item type="text" title='{{ i18n "pages.settings.telegramNotifyTime"}}' desc='{{ i18n "pages.settings.telegramNotifyTimeDesc"}}' v-model="allSetting.tgRunTime"></setting-list-item>
+                                <setting-list-item type="switch" title='{{ i18n "pages.settings.tgNotifyBackup" }}' desc='{{ i18n "pages.settings.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
+                                <setting-list-item type="number" title='{{ i18n "pages.settings.tgNotifyCpu" }}' desc='{{ i18n "pages.settings.tgNotifyCpuDesc" }}' v-model="allSetting.tgCpu" :min="0" :max="100"></setting-list-item>
+                            </a-list>
+                        </a-tab-pane>
+                    </a-tabs>
+                </a-space>
+            </a-spin>
+        </a-layout-content>
     </a-layout>
-    {{template "js" .}}
-    {{template "component/setting"}}
-    <script>
+</a-layout>
 
-        const app = new Vue({
-            delimiters: ['[[', ']]'],
-            el: '#app',
-            data: {
-                siderDrawer,
-                spinning: false,
-                oldAllSetting: new AllSetting(),
-                allSetting: new AllSetting(),
-                saveBtnDisable: true,
-                user: new User(),
-                lang: getLang(),
-                ipv4Settings: {
-                    tag: "IPv4",
-                    protocol: "freedom",
-                    settings: {
-                        domainStrategy: "UseIPv4"
-                    }
-                },
-                warpSettings: {
-                    tag: "WARP",
-                    protocol: "socks",
-                    settings: {
-                        servers: [
-                            {
-                                address: "127.0.0.1",
-                                port: 40000
-                            }
-                        ]
-                    }
-                },
-                settingsData: {
-                    protocols: {
-                        bittorrent: ["bittorrent"],
-                    },
-                    ips: {
-                        local: ["geoip:private"],
-                        google: ["geoip:google"],
-                        cn: ["geoip:cn"],
-                        ir: ["geoip:ir"],
-                        ru: ["geoip:ru"],
-                    },
-                    domains: {
-                        ads: [
-                            "geosite:category-ads-all",
-                            "geosite:category-ads",
-                            "geosite:google-ads",
-                            "geosite:spotify-ads"
-                        ],
-                        porn: ["geosite:category-porn"],
-                        openai: ["geosite:openai"],
-                        google: ["geosite:google"],
-                        spotify: ["geosite:spotify"],
-                        netflix: ["geosite:netflix"],
-                        cn: [
-                            "geosite:cn",
-                            "regexp:.*\\.cn$"
-                            ],
-                        ru: [
-                            "geosite:category-gov-ru",
-                            "regexp:.*\\.ru$"
-                            ],
-                        ir: [
-                            "regexp:.*\\.ir$",
-                            "ext:iran.dat:ir",
-                            "ext:iran.dat:other",
-                            "ext:iran.dat:ads",
-                            "geosite:category-ir"
-                        ]
-                    },
+{{template "js" .}}
+{{template "component/themeSwitcher" .}}
+{{template "component/setting"}}
+
+<script>
+
+    const app = new Vue({
+        delimiters: ['[[', ']]'],
+        el: '#app',
+        data: {
+            themeSwitcher,
+            spinning: false,
+            oldAllSetting: new AllSetting(),
+            allSetting: new AllSetting(),
+            saveBtnDisable: true,
+            user: new User(),
+            lang: getLang(),
+            ipv4Settings: {
+                tag: "IPv4",
+                protocol: "freedom",
+                settings: {
+                    domainStrategy: "UseIPv4"
                 }
             },
-            methods: {
-                loading(spinning = true, obj) {
-                    if (obj == null) this.spinning = spinning;
-                },
-                async getAllSetting() {
-                    this.loading(true);
-                    const msg = await HttpUtil.post("/xui/setting/all");
-                    this.loading(false);
-                    if (msg.success) {
-                        this.oldAllSetting = new AllSetting(msg.obj);
-                        this.allSetting = new AllSetting(msg.obj);
-                        this.saveBtnDisable = true;
-                    }
-                    await this.getUserSecret();
-                },
-                async updateAllSetting() {
-                    this.loading(true);
-                    const msg = await HttpUtil.post("/xui/setting/update", this.allSetting);
-                    this.loading(false);
-                    if (msg.success) {
-                        await this.getAllSetting();
-                    }
+            warpSettings: {
+                tag: "WARP",
+                protocol: "socks",
+                settings: {
+                    servers: [
+                        {
+                            address: "127.0.0.1",
+                            port: 40000
+                        }
+                    ]
+                }
+            },
+            settingsData: {
+                protocols: {
+                    bittorrent: ["bittorrent"],
+                },
+                ips: {
+                    local: ["geoip:private"],
+                    google: ["geoip:google"],
+                    cn: ["geoip:cn"],
+                    ir: ["geoip:ir"],
+                    ru: ["geoip:ru"],
+                },
+                domains: {
+                    ads: [
+                        "geosite:category-ads-all",
+                        "geosite:category-ads",
+                        "geosite:google-ads",
+                        "geosite:spotify-ads"
+                    ],
+                    porn: ["geosite:category-porn"],
+                    openai: ["geosite:openai"],
+                    google: ["geosite:google"],
+                    spotify: ["geosite:spotify"],
+                    netflix: ["geosite:netflix"],
+                    cn: [
+                        "geosite:cn",
+                        "regexp:.*\\.cn$"
+                    ],
+                    ru: [
+                        "geosite:category-gov-ru",
+                        "regexp:.*\\.ru$"
+                    ],
+                    ir: [
+                        "regexp:.*\\.ir$",
+                        "ext:iran.dat:ir",
+                        "ext:iran.dat:other",
+                        "ext:iran.dat:ads",
+                        "geosite:category-ir"
+                    ]
                 },
-                async updateUser() {
-                    this.loading(true);
-                    const msg = await HttpUtil.post("/xui/setting/updateUser", this.user);
-                    this.loading(false);
-                    if (msg.success) {
-                        this.user = {};
-                        window.location.replace("/logout")
-                    }
-                },
-                async restartPanel() {
-                    await new Promise(resolve => {
-                        this.$confirm({
-                            title: '{{ i18n "pages.settings.restartPanel" }}',
-                            content: '{{ i18n "pages.settings.restartPanelDesc" }}',
-                            okText: '{{ i18n "sure" }}',
-                            cancelText: '{{ i18n "cancel" }}',
-                            onOk: () => resolve(),
-                        });
+            }
+        },
+        methods: {
+            loading(spinning = true, obj) {
+                if (obj == null) this.spinning = spinning;
+            },
+            async getAllSetting() {
+                this.loading(true);
+                const msg = await HttpUtil.post("/xui/setting/all");
+                this.loading(false);
+                if (msg.success) {
+                    this.oldAllSetting = new AllSetting(msg.obj);
+                    this.allSetting = new AllSetting(msg.obj);
+                    this.saveBtnDisable = true;
+                }
+                await this.getUserSecret();
+            },
+            async updateAllSetting() {
+                this.loading(true);
+                const msg = await HttpUtil.post("/xui/setting/update", this.allSetting);
+                this.loading(false);
+                if (msg.success) {
+                    await this.getAllSetting();
+                }
+            },
+            async updateUser() {
+                this.loading(true);
+                const msg = await HttpUtil.post("/xui/setting/updateUser", this.user);
+                this.loading(false);
+                if (msg.success) {
+                    this.user = {};
+                    window.location.replace("/logout")
+                }
+            },
+            async restartPanel() {
+                await new Promise(resolve => {
+                    this.$confirm({
+                        title: '{{ i18n "pages.settings.restartPanel" }}',
+                        content: '{{ i18n "pages.settings.restartPanelDesc" }}',
+                        okText: '{{ i18n "sure" }}',
+                        cancelText: '{{ i18n "cancel" }}',
+                        onOk: () => resolve(),
                     });
+                });
+                this.loading(true);
+                const msg = await HttpUtil.post("/xui/setting/restartPanel");
+                this.loading(false);
+                if (msg.success) {
                     this.loading(true);
-                    const msg = await HttpUtil.post("/xui/setting/restartPanel");
-                    this.loading(false);
-                    if (msg.success) {
-                        this.loading(true);
-                        await PromiseUtil.sleep(5000);
-                        location.reload();
-                    }
-                },
-                async getUserSecret() {
-                    const user_msg = await HttpUtil.post("/xui/setting/getUserSecret", this.user);
-                    if (user_msg.success) {
-                        this.user = user_msg.obj;
-                    }
-                    this.loading(false);
-                },
-                async updateSecret() {
-                    this.loading(true);
-                    const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user);
-                    if (msg.success) {
-                        this.user = msg.obj;
-                        window.location.replace("/logout")
-                    }
-                    this.loading(false);
-                    await this.updateAllSetting();
-                },
-                async getNewSecret() {
-                    this.loading(true);
-                    await PromiseUtil.sleep(1000);
-                    var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
-                    var string = "";
-                    var len = 64;
-                    for (var ii = 0; ii < len; ii++) {
-                        string += chars[Math.floor(Math.random() * chars.length)];
-                    }
-                    this.user.loginSecret = string;
-                    document.getElementById("token").value = this.user.loginSecret;
-                    this.loading(false);
-                },
-                async toggleToken(value) {
-                    if (value) this.getNewSecret();
-                    else this.user.loginSecret = "";
-                },
-                async resetXrayConfigToDefault() {
-                    this.loading(true);
-                    const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig");
-                    this.loading(false);
-                    if (msg.success) {
-                        this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
-                        this.saveBtnDisable = true;
-                    }
-                },
-                checkRequiredOutbounds() {
-                    const newTemplateSettings = this.templateSettings;
-                    const haveIPv4Outbounds = newTemplateSettings.outbounds.some((o) => o?.tag === "IPv4");
-                    const haveIPv4Rules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === "IPv4");
-                    const haveWARPOutbounds = newTemplateSettings.outbounds.some((o) => o?.tag === "WARP");
-                    const haveWARPRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === "WARP");
-                    if (haveWARPRules && !haveWARPOutbounds) {
-                        newTemplateSettings.outbounds.push(this.warpSettings);
-                    }
-                    if (haveIPv4Rules && !haveIPv4Outbounds) {
-                        newTemplateSettings.outbounds.push(this.ipv4Settings);
-                    }
-                    this.templateSettings = newTemplateSettings;
-                },
-                templateRuleGetter(routeSettings) {
-                    const { data, property, outboundTag } = routeSettings;
-                    let result = false;
-                    if (this.templateSettings != null) {
-                        this.templateSettings.routing.rules.forEach(
-                            (routingRule) => {
-                                if (
-                                    routingRule.hasOwnProperty(property) &&
-                                    routingRule.hasOwnProperty("outboundTag") &&
-                                    routingRule.outboundTag === outboundTag
-                                ) {
-                                    if (data.includes(routingRule[property][0])) {
-                                        result = true;
-                                    }
+                    await PromiseUtil.sleep(5000);
+                    location.reload();
+                }
+            },
+            async getUserSecret() {
+                const user_msg = await HttpUtil.post("/xui/setting/getUserSecret", this.user);
+                if (user_msg.success) {
+                    this.user = user_msg.obj;
+                }
+                this.loading(false);
+            },
+            async updateSecret() {
+                this.loading(true);
+                const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user);
+                if (msg.success) {
+                    this.user = msg.obj;
+                    window.location.replace("/logout")
+                }
+                this.loading(false);
+                await this.updateAllSetting();
+            },
+            async getNewSecret() {
+                this.loading(true);
+                await PromiseUtil.sleep(1000);
+                var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+                var string = "";
+                var len = 64;
+                for (var ii = 0; ii < len; ii++) {
+                    string += chars[Math.floor(Math.random() * chars.length)];
+                }
+                this.user.loginSecret = string;
+                document.getElementById("token").value = this.user.loginSecret;
+                this.loading(false);
+            },
+            async toggleToken(value) {
+                if (value) this.getNewSecret();
+                else this.user.loginSecret = "";
+            },
+            async resetXrayConfigToDefault() {
+                this.loading(true);
+                const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig");
+                this.loading(false);
+                if (msg.success) {
+                    this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
+                    this.saveBtnDisable = true;
+                }
+            },
+            checkRequiredOutbounds() {
+                const newTemplateSettings = this.templateSettings;
+                const haveIPv4Outbounds = newTemplateSettings.outbounds.some((o) => o?.tag === "IPv4");
+                const haveIPv4Rules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === "IPv4");
+                const haveWARPOutbounds = newTemplateSettings.outbounds.some((o) => o?.tag === "WARP");
+                const haveWARPRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === "WARP");
+                if (haveWARPRules && !haveWARPOutbounds) {
+                    newTemplateSettings.outbounds.push(this.warpSettings);
+                }
+                if (haveIPv4Rules && !haveIPv4Outbounds) {
+                    newTemplateSettings.outbounds.push(this.ipv4Settings);
+                }
+                this.templateSettings = newTemplateSettings;
+            },
+            templateRuleGetter(routeSettings) {
+                const { data, property, outboundTag } = routeSettings;
+                let result = false;
+                if (this.templateSettings != null) {
+                    this.templateSettings.routing.rules.forEach(
+                        (routingRule) => {
+                            if (
+                                routingRule.hasOwnProperty(property) &&
+                                routingRule.hasOwnProperty("outboundTag") &&
+                                routingRule.outboundTag === outboundTag
+                            ) {
+                                if (data.includes(routingRule[property][0])) {
+                                    result = true;
                                 }
                             }
-                        );
-                    }
-                    return result;
-                },
-                templateRuleSetter(routeSettings) {
-                    const { newValue, data, property, outboundTag } = routeSettings;
-                    const oldTemplateSettings = this.templateSettings;
-                    const newTemplateSettings = oldTemplateSettings;
-                    if (newValue) {
-                        const propertyRule = {
-                            type: "field",
-                            outboundTag,
-                            [property]: data
-                        };
-                        newTemplateSettings.routing.rules.push(propertyRule);
-                    }
-                    else {
-                        const newRules = [];
-                        newTemplateSettings.routing.rules.forEach(
-                            (routingRule) => {
-                                if (
-                                    routingRule.hasOwnProperty(property) &&
-                                    routingRule.hasOwnProperty("outboundTag") &&
-                                    routingRule.outboundTag === outboundTag
-                                ) {
-                                    if (data.includes(routingRule[property][0])) {
-                                        return;
-                                    }
+                        }
+                    );
+                }
+                return result;
+            },
+            templateRuleSetter(routeSettings) {
+                const { newValue, data, property, outboundTag } = routeSettings;
+                const oldTemplateSettings = this.templateSettings;
+                const newTemplateSettings = oldTemplateSettings;
+                if (newValue) {
+                    const propertyRule = {
+                        type: "field",
+                        outboundTag,
+                        [property]: data
+                    };
+                    newTemplateSettings.routing.rules.push(propertyRule);
+                }
+                else {
+                    const newRules = [];
+                    newTemplateSettings.routing.rules.forEach(
+                        (routingRule) => {
+                            if (
+                                routingRule.hasOwnProperty(property) &&
+                                routingRule.hasOwnProperty("outboundTag") &&
+                                routingRule.outboundTag === outboundTag
+                            ) {
+                                if (data.includes(routingRule[property][0])) {
+                                    return;
                                 }
-                                newRules.push(routingRule);
                             }
-                        );
-                        newTemplateSettings.routing.rules = newRules;
-                    }
-                    this.templateSettings = newTemplateSettings;
-                    this.checkRequiredOutbounds();
+                            newRules.push(routingRule);
+                        }
+                    );
+                    newTemplateSettings.routing.rules = newRules;
                 }
+                this.templateSettings = newTemplateSettings;
+                this.checkRequiredOutbounds();
+            }
+        },
+        async mounted() {
+            await this.getAllSetting();
+            while (true) {
+                await PromiseUtil.sleep(1000);
+                this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
+            }
+        },
+        computed: {
+            templateSettings: {
+                get: function () { return this.allSetting.xrayTemplateConfig ? JSON.parse(this.allSetting.xrayTemplateConfig) : null; },
+                set: function (newValue) { this.allSetting.xrayTemplateConfig = JSON.stringify(newValue, null, 2) },
             },
-            async mounted() {
-                await this.getAllSetting();
-                while (true) {
-                    await PromiseUtil.sleep(1000);
-                    this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
-                }
+            inboundSettings: {
+                get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
+                set: function (newValue) {
+                    newTemplateSettings = this.templateSettings;
+                    newTemplateSettings.inbounds = JSON.parse(newValue)
+                    this.templateSettings = newTemplateSettings
+                },
             },
-            computed: {
-                templateSettings: {
-                    get: function () { return this.allSetting.xrayTemplateConfig ? JSON.parse(this.allSetting.xrayTemplateConfig) : null; },
-                    set: function (newValue) { this.allSetting.xrayTemplateConfig = JSON.stringify(newValue, null, 2) },
-                },
-                inboundSettings: {
-                    get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
-                    set: function (newValue) {
-                        newTemplateSettings = this.templateSettings;
-                        newTemplateSettings.inbounds = JSON.parse(newValue)
-                        this.templateSettings = newTemplateSettings
-                    },
-                },
-                outboundSettings: {
-                    get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.outbounds, null, 2) : null; },
-                    set: function (newValue) {
-                        newTemplateSettings = this.templateSettings;
-                        newTemplateSettings.outbounds = JSON.parse(newValue)
-                        this.templateSettings = newTemplateSettings
-                    },
-                },
-                routingRuleSettings: {
-                    get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; },
-                    set: function (newValue) {
-                        newTemplateSettings = this.templateSettings;
-                        newTemplateSettings.routing.rules = JSON.parse(newValue)
-                        this.templateSettings = newTemplateSettings
-                    },
-                },
-                torrentSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "protocol",
-                            data: this.settingsData.protocols.bittorrent
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "protocol",
-                            data: this.settingsData.protocols.bittorrent
-                        });
-                    },
-                },
-                privateIpSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.local
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.local
-                        });
-                    },
-                },
-                AdsSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ads
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ads
-                        });
-                    },
-                },
-                PornSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.porn
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.porn
-                        });
-                    },
-                },
-                GoogleIPv4Settings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "IPv4",
-                            property: "domain",
-                            data: this.settingsData.domains.google
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "IPv4",
-                            property: "domain",
-                            data: this.settingsData.domains.google
-                        });
-                    },
-                },
-                NetflixIPv4Settings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "IPv4",
-                            property: "domain",
-                            data: this.settingsData.domains.netflix
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "IPv4",
-                            property: "domain",
-                            data: this.settingsData.domains.netflix
-                        });
-                    },
-                },
-                IRIpSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.ir
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.ir
-                        });
-                    },
-                },
-                IRDomainSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ir
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ir
-                        });
-                    },
-                },
-                ChinaIpSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.cn
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.cn
-                        });
-                    },
-                },
-                ChinaDomainSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.cn
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.cn
-                        });
-                    },
-                },
-                RussiaIpSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.ru
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "ip",
-                            data: this.settingsData.ips.ru
-                        });
-                    },
-                },
-                RussiaDomainSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ru
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "blocked",
-                            property: "domain",
-                            data: this.settingsData.domains.ru
-                        });
-                    },
-                },
-                GoogleWARPSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.google
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.google
-                        });
-                    },
-                },
-                OpenAIWARPSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.openai
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.openai
-                        });
-                    },
-                },
-                NetflixWARPSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.netflix
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.netflix
-                        });
-                    },
-                },
-                SpotifyWARPSettings: {
-                    get: function () {
-                        return this.templateRuleGetter({
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.spotify
-                        });
-                    },
-                    set: function (newValue) {
-                        this.templateRuleSetter({
-                            newValue,
-                            outboundTag: "WARP",
-                            property: "domain",
-                            data: this.settingsData.domains.spotify
-                        });
-                    },
+            outboundSettings: {
+                get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.outbounds, null, 2) : null; },
+                set: function (newValue) {
+                    newTemplateSettings = this.templateSettings;
+                    newTemplateSettings.outbounds = JSON.parse(newValue)
+                    this.templateSettings = newTemplateSettings
                 },
-            }
-        });
+            },
+            routingRuleSettings: {
+                get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; },
+                set: function (newValue) {
+                    newTemplateSettings = this.templateSettings;
+                    newTemplateSettings.routing.rules = JSON.parse(newValue)
+                    this.templateSettings = newTemplateSettings
+                },
+            },
+            torrentSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "protocol",
+                        data: this.settingsData.protocols.bittorrent
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "protocol",
+                        data: this.settingsData.protocols.bittorrent
+                    });
+                },
+            },
+            privateIpSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.local
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.local
+                    });
+                },
+            },
+            AdsSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ads
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ads
+                    });
+                },
+            },
+            PornSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.porn
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.porn
+                    });
+                },
+            },
+            GoogleIPv4Settings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "IPv4",
+                        property: "domain",
+                        data: this.settingsData.domains.google
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "IPv4",
+                        property: "domain",
+                        data: this.settingsData.domains.google
+                    });
+                },
+            },
+            NetflixIPv4Settings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "IPv4",
+                        property: "domain",
+                        data: this.settingsData.domains.netflix
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "IPv4",
+                        property: "domain",
+                        data: this.settingsData.domains.netflix
+                    });
+                },
+            },
+            IRIpSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.ir
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.ir
+                    });
+                },
+            },
+            IRDomainSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ir
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ir
+                    });
+                },
+            },
+            ChinaIpSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.cn
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.cn
+                    });
+                },
+            },
+            ChinaDomainSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.cn
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.cn
+                    });
+                },
+            },
+            RussiaIpSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.ru
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "ip",
+                        data: this.settingsData.ips.ru
+                    });
+                },
+            },
+            RussiaDomainSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ru
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "blocked",
+                        property: "domain",
+                        data: this.settingsData.domains.ru
+                    });
+                },
+            },
+            GoogleWARPSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.google
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.google
+                    });
+                },
+            },
+            OpenAIWARPSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.openai
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.openai
+                    });
+                },
+            },
+            NetflixWARPSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.netflix
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.netflix
+                    });
+                },
+            },
+            SpotifyWARPSettings: {
+                get: function () {
+                    return this.templateRuleGetter({
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.spotify
+                    });
+                },
+                set: function (newValue) {
+                    this.templateRuleSetter({
+                        newValue,
+                        outboundTag: "WARP",
+                        property: "domain",
+                        data: this.settingsData.domains.spotify
+                    });
+                },
+            },
+        }
+    });
 
-    </script>
+</script>
 </body>
 
 </html>