Files
mycode/jsonhero-web/app/root.tsx
2026-02-04 12:18:35 +08:00

124 lines
3.2 KiB
TypeScript

import {
Links,
LiveReload,
LoaderFunction,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLoaderData,
useLocation,
} from "remix";
import type { MetaFunction } from "remix";
import clsx from "clsx";
import {
NonFlashOfWrongThemeEls,
Theme,
ThemeProvider,
useTheme,
} from "~/components/ThemeProvider";
import openGraphImage from "~/assets/images/opengraph.png";
export const meta: MetaFunction = ({ location }) => {
const description =
"JSON Hero 通过提供干净美观的 UI 和丰富的额外功能,让阅读和理解 JSON 文件变得简单。";
return {
title: "JSON Hero - 一款美观的 Web JSON 查看器",
viewport: "width=device-width,initial-scale=1",
description,
"og:image": `https://jsonhero.io${openGraphImage}`,
"og:url": `https://jsonhero.io${location.pathname}`,
"og:title": "JSON Hero - 一款美观的 JSON 查看器",
"og:description": description,
"twitter:image": `https://jsonhero.io${openGraphImage}`,
"twitter:card": "summary_large_image",
"twitter:creator": "@json_hero",
"twitter:site": "@json_hero",
"twitter:title": "JSON Hero",
"twitter:description": description,
};
};
import styles from "./tailwind.css";
import { getThemeSession } from "./theme.server";
import { getStarCount } from "./services/github.server";
import { StarCountProvider } from "./components/StarCountProvider";
import { PreferencesProvider } from "~/components/PreferencesProvider";
export function links() {
return [{ rel: "stylesheet", href: styles }];
}
export type LoaderData = {
theme?: Theme;
starCount?: number;
themeOverride?: Theme;
};
export const loader: LoaderFunction = async ({ request }) => {
const themeSession = await getThemeSession(request);
const starCount = await getStarCount();
const themeOverride = getThemeFromRequest(request);
const data: LoaderData = {
theme: themeSession.getTheme(),
starCount,
themeOverride,
};
return data;
};
function getThemeFromRequest(request: Request): Theme | undefined {
const url = new URL(request.url);
const theme = url.searchParams.get("theme");
if (theme) {
return theme as Theme;
}
return undefined;
}
function App() {
const [theme] = useTheme();
return (
<html lang="en" className={clsx(theme)}>
<head>
<Meta />
<meta charSet="utf-8" />
<Links />
<NonFlashOfWrongThemeEls ssrTheme={Boolean(theme)} />
</head>
<body className="overscroll-none">
<Outlet />
<ScrollRestoration />
<Scripts />
{process.env.NODE_ENV === "development" && <LiveReload />}
</body>
</html>
);
}
export default function AppWithProviders() {
const { theme, starCount, themeOverride } = useLoaderData<LoaderData>();
const location = useLocation();
// Force dark mode on the homepage
const forceDarkMode = location.pathname === "/";
return (
<ThemeProvider
specifiedTheme={theme}
themeOverride={forceDarkMode ? "dark" : themeOverride}
>
<PreferencesProvider>
<StarCountProvider starCount={starCount}>
<App />
</StarCountProvider>
</PreferencesProvider>
</ThemeProvider>
);
}