Files
9router/src/app/layout.js
docaohieu2808 925cb4aade fix(ui): apply persisted theme before first paint to avoid flash on reload
Theme was applied from the client store in useEffect (after hydration),
so a reload painted the default light theme for a frame before the
stored dark theme was reapplied. Add a blocking head script that reads
the persisted zustand theme key and sets the dark class on
documentElement before first paint, mirroring applyTheme() including
system -> prefers-color-scheme resolution.
2026-09-02 20:05:52 +07:00

60 lines
2.3 KiB
JavaScript

import { Inter } from "next/font/google";
import { GoogleAnalytics } from "@next/third-parties/google";
import "material-symbols/outlined.css";
import "./globals.css";
import { ThemeProvider } from "@/shared/components/ThemeProvider";
import "@/lib/network/initOutboundProxy"; // Auto-initialize outbound proxy env
import "@/shared/services/bootstrap"; // Auto-run initializeApp (watchdog, auto-resume tunnel)
import { initConsoleLogCapture } from "@/lib/consoleLogBuffer";
import { RuntimeI18nProvider } from "@/i18n/RuntimeI18nProvider";
// Hook console immediately at module load time (server-side only, runs once)
initConsoleLogCapture();
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
export const metadata = {
title: "9Router - AI Infrastructure Management",
description: "One endpoint for all your AI providers. Manage keys, monitor usage, and scale effortlessly.",
icons: {
icon: "/favicon.svg",
},
};
export const viewport = {
themeColor: "#0a0a0a",
};
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
{/* Apply persisted theme before first paint so a reload does not flash the
default (light) theme before the client store hydrates. Mirrors the
zustand-persist "theme" key and the `dark` class applyTheme() sets. */}
<script
dangerouslySetInnerHTML={{
__html: `(function(){try{var s=localStorage.getItem('theme');var t=s?(JSON.parse(s).state||{}).theme:'system';t=t||'system';var m=window.matchMedia('(prefers-color-scheme: dark)').matches;if(t==='dark'||(t==='system'&&m)){document.documentElement.classList.add('dark')}}catch(e){}})();`,
}}
/>
<script
dangerouslySetInnerHTML={{
__html: `var d=document,r=d.documentElement,f=function(){r.classList.add('fonts-loaded')};if(d.fonts&&d.fonts.load){d.fonts.load('24px "Material Symbols Outlined"').then(f).catch(f);setTimeout(f,3000)}else{f()}`,
}}
/>
</head>
<body className={`${inter.variable} font-sans antialiased`}>
<ThemeProvider>
<RuntimeI18nProvider>
{children}
</RuntimeI18nProvider>
</ThemeProvider>
<GoogleAnalytics gaId={"G-LC959F603F"} />
</body>
</html>
);
}