Umami never received data: the client-side path (browser beacon to /stats/api/send) has been dead in production. Pageviews are now classified and recorded entirely on the server in the request proxy; no tracking script ships to the browser anymore. - Pure domain decision (decidePageview) filters real page views from assets, infrastructure paths, prefetches and bot user agents - Pure payload builder maps facts onto the Umami send contract as an unnamed event, with the client identity travelling in headers - proxy.ts wires request facts into the domain and fires the send without delaying the response; configured via UMAMI_WEBSITE_ID and UMAMI_API_ENDPOINT - Removes the dead client analytics stack (components, adapters, /stats proxy route) and its layout mount
78 lines
1.9 KiB
TypeScript
78 lines
1.9 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { Inter, JetBrains_Mono, Newsreader } from "next/font/google";
|
|
import { BitBurst } from "@/src/components/bits/BitBurst";
|
|
import { Footer } from "@/src/components/Footer";
|
|
import { Header } from "@/src/components/Header";
|
|
import { InteractiveElements } from "@/src/components/InteractiveElements";
|
|
import {
|
|
SITE_DESCRIPTION,
|
|
SITE_NAME,
|
|
SITE_TITLE,
|
|
SITE_URL,
|
|
} from "@/src/content/site";
|
|
import "./globals.css";
|
|
|
|
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
|
const jetbrainsMono = JetBrains_Mono({
|
|
subsets: ["latin"],
|
|
variable: "--font-jetbrains-mono",
|
|
display: "swap",
|
|
});
|
|
const newsreader = Newsreader({
|
|
subsets: ["latin"],
|
|
variable: "--font-newsreader",
|
|
style: "italic",
|
|
display: "swap",
|
|
});
|
|
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL(SITE_URL),
|
|
title: {
|
|
default: SITE_TITLE,
|
|
template: `%s | ${SITE_NAME}`,
|
|
},
|
|
description: SITE_DESCRIPTION,
|
|
applicationName: SITE_NAME,
|
|
authors: [{ name: SITE_NAME, url: SITE_URL }],
|
|
creator: SITE_NAME,
|
|
icons: {
|
|
icon: "/favicon.svg",
|
|
apple: "/apple-touch-icon.png",
|
|
},
|
|
// The images come from opengraph-image.png and twitter-image.png next to this file.
|
|
openGraph: {
|
|
type: "website",
|
|
url: "/",
|
|
siteName: SITE_NAME,
|
|
locale: "en_US",
|
|
title: SITE_TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: SITE_TITLE,
|
|
description: SITE_DESCRIPTION,
|
|
},
|
|
};
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
return (
|
|
<html
|
|
lang="en"
|
|
className={`${inter.variable} ${newsreader.variable} ${jetbrainsMono.variable}`}
|
|
>
|
|
<body className="min-h-screen bg-white">
|
|
<Header />
|
|
<main>{children}</main>
|
|
<Footer />
|
|
<InteractiveElements />
|
|
<BitBurst />
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|