mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-13 05:51:58 +00:00
feat(frontend): unify theming on vanilla AD-Vue light/dark/ultra-dark
The legacy panel CSS (custom.min.css ported as legacy.css) tinted every non-primary button teal-green via .dark .ant-btn:not(.ant-btn-primary) overrides while AD-Vue 4's darkAlgorithm kept primary buttons blue — producing the mixed blue/green button look on dark mode. Drop legacy.css entirely and let AD-Vue 4's algorithms own the palette. Centralize antdThemeConfig in useTheme.js so every page resolves to the same source of truth (light = defaultAlgorithm, dark = darkAlgorithm, ultra-dark = darkAlgorithm + deeper colorBgBase/Layout/Container/ Elevated tokens). Each page's <a-config-provider> now imports the shared computed instead of defining its own copy. Drops the 67 KB legacy CSS chunk; per-page CSS bundles fall to ≤5.9 KB. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
1e1a585541
commit
5f1aba28b0
12 changed files with 38 additions and 56 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { reactive, computed, watchEffect } from 'vue';
|
||||
import { theme as antdTheme } from 'ant-design-vue';
|
||||
|
||||
// Single shared theme state. `import { theme } from '@/composables/useTheme.js'`
|
||||
// from any component to read/toggle. Boot side-effects (apply current
|
||||
|
|
@ -24,6 +25,27 @@ export const theme = reactive({
|
|||
|
||||
export const currentTheme = computed(() => (theme.isDark ? 'dark' : 'light'));
|
||||
|
||||
// AD-Vue 4 theme config consumed by every page's <a-config-provider>.
|
||||
// Three modes — light / dark / ultra-dark — all share AD-Vue's vanilla
|
||||
// blue primary. Ultra-dark layers deeper background tokens on top of
|
||||
// darkAlgorithm so layouts/cards/popups all darken together.
|
||||
const ULTRA_DARK_TOKENS = {
|
||||
colorBgBase: '#000',
|
||||
colorBgLayout: '#000',
|
||||
colorBgContainer: '#0a0a0a',
|
||||
colorBgElevated: '#141414',
|
||||
};
|
||||
|
||||
export const antdThemeConfig = computed(() => {
|
||||
if (!theme.isDark) {
|
||||
return { algorithm: antdTheme.defaultAlgorithm };
|
||||
}
|
||||
return {
|
||||
algorithm: antdTheme.darkAlgorithm,
|
||||
token: theme.isUltra ? ULTRA_DARK_TOKENS : undefined,
|
||||
};
|
||||
});
|
||||
|
||||
export function toggleTheme() {
|
||||
theme.isDark = !theme.isDark;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createApp } from 'vue';
|
||||
import Antd, { message } from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
import '@/styles/legacy.css';
|
||||
|
||||
import { setupAxios } from '@/api/axios-init.js';
|
||||
import '@/composables/useTheme.js';
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { createApp } from 'vue';
|
||||
import Antd, { message } from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
// Legacy panel CSS — overrides AD-Vue defaults to match the
|
||||
// pre-migration look (palette, dark mode contrast, tag colors,
|
||||
// table/tooltip styling). Loaded after AD-Vue's reset so its
|
||||
// rules win.
|
||||
import '@/styles/legacy.css';
|
||||
|
||||
import { setupAxios } from '@/api/axios-init.js';
|
||||
// Importing useTheme triggers the boot side-effect that applies the
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createApp } from 'vue';
|
||||
import Antd, { message } from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
import '@/styles/legacy.css';
|
||||
|
||||
import { setupAxios } from '@/api/axios-init.js';
|
||||
// Importing this module triggers the boot side-effect that applies the
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { theme as antdTheme, Modal, message } from 'ant-design-vue';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import {
|
||||
SwapOutlined,
|
||||
PieChartOutlined,
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
|
||||
import { HttpUtil, SizeFormatter, RandomUtil } from '@/utils';
|
||||
import { Inbound } from '@/models/inbound.js';
|
||||
import { theme as themeState } from '@/composables/useTheme.js';
|
||||
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
|
||||
import { useMediaQuery } from '@/composables/useMediaQuery.js';
|
||||
import AppSidebar from '@/components/AppSidebar.vue';
|
||||
import CustomStatistic from '@/components/CustomStatistic.vue';
|
||||
|
|
@ -28,10 +28,6 @@ import { useInbounds } from './useInbounds.js';
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const antdThemeConfig = computed(() => ({
|
||||
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}));
|
||||
|
||||
const {
|
||||
fetched,
|
||||
refreshing,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { theme as antdTheme } from 'ant-design-vue';
|
||||
import {
|
||||
BarsOutlined,
|
||||
ControlOutlined,
|
||||
|
|
@ -19,7 +18,7 @@ import {
|
|||
const { t } = useI18n();
|
||||
|
||||
import { HttpUtil, SizeFormatter, TimeFormatter } from '@/utils';
|
||||
import { theme as themeState } from '@/composables/useTheme.js';
|
||||
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
|
||||
import { useStatus } from '@/composables/useStatus.js';
|
||||
import { useMediaQuery } from '@/composables/useMediaQuery.js';
|
||||
import AppSidebar from '@/components/AppSidebar.vue';
|
||||
|
|
@ -34,11 +33,6 @@ import CpuHistoryModal from './CpuHistoryModal.vue';
|
|||
import XrayLogModal from './XrayLogModal.vue';
|
||||
import VersionModal from './VersionModal.vue';
|
||||
|
||||
// Drive AD-Vue 4's built-in dark algorithm from our reactive theme.
|
||||
const antdThemeConfig = computed(() => ({
|
||||
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}));
|
||||
|
||||
const { status, fetched, refresh } = useStatus();
|
||||
const { isMobile } = useMediaQuery();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { UserOutlined, LockOutlined, KeyOutlined, SettingOutlined } from '@ant-design/icons-vue';
|
||||
import { theme as antdTheme } from 'ant-design-vue';
|
||||
|
||||
import { HttpUtil } from '@/utils';
|
||||
import { currentTheme, theme as themeState } from '@/composables/useTheme.js';
|
||||
import {
|
||||
antdThemeConfig,
|
||||
currentTheme,
|
||||
theme as themeState,
|
||||
} from '@/composables/useTheme.js';
|
||||
import ThemeSwitchLogin from '@/components/ThemeSwitchLogin.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
// Drive AD-Vue 4's built-in dark algorithm from our useTheme state.
|
||||
// This re-themes every AD-Vue component without depending on the
|
||||
// legacy panel's custom.min.css.
|
||||
const antdThemeConfig = computed(() => ({
|
||||
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}));
|
||||
|
||||
// Cycle the title between "Hello" and "Welcome" — matches the legacy
|
||||
// panel's Vue 2 .is-visible / .is-hidden DOM-class swap, but driven
|
||||
// reactively + with a Vue 3 <Transition> for the fade.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { theme as antdTheme, Modal } from 'ant-design-vue';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import {
|
||||
SettingOutlined,
|
||||
SafetyOutlined,
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from '@ant-design/icons-vue';
|
||||
|
||||
import { HttpUtil, PromiseUtil } from '@/utils';
|
||||
import { theme as themeState } from '@/composables/useTheme.js';
|
||||
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
|
||||
import { useMediaQuery } from '@/composables/useMediaQuery.js';
|
||||
import AppSidebar from '@/components/AppSidebar.vue';
|
||||
import { useAllSetting } from './useAllSetting.js';
|
||||
|
|
@ -23,10 +23,6 @@ import SubscriptionFormatsTab from './SubscriptionFormatsTab.vue';
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const antdThemeConfig = computed(() => ({
|
||||
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}));
|
||||
|
||||
const { fetched, spinning, saveDisabled, allSetting, fetchAll, saveAll } = useAllSetting();
|
||||
const { isMobile } = useMediaQuery();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { theme as antdTheme, Modal } from 'ant-design-vue';
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import {
|
||||
SettingOutlined,
|
||||
SwapOutlined,
|
||||
|
|
@ -12,9 +12,8 @@ import {
|
|||
QuestionCircleOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
|
||||
import { theme as themeState } from '@/composables/useTheme.js';
|
||||
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
|
||||
import { useMediaQuery } from '@/composables/useMediaQuery.js';
|
||||
import { message } from 'ant-design-vue';
|
||||
import AppSidebar from '@/components/AppSidebar.vue';
|
||||
import BasicsTab from './BasicsTab.vue';
|
||||
import RoutingTab from './RoutingTab.vue';
|
||||
|
|
@ -27,17 +26,6 @@ import { useXraySetting } from './useXraySetting.js';
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
// Phase 6-i: scaffold + advanced JSON tab. Other tabs (Basics, Routing,
|
||||
// Outbounds, Balancers, DNS) land in subsequent 6-ii…vi commits — they
|
||||
// each need their own tree of structured forms or a dedicated modal.
|
||||
// For now they show an a-empty placeholder so the navigation is
|
||||
// stable and users can still edit the full config via the Advanced
|
||||
// (JSON) tab.
|
||||
|
||||
const antdThemeConfig = computed(() => ({
|
||||
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}));
|
||||
|
||||
const {
|
||||
fetched,
|
||||
spinning,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { createApp } from 'vue';
|
||||
import Antd, { message } from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
import '@/styles/legacy.css';
|
||||
|
||||
import { setupAxios } from '@/api/axios-init.js';
|
||||
// Importing useTheme triggers the boot side-effect that applies the
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,6 @@
|
|||
import { createApp } from 'vue';
|
||||
import Antd, { message } from 'ant-design-vue';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
import '@/styles/legacy.css';
|
||||
|
||||
import { setupAxios } from '@/api/axios-init.js';
|
||||
import '@/composables/useTheme.js';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue