feat(plugin page): robust SEO + sitemap inclusion

- Richer metadata: 14 targeted keywords, explicit robots directives
  (index/follow + googleBot max-image-preview), full OpenGraph with OG
  image, twitter summary_large_image, authors/creator/publisher fields
- 3 JSON-LD blocks: SoftwareApplication (version, downloadUrl, featureList,
  screenshot), FAQPage (4 install/usage Q&As), BreadcrumbList
- Add /plugin to STATIC_PAGES sitemap (monthly, priority 0.7)
- Update sitemap tests: static count 7→8, assert /plugin present
- All 25 sitemap tests pass
This commit is contained in:
2026-06-29 00:10:07 +08:00
parent b416f49e3d
commit 59ecb4e4a6
3 changed files with 119 additions and 15 deletions
+4 -3
View File
@@ -159,12 +159,12 @@ describe("Sitemap Generator (app/sitemap.ts)", () => {
// ── sitemap({ id: 'static' }) ────────────────────────────────────── // ── sitemap({ id: 'static' }) ──────────────────────────────────────
describe("sitemap({ id: 'static' })", () => { describe("sitemap({ id: 'static' })", () => {
it("returns 7 static page entries", async () => { it("returns 8 static page entries", async () => {
const mod = await import("@/app/sitemap") const mod = await import("@/app/sitemap")
const result = await mod.default({ id: Promise.resolve("static") }) const result = await mod.default({ id: Promise.resolve("static") })
expect(Array.isArray(result)).toBe(true) expect(Array.isArray(result)).toBe(true)
expect(result).toHaveLength(7) expect(result).toHaveLength(8)
}) })
it("first entry is homepage with priority 1.0", async () => { it("first entry is homepage with priority 1.0", async () => {
@@ -182,6 +182,7 @@ describe("Sitemap Generator (app/sitemap.ts)", () => {
const urls = result.map((e: { url: string }) => e.url) const urls = result.map((e: { url: string }) => e.url)
expect(urls).toContain("https://deckyvault.xyz/games") expect(urls).toContain("https://deckyvault.xyz/games")
expect(urls).toContain("https://deckyvault.xyz/compare") expect(urls).toContain("https://deckyvault.xyz/compare")
expect(urls).toContain("https://deckyvault.xyz/plugin")
}) })
it("includes lastModified on all entries", async () => { it("includes lastModified on all entries", async () => {
@@ -360,7 +361,7 @@ describe("Sitemap Generator (app/sitemap.ts)", () => {
const mod = await import("@/app/sitemap") const mod = await import("@/app/sitemap")
const result = await mod.default({ id: Promise.resolve("static") }) const result = await mod.default({ id: Promise.resolve("static") })
expect(result).toHaveLength(7) expect(result).toHaveLength(8)
}) })
it("unknown id returns empty array", async () => { it("unknown id returns empty array", async () => {
+113 -12
View File
@@ -4,50 +4,147 @@ import { PluginPageClient } from "./page-client"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
const CANONICAL = "https://deckyvault.xyz/plugin"
const TITLE = "DeckyVault Plugin — Record & Upload Steam Deck Benchmarks"
const DESCRIPTION =
"Install the DeckyVault Decky Loader plugin to capture FPS, frame times, and power draw with MangoHud, then upload performance entries straight to DeckyVault from your Steam Deck. Free, open source, controller-friendly."
export const metadata: Metadata = { export const metadata: Metadata = {
title: "DeckyVault Plugin — Record & Upload Steam Deck Benchmarks", title: TITLE,
description: description: DESCRIPTION,
"Install the DeckyVault Decky Loader plugin to capture FPS, frame times, and power draw with MangoHud, then upload performance entries straight to DeckyVault from your Steam Deck.",
keywords: [ keywords: [
"decky loader plugin", "decky loader plugin",
"deckyvault plugin",
"steam deck plugin", "steam deck plugin",
"steam deck benchmark tool",
"mangohud", "mangohud",
"mangohud steam deck",
"steam deck performance logging",
"steam deck fps recorder",
"steam deck benchmark", "steam deck benchmark",
"performance logging", "performance logging",
"deckyvault", "deckyvault",
"steam deck tdp",
"steam deck frame times",
"decky plugin install",
], ],
alternates: { canonical: "https://deckyvault.xyz/plugin" }, alternates: { canonical: CANONICAL },
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-image-preview": "large",
"max-snippet": -1,
"max-video-preview": -1,
},
},
openGraph: { openGraph: {
title: "DeckyVault Plugin — Record & Upload Steam Deck Benchmarks", title: TITLE,
description: description:
"Capture FPS and power draw with MangoHud, then upload to DeckyVault straight from your Steam Deck.", "Capture FPS and power draw with MangoHud, then upload to DeckyVault straight from your Steam Deck. QR-code pairing, one-tap upload, auto game detection.",
url: "https://deckyvault.xyz/plugin", url: CANONICAL,
siteName: "DeckyVault", siteName: "DeckyVault",
type: "website", type: "website",
locale: "en_US",
images: [
{
url: "/opengraph-image",
width: 1200,
height: 630,
alt: "DeckyVault Plugin for Steam Deck",
},
],
}, },
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: "DeckyVault Plugin", title: "DeckyVault Plugin — Steam Deck Benchmark Recorder",
description: description:
"Record Steam Deck performance and upload to DeckyVault with the Decky Loader plugin.", "Record Steam Deck performance with MangoHud and upload to DeckyVault with the Decky Loader plugin. QR pairing, one-tap upload.",
images: ["/opengraph-image"],
}, },
category: "technology",
authors: [{ name: "DeckyVault", url: "https://deckyvault.xyz" }],
creator: "DeckyVault",
publisher: "DeckyVault",
} }
// ── Structured data ────────────────────────────────────────────────
// SoftwareApplication describes the plugin itself; FAQPage captures the
// install/setup steps so search engines can surface rich results.
const jsonLd = { const jsonLd = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "SoftwareApplication", "@type": "SoftwareApplication",
name: "DeckyVault Plugin", name: "DeckyVault Plugin",
applicationCategory: "UtilitiesApplication", applicationCategory: "UtilitiesApplication",
operatingSystem: "SteamOS", operatingSystem: "SteamOS",
softwareVersion: "1.0.0",
offers: { "@type": "Offer", price: "0", priceCurrency: "USD" }, offers: { "@type": "Offer", price: "0", priceCurrency: "USD" },
description: description:
"A Decky Loader plugin that records Steam Deck performance metrics with MangoHud and uploads them to DeckyVault.", "A Decky Loader plugin that records Steam Deck performance metrics (FPS, frame times, power draw) with MangoHud and uploads them to DeckyVault.",
url: "https://deckyvault.xyz/plugin", url: CANONICAL,
downloadUrl:
"https://github.com/AdrianBonpin/deckyvault/releases",
author: {
"@type": "Organization",
name: "DeckyVault",
url: "https://deckyvault.xyz",
},
featureList: [
"Capture FPS, frame times, and 1% lows with MangoHud",
"Measure CPU + GPU power draw with peripheral overhead",
"QR-code pairing with your DeckyVault account",
"One-tap upload of performance entries",
"Automatic game and Proton version detection",
"Controller-friendly Quick Access Menu UI",
],
screenshot: "https://deckyvault.xyz/plugin/panel-overview.jpg",
}
const faqLd = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: [
{
"@type": "Question",
name: "How do I install the DeckyVault plugin?",
acceptedAnswer: {
"@type": "Answer",
text: "Install Decky Loader on your Steam Deck, then either use 'Install Plugin from ZIP File' with the downloaded plugin ZIP, or 'Install Plugin from URL' with the direct download link from the GitHub release.",
},
},
{
"@type": "Question",
name: "Do I need to manually copy an API key?",
acceptedAnswer: {
"@type": "Answer",
text: "No. Open the plugin in the Quick Access Menu, tap 'Pair with Phone', and scan the QR code with your phone. Confirm on deckyvault.xyz and your account links automatically — the API key is created and saved for you.",
},
},
{
"@type": "Question",
name: "What does the plugin record?",
acceptedAnswer: {
"@type": "Answer",
text: "It captures FPS averages, frame times, 1% lows, and total power draw (CPU + GPU plus peripheral overhead) using MangoHud, along with the detected game, Steam App ID, and Proton version.",
},
},
{
"@type": "Question",
name: "Is the DeckyVault plugin free?",
acceptedAnswer: {
"@type": "Answer",
text: "Yes, the plugin is free and open source.",
},
},
],
} }
const breadcrumbs = buildBreadcrumbList([ const breadcrumbs = buildBreadcrumbList([
{ name: "Home", url: "https://deckyvault.xyz/" }, { name: "Home", url: "https://deckyvault.xyz/" },
{ name: "Plugin", url: "https://deckyvault.xyz/plugin" }, { name: "Plugin", url: CANONICAL },
]) ])
export default function PluginPage() { export default function PluginPage() {
@@ -57,6 +154,10 @@ export default function PluginPage() {
type="application/ld+json" type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/> />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqLd) }}
/>
<script <script
type="application/ld+json" type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbs) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbs) }}
+2
View File
@@ -79,6 +79,8 @@ export const STATIC_PAGES: StaticPageDef[] = [
// Core browse pages // Core browse pages
{ urlPath: "/games", changeFrequency: "daily", priority: 0.9 }, { urlPath: "/games", changeFrequency: "daily", priority: 0.9 },
{ urlPath: "/devices", changeFrequency: "weekly", priority: 0.7 }, { urlPath: "/devices", changeFrequency: "weekly", priority: 0.7 },
// Plugin landing page
{ urlPath: "/plugin", changeFrequency: "monthly", priority: 0.7 },
{ urlPath: "/updates", changeFrequency: "weekly", priority: 0.6 }, { urlPath: "/updates", changeFrequency: "weekly", priority: 0.6 },
// Utility pages // Utility pages
{ urlPath: "/compare", changeFrequency: "weekly", priority: 0.5 }, { urlPath: "/compare", changeFrequency: "weekly", priority: 0.5 },