96 lines
5 KiB
TypeScript
96 lines
5 KiB
TypeScript
|
|
/**
|
||
|
|
* Inline script that runs in <head> before hydration.
|
||
|
|
*
|
||
|
|
* Reads the persisted theme id + mode from localStorage and applies them
|
||
|
|
* to <html> so the user never sees a flash of the default theme.
|
||
|
|
*
|
||
|
|
* MUST stay dependency-free — it's bundled as a string and inlined.
|
||
|
|
*/
|
||
|
|
export function ThemeBootstrapScript() {
|
||
|
|
const code = `
|
||
|
|
(function() {
|
||
|
|
try {
|
||
|
|
var themeId = localStorage.getItem('wfdash-theme') || 'crisp';
|
||
|
|
var mode = localStorage.getItem('wfdash-mode') || 'warm';
|
||
|
|
var root = document.documentElement;
|
||
|
|
root.setAttribute('data-theme', themeId);
|
||
|
|
root.classList.remove('warm', 'dark');
|
||
|
|
root.classList.add(mode);
|
||
|
|
var radiusMap = {
|
||
|
|
'crisp': '0.625rem',
|
||
|
|
'midnight': '0.75rem',
|
||
|
|
'editorial': '0.375rem',
|
||
|
|
'bento': '1.125rem',
|
||
|
|
'field-ops': '0.5rem',
|
||
|
|
'terminal': '0',
|
||
|
|
'blueprint': '0.125rem',
|
||
|
|
'confetti': '1.25rem'
|
||
|
|
};
|
||
|
|
if (radiusMap[themeId]) root.style.setProperty('--radius', radiusMap[themeId]);
|
||
|
|
|
||
|
|
// If the active theme is a custom theme, apply its inline token overrides
|
||
|
|
// so the page renders correctly before React hydrates.
|
||
|
|
var activeCustomId = localStorage.getItem('wfdash-active-custom-id');
|
||
|
|
if (activeCustomId) {
|
||
|
|
try {
|
||
|
|
var raw = localStorage.getItem('wfdash-custom-themes');
|
||
|
|
if (raw) {
|
||
|
|
var list = JSON.parse(raw);
|
||
|
|
var found = null;
|
||
|
|
for (var i = 0; i < list.length; i++) {
|
||
|
|
if (list[i].id === activeCustomId) { found = list[i]; break; }
|
||
|
|
}
|
||
|
|
if (found && found.colors) {
|
||
|
|
var c = found.colors;
|
||
|
|
// Direct mappings
|
||
|
|
root.style.setProperty('--background', c.background);
|
||
|
|
root.style.setProperty('--foreground', c.foreground);
|
||
|
|
root.style.setProperty('--primary', c.primary);
|
||
|
|
root.style.setProperty('--accent', c.accent);
|
||
|
|
root.style.setProperty('--destructive', c.destructive);
|
||
|
|
root.style.setProperty('--sidebar', c.sidebar);
|
||
|
|
root.style.setProperty('--border', c.border);
|
||
|
|
// Contrast foregrounds
|
||
|
|
function lum(hex){
|
||
|
|
var r = parseInt(hex.slice(1,3),16)/255;
|
||
|
|
var g = parseInt(hex.slice(3,5),16)/255;
|
||
|
|
var b = parseInt(hex.slice(5,7),16)/255;
|
||
|
|
var f = function(v){return v<=0.03928 ? v/12.92 : Math.pow((v+0.055)/1.055,2.4);};
|
||
|
|
return 0.2126*f(r)+0.7152*f(g)+0.0722*f(b);
|
||
|
|
}
|
||
|
|
function fgFor(bg){ return lum(bg) > 0.55 ? '#0F172A' : '#FFFFFF'; }
|
||
|
|
root.style.setProperty('--primary-foreground', fgFor(c.primary));
|
||
|
|
root.style.setProperty('--accent-foreground', fgFor(c.accent));
|
||
|
|
root.style.setProperty('--destructive-foreground', fgFor(c.destructive));
|
||
|
|
root.style.setProperty('--sidebar-foreground', fgFor(c.sidebar));
|
||
|
|
// Derived
|
||
|
|
function hexA(hex,a){return hex+(Math.round(a*2.55).toString(16).padStart(2,'0'));}
|
||
|
|
function mix(a,b,p){var ar=parseInt(a.slice(1,3),16),ag=parseInt(a.slice(3,5),16),ab=parseInt(a.slice(5,7),16),br=parseInt(b.slice(1,3),16),bg=parseInt(b.slice(3,5),16),bb=parseInt(b.slice(5,7),16);var k=p/100;var r=Math.round(ar*k+br*(1-k)),g=Math.round(ag*k+bg*(1-k)),bb2=Math.round(ab*k+bb*(1-k));var h=function(n){return n.toString(16).padStart(2,'0');};return '#'+h(r)+h(g)+h(bb2);}
|
||
|
|
root.style.setProperty('--card', mix('#FFFFFF', c.background, 2));
|
||
|
|
root.style.setProperty('--popover', mix('#FFFFFF', c.background, 2));
|
||
|
|
root.style.setProperty('--muted', mix('#000000', c.background, 4));
|
||
|
|
root.style.setProperty('--secondary', mix('#000000', c.background, 3));
|
||
|
|
root.style.setProperty('--card-foreground', c.foreground);
|
||
|
|
root.style.setProperty('--popover-foreground', c.foreground);
|
||
|
|
root.style.setProperty('--secondary-foreground', c.foreground);
|
||
|
|
root.style.setProperty('--muted-foreground', mix('#94A3B8', c.foreground, 60));
|
||
|
|
root.style.setProperty('--input', c.border);
|
||
|
|
root.style.setProperty('--ring', c.primary);
|
||
|
|
root.style.setProperty('--sidebar-primary', c.primary);
|
||
|
|
root.style.setProperty('--sidebar-primary-foreground', fgFor(c.primary));
|
||
|
|
root.style.setProperty('--sidebar-accent', mix('#FFFFFF', c.sidebar, 8));
|
||
|
|
root.style.setProperty('--sidebar-accent-foreground', fgFor(c.sidebar));
|
||
|
|
root.style.setProperty('--sidebar-border', mix('#FFFFFF', c.sidebar, 12));
|
||
|
|
root.style.setProperty('--sidebar-ring', c.primary);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} catch(e2) { /* ignore */ }
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
// localStorage may be blocked — fall back to defaults silently
|
||
|
|
}
|
||
|
|
})();
|
||
|
|
`;
|
||
|
|
return <script dangerouslySetInnerHTML={{ __html: code }} />;
|
||
|
|
}
|