import type { Metadata } from "next"; import { cookies } from "next/headers"; import { Plus_Jakarta_Sans, Noto_Sans_SC, JetBrains_Mono } from "next/font/google"; import "./globals.css"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import Toaster from "@/components/Toaster"; import { fetchMe, fetchUnreadCount, type User } from "@/lib/api"; // 构建期下载并自托管字体:同源加载 + 自动 preload + fallback 度量校正, // 消除 F5 时 Google CDN 字体到达后整页"由粗变细"的 FOUT 闪动 const jakarta = Plus_Jakarta_Sans({ subsets: ["latin"], weight: ["400", "500", "600", "700", "800"], variable: "--font-jakarta", display: "swap", }); const notoSC = Noto_Sans_SC({ weight: ["400", "500", "700", "900"], variable: "--font-noto-sc", display: "swap", }); const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"], weight: ["400", "500", "700"], variable: "--font-jetbrains", display: "swap", }); export const metadata: Metadata = { title: { default: "姜十三论坛 · J13 FORUM", template: "%s · 姜十三论坛", }, description: "姜十三论坛 - 技术分享与讨论社区", openGraph: { title: "姜十三论坛 · J13 FORUM", description: "姜十三论坛 - 技术分享与讨论社区", type: "website", }, }; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { const cookieStore = await cookies(); // 浅色优先的设计系统,仅在显式选择暗色时挂 .dark const theme = cookieStore.get("j13-theme")?.value === "dark" ? "dark" : "light"; // SSR 直出登录态(access 过期时 middleware 已先行轮转,见 middleware.ts), // 保证 F5 初始 HTML 中的右上角就是正确的用户区,而非先闪"登录/注册" const cookieHeader = cookieStore.toString(); let user: User | null = null; let unread = 0; if (cookieStore.get("j13_refresh")?.value) { try { const [me, count] = await Promise.all([ fetchMe(cookieHeader), fetchUnreadCount(cookieHeader), ]); user = me.user; unread = count; } catch { // 后端不可用时降级为游客视图,不阻塞页面渲染 } } return (
跳到主要内容