Sitemap: - Switch from ISR (revalidate=3600) to force-dynamic for per-request generation - Flatten into single app/sitemap.ts with inlined static entries and DB queries - NULL-safe syncStatus filter: ne(games.syncStatus, 'failed') OR isNull(games.syncStatus) - Structured JSON logging for generated URLs and DB errors - Delete lib/sitemap/* helpers and app/api/revalidate-sitemap route - Add basic vitest coverage for sitemap exports PWA & Offline: - Add @serwist/next service worker (webpack build) with runtime caching - Cache strategies: stale-while-revalidate for game pages, network-first for listings/API, cache-first for Steam CDN images - Offline fallback page (public/offline.html) - Manifest icons: 192px maskable + 512px any - Viewport meta with viewport-fit=cover, user-scalable=no - Apple mobile web app meta tags Search / Filter Refinements: - Multi-genre OR support in listing API (comma-separated genres) - Device-scoped FPS filter (min/max FPS constrained to selected device) - Client-side URL state sync via router.replace for shareable filtered views - Initialize filter state from URL params on mount - Auto-collapse filter panel on saved-filter load - Fix multi-genre saved filter parsing (comma-separated) Steam Deck / Touch / Gamepad UX: - WCAG 2.1 AA touch targets (44x44px) on all filter controls - .gamepad-focus CSS focus ring for controller navigation - useGamepadNavigation hook: D-pad/left-stick roving tabindex, A/B/X/Y actions - Integrate gamepad hook into games page (X=search, Y=toggle filters) Chore: - Bump version to 2026.0.97
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import type { Metadata, Viewport } from "next"
|
|
import { Lexend } from "next/font/google"
|
|
import "./globals.css"
|
|
import Script from "next/script"
|
|
import Navbar from "@/components/navbar"
|
|
import { Suspense } from "react"
|
|
|
|
const font = Lexend({
|
|
variable: "--font-lexend",
|
|
subsets: ["latin"],
|
|
})
|
|
|
|
export const viewport: Viewport = {
|
|
width: "device-width",
|
|
initialScale: 1,
|
|
viewportFit: "cover",
|
|
maximumScale: 1,
|
|
userScalable: false,
|
|
themeColor: "#eb3779",
|
|
}
|
|
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL("https://deckyvault.xyz"),
|
|
title: {
|
|
default: "DeckyVault - Steam Deck Benchmarks & Settings",
|
|
template: "%s | DeckyVault",
|
|
},
|
|
description:
|
|
"A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
|
|
keywords: [
|
|
"Steam Deck",
|
|
"benchmarks",
|
|
"settings",
|
|
"performance",
|
|
"FPS",
|
|
"gaming",
|
|
"Steam Machine",
|
|
"Proton",
|
|
"FSR",
|
|
"compatibility",
|
|
],
|
|
authors: [
|
|
{ name: "Adrian Bonpin", url: "https://github.com/AdrianBonpin" },
|
|
],
|
|
creator: "@adrianbonpin",
|
|
openGraph: {
|
|
type: "website",
|
|
locale: "en_US",
|
|
url: "https://deckyvault.xyz",
|
|
siteName: "DeckyVault",
|
|
title: "DeckyVault - Steam Deck Benchmarks & Settings",
|
|
description:
|
|
"A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
|
|
images: [
|
|
{
|
|
url: "/opengraph-image",
|
|
width: 1200,
|
|
height: 630,
|
|
alt: "DeckyVault - Steam Deck Benchmarks & Settings",
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: "DeckyVault - Steam Deck Benchmarks & Settings",
|
|
description:
|
|
"A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
|
|
creator: "@adrianbonpin",
|
|
images: ["/twitter-image"],
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
"max-video-preview": -1,
|
|
"max-image-preview": "large",
|
|
"max-snippet": -1,
|
|
},
|
|
},
|
|
}
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
}: Readonly<{
|
|
children: React.ReactNode
|
|
}>) {
|
|
return (
|
|
<html
|
|
lang='en'
|
|
className={`${font.variable} bg-background text-text antialiased overscroll-none`}
|
|
>
|
|
<head>
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
</head>
|
|
<body className='min-h-full w-dvw flex flex-col relative'>
|
|
<Suspense>
|
|
<Navbar />
|
|
</Suspense>
|
|
{children}
|
|
<Script
|
|
src='https://analytics.ranlabs.space/api/script.js'
|
|
data-site-id='b9817e8df599'
|
|
strategy='afterInteractive'
|
|
/>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|