feat: updates page and lint fixes
This commit is contained in:
@@ -169,7 +169,7 @@ export function GamesClient() {
|
||||
setSyncCompleted(true)
|
||||
setSelectedIds(new Set())
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,7 @@ export function GamesClient() {
|
||||
}))
|
||||
setSyncCompleted(true)
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,18 +485,18 @@ export function GamesPageClient({
|
||||
sortBy: sort,
|
||||
}}
|
||||
onLoad={(filters) => {
|
||||
setMinFps(filters.minFps || "")
|
||||
setMaxFps(filters.maxFps || "")
|
||||
setFsrSupport(filters.fsrSupport || false)
|
||||
setProtonNative(filters.protonNative || "any")
|
||||
setAntiCheatStatus(filters.antiCheatStatus || "any")
|
||||
setPlayabilityStatus(filters.playabilityStatus || "")
|
||||
setSteamReviewMin(filters.steamReviewMin || "")
|
||||
setIsFree(filters.isFree || false)
|
||||
setHasMultiplayer(filters.hasMultiplayer || false)
|
||||
if (filters.genre) setSelectedGenres([filters.genre])
|
||||
setMinFps((filters.minFps as string) || "")
|
||||
setMaxFps((filters.maxFps as string) || "")
|
||||
setFsrSupport((filters.fsrSupport as boolean) || false)
|
||||
setProtonNative((filters.protonNative as string) || "any")
|
||||
setAntiCheatStatus((filters.antiCheatStatus as string) || "any")
|
||||
setPlayabilityStatus((filters.playabilityStatus as string) || "")
|
||||
setSteamReviewMin((filters.steamReviewMin as string) || "")
|
||||
setIsFree((filters.isFree as boolean) || false)
|
||||
setHasMultiplayer((filters.hasMultiplayer as boolean) || false)
|
||||
if (filters.genre) setSelectedGenres([filters.genre as string])
|
||||
else setSelectedGenres([])
|
||||
if (filters.device) setSelectedDevice(filters.device)
|
||||
if (filters.device) setSelectedDevice(filters.device as string)
|
||||
else setSelectedDevice("")
|
||||
if (filters.sortBy) setSort(filters.sortBy as SortOption)
|
||||
}}
|
||||
|
||||
+7
-149
@@ -1,4 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@theme inline {
|
||||
/* Colors */
|
||||
@@ -9,158 +10,15 @@
|
||||
--color-accent: #fb793c;
|
||||
--color-text: #ebe4f1;
|
||||
--color-border: color-mix(in srgb, var(--color-text) 20%, transparent);
|
||||
--color-border-active: color-mix(in srgb, var(--color-text) 60%, transparent);
|
||||
--color-border-active: color-mix(
|
||||
in srgb,
|
||||
var(--color-text) 60%,
|
||||
transparent
|
||||
);
|
||||
|
||||
/* Defaults */
|
||||
--default-transition-duration: 0.3s;
|
||||
|
||||
/* Fonts */
|
||||
--font-sans: var(--font-lexend);
|
||||
}
|
||||
|
||||
.scrollbar-hide {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Markdown content styles */
|
||||
.update-content {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.update-content h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.update-content h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.update-content p {
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.7;
|
||||
color: rgba(235, 228, 241, 0.8);
|
||||
}
|
||||
|
||||
.update-content ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.update-content ol {
|
||||
list-style-type: decimal;
|
||||
padding-left: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.update-content li {
|
||||
margin-bottom: 0.375rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.update-content li > ul,
|
||||
.update-content li > ol {
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.update-content a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.update-content a:hover {
|
||||
text-decoration: underline;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Hide anchor link icons from rehype-autolink-headings by default */
|
||||
.update-content a[aria-hidden="true"] {
|
||||
text-decoration: none;
|
||||
margin-right: 0.375rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.update-content h2:hover a[aria-hidden="true"],
|
||||
.update-content h3:hover a[aria-hidden="true"] {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.update-content a[aria-hidden="true"]::before {
|
||||
content: "#";
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.update-content code {
|
||||
color: var(--color-accent);
|
||||
background: rgba(235, 228, 241, 0.06);
|
||||
padding: 0.125rem 0.375rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
.update-content pre {
|
||||
background: rgba(235, 228, 241, 0.06);
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.update-content pre code {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.update-content strong {
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.update-content hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--color-border);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.update-content blockquote {
|
||||
border-left: 3px solid var(--color-primary);
|
||||
padding-left: 1rem;
|
||||
margin: 1rem 0;
|
||||
color: rgba(235, 228, 241, 0.7);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.update-content table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.update-content th,
|
||||
.update-content td {
|
||||
border: 1px solid var(--color-border);
|
||||
padding: 0.5rem 0.75rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.update-content th {
|
||||
font-weight: 600;
|
||||
background: rgba(235, 228, 241, 0.05);
|
||||
}
|
||||
}
|
||||
+41
-24
@@ -33,16 +33,18 @@ export default function Landing() {
|
||||
|
||||
return (
|
||||
<section
|
||||
id="hero"
|
||||
className="w-full h-[calc(100vh-3.6rem)] flex flex-col items-center justify-center relative p-4"
|
||||
id='hero'
|
||||
className='w-full h-[calc(100vh-3.6rem)] flex flex-col items-center justify-center relative p-4'
|
||||
>
|
||||
<motion.h1
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="font-bold text-3xl md:text-5xl text-center flex flex-row gap-2 items-center justify-center"
|
||||
className='font-bold text-3xl md:text-5xl text-center flex flex-row gap-2 items-center justify-center'
|
||||
>
|
||||
DeckyVault
|
||||
<span className="border border-border text-xs md:text-base px-2 py-1 rounded-md bg-primary/10 font-medium">beta</span>
|
||||
<span className='border border-border text-xs md:text-base px-2 py-1 rounded-md bg-primary/10 font-medium'>
|
||||
beta
|
||||
</span>
|
||||
</motion.h1>
|
||||
<motion.h2
|
||||
layout
|
||||
@@ -50,18 +52,18 @@ export default function Landing() {
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{ opacity: 0.8, transition: { delay: 0.5 } }}
|
||||
className="mt-4 flex flex-row flex-wrap items-center justify-center gap-x-1 md:gap-x-2 text-base md:text-xl"
|
||||
className='mt-4 flex flex-row flex-wrap items-center justify-center gap-x-1 md:gap-x-2 text-base md:text-xl'
|
||||
>
|
||||
{"Find your game".split(" ").map((word, index) => (
|
||||
<motion.span
|
||||
key={index}
|
||||
className="text-center"
|
||||
className='text-center'
|
||||
>
|
||||
{word}
|
||||
</motion.span>
|
||||
))}
|
||||
<AnimatePresence
|
||||
mode="wait"
|
||||
mode='wait'
|
||||
initial={false}
|
||||
>
|
||||
<motion.span
|
||||
@@ -69,7 +71,7 @@ export default function Landing() {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="text-primary font-bold"
|
||||
className='text-primary font-bold'
|
||||
>
|
||||
{words[currentWord]}
|
||||
</motion.span>
|
||||
@@ -77,27 +79,27 @@ export default function Landing() {
|
||||
</motion.h2>
|
||||
<AnimatePresence>
|
||||
<motion.label
|
||||
key="search-bar"
|
||||
layoutId="search-bar"
|
||||
key='search-bar'
|
||||
layoutId='search-bar'
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="mt-8 flex flex-row items-center gap-4 bg-text/5 px-2 py-2 rounded-md border border-border placeholder:text-text/60 group hover:border-border-active transition-colors focus-within:border-primary/80! focus-within:ring-2 focus-within:ring-primary/50! focus-within:ring-offset-2 focus-within:ring-offset-background cursor-text w-full max-w-md"
|
||||
className='mt-8 flex flex-row items-center gap-4 bg-text/5 px-2 py-2 rounded-md border border-border placeholder:text-text/60 group hover:border-border-active transition-colors focus-within:border-primary/80! focus-within:ring-2 focus-within:ring-primary/50! focus-within:ring-offset-2 focus-within:ring-offset-background cursor-text w-full max-w-md'
|
||||
>
|
||||
<Gamepad2Icon className="h-6 w-6 group-focus-within:stroke-accent transition-colors shrink-0" />
|
||||
<Gamepad2Icon className='h-6 w-6 group-focus-within:stroke-accent transition-colors shrink-0' />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="search by game or appid..."
|
||||
type='text'
|
||||
placeholder='search by game or appid...'
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="flex-1 outline-none bg-transparent text-xl min-w-0"
|
||||
className='flex-1 outline-none bg-transparent text-xl min-w-0'
|
||||
/>
|
||||
<motion.button
|
||||
onClick={handleSearchSubmit}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="text-sm flex flex-row gap-1 items-center bg-text text-background px-2 py-1 rounded-sm cursor-pointer hover:opacity-60 transition-opacity shrink-0"
|
||||
className='text-sm flex flex-row gap-1 items-center bg-text text-background px-2 py-1 rounded-sm cursor-pointer hover:opacity-60 transition-opacity shrink-0'
|
||||
>
|
||||
<SearchIcon className="h-3 w-3" />
|
||||
<SearchIcon className='h-3 w-3' />
|
||||
search
|
||||
</motion.button>
|
||||
</motion.label>
|
||||
@@ -105,26 +107,41 @@ export default function Landing() {
|
||||
<motion.small
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { delay: 1.5 } }}
|
||||
className="mt-8 text-center text-xs flex flex-row gap-1"
|
||||
className='mt-8 text-center text-xs flex flex-row gap-1'
|
||||
>
|
||||
<span className="opacity-60">2026 DeckyVault.</span>
|
||||
<Link
|
||||
title="Visit our Github Repository"
|
||||
href="https://github.com/AdrianBonpin/deckyvault"
|
||||
className="text-accent opacity-60 hover:opacity-100 transition-opacity cursor-pointer"
|
||||
title='Visit our Github Repository'
|
||||
href='/updates'
|
||||
className='text-accent opacity-60 hover:opacity-100 transition-opacity cursor-pointer'
|
||||
>
|
||||
See what{"'"}s new.
|
||||
</Link>
|
||||
</motion.small>
|
||||
<motion.small
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { delay: 1.5 } }}
|
||||
className='mt-2 text-center text-xs flex flex-row gap-1'
|
||||
>
|
||||
<span className='opacity-60'>2026 DeckyVault.</span>
|
||||
<Link
|
||||
title='Visit our Github Repository'
|
||||
href='https://github.com/AdrianBonpin/deckyvault'
|
||||
className='text-accent opacity-60 hover:opacity-100 transition-opacity cursor-pointer'
|
||||
>
|
||||
Github.
|
||||
</Link>
|
||||
<span className="opacity-60">v{process.env.NEXT_PUBLIC_APP_VERSION}</span>
|
||||
</motion.small>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
name: "DeckyVault",
|
||||
url: "https://deckyvault.xyz",
|
||||
description: "Steam Deck benchmarks, settings, and performance guides",
|
||||
description:
|
||||
"Steam Deck benchmarks, settings, and performance guides",
|
||||
potentialAction: {
|
||||
"@type": "SearchAction",
|
||||
target: "https://deckyvault.xyz/search?q={search_term_string}",
|
||||
|
||||
@@ -43,7 +43,7 @@ export default async function UpdatePage({
|
||||
|
||||
return (
|
||||
<main className="w-full">
|
||||
<div className="w-full max-w-4xl mx-auto px-4 pt-4">
|
||||
<div className="w-full max-w-7xl mx-auto px-4 pt-4">
|
||||
<Link
|
||||
href="/updates"
|
||||
className="inline-flex items-center gap-1 text-sm text-text/60 hover:text-primary transition-colors"
|
||||
|
||||
+34
-28
@@ -5,36 +5,42 @@ import { UpdateCard } from "@/components/updates/update-card"
|
||||
import { ArrowLeftIcon } from "lucide-react"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Updates",
|
||||
description: "See what's new in DeckyVault — version release notes, features, and improvements.",
|
||||
title: "Updates",
|
||||
description:
|
||||
"See what's new in DeckyVault — version release notes, features, and improvements.",
|
||||
}
|
||||
|
||||
export default function UpdatesPage() {
|
||||
const updates = getAllUpdates()
|
||||
const updates = getAllUpdates()
|
||||
|
||||
return (
|
||||
<main className="w-full max-w-2xl mx-auto px-4 py-8">
|
||||
<div className="flex flex-row items-center gap-2 mb-6">
|
||||
<Link
|
||||
href="/"
|
||||
className="text-text/60 hover:text-primary transition-colors"
|
||||
>
|
||||
<ArrowLeftIcon className="w-4 h-4" />
|
||||
</Link>
|
||||
<h1 className="text-2xl font-bold">Updates</h1>
|
||||
</div>
|
||||
<p className="text-text/60 mb-8">
|
||||
Release notes and changelogs for every DeckyVault version.
|
||||
</p>
|
||||
{updates.length === 0 ? (
|
||||
<p className="text-text/40 text-center py-16">No updates yet.</p>
|
||||
) : (
|
||||
<div className="flex flex-col gap-3">
|
||||
{updates.map((update) => (
|
||||
<UpdateCard key={update.slug} update={update} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
return (
|
||||
<main className='w-full max-w-7xl mx-auto px-4 py-8'>
|
||||
<div className='flex flex-row items-center gap-2 mb-6'>
|
||||
<Link
|
||||
href='/'
|
||||
className='text-text/60 hover:text-primary transition-colors'
|
||||
>
|
||||
<ArrowLeftIcon className='w-4 h-4' />
|
||||
</Link>
|
||||
<h1 className='text-2xl font-bold'>Updates</h1>
|
||||
</div>
|
||||
<p className='text-text/60 mb-8'>
|
||||
Release notes, changelogs, and the like for DeckyVault.
|
||||
</p>
|
||||
{updates.length === 0 ? (
|
||||
<p className='text-text/40 text-center py-16'>
|
||||
No updates yet.
|
||||
</p>
|
||||
) : (
|
||||
<div className='flex flex-col gap-3'>
|
||||
{updates.map((update) => (
|
||||
<UpdateCard
|
||||
key={update.slug}
|
||||
update={update}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user