Browse Source

Optimize Dark Theme & Ultra Dark (#1889)

---------

Co-authored-by: MHSanaei <[email protected]>
Co-authored-by: Alireza Ahmadi <[email protected]>
Tara Rostami 1 year ago
parent
commit
836ee29b78

+ 2 - 0
README.md

@@ -1,5 +1,7 @@
 # 3X-UI
 
+<p align="center"><img src="./media/3X-UI.png"></p>
+
 **An Advanced Web Panel • Built on Xray Core**
 
 [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)

BIN
media/3X-UI.png


+ 7 - 7
web/assets/codemirror/xq.css

@@ -45,12 +45,12 @@ THE SOFTWARE.
 .cm-s-xq .CodeMirror-activeline-background { background: #e8f2ff; }
 .cm-s-xq .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; }
 
-.dark .cm-s-xq.CodeMirror { background-color: #000000; border-color: #25272a; color: rgb(255 255 255 / 85%); }
-.dark .cm-s-xq.CodeMirror:hover { background-color: rgb(0 50 42 / 20%); border-color: #008771; transition: all .3s; }
-.dark .cm-s-xq div.CodeMirror-selected { background: rgba(0, 0, 0, 0.5);  }
-.dark .cm-s-xq .CodeMirror-line::selection, .dark .cm-s-xq .CodeMirror-line > span::selection, .dark .cm-s-xq .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); }
-.dark .cm-s-xq .CodeMirror-line::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); }
-.dark .cm-s-xq .CodeMirror-gutters { background: rgb(0 0 0 / 30%); border-right: 1px solid #2c3950; }
+.dark .cm-s-xq.CodeMirror { background-color: var(--dark-color-surface-200); border-color: var(--dark-color-surface-300); color: rgb(255 255 255 / 65%); }
+.dark .cm-s-xq.CodeMirror:hover { background-color: rgb(0 50 42 / 30%); border-color: #008771; transition: all .3s; }
+.dark .cm-s-xq div.CodeMirror-selected { background: var(--dark-color-codemirror-line-selection); }
+.dark .cm-s-xq .CodeMirror-line::selection, .dark .cm-s-xq .CodeMirror-line > span::selection, .dark .cm-s-xq .CodeMirror-line > span > span::selection { background: var(--dark-color-codemirror-line-selection); }
+.dark .cm-s-xq .CodeMirror-line::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span > span::-moz-selection { background: var(--dark-color-codemirror-line-selection); }
+.dark .cm-s-xq .CodeMirror-gutters { background: rgb(0 0 0 / 30%); border-right: 1px solid var(--dark-color-surface-300); }
 .dark .cm-s-xq .CodeMirror-guttermarker { color: #FFBD40; }
 .dark .cm-s-xq .CodeMirror-guttermarker-subtle { color: rgb(255 255 255 / 70%); }
 .dark .cm-s-xq .CodeMirror-linenumber { color: rgb(255 255 255 / 50%); }
@@ -80,7 +80,7 @@ THE SOFTWARE.
 
 .Line-Hover{transition: all .2s;}
 .Line-Hover:hover{ background-color: rgba(0, 102, 85, 0.05) !important; }  
-.dark .Line-Hover:hover{ background-color: rgb(0 0 0 / 20%) !important; }
+.dark .Line-Hover:hover{ background-color: var(--dark-color-codemirror-line-hover) !important; }
 
 .CodeMirror-foldmarker { color: #fc8800; text-shadow: #ffd8aa 1px 1px 2px, #ffd8aa -1px -1px 2px, #ffd8aa 1px -1px 2px, #ffd8aa -1px 1px 2px; font-family: arial; line-height: .3; cursor: pointer; }
 .dark .CodeMirror-foldmarker { color: #ffffff; text-shadow: #bbb 1px 1px 2px, #bbb -1px -1px 2px, #bbb 1px -1px 2px, #bbb -1px 1px 2px; font-family: arial; line-height: .3; cursor: pointer; }

+ 136 - 62
web/assets/css/custom.css

@@ -1,4 +1,44 @@
 :root {
+    --color-primary-100: #008771;
+    --dark-color-background: #0a1222;
+    --dark-color-surface-100: #151f31;
+    --dark-color-surface-200: #222d42;
+    --dark-color-surface-300: #2c3950;
+    --dark-color-surface-400: rgba(65, 85, 119, 0.5); /* line */
+    --dark-color-surface-500: #2c3950; /* popover & switch btn */
+    --dark-color-surface-600: #313f5a; /* dropmenu hover */ 
+    --dark-color-surface-700: #111929; /* modals */
+    --dark-color-table-hover: rgba(44, 57, 80, 0.2);
+    --dark-color-text-primary: rgba(255, 255, 255, 0.75);
+    --dark-color-stroke: #2c3950;
+    --dark-color-btn-danger: #cd3838;
+    --dark-color-btn-danger-border: transparent;
+    --dark-color-btn-danger-hover: #e94b4b;
+    --dark-color-tag-bg: rgba(255, 255, 255, 0.05);
+    --dark-color-tag-border:rgba(255, 255, 255, 0.15);
+    --dark-color-tag-color:rgba(255, 255, 255, 0.75);
+    --dark-color-tag-green-bg: #112421;
+    --dark-color-tag-green-border: #195141;
+    --dark-color-tag-green-color: #3ad3ba;
+    --dark-color-tag-purple-bg: #201425;
+    --dark-color-tag-purple-border: #5a2969;
+    --dark-color-tag-purple-color: #d988cd;
+    --dark-color-tag-red-bg: #291515;
+    --dark-color-tag-red-border: #5c2626;
+    --dark-color-tag-red-color: #e04141;
+    --dark-color-tag-orange-bg: #312313;
+    --dark-color-tag-orange-border: #593914;
+    --dark-color-tag-orange-color: #ffa031;
+    --dark-color-tag-blue-bg: #111a2c;
+    --dark-color-tag-blue-border: #1348ab;
+    --dark-color-tag-blue-color: #529fff;
+    --dark-color-codemirror-line-hover: rgba(0, 135, 113, 0.2);
+    --dark-color-codemirror-line-selection: rgba(0, 135, 113, 0.3);
+    --dark-color-login-background: var(--dark-color-background);
+    --dark-color-login-wave: var(--dark-color-surface-200);
+}
+
+html[data-theme='ultra-dark'] {
     --dark-color-background: #21242a;
     --dark-color-surface-100: #0c0e12;
     --dark-color-surface-200: #222327;
@@ -6,11 +46,40 @@
     --dark-color-surface-400: rgba(255, 255, 255, 0.1);
     --dark-color-surface-500: #3b404b;
     --dark-color-surface-600: #505663;
+    --dark-color-surface-700: #101113;
+    --dark-color-table-hover: rgba(89, 89, 89, 0.15);
     --dark-color-text-primary: rgb(255 255 255 / 85%);
     --dark-color-stroke: #202025;
-    --dark-color-btn-danger: #cd3838;
-    --dark-color-btn-danger-border: transparent;
-    --dark-color-btn-danger-hover: #e94b4b;
+    --dark-color-tag-green-bg: #112421;
+    --dark-color-tag-green-border: #1d5f4d;
+    --dark-color-tag-green-color: #59cbac;
+    --dark-color-tag-purple-bg: #241121;
+    --dark-color-tag-purple-border: #5a2969;
+    --dark-color-tag-purple-color: #d686ca;
+    --dark-color-tag-red-bg: #2a1215;
+    --dark-color-tag-red-border: #58181c;
+    --dark-color-tag-red-color: #e84749;
+    --dark-color-tag-orange-bg: #2b1d11;
+    --dark-color-tag-orange-border: #593815;
+    --dark-color-tag-orange-color: #e89a3c;
+    --dark-color-tag-blue-bg: #111a2c;
+    --dark-color-tag-blue-border: #0f367e;
+    --dark-color-tag-blue-color: #3c89e8;
+    --dark-color-codemirror-line-hover: rgba(85, 85, 85, 0.3);
+    --dark-color-codemirror-line-selection: rgba(85, 85, 85, 0.4);
+    --dark-color-login-background: #0a2227;
+    --dark-color-login-wave: #0f2d32;
+    .ant-dropdown-menu-dark {
+        background-color: var(--dark-color-surface-500);
+    }
+    .dark .ant-dropdown-menu-submenu-title:hover,
+    .dark .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled),
+    .dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
+        background-color: var(--dark-color-surface-300);
+    }
+    .dark .waves-header {
+        background-color: #0a2227;
+    }
 }
 
 html,
@@ -31,7 +100,7 @@ body {
     font-feature-settings: "tnum";
 }
 html {
-    --antd-wave-shadow-color: #008771;
+    --antd-wave-shadow-color: var(--color-primary-100);
     line-height: 1.15;
     text-size-adjust: 100%;
     -webkit-text-size-adjust: 100%;
@@ -41,7 +110,7 @@ html {
 }
 
 ::selection {
-    color: #008771;
+    color: var(--color-primary-100);
     background-color: #cfe8e4;
 }
 
@@ -149,7 +218,7 @@ style attribute {
         padding: 0.5rem;
     }
     .ant-modal-body {
-        padding: 10px;
+        padding: 20px;
     }
     .ant-form-item-label {
         line-height: 1.5;
@@ -233,7 +302,7 @@ style attribute {
 .ant-menu-submenu-active,
 .ant-menu-submenu-title:hover,
 .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open {
-    color: #008771;
+    color: var(--color-primary-100);
     background-color: rgb(232 244 242);
     border-radius: 0.5rem;
 }
@@ -495,14 +564,14 @@ style attribute {
 }
 
 .normal-icon:hover {
-    color: #008771;
+    color: var(--color-primary-100);
 }
 
 /* DARK THEME */
 
 .dark ::selection {
     color: #fff;
-    background-color: #008771;
+    background-color: var(--color-primary-100);
 }
 
 .dark .normal-icon:hover {
@@ -523,6 +592,7 @@ style attribute {
 
 .dark .ant-card-hoverable:hover,
 .dark .ant-space-item > .ant-tabs:hover {
+    /* box-shadow: 0 1px 10px -1px rgb(154 175 238 / 80%); */
     box-shadow: 0 2px 8px transparent;
 }
 
@@ -612,7 +682,7 @@ style attribute {
 .dark .ant-calendar-year-panel-year,
 .dark .ant-calendar-month-panel-month,
 .dark .ant-calendar-decade-panel-decade {
-    color: rgba(255, 255, 255, 0.85);
+    color: var(--dark-color-text-primary);
 }
 
 .dark .ant-list-item-meta-description {
@@ -643,7 +713,7 @@ style attribute {
 .dark .ant-calendar-time-picker-inner {
     background-color: var(--dark-color-surface-200);
     border-color: var(--dark-color-surface-300);
-    color: rgba(255, 255, 255, 0.85);
+    color: var(--dark-color-text-primary);
 }
 
 .dark .ant-select-selection:hover,
@@ -653,27 +723,27 @@ style attribute {
 .dark .ant-input:hover,
 .dark .ant-input:focus {
     background-color: rgba(0, 135, 113, 0.3);
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-btn:not(.ant-btn-primary):not(.ant-btn-danger) {
-    color: rgba(255, 255, 255, 0.85);
+    color: var(--dark-color-text-primary);
     background-color: rgb(10 117 87 / 30%);
-    border: 1px solid #008771;
+    border: 1px solid var(--color-primary-100);
 }
 
 .dark .ant-radio-button-wrapper,
 .dark .ant-radio-button-wrapper:before {
-    color: rgb(255 255 255 / 65%);
+    color: var(--dark-color-text-primary);
     background-color: rgba(0, 135, 113, 0.3);
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),
 .dark .ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger) {
     color: #fff;
     background-color: rgb(10 117 87 / 50%);
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-btn-primary[disabled],
@@ -689,7 +759,7 @@ style attribute {
     > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
     > td,
 .dark .client-table-odd-row {
-    background-color: rgb(89 89 89 / 15%);
+    background-color: var(--dark-color-table-hover);
 }
 
 .dark .ant-table-row-expand-icon {
@@ -699,9 +769,9 @@ style attribute {
 }
 
 .dark .ant-table-row-expand-icon:hover {
-    color: #008771;
+    color: var(--color-primary-100);
     background-color: #fff0;
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-switch:not(.ant-switch-checked),
@@ -713,7 +783,6 @@ style attribute {
     stroke: var(--dark-color-stroke) !important;
 }
 
-.ant-dropdown-menu-dark,
 .dark .ant-popover-inner {
     background-color: var(--dark-color-surface-500);
 }
@@ -738,46 +807,46 @@ style attribute {
 }
 
 .dark .ant-tag {
-    color: rgba(255, 255, 255, 0.85);
-    background-color: rgba(255, 255, 255, 0.08);
-    border-color: rgba(255, 255, 255, 0.15);
+    color: var(--dark-color-tag-color);
+    background-color: var(--dark-color-tag-bg);
+    border-color: var(--dark-color-tag-border);
 }
 
 .dark .ant-tag-blue {
-    background-color: #111a2c;
-    border-color: #0f367e;
-    color: #3c89e8;
+    background-color: var(--dark-color-blue-red-bg);
+    border-color: var(--dark-color-tag-blue-border);
+    color: var(--dark-color-tag-blue-color);
 }
 
 .dark .ant-tag-red,
 .dark .ant-alert-error {
-    background-color: #2a1215;
-    border-color: #58181c;
-    color: #e84749;
+    background-color: var(--dark-color-tag-red-bg);
+    border-color: var(--dark-color-tag-red-border);
+    color: var(--dark-color-tag-red-color);
 }
 
 .dark .ant-tag-orange,
 .dark .ant-alert-warning {
-    background-color: #2b1d11;
-    border-color: #593815;
-    color: #e89a3c;
+    background-color: var(--dark-color-tag-orange-bg);
+    border-color: var(--dark-color-tag-orange-border);
+    color: var(--dark-color-tag-orange-color);
 }
 
 .dark .ant-tag-green {
-    background-color: #112421;
-    border-color: #195544;
-    color: #59cbac;
+    background-color: var(--dark-color-tag-green-bg);
+    border-color: var(--dark-color-tag-green-border);
+    color: var(--dark-color-tag-green-color);
 }
 
 .dark .ant-tag-purple {
-    background-color: #241121;
-    border-color: #5a2969;
-    color: #d686ca;
+    background-color: var(--dark-color-tag-purple-bg);
+    border-color: var(--dark-color-tag-purple-border);
+    color: var(--dark-color-tag-purple-color);
 }
 
 .dark .ant-modal-content,
 .dark .ant-modal-header {
-    background-color: #101113;
+    background-color: var(--dark-color-surface-700);
 }
 
 .dark .ant-calendar-next-month-btn-day .ant-calendar-date,
@@ -786,13 +855,13 @@ style attribute {
 }
 
 .dark .ant-calendar-selected-day .ant-calendar-date {
-    background-color: #008771 !important;
+    background-color: var(--color-primary-100) !important;
     color: #fff;
 }
 
 .dark .ant-calendar-date:hover,
 .dark .ant-calendar-time-picker-select li:hover {
-    background-color: var(--dark-color-surface-300);
+    background-color: var(--dark-color-surface-600);
     color: #fff;
 }
 
@@ -806,13 +875,15 @@ style attribute {
     color: #fff;
     font-weight: 600;
     outline: none;
-    background-color: #008771;
+    background-color: var(--color-primary-100);
 }
 
 .dark .ant-calendar-time-picker-select {
     border-right-color: var(--dark-color-surface-300);
 }
 
+.has-warning .ant-select-selection,
+.has-warning .ant-select-selection:hover,
 .has-warning .ant-input,
 .has-warning .ant-input:hover {
     background-color: #ffeee1;
@@ -827,6 +898,8 @@ style attribute {
     border-color: #fec093;
 }
 
+.dark .has-warning .ant-select-selection,
+.dark .has-warning .ant-select-selection:hover,
 .dark .has-warning .ant-input,
 .dark .has-warning .ant-input:hover {
     border-color: #784e1d;
@@ -842,7 +915,7 @@ style attribute {
 }
 
 .dark .has-success .anticon {
-    color: #61bf39;
+    color: var(--color-primary-100);
     animation-name: diffZoomIn1 !important;
 }
 
@@ -888,19 +961,19 @@ style attribute {
 }
 
 .ant-calendar-today .ant-calendar-date {
-    color: #008771;
+    color: var(--color-primary-100);
     font-weight: 700;
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-calendar-today .ant-calendar-date {
     color: #fff;
     font-weight: 700;
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 
 .ant-calendar-selected-day .ant-calendar-date {
-    background: #008771;
+    background: var(--color-primary-100);
     color: #ffffff;
 }
 
@@ -934,7 +1007,7 @@ li.ant-select-dropdown-menu-item:empty:after {
 .ant-select-dropdown.ant-select-dropdown--multiple
     .ant-select-dropdown-menu-item-selected:hover
     .ant-select-selected-icon {
-    color: #008771;
+    color: var(--color-primary-100);
 }
 .ant-select-selection:hover,
 .ant-input-number-focused,
@@ -943,7 +1016,7 @@ li.ant-select-dropdown-menu-item:empty:after {
 }
 
 .dark .ant-input-number-handler:active {
-    background-color: #008771;
+    background-color: var(--color-primary-100);
 }
 
 .dark .ant-input-number-handler:hover .ant-input-number-handler-down-inner,
@@ -979,10 +1052,11 @@ li.ant-select-dropdown-menu-item:empty:after {
     color: rgba(255, 255, 255, 0.35);
 }
 
+.ant-dropdown-menu-dark,
 .dark .ant-calendar-year-panel-year:hover,
 .dark .ant-calendar-month-panel-month:hover,
 .dark .ant-calendar-decade-panel-decade:hover {
-    background-color: var(--dark-color-surface-600);
+    background-color: var(--dark-color-surface-200);
 }
 
 .dark .ant-calendar-header a:hover {
@@ -1014,7 +1088,7 @@ li.ant-select-dropdown-menu-item:empty:after {
     .ant-calendar-decade-panel-selected-cell
     .ant-calendar-decade-panel-decade:hover {
     color: #fff;
-    background-color: #008771;
+    background-color: var(--color-primary-100);
 }
 
 .dark .ant-calendar-last-month-cell .ant-calendar-date,
@@ -1028,8 +1102,8 @@ li.ant-select-dropdown-menu-item:empty:after {
 
 .dark .ant-calendar-today .ant-calendar-date:hover {
     color: #fff;
-    border-color: #008771;
-    background-color: #008771;
+    border-color: var(--color-primary-100);
+    background-color: var(--color-primary-100);
 }
 
 .dark
@@ -1052,8 +1126,8 @@ li.ant-select-dropdown-menu-item:empty:after {
 }
 
 .dark .ant-checkbox-checked .ant-checkbox-inner {
-    background-color: #008771;
-    border-color: #008771;
+    background-color: var(--color-primary-100);
+    border-color: var(--color-primary-100);
 }
 
 .dark .ant-calendar-input {
@@ -1117,7 +1191,7 @@ li.ant-select-dropdown-menu-item:empty:after {
 .dark .ant-dropdown-menu-submenu-title:hover,
 .dark .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled),
 .dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
-    background-color: var(--dark-color-surface-300);
+    background-color: var(--dark-color-surface-600);
 }
 
 .ant-select-dropdown,
@@ -1140,7 +1214,7 @@ li.ant-select-dropdown-menu-item:empty:after {
     border-radius: 1rem;
 }
 
-.ant-input-group-addon:not(:first-child):not(:last-child), .ant-input-group-wrap:not(:first-child):not(:last-child), .ant-input-group>.ant-input:not(:first-child):not(:last-child) {
+.ant-input-group-addon:not(:first-child):not(:last-child) {
     border-radius: 0rem 1rem 1rem 0rem;
 }
 
@@ -1157,9 +1231,9 @@ b, strong {
 }
 
 .dark .ant-message-notice-content {
-    background-color: #000000;
-    border: 1px solid #303134;
-    color: rgba(255, 255, 255, 0.85);
+    background-color: var(--dark-color-surface-200);
+    border: 1px solid var(--dark-color-surface-300); 
+    color: var(--dark-color-text-primary);
 }
 
 .ant-btn-danger {
@@ -1174,4 +1248,4 @@ b, strong {
 
 .dark .ant-alert-close-icon .anticon-close:hover {
     color: rgb(255 255 255);
-}
+}

+ 16 - 16
web/assets/persian-datepicker/persian-datepicker.min.css

@@ -206,15 +206,15 @@ jdp-container .jdp-day-name {
 }
 jdp-container .jdp-day-name.today,
 jdp-container .jdp-day.today {
-    border-color: #008771;
-    color: #008771;
+    border-color: var(--color-primary-100);
+    color: var(--color-primary-100);
     font-weight: 700;
 }
 .dark jdp-container .jdp-day-name.selected,
 .dark jdp-container .jdp-day.selected,
 jdp-container .jdp-day-name.selected,
 jdp-container .jdp-day.selected {
-    background-color: #008771 !important;
+    background-color: var(--color-primary-100) !important;
     color: #fff !important;
     opacity: 1 !important;
 }
@@ -267,7 +267,7 @@ jdp-container .jdp-btn-empty,
 jdp-container .jdp-btn-today {
     background: #00877000;
     border-radius: 5px;
-    color: #008771;
+    color: var(--color-primary-100);
     cursor: pointer;
     display: inline-block;
     font-size: 90%;
@@ -369,26 +369,26 @@ jdp-container .jdp-time-container.jdp-only-time .jdp-time:after {
 }
 
 .dark jdp-container .jdp-days {
-    border-color: #32353b;
+    border-color: var(--dark-color-surface-400);
 }
 
 .dark jdp-overlay {
     background-color: #181f2c;
 }
 .dark jdp-container {
-    background: #000000;
+    background: var(--dark-color-background);
     border-color: #2c3950;
     box-shadow: 0 2px 8px rgba(0,0,0,.15);
     color: #fff;
 }
 .dark jdp-container .jdp-icon-minus,
 .dark jdp-container .jdp-icon-plus {
-    outline-color: #32353b;
+    outline-color: var(--dark-color-surface-600);
 }
 
 .dark jdp-container .jdp-icon-minus:hover,
 .dark jdp-container .jdp-icon-plus:hover {
-    background-color: #32353b;
+    background-color: var(--dark-color-surface-600);
 }
 
 .dark jdp-container .jdp-months,
@@ -405,27 +405,27 @@ jdp-container .jdp-time-container.jdp-only-time .jdp-time:after {
 .dark jdp-container .jdp-year,
 .dark jdp-container .jdp-year input,
 .dark jdp-container .jdp-year select {
-    background: #000000;
-    color: rgba(255, 255, 255, 0.85);
+    background: var(--dark-color-background);
+    color: var(--dark-color-text-primary);
 }
 .dark jdp-container .jdp-day,
 .dark jdp-container .jdp-day-name {
     border: 1px solid transparent;
-    color: rgba(255, 255, 255, 0.85);
+    color: var(--dark-color-text-primary);
 }
 .dark jdp-container .jdp-day-name.today,
 .dark jdp-container .jdp-day.today {
-    border-color: #008771;
+    border-color: var(--color-primary-100);
 }
 .dark jdp-container .jdp-day.disabled-day {
     opacity: 0.15;
 }
 .dark jdp-container .jdp-day:not(.disabled-day):hover {
-    background-color: #32353b;
+    background-color: var(--dark-color-surface-600);
     color: #fff;
 }
 .dark jdp-container .jdp-footer {
-    border-color: #32353b;
+    border-color: var(--dark-color-surface-400);
 }
 .dark jdp-container .jdp-btn-close,
 .dark jdp-container .jdp-btn-empty,
@@ -446,10 +446,10 @@ jdp-container .jdp-time-container.jdp-only-time .jdp-time:after {
 }
 
 .dark jdp-container .jdp-time-container .jdp-time select:hover {
-    background-color: #32353b;
+    background-color: var(--dark-color-surface-600);
     color: #fff;
 }
 
 .dark jdp-container .jdp-time-container .jdp-time select {
-    border: 1px solid #32353b;
+    border: 1px solid var(--dark-color-surface-600);
 }

+ 107 - 96
web/html/login.html

@@ -49,6 +49,9 @@
     border-radius: 2rem;
     padding: 3rem;
     transition: all 0.3s;
+    user-select:none;
+    -webkit-user-select:none;
+    -moz-user-select: none;
   }
   #login:hover {
     box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
@@ -68,10 +71,10 @@
     z-index: 0;
   }
   .dark .under {
-    background-color: #0f2d32;
+    background-color: var(--dark-color-login-wave);
   }
   .dark #login {
-    background-color: #101113;
+    background-color: var(--dark-color-surface-100);
   }
   .dark h1 {
     color: rgba(255, 255, 255);
@@ -199,7 +202,7 @@
     z-index: -1;
   }
   .dark .waves-header {
-    background-color: #0a2227;
+    background-color: var(--dark-color-login-background);
   }
   .waves-inner-header {
     height: 50vh;
@@ -219,7 +222,7 @@
     animation: move-forever 25s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
   }
   .dark .parallax > use {
-    fill: #0f2d32;
+    fill: var(--dark-color-login-wave);
   }
   .parallax > use:nth-child(1) {
     animation-delay: -2s;
@@ -373,98 +376,106 @@
   }
 </style>
 <body>
-<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
-  <transition name="list" appear>
-    <a-layout-content class="under" style="min-height: 0;">
-      <div class="waves-header">
-        <div class="waves-inner-header"></div>
-        <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
-          viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
-          <defs>
-            <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
-          </defs>
-          <g class="parallax">
-            <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" />
-            <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" />
-            <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" />
-            <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" />
-          </g>
-        </svg>
-      </div>
-      <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
-        <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
-          <a-row type="flex" justify="center">
-            <a-col style="width: 100%;">
-              <h1 class="title headline zoom">
-                <span class="words-wrapper">
-                  <b class="is-visible">{{ i18n "pages.login.title" }}</b>
-                  <b>Hello!</b>
-                </span>
-              </h1>
-            </a-col>
-          </a-row>
-          <a-row type="flex" justify="center">
-            <a-col span="24">
-              <a-form>
-                <a-form-item>
-                  <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
-                    @keydown.enter.native="login" autofocus>
-                    <a-icon slot="prefix" type="user" style="font-size: 16px;" />
-                  </a-input>
-                </a-form-item>
-                <a-form-item>
-                  <password-input icon="lock" v-model.trim="user.password" placeholder='{{ i18n "password" }}'
-                    @keydown.enter.native="login">
-                  </password-input>
-                </a-form-item>
-                <a-form-item v-if="secretEnable">
-                  <password-input icon="key" v-model.trim="user.loginSecret" placeholder='{{ i18n "secretToken" }}'
-                    @keydown.enter.native="login">
-                  </password-input>
-                  </a-input>
-                </a-form-item>
-                <a-form-item>
-                  <a-row justify="center" class="centered">
-                    <div class="wave-btn-bg wave-btn-bg-cl"
-                      :style="loading ? { width: '52px' } : { display: 'inline-block' }">
-                      <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login"
-                        :icon="loading ? 'poweroff' : undefined">
-                        [[ loading ? '' : '{{ i18n "login" }}' ]]
-                      </a-button>
-                    </div>
-                  </a-row>
-                </a-form-item>
-                <a-form-item>
-                  <a-row justify="center" class="centered">
-                    <a-col :span="24">
-                      <a-select ref="selectLang" v-model="lang" @change="setLang(lang)" style="width: 150px;"
-                        :dropdown-class-name="themeSwitcher.currentTheme">
-                        <a-select-option :value="l.value" label="English" 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>
-                    </a-col>
-                  </a-row>
-                </a-form-item>
-                <a-form-item>
-                  <a-row justify="center" class="centered">
-                    <a-col>
-                      <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
-                    </a-col>
-                    <a-col>
-                      <theme-switch />
-                    </a-col>
-                  </a-row>
-                </a-form-item>
-              </a-form>
-            </a-col>
-          </a-row>
-        </a-col>
-      </a-row>
-    </a-layout-content>
-  </transition>
-</a-layout>
+  <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
+    <transition name="list" appear>
+      <a-layout-content class="under" style="min-height: 0;">
+        <div class="waves-header">
+          <div class="waves-inner-header"></div>
+          <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+               viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
+            <defs>
+              <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
+            </defs>
+            <g class="parallax">
+              <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" />
+              <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" />
+              <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" />
+              <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" />
+            </g>
+          </svg>
+        </div>
+        <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
+          <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
+            <a-row type="flex" justify="center">
+              <a-col style="width: 100%;">
+                <h1 class="title headline zoom">
+                  <span class="words-wrapper">
+                    <b class="is-visible">{{ i18n "pages.login.hello" }}</b>
+                    <b>{{ i18n "pages.login.title" }}</b>
+                  </span>
+                </h1>
+              </a-col>
+            </a-row>
+            <a-row type="flex" justify="center">
+              <a-col span="24">
+                <a-form>
+                  <a-form-item>
+                    <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
+                             @keydown.enter.native="login" autofocus>
+                      <a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon>
+                    </a-input>
+                  </a-form-item>
+                  <a-form-item>
+                    <password-input icon="lock" v-model.trim="user.password"
+                                    placeholder='{{ i18n "password" }}'
+                                    @keydown.enter.native="login">
+                    </password-input>
+                  </a-form-item>
+                  <a-form-item v-if="secretEnable">
+                    <password-input icon="key" v-model.trim="user.loginSecret"
+                                    placeholder='{{ i18n "secretToken" }}'
+                                    @keydown.enter.native="login">
+                    </password-input>
+                  </a-form-item>
+                  <a-form-item>
+                    <a-row justify="center" class="centered">
+                      <div style="height: 50px;" class="wave-btn-bg wave-btn-bg-cl"
+                           :style="loading ? { width: '52px' } : { display: 'inline-block' }">
+                        <a-button class="ant-btn-primary-login" type="primary"
+                                  :loading="loading" @click="login"
+                                  :icon="loading ? 'poweroff' : undefined">
+                                  [[ loading ? '' : '{{ i18n "login" }}' ]]
+                        </a-button>
+                      </div>
+                    </a-row>
+                  </a-form-item>
+                  <a-form-item>
+                    <a-row justify="center" class="centered">
+                      <a-col :span="24">
+                        <a-select ref="selectLang" v-model="lang"
+                                  @change="setLang(lang)" style="width: 150px;"
+                                  :dropdown-class-name="themeSwitcher.currentTheme">
+                          <a-select-option :value="l.value" label="English" 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>
+                      </a-col>
+                    </a-row>
+                  </a-form-item>
+                  <a-form-item>
+                    <a-row justify="center" class="centered">
+                      <a-col>
+                        <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
+                      </a-col>
+                      <a-col>
+                        <theme-switch></theme-switch>
+                        <a-checkbox v-if="themeSwitcher.isDarkTheme" style="padding-left: 1rem; vertical-align: middle;"
+                                    :checked="themeSwitcher.isUltra"
+                                    @click="themeSwitcher.toggleUltra()">
+                                    Ultra
+                        </a-checkbox>
+                      </a-col>
+                    </a-row>
+                  </a-form-item>
+                </a-form>
+              </a-col>
+            </a-row>
+          </a-col>
+        </a-row>
+      </a-layout-content>
+    </transition>
+  </a-layout>
 {{template "js" .}}
 {{template "component/themeSwitcher" .}}
 {{template "component/password" .}}

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

@@ -15,10 +15,6 @@
     <a-icon type="tool"></a-icon>
     <span><b>{{ i18n "menu.xray"}}</b></span>
 </a-menu-item>
-<!--<a-menu-item key="{{ .base_path }}panel/clients">-->
-<!--    <a-icon type="laptop"></a-icon>-->
-<!--    <span>Client</span>-->
-<!--</a-menu-item>-->
 <a-menu-item key="{{ .base_path }}logout">
     <a-icon type="logout"></a-icon>
     <span><b>{{ i18n "menu.logout"}}</b></span>
@@ -31,7 +27,13 @@
     <a-menu :theme="themeSwitcher.currentTheme" mode="inline" selected-keys="">
         <a-menu-item mode="inline">
             <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
-            <theme-switch />
+            <theme-switch>
+            </theme-switch>
+            <a-checkbox v-if="themeSwitcher.isDarkTheme" style="padding-left: 1rem; vertical-align: middle;"
+                        :checked="themeSwitcher.isUltra"
+                        @click="themeSwitcher.toggleUltra()">
+                        Ultra
+            </a-checkbox>
         </a-menu-item>
     </a-menu>
     <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']"
@@ -50,7 +52,13 @@
     <a-menu :theme="themeSwitcher.currentTheme" mode="inline" selected-keys="">
         <a-menu-item mode="inline">
             <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
-            <theme-switch />
+            <theme-switch>
+            </theme-switch>
+            <a-checkbox v-if="themeSwitcher.isDarkTheme" style="padding-left: 1rem; vertical-align: middle;"
+                        :checked="themeSwitcher.isUltra"
+                        @click="themeSwitcher.toggleUltra()">
+                        Ultra
+            </a-checkbox>
         </a-menu-item>
     </a-menu>
     <a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']"

+ 23 - 7
web/html/xui/component/themeSwitch.html

@@ -10,32 +10,48 @@
 <script>
   function createThemeSwitcher() {
     const isDarkTheme = localStorage.getItem('dark-mode') === 'true';
+    const isUltra = localStorage.getItem('isUltraDarkThemeEnabled') === 'true';
+    if (isUltra) {
+      document.documentElement.setAttribute('data-theme', 'ultra-dark');
+    }
     const theme = isDarkTheme ? 'dark' : 'light';
-    document.querySelector('body').setAttribute('class', theme)
+    document.querySelector('body').setAttribute('class', theme);
     return {
       isDarkTheme,
+      isUltra,
       get currentTheme() {
         return this.isDarkTheme ? 'dark' : 'light';
       },
       toggleTheme() {
         this.isDarkTheme = !this.isDarkTheme;
         localStorage.setItem('dark-mode', this.isDarkTheme);
-        document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light')
+        document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light');
         document.getElementById('message').className = themeSwitcher.currentTheme;
       },
+      toggleUltra() {
+        this.isUltra = !this.isUltra;
+        if (this.isUltra) {
+          document.documentElement.setAttribute('data-theme', 'ultra-dark');
+        } else {
+          document.documentElement.removeAttribute('data-theme');
+        }
+        localStorage.setItem('isUltraDarkThemeEnabled', this.isUltra.toString());
+      }
     };
   }
-
   const themeSwitcher = createThemeSwitcher();
-
   Vue.component('theme-switch', {
     props: [],
     template: `{{template "component/themeSwitchTemplate"}}`,
-    data: () => ({ themeSwitcher }),
+    data: () => ({
+      themeSwitcher
+    }),
     mounted() {
-      this.$message.config({getContainer: () => document.getElementById('message')});
+      this.$message.config({
+        getContainer: () => document.getElementById('message')
+      });
       document.getElementById('message').className = themeSwitcher.currentTheme;
     }
   });
 </script>
-{{end}}
+{{end}}

+ 5 - 17
web/html/xui/index.html

@@ -10,23 +10,11 @@
             margin-inline: 0.3rem;
         }
     }
-
     .ant-col-sm-24 {
         margin-top: 10px;
     }
-
     .ant-card-dark h2 {
-        color: hsla(0, 0%, 100%, .65);
-    }
-
-    .dark .ant-card-hoverable:hover,
-    .dark .ant-space-item > .ant-tabs:hover {
-        transform: scale(0.987);
-        outline-color: #40434d;
-    }
-
-    .dark .ant-card-bordered {
-        outline: 2px solid var(--dark-color-background);
+        color: var(--dark-color-text-primary);
     }
 </style>
 
@@ -104,8 +92,8 @@
                     <a-col :sm="24" :lg="12">
                         <a-card hoverable>
                             <b>{{ i18n "pages.index.operationHours" }}:</b>
-                            <a-tag color="green">Xray [[ formatSecond(status.appStats.uptime) ]]</a-tag>
-                            <a-tag color="green">OS [[ formatSecond(status.uptime) ]]</a-tag>
+                            <a-tag :color="status.xray.color">Xray: [[ formatSecond(status.appStats.uptime) ]]</a-tag>
+                            <a-tag color="green">OS: [[ formatSecond(status.uptime) ]]</a-tag>
                         </a-card>
                     </a-col>
                     <a-col :sm="24" :lg="12">
@@ -153,10 +141,10 @@
                         <a-card hoverable>
                             <b>{{ i18n "usage"}}:</b>
                             <a-tag color="green">
-                            RAM [[ sizeFormat(status.appStats.mem) ]]
+                                RAM: [[ sizeFormat(status.appStats.mem) ]]
                             </a-tag>
                             <a-tag color="green">
-                                Threads [[ status.appStats.threads ]]
+                                Threads: [[ status.appStats.threads ]]
                             </a-tag>
                         </a-card>
                     </a-col>

+ 3 - 3
web/html/xui/xray.html

@@ -74,8 +74,8 @@
                 </transition>
                 <a-space direction="vertical">
                     <a-card hoverable style="margin-bottom: .5rem;">
-                        <a-row>
-                            <a-col :xs="24" :sm="8" style="padding: 4px;">
+                        <a-row style="display: flex; flex-wrap: wrap; align-items: center;">
+                            <a-col :xs="24" :sm="10" style="padding: 4px;">
                                 <a-space direction="horizontal">
                                     <a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
                                     <a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
@@ -89,7 +89,7 @@
                                     </a-popover>
                                 </a-space>
                             </a-col>
-                            <a-col :xs="24" :sm="16">
+                            <a-col :xs="24" :sm="14">
                                 <template>
                                     <div>
                                         <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">

+ 1 - 0
web/translation/translate.en_US.toml

@@ -70,6 +70,7 @@
 "link" = "Manage"
 
 [pages.login]
+"hello" = "Hello"
 "title" = "Welcome"
 "loginAgain" = "Your session has expired, please log in again"
 

+ 1 - 0
web/translation/translate.es_ES.toml

@@ -70,6 +70,7 @@
 "link" = "Gestionar"
 
 [pages.login]
+"hello" = "Hola"
 "title" = "Bienvenido"
 "loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente."
 

+ 1 - 0
web/translation/translate.fa_IR.toml

@@ -70,6 +70,7 @@
 "link" = "مدیریت"
 
 [pages.login]
+"hello" = "سلام"
 "title" = "خوش‌آمدید"
 "loginAgain" = "مدت زمان استفاده به‌اتمام‌رسیده، لطفا دوباره وارد شوید"
 

+ 1 - 0
web/translation/translate.id_ID.toml

@@ -70,6 +70,7 @@
 "link" = "Kelola"
 
 [pages.login]
+"hello" = "Halo"
 "title" = "Selamat Datang"
 "loginAgain" = "Sesi Anda telah berakhir, harap masuk kembali"
 

+ 1 - 0
web/translation/translate.ru_RU.toml

@@ -70,6 +70,7 @@
 "link" = "менеджмент"
 
 [pages.login]
+"hello" = "Привет"
 "title" = "Добро пожаловать"
 "loginAgain" = "Время пребывания в сети вышло. Пожалуйста, войдите в систему снова"
 

+ 1 - 0
web/translation/translate.uk_UA.toml

@@ -70,6 +70,7 @@
 "link" = "Керувати"
 
 [pages.login]
+"hello" = "Привіт"
 "title" = "Ласкаво просимо"
 "loginAgain" = "Ваш сеанс закінчився, увійдіть знову"
 

+ 1 - 0
web/translation/translate.vi_VN.toml

@@ -70,6 +70,7 @@
 "link" = "Quản lý"
 
 [pages.login]
+"hello" = "Xin chào"
 "title" = "Chào mừng"
 "loginAgain" = "Thời hạn đăng nhập đã hết. Vui lòng đăng nhập lại."
 

+ 2 - 1
web/translation/translate.zh_Hans.toml

@@ -42,7 +42,7 @@
 "online" = "在线"
 "domainName" = "域名"
 "monitor" = "监听"
-"certificate" = "Digitales Zertifikat"
+"certificate" = "数字证书"
 "fail" = "失败"
 "success" = "成功"
 "getVersion" = "获取版本"
@@ -70,6 +70,7 @@
 "link" = "管理"
 
 [pages.login]
+"hello" = "你好"
 "title" = "欢迎"
 "loginAgain" = "登录时效已过,请重新登录"