fix: pre-apply cached brand/sidebar theme before first paint

The static :root fallback for --sun-ui-*/--sun-sidebar-* hardcodes the
"Standard Purple" preset (SIDEBAR_STANDARD's exact values). Any
workspace that has actually saved a different theme only gets it
applied once app-runtime.js's brand-theme module loads and runs
applyActual(), which reads the same localStorage cache
(sunBrandThemeV1) it always did - but by then the purple/mixed
defaults have already painted, so every load flashes the wrong colors
before snapping to the real ones.

Reads the same localStorage key synchronously in the
sun-startup-stability-guard script (already first in <head>, before
any stylesheet or the app-runtime.js bundle loads) and sets the same
CSS custom properties as inline styles on <html>. Inline element
styles always win over a stylesheet's :root selector regardless of
load order, so the correct theme is already active for the very first
paint. Workspaces with no saved theme yet are unaffected - the
existing static fallback still shows, same as today.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
pavlov346346-source 2026-09-12 17:56:19 +03:00
parent 1af9733154
commit edfce3b7e6

View File

@ -20,6 +20,30 @@
});
}catch(_){ }
// Apply the workspace's saved brand/sidebar theme before first paint, straight from the
// localStorage cache. Without this the page briefly shows the hardcoded "Standard Purple"
// :root fallback until app-runtime.js's theme module loads and corrects it - a visible
// color flash on every load for any workspace that isn't actually using that preset.
try{
const rawTheme=localStorage.getItem('sunBrandThemeV1');
if(rawTheme){
const parsed=JSON.parse(rawTheme)||{};
const root=document.documentElement.style;
const isHex=v=>/^#[0-9a-f]{6}$/i.test(String(v||''));
if(parsed.ui&&typeof parsed.ui==='object'){
Object.entries(parsed.ui).forEach(([k,v])=>{
if(isHex(v))root.setProperty(`--sun-ui-${k.replace(/[A-Z]/g,m=>'-'+m.toLowerCase())}`,v);
});
}
if(parsed.sidebar&&typeof parsed.sidebar==='object'){
const sideMap={background:'--sun-sidebar-bg',background2:'--sun-sidebar-bg2',glow:'--sun-sidebar-glow',border:'--sun-sidebar-border',icons:'--sun-sidebar-icons',iconBackground:'--sun-sidebar-icon-bg',text:'--sun-sidebar-text',muted:'--sun-sidebar-muted',activeBackground:'--sun-sidebar-active-bg',activeText:'--sun-sidebar-active-text',activeIcon:'--sun-sidebar-active-icon',indicator:'--sun-sidebar-indicator',toolsBackground:'--sun-sidebar-tools-bg',toolsBorder:'--sun-sidebar-tools-border',collapseBackground:'--sun-sidebar-collapse-bg',collapseText:'--sun-sidebar-collapse-text',badgeBackground:'--sun-sidebar-badge-bg',badgeText:'--sun-sidebar-badge-text'};
Object.entries(sideMap).forEach(([k,cssVar])=>{
if(isHex(parsed.sidebar[k]))root.setProperty(cssVar,parsed.sidebar[k]);
});
}
}
}catch(_){ }
window.sunFetchWithTimeout=window.sunFetchWithTimeout||async function(url,options={},timeout=8000){
const controller=new AbortController();
const timer=setTimeout(()=>controller.abort(),Math.max(1000,Number(timeout)||8000));