spb/chat-spboucher Public
Private universal chat interface over the OpenRouter ecosystem — 400+ models, branching, streaming, usage tracking. Next.js 16 + SQLite, PWA, deployed on m4m64a at chat.spboucher.ai
TypeScript 78.8%
CSS 15.1%
JavaScript 4.9%
Shell 1.2%
1// Author: Simon-Pierre Boucher2// Contact: contact@spboucher.ai3// Project: chat.spboucher.ai45import type { Metadata, Viewport } from "next";6import { Inter, JetBrains_Mono } from "next/font/google";7import "./globals.css";8import "katex/dist/katex.min.css";9import "highlight.js/styles/github-dark-dimmed.css";1011const inter = Inter({ subsets: ["latin"], variable: "--font-ui" });12const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-mono", weight: ["400", "500", "600"] });1314export const metadata: Metadata = {15 title: "chat.spboucher.ai",16 description: "Personal instrument for operating hundreds of AI models.",17 manifest: "/manifest.webmanifest",18 appleWebApp: {19 capable: true,20 statusBarStyle: "black-translucent",21 title: "spboucher.ai",22 },23 icons: {24 icon: "/icons/icon.svg",25 apple: "/icons/apple-touch-icon.png",26 },27};2829export const viewport: Viewport = {30 width: "device-width",31 initialScale: 1,32 viewportFit: "cover",33 themeColor: [34 { media: "(prefers-color-scheme: dark)", color: "#0E1116" },35 { media: "(prefers-color-scheme: light)", color: "#F6F8FA" },36 ],37};3839export default function RootLayout({ children }: { children: React.ReactNode }) {40 return (41 <html lang="en" suppressHydrationWarning>42 <head>43 <script44 // Apply persisted theme before first paint to avoid a flash.45 dangerouslySetInnerHTML={{46 __html: `try{var t=localStorage.getItem('spb-theme');if(t==='light'||t==='dark'){document.documentElement.dataset.theme=t}}catch(e){}`,47 }}48 />49 </head>50 <body className={`${inter.variable} ${mono.variable}`}>{children}</body>51 </html>52 );53}54