openreader/src/app/layout.tsx
Richard R 3a21f2a5f5 refactor(config): migrate compute and runtime env vars to new naming scheme
- Replace `OPENREADER_COMPUTE_MODE` and related variables with `COMPUTE_MODE`
- Replace `OPENREADER_*` PDF/Whisper model URLs with `PDF_LAYOUT_MODEL_BASE_URL` and `WHISPER_MODEL_BASE_URL`
- Remove legacy `NEXT_PUBLIC_*` runtime config seeds in favor of `RUNTIME_SEED_*`
- Update documentation, code, and environment references to match new variable names
- Remove deprecated `scripts/fetch-models.mjs` and related npm script
- Update runtime config SSR injection from `window.__OPENREADER_RUNTIME_CONFIG__` to `window.__RUNTIME_CONFIG__`
- Adjust Next.js config to use new compute mode env var and optimize output file tracing for ONNX dependencies

BREAKING CHANGE: Environment variable names for compute mode, model URLs, and runtime config seeding have changed. Update `.env` files and deployment configs to use `COMPUTE_MODE`, `PDF_LAYOUT_MODEL_BASE_URL`, `WHISPER_MODEL_BASE_URL`, and `RUNTIME_SEED_*` as appropriate. Legacy `OPENREADER_*` and `NEXT_PUBLIC_*` variables are no longer supported.
2026-05-19 13:27:07 -06:00

75 lines
2.6 KiB
TypeScript

import "./globals.css";
import { ReactNode } from "react";
import { Metadata } from "next";
import { Figtree } from "next/font/google";
import { ConsentAwareAnalytics } from "@/components/ConsentAwareAnalytics";
import { CookieConsentBanner } from "@/components/CookieConsentBanner";
import { getResolvedRuntimeConfig } from "@/lib/server/runtime-config";
import pkg from "../../package.json";
const figtree = Figtree({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700", "800"],
display: "swap",
variable: "--font-display",
});
const themeInitScript = `
(() => {
const themes = ['system', 'light', 'dark', 'ocean', 'forest', 'sunset', 'sea', 'mint', 'lavender', 'rose', 'sand', 'sky', 'slate'];
const lightThemes = new Set(['light', 'lavender', 'rose', 'sand', 'sky', 'slate']);
const root = document.documentElement;
const stored = localStorage.getItem('theme');
const selected = stored && themes.includes(stored) ? stored : 'system';
const effective = selected === 'system'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: selected;
root.classList.remove(...themes);
root.classList.add(effective);
root.style.colorScheme = lightThemes.has(effective) ? 'light' : 'dark';
})();
`;
export const metadata: Metadata = {
title: {
default: "OpenReader",
template: "%s | OpenReader",
},
manifest: "/manifest.json",
metadataBase: new URL("https://openreader.richardr.dev"),
verification: {
google: "MJXyTudn1kgQF8EtGD-tsnAWev7Iawso9hEvqeGHB3U",
},
};
function jsonEmbedSafe(value: unknown): string {
// Prevent `</script>` and U+2028/U+2029 from breaking the inline script.
return JSON.stringify(value)
.replace(/</g, '\\u003c')
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
}
export default async function RootLayout({ children }: { children: ReactNode }) {
const runtimeConfig = await getResolvedRuntimeConfig();
const runtimeConfigWithAppVersion = {
...runtimeConfig,
appVersion: pkg.version,
};
const runtimeConfigInit = `window.__RUNTIME_CONFIG__=${jsonEmbedSafe(runtimeConfigWithAppVersion)};`;
return (
<html lang="en" className={figtree.variable} suppressHydrationWarning>
<head>
<meta name="color-scheme" content="light dark" />
<script dangerouslySetInnerHTML={{ __html: runtimeConfigInit }} />
<script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
</head>
<body className="antialiased">
{children}
<CookieConsentBanner />
<ConsentAwareAnalytics />
</body>
</html>
);
}