Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShishkevich D. <135337715+shishkevichd@users.noreply.github.com>2025-03-17 14:26:07 +0300
committerGitHub <noreply@github.com>2025-03-17 14:26:07 +0300
commitdb62a07fb89e08e974210ccb0f62cc648d799e94 (patch)
tree50af3c5e77ae27c3e5b360eb32b9e95fe795aa25
parente3120c402882e3fccc0112b1c0197126fb613329 (diff)
Code refactoring (#2785)
* chore: pretty theme menu in sidebar * refactor: renaming component templates * refactor: create custom `a-statistic` component * fix: display button text only on large screens * chore: remove loading background in overview page * fix: show `Version` text when xray version is unknown
-rw-r--r--web/html/login.html4
-rw-r--r--web/html/xui/common_sider.html20
-rw-r--r--web/html/xui/component/aCustomStatistic.html55
-rw-r--r--web/html/xui/component/aPasswordInput.html (renamed from web/html/xui/component/password.html)2
-rw-r--r--web/html/xui/component/aPersianDatepicker.html (renamed from web/html/xui/component/persianDatepicker.html)2
-rw-r--r--web/html/xui/component/aSettingListItem.html (renamed from web/html/xui/component/setting.html)10
-rw-r--r--web/html/xui/component/aTableSortable.html (renamed from web/html/xui/component/sortableTable.html)2
-rw-r--r--web/html/xui/component/aThemeSwitch.html (renamed from web/html/xui/component/themeSwitch.html)16
-rw-r--r--web/html/xui/inbounds.html4
-rw-r--r--web/html/xui/index.html71
-rw-r--r--web/html/xui/settings.html6
-rw-r--r--web/html/xui/xray.html6
-rw-r--r--web/translation/translate.en_US.toml3
-rw-r--r--web/translation/translate.es_ES.toml3
-rw-r--r--web/translation/translate.fa_IR.toml3
-rw-r--r--web/translation/translate.id_ID.toml3
-rw-r--r--web/translation/translate.ja_JP.toml3
-rw-r--r--web/translation/translate.pt_BR.toml3
-rw-r--r--web/translation/translate.ru_RU.toml3
-rw-r--r--web/translation/translate.tr_TR.toml3
-rw-r--r--web/translation/translate.uk_UA.toml3
-rw-r--r--web/translation/translate.vi_VN.toml3
-rw-r--r--web/translation/translate.zh_CN.toml3
-rw-r--r--web/translation/translate.zh_TW.toml3
24 files changed, 155 insertions, 79 deletions
diff --git a/web/html/login.html b/web/html/login.html
index fc48b9ec..b27333a5 100644
--- a/web/html/login.html
+++ b/web/html/login.html
@@ -473,8 +473,8 @@
</transition>
</a-layout>
{{template "js" .}}
-{{template "component/themeSwitcher" .}}
-{{template "component/password" .}}
+{{template "component/aThemeSwitch" .}}
+{{template "component/aPasswordInput" .}}
<script>
class User {
constructor() {
diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html
index 1d5fe075..97114c15 100644
--- a/web/html/xui/common_sider.html
+++ b/web/html/xui/common_sider.html
@@ -1,33 +1,23 @@
{{define "menuItems"}}
<a-menu-item key="{{ .base_path }}panel/">
<a-icon type="dashboard"></a-icon>
- <span>
- <b>{{ i18n "menu.dashboard"}}</b>
- </span>
+ <span>{{ i18n "menu.dashboard"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/inbounds">
<a-icon type="user"></a-icon>
- <span>
- <b>{{ i18n "menu.inbounds"}}</b>
- </span>
+ <span>{{ i18n "menu.inbounds"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/settings">
<a-icon type="setting"></a-icon>
- <span>
- <b>{{ i18n "menu.settings"}}</b>
- </span>
+ <span>{{ i18n "menu.settings"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}panel/xray">
<a-icon type="tool"></a-icon>
- <span>
- <b>{{ i18n "menu.xray"}}</b>
- </span>
+ <span>{{ i18n "menu.xray"}}</span>
</a-menu-item>
<a-menu-item key="{{ .base_path }}logout">
<a-icon type="logout"></a-icon>
- <span>
- <b>{{ i18n "menu.logout"}}</b>
- </span>
+ <span>{{ i18n "menu.logout"}}</span>
</a-menu-item>
{{end}}
diff --git a/web/html/xui/component/aCustomStatistic.html b/web/html/xui/component/aCustomStatistic.html
new file mode 100644
index 00000000..41695e90
--- /dev/null
+++ b/web/html/xui/component/aCustomStatistic.html
@@ -0,0 +1,55 @@
+{{define "component/customStatistic"}}
+<template>
+ <a-statistic :title="title" :value="value">
+ <template #prefix>
+ <slot name="prefix"></slot>
+ </template>
+ <template #suffix>
+ <slot name="suffix"></slot>
+ </template>
+ </a-statistic>
+</template>
+{{end}}
+
+{{define "component/aCustomStatistic"}}
+<style>
+ .dark .ant-statistic-title,
+ .dark .ant-statistic-content {
+ color: var(--dark-color-text-primary) !important
+ }
+
+ .dark .ant-statistic-title {
+ user-select: none;
+ opacity: 0.55;
+ }
+
+ .ant-statistic-title {
+ margin-bottom: 0 !important;
+ }
+
+ .ant-statistic-content-prefix {
+ margin-right: 6px !important;
+ }
+
+ .ant-statistic-content-prefix,
+ .ant-statistic-content-value {
+ font-size: 1.05rem;
+ }
+</style>
+
+<script>
+ Vue.component('a-custom-statistic', {
+ props: {
+ 'title': {
+ type: String,
+ required: false,
+ },
+ 'value': {
+ type: String,
+ required: false
+ }
+ },
+ template: `{{template "component/customStatistic"}}`,
+ });
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/xui/component/password.html b/web/html/xui/component/aPasswordInput.html
index 0fea1430..460e7e76 100644
--- a/web/html/xui/component/password.html
+++ b/web/html/xui/component/aPasswordInput.html
@@ -12,7 +12,7 @@
</template>
{{end}}
-{{define "component/password"}}
+{{define "component/aPasswordInput"}}
<script>
Vue.component('a-password-input', {
props: {
diff --git a/web/html/xui/component/persianDatepicker.html b/web/html/xui/component/aPersianDatepicker.html
index 2df79a06..58132ddd 100644
--- a/web/html/xui/component/persianDatepicker.html
+++ b/web/html/xui/component/aPersianDatepicker.html
@@ -12,7 +12,7 @@
</template>
{{end}}
-{{define "component/persianDatepicker"}}
+{{define "component/aPersianDatepicker"}}
<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css?{{ .cur_ver }}" />
<script src="{{ .base_path }}assets/moment/moment-jalali.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.js?{{ .cur_ver }}"></script>
diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/aSettingListItem.html
index 85220d25..6a7d31d0 100644
--- a/web/html/xui/component/setting.html
+++ b/web/html/xui/component/aSettingListItem.html
@@ -18,18 +18,10 @@
</a-list-item>
{{end}}
-{{define "component/setting"}}
+{{define "component/aSettingListItem"}}
<script>
Vue.component('a-setting-list-item', {
props: {
- 'title': {
- type: String,
- required: true,
- },
- 'description': {
- type: String,
- required: false,
- },
'paddings': {
type: String,
required: false,
diff --git a/web/html/xui/component/sortableTable.html b/web/html/xui/component/aTableSortable.html
index 67bbcee2..7dbc3d0a 100644
--- a/web/html/xui/component/sortableTable.html
+++ b/web/html/xui/component/aTableSortable.html
@@ -3,7 +3,7 @@
@click="clickHandler" />
{{end}}
-{{define "component/sortableTable"}}
+{{define "component/aTableSortable"}}
<script>
const DRAGGABLE_ROW_CLASS = 'draggable-row';
const findParentRowElement = (el) => {
diff --git a/web/html/xui/component/themeSwitch.html b/web/html/xui/component/aThemeSwitch.html
index 28cb79b1..9a213ca6 100644
--- a/web/html/xui/component/themeSwitch.html
+++ b/web/html/xui/component/aThemeSwitch.html
@@ -4,15 +4,15 @@
<a-sub-menu>
<span slot="title">
<a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
- <span>Theme</span>
+ <span>{{ i18n "menu.theme" }}</span>
</span>
- <a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()"> Dark
- <a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme"
- @change="themeSwitcher.toggleTheme()"></a-switch>
+ <a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
+ <span>{{ i18n "menu.dark" }}</span>
+ <a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
</a-menu-item>
- <a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
- @mousedown="themeSwitcher.animationsOffUltra()"> Ultra <a-checkbox style="margin-left: 2px;"
- :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
+ <a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOffUltra()">
+ <span>{{ i18n "menu.ultraDark" }}</span>
+ <a-checkbox style="margin-left: 2px;" :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
</a-menu-item>
</a-sub-menu>
</a-menu>
@@ -36,7 +36,7 @@
</template>
{{end}}
-{{define "component/themeSwitcher"}}
+{{define "component/aThemeSwitch"}}
<script>
function createThemeSwitcher() {
const isDarkTheme = localStorage.getItem('dark-mode') === 'true';
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index c8cf93d3..d8b3c40a 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -548,8 +548,8 @@
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
-{{template "component/themeSwitcher" .}}
-{{template "component/persianDatepicker" .}}
+{{template "component/aThemeSwitch" .}}
+{{template "component/aPersianDatepicker" .}}
<script>
const columns = [{
title: "ID",
diff --git a/web/html/xui/index.html b/web/html/xui/index.html
index bd7d3878..0e1c21e2 100644
--- a/web/html/xui/index.html
+++ b/web/html/xui/index.html
@@ -27,8 +27,6 @@
.dark .ant-backup-list-item svg,
.dark .ant-card-actions>li>*,
.dark .ant-badge-status-text,
- .dark .ant-statistic-title,
- .dark .ant-statistic-content,
.dark .ant-card-extra {
color: var(--dark-color-text-primary) !important;
}
@@ -44,12 +42,6 @@
.ant-card-actions {
background: transparent !important;
}
- .ant-statistic-content-prefix {
- font-size: 20px;
- }
- .ant-statistic-content-value {
- font-size: 18px;
- }
.ip-hidden {
filter: blur(10px);
}
@@ -123,14 +115,22 @@
</transition>
<transition name="list" appear>
<template v-if="!status.isLoaded">
- <a-card hoverable style="text-align: center; padding: 30px 0; margin-top: 10px;">
+ <div style="text-align: center; padding: 30px 0; margin-top: 10px;">
<a-spin size="large"></a-spin>
- </a-card>
+ </div>
</template>
<template v-else>
<a-row>
<a-col :sm="24" :lg="12">
- <a-card title='{{ i18n "pages.index.xrayStatus" }}' hoverable>
+ <a-card hoverable>
+ <template #title>
+ <a-space direction="horizontal">
+ <span>{{ i18n "pages.index.xrayStatus" }}</span>
+ <a-tag v-if="isMobile && status.xray.version != 'Unknown'" color="green">
+ v[[ status.xray.version ]]
+ </a-tag>
+ </a-space>
+ </template>
<template #extra>
<template v-if="status.xray.state != State.Error">
<a-badge :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
@@ -150,15 +150,17 @@
<template #actions>
<a-space direction="horizontal" @click="stopXrayService" style="justify-content: center;">
<a-icon type="poweroff"></a-icon>
- <span>{{ i18n "pages.index.stopXray" }}</span>
+ <span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
</a-space>
<a-space direction="horizontal" @click="restartXrayService" style="justify-content: center;">
<a-icon type="reload"></a-icon>
- <span>{{ i18n "pages.index.restartXray" }}</span>
+ <span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span>
</a-space>
<a-space direction="horizontal" @click="openSelectV2rayVersion" style="justify-content: center;">
<a-icon type="tool"></a-icon>
- <span>v[[ status.xray.version ]]</span>
+ <span v-if="!isMobile">
+ [[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]]
+ </span>
</a-space>
</template>
</a-card>
@@ -168,15 +170,15 @@
<template #actions>
<a-space direction="horizontal" @click="openLogs()" style="justify-content: center;">
<a-icon type="bars"></a-icon>
- <span>{{ i18n "pages.index.logs" }}</span>
+ <span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
</a-space>
<a-space direction="horizontal" @click="openConfig" style="justify-content: center;">
<a-icon type="control"></a-icon>
- <span>{{ i18n "pages.index.config" }}</span>
+ <span v-if="!isMobile">{{ i18n "pages.index.config" }}</span>
</a-space>
<a-space direction="horizontal" @click="openBackup" style="justify-content: center;">
<a-icon type="cloud-server"></a-icon>
- <span>{{ i18n "pages.index.backup" }}</span>
+ <span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span>
</a-space>
</template>
</a-card>
@@ -223,18 +225,18 @@
</template>
<a-row :class="showIp ? 'ip-visible' : 'ip-hidden'">
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title="IPv4" :value="status.publicIP.ipv4">
+ <a-custom-statistic title="IPv4" :value="status.publicIP.ipv4">
<template #prefix>
<a-icon type="global" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title="IPv6" :value="status.publicIP.ipv6">
+ <a-custom-statistic title="IPv6" :value="status.publicIP.ipv6">
<template #prefix>
<a-icon type="global" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
</a-row>
</a-card>
@@ -243,18 +245,18 @@
<a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title="TCP" :value="status.tcpCount">
+ <a-custom-statistic title="TCP" :value="status.tcpCount">
<template #prefix>
<a-icon type="swap" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title="UDP" :value="status.udpCount">
+ <a-custom-statistic title="UDP" :value="status.udpCount">
<template #prefix>
<a-icon type="swap" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
</a-row>
</a-card>
@@ -263,24 +265,24 @@
<a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
+ <a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
<template #prefix>
<a-icon type="arrow-up" />
</template>
<template #suffix>
/s
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)">
+ <a-custom-statistic title='{{ i18n "pages.index.download" }}' :value="SizeFormatter.sizeFormat(status.netIO.down)">
<template #prefix>
<a-icon type="arrow-down" />
</template>
<template #suffix>
/s
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
</a-row>
</a-card>
@@ -289,18 +291,18 @@
<a-card title='{{ i18n "pages.index.totalData" }}' hoverable>
<a-row>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
+ <a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
<template #prefix>
<a-icon type="cloud-upload" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
<a-col :lg="12" :sm="24" :style="{ marginTop: isMobile ? '10px' : 0 }">
- <a-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)">
+ <a-custom-statistic title='{{ i18n "pages.index.received" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.recv)">
<template #prefix>
<a-icon type="cloud-download" />
</template>
- </a-statistic>
+ </a-custom-statistic>
</a-col>
</a-row>
</a-card>
@@ -397,7 +399,8 @@
</a-modal>
</a-layout>
{{template "js" .}}
-{{template "component/themeSwitcher" .}}
+{{template "component/aThemeSwitch" .}}
+{{template "component/aCustomStatistic" .}}
{{template "textModal"}}
<script>
const State = {
diff --git a/web/html/xui/settings.html b/web/html/xui/settings.html
index 7ccada90..febb26e8 100644
--- a/web/html/xui/settings.html
+++ b/web/html/xui/settings.html
@@ -663,9 +663,9 @@
</a-layout>
{{template "js" .}}
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
-{{template "component/themeSwitcher" .}}
-{{template "component/password" .}}
-{{template "component/setting"}}
+{{template "component/aThemeSwitch" .}}
+{{template "component/aPasswordInput" .}}
+{{template "component/aSettingListItem" .}}
<script>
const app = new Vue({
delimiters: ['[[', ']]'],
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index 99f5f990..8ba14f7d 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -788,9 +788,9 @@
</a-layout>
</a-layout>
{{template "js" .}}
-{{template "component/themeSwitcher" .}}
-{{template "component/sortableTable" .}}
-{{template "component/setting"}}
+{{template "component/aThemeSwitch" .}}
+{{template "component/aTableSortable" .}}
+{{template "component/aSettingListItem" .}}
{{template "ruleModal"}}
{{template "outModal"}}
{{template "reverseModal"}}
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 37e9eb8c..e3090667 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "No added reverse proxies."
[menu]
+"theme" = "Theme"
+"dark" = "Dark"
+"ultraDark" = "Ultra Dark"
"dashboard" = "Overview"
"inbounds" = "Inbounds"
"settings" = "Panel Settings"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 29e099e8..6c5aa9df 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "No hay proxies inversos añadidos."
[menu]
+"theme" = "Tema"
+"dark" = "Oscuro"
+"ultraDark" = "Ultra Oscuro"
"dashboard" = "Estado del Sistema"
"inbounds" = "Entradas"
"settings" = "Configuraciones"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 4a1e4177..e120bb08 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "هیچ پروکسی معکوس اضافه نشده است."
[menu]
+"theme" = "تم"
+"dark" = "تیره"
+"ultraDark" = "فوق تیره"
"dashboard" = "نمای کلی"
"inbounds" = "ورودی‌ها"
"settings" = "تنظیمات پنل"
diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml
index 9d129830..5f5f4354 100644
--- a/web/translation/translate.id_ID.toml
+++ b/web/translation/translate.id_ID.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Tidak ada proxy terbalik yang ditambahkan."
[menu]
+"theme" = "Tema"
+"dark" = "Gelap"
+"ultraDark" = "Sangat Gelap"
"dashboard" = "Ikhtisar"
"inbounds" = "Masuk"
"settings" = "Pengaturan Panel"
diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml
index 53d2076c..8283b0b8 100644
--- a/web/translation/translate.ja_JP.toml
+++ b/web/translation/translate.ja_JP.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "追加されたリバースプロキシはありません。"
[menu]
+"theme" = "テーマ"
+"dark" = "ダーク"
+"ultraDark" = "ウルトラダーク"
"dashboard" = "ダッシュボード"
"inbounds" = "インバウンド一覧"
"settings" = "パネル設定"
diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml
index 7f0badb7..dbdaa7b0 100644
--- a/web/translation/translate.pt_BR.toml
+++ b/web/translation/translate.pt_BR.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Nenhum proxy reverso adicionado."
[menu]
+"theme" = "Tema"
+"dark" = "Escuro"
+"ultraDark" = "Ultra Escuro"
"dashboard" = "Visão Geral"
"inbounds" = "Inbounds"
"settings" = "Panel Settings"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 5be87e09..dcaf0855 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Нет добавленных обратных прокси."
[menu]
+"theme" = "Тема"
+"dark" = "Темная"
+"ultraDark" = "Ультра темная"
"dashboard" = "Статус системы"
"inbounds" = "Подключения"
"settings" = "Настройки панели"
diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml
index 59149d78..efa632e5 100644
--- a/web/translation/translate.tr_TR.toml
+++ b/web/translation/translate.tr_TR.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Eklenmiş ters proxy yok."
[menu]
+"theme" = "Tema"
+"dark" = "Koyu"
+"ultraDark" = "Ultra Koyu"
"dashboard" = "Genel Bakış"
"inbounds" = "Gelenler"
"settings" = "Panel Ayarları"
diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml
index eb9e93fc..da4723ba 100644
--- a/web/translation/translate.uk_UA.toml
+++ b/web/translation/translate.uk_UA.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Немає доданих зворотних проксі."
[menu]
+"theme" = "Тема"
+"dark" = "Темна"
+"ultraDark" = "Ультра темна"
"dashboard" = "Огляд"
"inbounds" = "Вхідні"
"settings" = "Параметри панелі"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index c938449f..53563529 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "Không có proxy ngược nào được thêm."
[menu]
+"theme" = "Chủ đề"
+"dark" = "Tối"
+"ultraDark" = "Siêu tối"
"dashboard" = "Trạng thái hệ thống"
"inbounds" = "Đầu vào khách hàng"
"settings" = "Cài đặt bảng điều khiển"
diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml
index 770caed3..917207a3 100644
--- a/web/translation/translate.zh_CN.toml
+++ b/web/translation/translate.zh_CN.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "未添加反向代理。"
[menu]
+"theme" = "主题"
+"dark" = "暗色"
+"ultraDark" = "超暗色"
"dashboard" = "系统状态"
"inbounds" = "入站列表"
"settings" = "面板设置"
diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml
index 0ad36d27..23709356 100644
--- a/web/translation/translate.zh_TW.toml
+++ b/web/translation/translate.zh_TW.toml
@@ -67,6 +67,9 @@
"emptyReverseDesc" = "未添加反向代理。"
[menu]
+"theme" = "主題"
+"dark" = "深色"
+"ultraDark" = "超深色"
"dashboard" = "系統狀態"
"inbounds" = "入站列表"
"settings" = "面板設定"