import type { Metadata } from "next";
import type { ReactNode } from "react";
import { cookies } from "next/headers";
import Link from "next/link";
import { Doto, Geist, Geist_Mono } from "next/font/google";
import { BRAND, LogoMark } from "@/components/brand";
import { SiteHeader, SiteSidebar, TopBar } from "@/components/header";
import { Providers } from "@/components/providers";
import { ThemeToggle } from "@/components/theme-toggle";
import { THEME_COOKIE, colorwayOf, type Theme } from "@/lib/theme";
import { getViewer } from "@/lib/auth";
import "@unlocalhosted/metalui/styles.css";
import "@unlocalhosted/metalui/icons.css";
import "./globals.css";
const geist = Geist({
variable: "--font-geist",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
/** MetalUI's dot-matrix face, for its pixel type roles. */
const doto = Doto({
variable: "--font-doto",
subsets: ["latin"],
weight: "700",
});
export const metadata: Metadata = {
metadataBase: new URL(`https://${BRAND.domain}`),
title: { default: BRAND.name, template: `%s · ${BRAND.name}` },
description: "A self-hosted home for your git repositories",
};
export default async function RootLayout({ children }: LayoutProps<"/">) {
const viewer = await getViewer();
const stored = (await cookies()).get(THEME_COOKIE)?.value;
const theme: Theme = stored === "light" || stored === "dark" ? stored : "system";
return (
<html
lang="en"
data-mu-colorway={colorwayOf(theme)}
className={`${geist.variable} ${geistMono.variable} ${doto.variable} h-full antialiased`}
>
<body className="text-[13px]">
<Providers>
{viewer ? (
<div className="flex min-h-screen">
<SiteSidebar viewer={viewer} theme={theme} />
<div className="flex min-w-0 flex-1 flex-col">
<TopBar viewer={viewer} />
<Sheet besideSidebar footer={<Footer signedIn />}>{children}</Sheet>
</div>
</div>
) : (
<div className="flex min-h-screen flex-col">
<SiteHeader />
<Sheet footer={<Footer theme={theme} />}>{children}</Sheet>
</div>
)}
</Providers>
</body>
</html>
);
}
/** The raised plate all page content sits on, inset from the page. */
function Sheet({ children, footer, besideSidebar }: { children: ReactNode; footer: ReactNode; besideSidebar?: boolean }) {
return (
<div className={`plate mx-2 mb-2 flex flex-1 flex-col rounded-[24px] ${besideSidebar ? "lg:ml-0" : ""}`}>
<div className="flex flex-1 flex-col">{children}</div>
{footer}
</div>
);
}
function Footer({ signedIn, theme }: { signedIn?: boolean; theme?: Theme }) {
return (
<footer className="rule-t mt-12 px-5 py-5">
<div className="mx-auto flex max-w-[1400px] flex-wrap items-center gap-x-6 gap-y-3">
<span className="flex items-center gap-2">
<LogoMark size={16} />
<span className="type-label engraved">
<b>{BRAND.name}</b>
<span className="hidden sm:inline"> · {BRAND.tagline}</span>
</span>
</span>
<nav className="ml-auto flex items-center gap-5 type-ui text-ink2">
<Link href="/explore" className="transition-colors hover:text-ink">Explore</Link>
{signedIn && <Link href="/settings/keys" className="transition-colors hover:text-ink">SSH keys</Link>}
{signedIn && <Link href="/settings/tokens" className="transition-colors hover:text-ink">API tokens</Link>}
{theme && <ThemeToggle initial={theme} />}
</nav>
</div>
</footer>
);
}