Files
Jyotisha/frontend/src/app/layout.tsx
T

82 lines
3.0 KiB
TypeScript

import type { Metadata, Viewport } from "next";
import localFont from "next/font/local";
import Script from "next/script";
import { Toaster } from "@/components/ui/sonner";
import { StaleBuildGuard } from "@/components/stale-build-guard";
import { StaleClientRecovery } from "@/components/stale-client-recovery";
import { ViewportScrollLock } from "@/components/viewport-scroll-lock";
import { themePreferenceBootScript } from "@/lib/theme-preference";
// Vendored Inter latin variable (OFL). Do not switch back to next/font/google;
// the publish image cannot download fonts at build time.
const inter = localFont({
src: "./fonts/InterVariable-latin.woff2",
display: "swap",
variable: "--font-inter",
weight: "100 900",
});
// Vendored 9-glyph Noto Sans Symbols subset (OFL) — ☉ ☽ ☿ ♀ ♂ ♃ ♄ ☊ ☋, 1.5 KB.
// The North Indian chart draws planets as these symbols, so the face has to be
// present rather than hoped for: Apple Symbols / Segoe UI Symbol cover the set
// on their own platforms, Android and most Linux browsers do not.
// `adjustFontFallback: false` on purpose: the synthetic Arial-metric fallback
// next/font/local would otherwise inject carries its own ♀ ♂ and would swallow
// the rest of the stack for the glyphs it cannot draw.
const planetGlyphs = localFont({
src: "./fonts/NotoSansSymbols-planets.woff2",
display: "swap",
variable: "--font-planet-glyphs",
weight: "400",
style: "normal",
adjustFontFallback: false,
});
export const metadata: Metadata = {
title: "Jyotisha · 印度占星",
description: "与 Mastra Agent 对话,基于星盘证据讨论事业、关系与时间窗口。",
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
viewportFit: "cover",
interactiveWidget: "resizes-content",
};
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
const enableReactDevTools = process.env.NODE_ENV === "development"
&& process.env.NEXT_PUBLIC_ENABLE_REACT_DEVTOOLS === "1";
return (
<html lang="zh-CN" className={`${inter.variable} ${planetGlyphs.variable}`} suppressHydrationWarning>
<head>
{/* Synchronous on purpose: a pinned theme must be on <html> before the
first paint, or the page flashes the other theme on every load. */}
<script dangerouslySetInnerHTML={{ __html: themePreferenceBootScript }} />
{enableReactDevTools && (
<>
<Script
crossOrigin="anonymous"
src="https://unpkg.com/react-grab/dist/index.global.js"
strategy="lazyOnload"
/>
<Script
crossOrigin="anonymous"
src="https://unpkg.com/react-scan/dist/auto.global.js"
strategy="lazyOnload"
/>
</>
)}
</head>
<body>
<StaleClientRecovery />
<ViewportScrollLock />
<StaleBuildGuard />
{children}
<Toaster />
</body>
</html>
);
}