add theme stylesheet loader

This commit is contained in:
jeffvli 2025-09-06 03:02:58 -07:00
parent bb7d561d0f
commit ea122f5a4f

View file

@ -1,5 +1,5 @@
import { useMantineColorScheme } from '@mantine/core'; import { useMantineColorScheme } from '@mantine/core';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSettingsStore } from '/@/renderer/store/settings.store'; import { useSettingsStore } from '/@/renderer/store/settings.store';
import { createMantineTheme } from '/@/renderer/themes/mantine-theme'; import { createMantineTheme } from '/@/renderer/themes/mantine-theme';
@ -17,6 +17,7 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
const nativeImageAspect = useSettingsStore((store) => store.general.nativeAspectRatio); const nativeImageAspect = useSettingsStore((store) => store.general.nativeAspectRatio);
const { builtIn, custom, system, type } = useSettingsStore((state) => state.font); const { builtIn, custom, system, type } = useSettingsStore((state) => state.font);
const textStyleRef = useRef<HTMLStyleElement | null>(null); const textStyleRef = useRef<HTMLStyleElement | null>(null);
const loadedStylesheetsRef = useRef<Set<string>>(new Set());
const getCurrentTheme = () => window.matchMedia('(prefers-color-scheme: dark)').matches; const getCurrentTheme = () => window.matchMedia('(prefers-color-scheme: dark)').matches;
const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme()); const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme());
const { followSystemTheme, theme, themeDark, themeLight } = useSettingsStore( const { followSystemTheme, theme, themeDark, themeLight } = useSettingsStore(
@ -27,6 +28,56 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
setIsDarkTheme(e.matches); setIsDarkTheme(e.matches);
}; };
const loadStylesheet = (href: string): Promise<void> => {
return new Promise((resolve, reject) => {
if (loadedStylesheetsRef.current.has(href)) {
resolve();
return;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.onload = () => {
loadedStylesheetsRef.current.add(href);
resolve();
};
link.onerror = () => {
console.warn(`Failed to load stylesheet: ${href}`);
reject(new Error(`Failed to load stylesheet: ${href}`));
};
document.head.appendChild(link);
});
};
const unloadStylesheet = (href: string) => {
const existingLink = document.querySelector(`link[href="${href}"]`);
if (existingLink) {
existingLink.remove();
loadedStylesheetsRef.current.delete(href);
}
};
const loadThemeStylesheets = useCallback(async (stylesheets: string[] = []) => {
if (loadedStylesheetsRef.current.size > 0) {
loadedStylesheetsRef.current.forEach((href) => unloadStylesheet(href));
loadedStylesheetsRef.current.clear();
}
if (stylesheets.length === 0) {
return;
}
const loadPromises = stylesheets.map((href) =>
loadStylesheet(href).catch((error) => {
console.warn(`Error loading stylesheet ${href}:`, error);
}),
);
await Promise.all(loadPromises);
}, []);
const getSelectedTheme = () => { const getSelectedTheme = () => {
if (overrideTheme) { if (overrideTheme) {
return overrideTheme; return overrideTheme;
@ -113,6 +164,12 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
root.style.setProperty('--theme-image-fit', nativeImageAspect ? 'contain' : 'cover'); root.style.setProperty('--theme-image-fit', nativeImageAspect ? 'contain' : 'cover');
}, [nativeImageAspect]); }, [nativeImageAspect]);
useEffect(() => {
if (appTheme?.stylesheets) {
loadThemeStylesheets(appTheme.stylesheets);
}
}, [selectedTheme, appTheme?.stylesheets, loadThemeStylesheets]);
const themeVars = useMemo(() => { const themeVars = useMemo(() => {
return Object.entries(appTheme?.app ?? {}) return Object.entries(appTheme?.app ?? {})
.map(([key, value]) => { .map(([key, value]) => {