Organize/app/layout.tsx

187 lines
6.8 KiB
TypeScript

import type { Metadata, Viewport } from "next";
import {
Architects_Daughter,
Caveat,
Figtree,
Inter,
JetBrains_Mono,
Orbitron,
Righteous,
VT323,
} from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme/theme-provider";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Toaster } from "@/components/ui/sonner";
import { RegisterServiceWorker } from "@/components/pwa/register-sw";
import { auth } from "@/auth";
import { prisma } from "@/lib/db";
import { themeInitScript, type RawTheme } from "@/lib/themes";
/** UI type: Inter -- a neutral, highly legible humanist sans that reads
* cleanly at small sizes (to-do lists, tables, nav). */
const inter = Inter({
variable: "--font-sans",
subsets: ["latin"],
});
/** Display type: Figtree -- a slightly friendlier, rounder geometric sans
* for headings and wordmarks. Same family, one step of personality. */
const figtree = Figtree({
variable: "--font-heading",
subsets: ["latin"],
});
/** Code: JetBrains Mono, used for the markdown editor's monospace bits. */
const jetbrainsMono = JetBrains_Mono({
variable: "--font-mono",
subsets: ["latin"],
});
/** Blueprint theme's drafting face (see app/globals.css) -- a hand-drawn
* architectural face, lettered like a drawing's title block. */
const architectsDaughter = Architects_Daughter({
variable: "--font-blueprint",
subsets: ["latin"],
weight: "400",
});
/** Cyberpunk theme's pixel-terminal face (see app/globals.css). */
const vt323 = VT323({
variable: "--font-cyberpunk",
subsets: ["latin"],
weight: "400",
});
/** Vaporwave theme's retro face (see app/globals.css) -- a rounded,
* retro-futuristic face for the pastel synthwave look. */
const righteous = Righteous({
variable: "--font-vaporwave",
subsets: ["latin"],
weight: "400",
});
/** Starfield theme's sci-fi face (see app/globals.css). */
const orbitron = Orbitron({
variable: "--font-starfield",
subsets: ["latin"],
});
/** Notebook theme's handwriting face (see app/globals.css) -- a casual pen
* script for the stationery look. */
const caveat = Caveat({
variable: "--font-notebook",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Organize",
description: "An extended to-do list and notes organizer.",
manifest: "/manifest.webmanifest",
icons: {
icon: [{ url: "/icons/icon-192.png", sizes: "192x192", type: "image/png" }],
apple: [{ url: "/icons/apple-touch-icon.png", sizes: "180x180", type: "image/png" }],
},
appleWebApp: {
capable: true,
statusBarStyle: "default",
title: "Organize",
},
};
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#fafafa" },
{ media: "(prefers-color-scheme: dark)", color: "#22252b" },
],
};
export default async function RootLayout({ children }: LayoutProps<"/">) {
// Per-user global theme (stored on the profile row; set from the theme
// menu, see saveUserTheme in lib/actions/profile.ts). When signed in it
// is authoritative: the no-FOUC script below applies it before first
// paint and the ThemeProvider starts from it and persists changes back
// -- so each account in a linked set keeps its own look in the same
// browser across account toggles (a toggle is a full navigation, and
// this layout re-renders for the new user). Anonymous visitors keep the
// old localStorage-based behavior.
const session = await auth();
let userTheme: RawTheme | undefined;
if (session?.user) {
// Stored as free text; valid values are enforced by ThemeSchema
// (lib/validation/profile.ts) at write time, so the assertion is safe
// -- same pattern as Project.theme.
const profile = await prisma.user.findUnique({
where: { id: session.user.id },
select: { theme: true },
});
userTheme = (profile?.theme as RawTheme | null) ?? "system";
}
return (
<html
lang="en"
className={`${inter.variable} ${figtree.variable} ${jetbrainsMono.variable} ${architectsDaughter.variable} ${vt323.variable} ${righteous.variable} ${orbitron.variable} ${caveat.variable} h-full antialiased`}
suppressHydrationWarning
>
<body className="min-h-full flex flex-col">
{/* Nineteen looks: ten light (Default, Sunset, Meadow, Honey, Rosé,
Lavender, Slate, Blueprint, Vaporwave, Notebook) and nine dark
(Dark, Ocean, Pine, Plum, Midnight, Ember, Rosewood, Cyberpunk,
Starfield) -- complete token sets in app/globals.css; Blueprint,
Cyberpunk, Vaporwave, Starfield, and Notebook additionally carry
their own fonts, backgrounds, and animations.
"system" resolves to Default/Dark by OS preference.
No-FOUC theme restore: runs before first paint, applies the
right class + color-scheme to <html>. Signed-in users get
their profile's stored theme (per account, so it survives
account toggles); anonymous visitors get the browser's
localStorage preference -- see themeInitScript in lib/themes.ts. */}
<script
dangerouslySetInnerHTML={{
__html: themeInitScript(userTheme),
}}
/>
{/* Vaporwave scenery (see app/globals.css): the outrun floor grid,
plus palm silhouettes at either side of the horizon. All fixed
behind the content, hidden by default; the theme class on <html>
switches them on. */}
<div className="vw-floor" aria-hidden />
<svg className="vw-palm-defs" aria-hidden>
<symbol id="vw-palm" viewBox="0 0 120 150">
<g fill="none" stroke="currentColor" strokeLinecap="round">
<path d="M60 150C57 110 56 80 60 45" strokeWidth="8" />
<path d="M60 45Q38 30 10 48" strokeWidth="9" />
<path d="M60 45Q44 18 20 12" strokeWidth="9" />
<path d="M60 45Q56 14 42 4" strokeWidth="9" />
<path d="M60 45Q66 12 84 6" strokeWidth="9" />
<path d="M60 45Q78 16 100 12" strokeWidth="9" />
<path d="M60 45Q84 30 110 50" strokeWidth="9" />
</g>
</symbol>
</svg>
<svg className="vw-palm vw-palm--left-main" aria-hidden>
<use href="#vw-palm" />
</svg>
<svg className="vw-palm vw-palm--left-small" aria-hidden>
<use href="#vw-palm" />
</svg>
<svg className="vw-palm vw-palm--right-main" aria-hidden>
<use href="#vw-palm" />
</svg>
<svg className="vw-palm vw-palm--right-small" aria-hidden>
<use href="#vw-palm" />
</svg>
<ThemeProvider userTheme={userTheme}>
<TooltipProvider delay={200}>
{children}
<Toaster />
<RegisterServiceWorker />
</TooltipProvider>
</ThemeProvider>
</body>
</html>
);
}