diff --git a/ui/app/layouts/default.vue b/ui/app/layouts/default.vue
index e743d17f..c7e7fd2f 100644
--- a/ui/app/layouts/default.vue
+++ b/ui/app/layouts/default.vue
@@ -176,11 +176,14 @@
-
>;
};
+type ColorModePreference = 'system' | 'light' | 'dark';
+
const socket = useSocketStore();
const config = useConfigStore();
const route = useRoute();
+const colorMode = useColorMode();
const loadedImage = ref();
const loadingImage = ref(false);
const bg_enable = useStorage('random_bg', true);
@@ -521,6 +527,57 @@ const updateCheckMessage = ref('Up to date - Click to check');
const showRouteSearch = ref(false);
const { alertDialog, confirmDialog } = useDialog();
+const colorModePreferences: Array = ['system', 'light', 'dark'];
+
+const colorModePreference = computed(() => {
+ const preference = colorMode.preference;
+ return colorModePreferences.includes(preference as ColorModePreference)
+ ? (preference as ColorModePreference)
+ : 'system';
+});
+
+const colorModeButtonIcon = computed(() => {
+ switch (colorModePreference.value) {
+ case 'light':
+ return 'i-lucide-sun';
+ case 'dark':
+ return 'i-lucide-moon';
+ default:
+ return 'i-lucide-monitor';
+ }
+});
+
+const nextColorModePreference = computed(() => {
+ const currentIndex = colorModePreferences.indexOf(colorModePreference.value);
+ return colorModePreferences[(currentIndex + 1) % colorModePreferences.length] ?? 'system';
+});
+
+const colorModeButtonTitle = computed(() => {
+ switch (colorModePreference.value) {
+ case 'light':
+ return 'Theme: Light';
+ case 'dark':
+ return 'Theme: Dark';
+ default:
+ return 'Theme: System';
+ }
+});
+
+const colorModeButtonAriaLabel = computed(() => {
+ switch (nextColorModePreference.value) {
+ case 'light':
+ return 'Switch theme to light';
+ case 'dark':
+ return 'Switch theme to dark';
+ default:
+ return 'Switch theme to system';
+ }
+});
+
+const cycleColorMode = (): void => {
+ colorMode.preference = nextColorModePreference.value;
+};
+
const dashboardSidebarUi = {
root: 'border-r border-default bg-default/95 backdrop-blur-sm',
header: 'border-b border-default px-2.5 py-3',
diff --git a/ui/nuxt.config.ts b/ui/nuxt.config.ts
index d1d0f065..d7516f2c 100644
--- a/ui/nuxt.config.ts
+++ b/ui/nuxt.config.ts
@@ -15,8 +15,10 @@ try {
}
} catch {}
+const isProd = 'production' === process.env.NODE_ENV;
export default defineNuxtConfig({
ssr: false,
+ sourcemap: false === isProd,
devtools: { enabled: true },
devServer: {
port: 8082,
@@ -63,14 +65,15 @@ export default defineNuxtConfig({
fallbackToApi: false,
clientBundle: {
scan: {
- globInclude: ['app/**/*.{vue,ts,js}'],
+ globInclude: ['app/**/*.{vue,ts,js}', 'node_modules/@nuxt/ui/dist/shared/ui*.mjs'],
+ globExclude: ['dist', 'build', 'coverage', 'test', 'tests', '.*'],
},
},
},
nitro: {
+ sourceMap: false === isProd,
output: {
- publicDir:
- 'production' === process.env.NODE_ENV ? __dirname + '/exported' : __dirname + '/dist',
+ publicDir: isProd ? __dirname + '/exported' : __dirname + '/dist',
},
...extraNitro,
},
@@ -93,6 +96,15 @@ export default defineNuxtConfig({
},
build: {
chunkSizeWarningLimit: 2000,
+ rollupOptions: {
+ onwarn(warning, warn) {
+ if ('SOURCEMAP_BROKEN' === warning.code) {
+ return;
+ }
+
+ warn(warning);
+ },
+ },
},
},
telemetry: false,