removed junk

This commit is contained in:
roormonger
2026-03-27 13:13:28 -04:00
parent 2bb3c150f5
commit 37e349d3e3
13 changed files with 51 additions and 875 deletions

View File

@@ -1,13 +0,0 @@
const fs = require('fs');
const content = fs.readFileSync('romm_swagger.json');
try {
let jsonString = content.toString('utf16le');
if (jsonString.charCodeAt(0) === 0xFEFF) { jsonString = jsonString.slice(1); }
const parsed = JSON.parse(jsonString);
console.log("LOGIN_OPENID:", JSON.stringify(parsed.paths['/api/login/openid'], null, 2));
console.log("OAUTH_OPENID:", JSON.stringify(parsed.paths['/api/oauth/openid'], null, 2));
} catch (e) {
const parsed = JSON.parse(content.toString('utf8'));
console.log("LOGIN_OPENID:", JSON.stringify(parsed.paths['/api/login/openid'], null, 2));
console.log("OAUTH_OPENID:", JSON.stringify(parsed.paths['/api/oauth/openid'], null, 2));
}

View File

@@ -1,13 +0,0 @@
const fs = require('fs');
const content = fs.readFileSync('romm_swagger.json');
try {
let jsonString = content.toString('utf16le');
if (jsonString.charCodeAt(0) === 0xFEFF) { jsonString = jsonString.slice(1); }
const parsed = JSON.parse(jsonString);
const paths = Object.keys(parsed.paths).filter(p => p.includes('auth') || p.includes('sso') || p.includes('oauth') || p.includes('login'));
console.log(paths.join('\n'));
} catch (e) {
const parsed = JSON.parse(content.toString('utf8'));
const paths = Object.keys(parsed.paths).filter(p => p.includes('auth') || p.includes('sso') || p.includes('oauth') || p.includes('login'));
console.log(paths.join('\n'));
}

View File

@@ -1,16 +0,0 @@
const fs = require('fs');
// read file using exact encoding
const content = fs.readFileSync('romm_swagger.json');
try {
let jsonString = content.toString('utf16le');
if (jsonString.charCodeAt(0) === 0xFEFF) {
jsonString = jsonString.slice(1);
}
const parsed = JSON.parse(jsonString);
const paths = Object.keys(parsed.paths).filter(p => p.includes('user') || p.includes('me'));
console.log(paths.join('\n'));
} catch (e) {
const parsed = JSON.parse(content.toString('utf8'));
const paths = Object.keys(parsed.paths).filter(p => p.includes('user') || p.includes('me'));
console.log(paths.join('\n'));
}

View File

@@ -1,18 +0,0 @@
const fs = require('fs');
// read file using exact encoding
const content = fs.readFileSync('romm_swagger.json');
try {
// Try reading it as utf16le just in case PowerShell dumped it with BOM
let jsonString = content.toString('utf16le');
if (jsonString.charCodeAt(0) === 0xFEFF) {
jsonString = jsonString.slice(1);
}
const parsed = JSON.parse(jsonString);
const paths = Object.keys(parsed.paths).filter(p => p.includes('user') || p.includes('me'));
console.log(paths.join('\n'));
} catch (e) {
// Fallback to utf8 just in case
const parsed = JSON.parse(content.toString('utf8'));
const paths = Object.keys(parsed.paths).filter(p => p.includes('user') || p.includes('me'));
console.log(paths.join('\n'));
}

View File

@@ -1,13 +0,0 @@
const fs = require('fs');
const content = fs.readFileSync('romm_swagger.json');
try {
let jsonString = content.toString('utf16le');
if (jsonString.charCodeAt(0) === 0xFEFF) { jsonString = jsonString.slice(1); }
const parsed = JSON.parse(jsonString);
const paths = Object.keys(parsed.paths).filter(p => p.includes('avatar') || p.includes('asset'));
console.log(paths.join('\n'));
} catch (e) {
const parsed = JSON.parse(content.toString('utf8'));
const paths = Object.keys(parsed.paths).filter(p => p.includes('avatar') || p.includes('asset'));
console.log(paths.join('\n'));
}

View File

@@ -1,16 +0,0 @@
const fs = require('fs');
try {
const content = fs.readFileSync('./romm_swagger.json', 'utf16le');
let data;
try {
data = JSON.parse(content);
} catch (e) {
// maybe it's utf8
data = JSON.parse(fs.readFileSync('./romm_swagger.json', 'utf8'));
}
const paths = Object.keys(data.paths);
const targets = paths.filter(p => p.toLowerCase().includes('stat') || p.toLowerCase().includes('count') || p.toLowerCase().includes('size') || p.toLowerCase().includes('system'));
console.log("Matching Endpoints:", targets);
} catch (e) {
console.error("Failed:", e.message);
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -142,6 +142,10 @@ export interface UserProfile {
// Function to safely extract base URL
const getBaseUrl = () => {
// During development, if we're on localhost, we prefer relative paths to leverage the Vite proxy and avoid CORS.
if (import.meta.env.DEV && window.location.hostname === 'localhost') {
return '';
}
const rawUrl = import.meta.env.VITE_ROMM_BASE_URL || 'http://localhost:8080';
const cleanUrl = rawUrl.endsWith('/') ? rawUrl.slice(0, -1) : rawUrl;
return cleanUrl;
@@ -192,6 +196,7 @@ const formatDate = (unixSeconds: any) => {
export const rommApiClient = {
get apiBase() {
const cleanUrl = getBaseUrl();
if (cleanUrl === '') return '/api';
return cleanUrl.endsWith('/api') ? cleanUrl : `${cleanUrl}/api`;
},
@@ -437,6 +442,9 @@ export const rommApiClient = {
...(json.screenshots || []).map((s: any) => s.url ? getFullImageUrl(s.url) || '' : ''),
...(getFirst('screenshots') || [])
])).filter(Boolean) as string[],
notes: json.all_user_notes || [],
saves: json.user_saves || [],
states: json.user_states || [],
};
},

View File

@@ -1,6 +1,5 @@
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { rommApiClient, DetailedGame, UserProfile } from '../api/client';
import { DetailedGame, UserProfile } from '../api/client';
import { FocusableItem } from './FocusableItem';
import { AchievementList } from './AchievementList';
@@ -32,22 +31,15 @@ export const GameDetailsPane = ({
mode
}: GameDetailsPaneProps) => {
const [activeTab, setActiveTab] = useState<'saves' | 'states'>('saves');
const savesQuery = useQuery({
queryKey: ['gameSaves', game.id],
queryFn: () => rommApiClient.fetchRomSaves(game.id),
enabled: !!game.id
});
const [showOthers, setShowOthers] = useState(false);
const saves = game.saves || [];
const states = game.states || [];
const notes = game.notes || [];
const statesQuery = useQuery({
queryKey: ['gameStates', game.id],
queryFn: () => rommApiClient.fetchRomStates(game.id),
enabled: !!game.id
});
const notesQuery = useQuery({
queryKey: ['gameNotes', game.id],
queryFn: () => rommApiClient.fetchRomNotes(game.id),
enabled: !!game.id
const filteredNotes = notes.filter(note => {
const isMine = user && note.username === user.username;
if (showOthers) return true;
return isMine;
});
const mediaItems: { type: 'video' | 'image', url: string, youtubeId?: string }[] = [];
@@ -255,14 +247,14 @@ export const GameDetailsPane = ({
<div className="flex-1 overflow-y-auto scrollbar-hoverable pr-2">
{activeTab === 'saves' ? (
(!savesQuery.data || savesQuery.data.length === 0) ? (
(saves.length === 0) ? (
<div className="h-full flex flex-col items-center justify-center text-center opacity-20">
<span className="material-symbols-outlined text-5xl mb-4">cloud_off</span>
<p className="geist-mono uppercase text-[0.625rem] tracking-widest">No saves found</p>
</div>
) : (
<div className="space-y-4">
{savesQuery.data.map(save => (
{saves.map(save => (
<div key={save.id} className="group/item flex items-center justify-between p-4 bg-white/[0.02] rounded-xl border border-white/5 hover:bg-white/[0.05] transition-all">
<div className="flex items-center gap-4 overflow-hidden">
<span className="material-symbols-outlined text-white/30">draft</span>
@@ -281,14 +273,14 @@ export const GameDetailsPane = ({
</div>
)
) : (
(!statesQuery.data || statesQuery.data.length === 0) ? (
(states.length === 0) ? (
<div className="h-full flex flex-col items-center justify-center text-center opacity-20">
<span className="material-symbols-outlined text-5xl mb-4">history</span>
<p className="geist-mono uppercase text-[0.625rem] tracking-widest">No states found</p>
</div>
) : (
<div className="space-y-4">
{statesQuery.data.map(state => (
{states.map(state => (
<div key={state.id} className="group/item flex items-center justify-between p-4 bg-white/[0.02] rounded-xl border border-white/5 hover:bg-white/[0.05] transition-all">
<div className="flex items-center gap-4 overflow-hidden">
<span className="material-symbols-outlined text-white/30">history</span>
@@ -315,10 +307,22 @@ export const GameDetailsPane = ({
<div className="flex items-center justify-between mb-1 px-2 h-10">
<div className="flex items-center gap-3 uppercase geist-mono font-black text-[0.75rem] tracking-[0.2em] text-white">
<span className="material-symbols-outlined text-[1.25rem]">edit_note</span>
Personal Notes
{showOthers ? 'Global Notes' : 'Personal Notes'}
</div>
<div className="flex items-center gap-2">
<FocusableItem
onFocus={setFocusZone}
onClick={() => setShowOthers(!showOthers)}
onEnterPress={() => setShowOthers(!showOthers)}
focusKey="TOGGLE_PUBLIC_NOTES"
>
{(focused) => (
<button className={`w-10 h-10 flex items-center justify-center rounded-lg transition-all ${focused ? 'bg-[#2563eb]/20 text-[#2563eb] ring-1 ring-[#2563eb] scale-110 shadow-lg' : showOthers ? 'text-[#2563eb] bg-[#2563eb]/10' : 'text-white/40 hover:text-white/60 hover:bg-white/5'}`}>
<span className="material-symbols-outlined text-[1.25rem]">{showOthers ? 'public' : 'public_off'}</span>
</button>
)}
</FocusableItem>
<FocusableItem onFocus={setFocusZone} focusKey="ADD_NOTE">
{(focused) => (
<button className={`w-10 h-10 flex items-center justify-center rounded-lg transition-all ${focused ? 'bg-[#2563eb]/20 text-[#2563eb] ring-1 ring-[#2563eb] scale-110 shadow-lg' : 'text-white/40 hover:text-white/60 hover:bg-white/5'}`}>
@@ -332,17 +336,24 @@ export const GameDetailsPane = ({
<div className="border-t border-white/5 mb-6"></div>
<div className="flex-1 overflow-y-auto scrollbar-hoverable pr-2">
{!notesQuery.data || notesQuery.data.length === 0 ? (
{filteredNotes.length === 0 ? (
<div className="h-full flex flex-col items-center justify-center text-center opacity-20">
<span className="material-symbols-outlined text-5xl mb-4">note_stack</span>
<p className="geist-mono uppercase text-[0.625rem] tracking-widest">No entries recorded</p>
<span className="material-symbols-outlined text-5xl mb-4">{showOthers ? 'public_off' : 'note_stack'}</span>
<p className="geist-mono uppercase text-[0.625rem] tracking-widest">{showOthers ? 'No public entries' : 'No entries recorded'}</p>
</div>
) : (
<div className="space-y-8">
{notesQuery.data.map(note => (
{filteredNotes.map(note => (
<div key={note.id} className="space-y-4">
<div className="flex items-center justify-between">
<div className="text-lg font-bold text-white/80 tracking-tight">{note.title}</div>
<div className="flex items-center gap-3">
<div className="text-lg font-bold text-white/80 tracking-tight">{note.title}</div>
{note.username !== user?.username && (
<span className="px-2 py-0.5 bg-white/5 rounded text-[0.5rem] geist-mono text-white/30 uppercase tracking-widest border border-white/10">
{note.username}
</span>
)}
</div>
<div className="text-[0.625rem] geist-mono text-white/20 uppercase">
{new Date(note.updated_at).toLocaleDateString()}
</div>

View File

@@ -1,448 +0,0 @@
<!DOCTYPE html>
<html class="dark" lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>The Digital Atelier - Home</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<style>
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.geist-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
.thumbnail-active {
opacity: 1 !important;
border: 2px solid #2563eb !important;
border-radius: 6px !important;
}
</style>
</head>
<body class="bg-[#10131f] text-[#c3c6d7] font-geist selection:bg-[#2563eb]/30 overflow-hidden">
<!-- SideNavBar Anchor -->
<aside class="w-64 h-screen fixed left-0 top-0 bg-[#181b28] shadow-[20px_0_50px_rgba(0,0,0,0.3)] flex flex-col py-8 z-[60]">
<div class="px-6 mb-10">
<h1 class="text-xl font-black text-white tracking-tighter uppercase geist-mono">The Digital Atelier</h1>
<p class="text-[10px] geist-mono uppercase tracking-[0.05em] text-[#c3c6d7] opacity-60">Curated Vault</p>
</div>
<nav class="flex-1 flex flex-col gap-1">
<a class="bg-[#2563eb]/10 text-[#2563eb] backdrop-blur-md rounded-full mx-2 py-3 px-4 flex items-center gap-3 active:scale-95 transition-all" href="#">
<span class="material-symbols-outlined" data-icon="home">home</span>
<span class="geist-mono tracking-tight text-sm uppercase">Home</span>
</a>
<a class="text-[#c3c6d7] hover:text-white hover:bg-white/5 mx-2 py-3 px-4 flex items-center gap-3 transition-all active:scale-95" href="#">
<span class="material-symbols-outlined" data-icon="bookmarks">bookmarks</span>
<span class="geist-mono tracking-tight text-sm uppercase">Platforms</span>
</a>
<a class="text-[#c3c6d7] hover:text-white hover:bg-white/5 mx-2 py-3 px-4 flex items-center gap-3 transition-all active:scale-95" href="#">
<span class="material-symbols-outlined" data-icon="library_books">library_books</span>
<span class="geist-mono tracking-tight text-sm uppercase">Collections</span>
</a>
<a class="text-[#c3c6d7] hover:text-white hover:bg-white/5 mx-2 py-3 px-4 flex items-center gap-3 transition-all active:scale-95" href="#">
<span class="material-symbols-outlined" data-icon="videogame_asset">videogame_asset</span>
<span class="geist-mono tracking-tight text-sm uppercase">Console</span>
</a>
</nav>
<div class="mt-auto flex flex-col gap-1 pt-6">
<a class="text-[#c3c6d7] hover:text-white hover:bg-white/5 mx-2 py-3 px-4 flex items-center gap-3 transition-all active:scale-95" href="#">
<span class="material-symbols-outlined" data-icon="settings">settings</span>
<span class="geist-mono tracking-tight text-sm uppercase">Settings</span>
</a>
<a class="text-[#c3c6d7] hover:text-white hover:bg-white/5 mx-2 py-3 px-4 flex items-center gap-3 transition-all active:scale-95" href="#">
<span class="material-symbols-outlined" data-icon="help_outline">help_outline</span>
<span class="geist-mono tracking-tight text-sm uppercase">Support</span>
</a>
<div class="px-6 mt-4 flex items-center gap-3">
<div class="w-8 h-8 rounded-full bg-white/10 overflow-hidden">
<img alt="User profile avatar" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD6-iqa8Rg2GOiRcezo2bHGcW-qSPDPSuPHswQITO1jjFzfy8Tz4emYZ01W2LDBP96TpLRx8Q_3RzztdNWc9lQYuWLgEQ9VlatIK_FiOAmJlhsVTC4eAVYo2H25CwF8aIOwq2lqI5wnmGVo_rmlgp9vIAHvdCg9eO77fG5uhaSJZZ156M7RYEhg4PTNPhKIVp3XkX4u-tD56XaMiytgjMTK7VJfl4642t-JYltjZ2r6XhgnyOCG1BqOh1F_Wo5-e96NHMTu4X1iLRw"/>
</div>
<div class="flex flex-col leading-none">
<span class="text-xs font-bold text-white geist-mono">Archivist</span>
<span class="text-[10px] text-[#c3c6d7] font-medium uppercase tracking-tighter geist-mono">Admin</span>
</div>
</div>
</div>
</aside>
<!-- TopNavBar -->
<header class="fixed top-0 right-0 left-64 h-16 z-50 bg-[#10131f]/80 backdrop-blur-2xl flex items-center justify-between px-12 w-auto border-b border-white/5">
<div class="flex items-center gap-6 w-full">
<div class="relative w-full max-w-md focus-within:ring-1 focus-within:ring-[#2563eb]/50 rounded-md overflow-hidden bg-white/5">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-[#c3c6d7] text-sm">search</span>
<input class="w-full bg-transparent border-none pl-10 pr-4 py-2 text-sm text-white placeholder-[#c3c6d7]/50 focus:ring-0 geist-mono" placeholder="Search the vault..." type="text"/>
</div>
<!-- One-liner stats -->
<div class="hidden xl:flex items-center gap-6 ml-4 geist-mono flex-1 justify-center">
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="account_tree">account_tree</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">12</span> Platforms</span>
</div>
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="sports_esports">sports_esports</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">1,402</span> Games</span>
</div>
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="hard_drive">hard_drive</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">2.4 TB</span></span>
</div>
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="save">save</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">842</span> Saves</span>
</div>
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="history">history</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">142</span> States</span>
</div>
<div class="flex items-center gap-2">
<span class="material-symbols-outlined text-[#2563eb] text-sm" data-icon="photo_library">photo_library</span>
<span class="text-[11px] uppercase text-[#c3c6d7] font-bold"><span class="text-white">5,103</span> Screens</span>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="ml-64 pt-16 h-screen overflow-y-auto no-scrollbar">
<!-- Featured Galleria Section -->
<section class="bg-[#10131f] border-b border-white/5" id="featured-section">
<div class="px-12 py-12">
<div class="flex gap-12 items-stretch max-w-full">
<!-- Large Hero Poster on the left -->
<div class="w-[400px] aspect-[3/4] shrink-0 bg-white/5 overflow-hidden rounded-md shadow-2xl">
<img alt="Hero Poster" class="w-full h-full object-cover" id="hero-poster" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBUhUkSXQHbjcUk0giuzo4xLtdCITpVPtDFEdrvea97_1irhgudxYgcyJQ3LOdTW1noAKV8wsN-ezHOrz9rerGXMsc8d-6lNiwf7ls7rihej6nuVloixoBODwfXdC9YnTeFuhoHmXnPySvjIZl9fR7B2dmUqWQS9HGWOiCzynYCDsh5MwikbBsg8GUSpcQLaEGigeJmABNTqjGEUTLSUzaJ_VArKU1pYQkTpc6gXSiMkC_AVb5ccr6YJp40LS7svN0PQ9G5vpkSHJw"/>
</div>
<!-- Content Area on the right -->
<div class="flex-1 flex flex-col justify-between">
<!-- Top portion -->
<div class="flex flex-col">
<div class="inline-flex items-center gap-2 px-3 py-1 bg-[#2563eb]/20 text-[#b4c5ff] mb-4 w-fit border border-[#2563eb]/30 rounded-md">
<span class="material-symbols-outlined text-[10px]" style="font-variation-settings: 'FILL' 1;">star</span>
<span class="text-[10px] font-black uppercase tracking-widest geist-mono" id="hero-tag">Action Gold</span>
</div>
<h2 class="text-[72px] font-black text-white tracking-tighter mb-4 leading-[0.9] uppercase geist-mono" id="hero-title">SUNSET OVERDRIVE</h2>
<!-- Metadata Grid -->
<div class="flex gap-10 mb-8 geist-mono">
<div class="flex flex-col">
<span class="text-[10px] text-[#c3c6d7] uppercase tracking-[0.2em] font-bold mb-1 opacity-60">Platform</span>
<span class="text-xs font-black text-white" id="hero-platform">XBOX ONE / PC</span>
</div>
<div class="flex flex-col">
<span class="text-[10px] text-[#c3c6d7] uppercase tracking-[0.2em] font-bold mb-1 opacity-60">Developer</span>
<span class="text-xs font-black text-white" id="hero-dev">Fizz Co</span>
</div>
<div class="flex flex-col">
<span class="text-[10px] text-[#c3c6d7] uppercase tracking-[0.2em] font-bold mb-1 opacity-60">Year</span>
<span class="text-xs font-black text-white" id="hero-year">2014</span>
</div>
<div class="flex flex-col">
<span class="text-[10px] text-[#c3c6d7] uppercase tracking-[0.2em] font-bold mb-1 opacity-60">Rating</span>
<span class="text-xs font-black text-white flex items-center gap-1" id="hero-rating">
<span class="material-symbols-outlined text-[10px] text-yellow-500" style="font-variation-settings: 'FILL' 1;">star</span> 8.8
</span>
</div>
</div>
<p class="text-sm text-white/90 leading-relaxed max-w-2xl mb-8 geist-mono" id="hero-desc">
Style over everything. Grind, jump, and wall-run through a colorful post-apocalyptic city in the most energetic shooter ever made.
</p>
<!-- Action Row -->
<div class="flex items-center gap-3 mb-8">
<button class="bg-[#2563eb] hover:bg-[#2563eb]/90 text-white h-11 px-8 font-black flex items-center gap-2 active:scale-95 transition-all text-[11px] uppercase geist-mono rounded-md">
<span class="material-symbols-outlined text-base" style="font-variation-settings: 'FILL' 1;">play_arrow</span>
Play Now
</button>
<button class="bg-white/5 hover:bg-white/10 border border-white/10 text-white h-11 px-8 font-black flex items-center gap-2 transition-all text-[11px] uppercase geist-mono rounded-md">
<span class="material-symbols-outlined text-base">download</span>
Download
</button>
<button class="bg-white/5 hover:bg-white/10 border border-white/10 text-white h-11 w-11 flex items-center justify-center transition-all rounded-md">
<span class="material-symbols-outlined text-base">library_add</span>
</button>
</div>
</div>
<!-- Bottom portion: Thumbnail row -->
<div class="relative w-full overflow-hidden">
<div class="flex gap-3 overflow-x-auto no-scrollbar scroll-smooth" id="carousel-container">
<!-- Items generated via script -->
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Other Sections -->
<div class="px-12 py-16 space-y-20 pb-24">
<!-- Recently Added -->
<section>
<div class="flex items-center justify-between mb-8 border-l-4 border-[#2563eb] pl-4">
<h3 class="text-xl font-black text-white uppercase tracking-tighter geist-mono">Recently Added</h3>
<a class="text-[10px] font-black text-[#2563eb] hover:underline uppercase geist-mono tracking-[0.3em]" href="#">View All</a>
</div>
<div class="flex gap-6 overflow-x-auto no-scrollbar pb-4">
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBE_Bwp3p2xwGRJbX0mMY130beG6FoRAeWcKQjFJQ1TemSkfm9oUpgoWokReYnkA8xlGeEKLPfSe6IS3KAMwk7uOIZ-W7lNFxuVRguT5HQsKMeIFNBwNAdzX6Dv-zB0UcR9MrZGeh-P937ZVdZlacyWKhd9UlrYYXKWs0WrlfzCwyih9iuOdBghRwGUPCSMZYl4GbaktvJvYaR4umqCssOOgxo4MJK2qMu_9MNT6TBPyxQAGAnMih8bz3c37weOAsALioyqUH_bjiY"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Lunar Colony</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">Strategy</p>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCdZ6xp9ZMjTyJAWKmRuJFddxg7y2QXMYoQ9LeHyylQzDzJrnhJ9AtMH09t2IagtwxKIlyOIEHjYKsXZ-oxOr0HIGPSQhfbqi18wFk_4GBQWixpT6DxIA49JS-Nc2Aaka7-duczcRYIisv650f40n_HxzapPIgrdgWYKY03ykvHBYF4LIvfA6IyJgSyesR90E8vb-LUcNjcoExS5QD3IHV-pmbMeIikXzUN9FsUnkNUK66mCjRb7YuZ69g1UpU4JDfpv6r-drxrJ-0"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">The Golem Project</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">Action</p>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBX4MjWOaUI2OT7G-1EH4iX8fwmchgRIyNRL7agFG5GxTi-RWqKUM2cD8qL_8kZS17opkEzmt0Z5pb7U90EzpaHtbYZxHsxP05NPSXm1qsDamkV4vtb-wovTv4W5CuQqINNI2TtUAbSKcjnlANabj_NfmrfjRjcjEjxsBVzt_25lrsJKa-PEgVhvF_stJeAlL-DVVw0uOO87ZyQ0ZLlQcVRx4St4BfA8WIRyY6UnL5Jtj03fa8hO-mqEsyARLPQ0wGBX_nZOfPao4Y"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Vector Prime</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">Arcade</p>
</div>
</div>
</section>
<!-- Continue Playing -->
<section>
<div class="flex items-center justify-between mb-8 border-l-4 border-[#2563eb] pl-4">
<h3 class="text-xl font-black text-white uppercase tracking-tighter geist-mono">Continue Playing</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div class="bg-white/5 p-6 flex gap-6 hover:bg-white/10 transition-all cursor-pointer group rounded-md ring-1 ring-white/10">
<div class="w-28 aspect-square bg-white/10 overflow-hidden shrink-0 rounded-md">
<img class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCg7VYbc9iGTxKmei73JUyk9phRdk_vhe8hLzpky0tU8YefgFG_WwZE21o-gGr2IWPZ58fE5a4sYVL2mdzcmyqQdDemL_erw6Ql0KB4jthOnvSAUCO5VTfmB11yD-sbjSFHOKKEqI4eiBileSBNLjdEewVvpvUNpx2zUvFhRtJl2Y5T7q06eMDtNh3phArXk8CFWDPymoa5H5YwKyhtwknjFQsxNIxQfdLALzyDCuAn4I5HeafBZiMhcuOVYM8egHBbW2raYd7xyqU"/>
</div>
<div class="flex-1 flex flex-col justify-center">
<h4 class="text-sm font-black text-white mb-3 geist-mono uppercase">Cyberpunk Reclamation</h4>
<div class="flex items-center justify-between text-[10px] text-[#c3c6d7] uppercase geist-mono mb-2 font-bold">
<span>84% Complete</span>
<span class="text-[#2563eb]">Active</span>
</div>
<div class="h-1 bg-white/5 overflow-hidden rounded-full">
<div class="h-full bg-[#2563eb] w-[84%]"></div>
</div>
</div>
</div>
</div>
</section>
<!-- Favorites -->
<section>
<div class="flex items-center justify-between mb-8 border-l-4 border-[#2563eb] pl-4">
<h3 class="text-xl font-black text-white uppercase tracking-tighter geist-mono">Favorites</h3>
<a class="text-[10px] font-black text-[#2563eb] hover:underline uppercase geist-mono tracking-[0.3em]" href="#">View All</a>
</div>
<div class="flex gap-6 overflow-x-auto no-scrollbar pb-4">
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCg7VYbc9iGTxKmei73JUyk9phRdk_vhe8hLzpky0tU8YefgFG_WwZE21o-gGr2IWPZ58fE5a4sYVL2mdzcmyqQdDemL_erw6Ql0KB4jthOnvSAUCO5VTfmB11yD-sbjSFHOKKEqI4eiBileSBNLjdEewVvpvUNpx2zUvFhRtJl2Y5T7q06eMDtNh3phArXk8CFWDPymoa5H5YwKyhtwknjFQsxNIxQfdLALzyDCuAn4I5HeafBZiMhcuOVYM8egHBbW2raYd7xyqU"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Cyberpunk Reclamation</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">RPG</p>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCdZ6xp9ZMjTyJAWKmRuJFddxg7y2QXMYoQ9LeHyylQzDzJrnhJ9AtMH09t2IagtwxKIlyOIEHjYKsXZ-oxOr0HIGPSQhfbqi18wFk_4GBQWixpT6DxIA49JS-Nc2Aaka7-duczcRYIisv650f40n_HxzapPIgrdgWYKY03ykvHBYF4LIvfA6IyJgSyesR90E8vb-LUcNjcoExS5QD3IHV-pmbMeIikXzUN9FsUnkNUK66mCjRb7YuZ69g1UpU4JDfpv6r-drxrJ-0"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">The Golem Project</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">Action</p>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBUhUkSXQHbjcUk0giuzo4xLtdCITpVPtDFEdrvea97_1irhgudxYgcyJQ3LOdTW1noAKV8wsN-ezHOrz9rerGXMsc8d-6lNiwf7ls7rihej6nuVloixoBODwfXdC9YnTeFuhoHmXnPySvjIZl9fR7B2dmUqWQS9HGWOiCzynYCDsh5MwikbBsg8GUSpcQLaEGigeJmABNTqjGEUTLSUzaJ_VArKU1pYQkTpc6gXSiMkC_AVb5ccr6YJp40LS7svN0PQ9G5vpkSHJw"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Sunset Overdrive</h4>
<p class="text-[10px] text-[#c3c6d7] uppercase geist-mono tracking-widest">Action</p>
</div>
</div>
</section>
<!-- Collections -->
<section>
<div class="flex items-center justify-between mb-8 border-l-4 border-[#2563eb] pl-4">
<h3 class="text-xl font-black text-white uppercase tracking-tighter geist-mono">Collections</h3>
<a class="text-[10px] font-black text-[#2563eb] hover:underline uppercase geist-mono tracking-[0.3em]" href="#">View All</a>
</div>
<div class="flex gap-6 overflow-x-auto no-scrollbar pb-4">
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBUhUkSXQHbjcUk0giuzo4xLtdCITpVPtDFEdrvea97_1irhgudxYgcyJQ3LOdTW1noAKV8wsN-ezHOrz9rerGXMsc8d-6lNiwf7ls7rihej6nuVloixoBODwfXdC9YnTeFuhoHmXnPySvjIZl9fR7B2dmUqWQS9HGWOiCzynYCDsh5MwikbBsg8GUSpcQLaEGigeJmABNTqjGEUTLSUzaJ_VArKU1pYQkTpc6gXSiMkC_AVb5ccr6YJp40LS7svN0PQ9G5vpkSHJw"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Neon Noir</h4>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCg7VYbc9iGTxKmei73JUyk9phRdk_vhe8hLzpky0tU8YefgFG_WwZE21o-gGr2IWPZ58fE5a4sYVL2mdzcmyqQdDemL_erw6Ql0KB4jthOnvSAUCO5VTfmB11yD-sbjSFHOKKEqI4eiBileSBNLjdEewVvpvUNpx2zUvFhRtJl2Y5T7q06eMDtNh3phArXk8CFWDPymoa5H5YwKyhtwknjFQsxNIxQfdLALzyDCuAn4I5HeafBZiMhcuOVYM8egHBbW2raYd7xyqU"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Cyberpunk Classics</h4>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBE_Bwp3p2xwGRJbX0mMY130beG6FoRAeWcKQjFJQ1TemSkfm9oUpgoWokReYnkA8xlGeEKLPfSe6IS3KAMwk7uOIZ-W7lNFxuVRguT5HQsKMeIFNBwNAdzX6Dv-zB0UcR9MrZGeh-P937ZVdZlacyWKhd9UlrYYXKWs0WrlfzCwyih9iuOdBghRwGUPCSMZYl4GbaktvJvYaR4umqCssOOgxo4MJK2qMu_9MNT6TBPyxQAGAnMih8bz3c37weOAsALioyqUH_bjiY"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Atmospheric Sims</h4>
</div>
</div>
</section>
<!-- Autogenerated Collections -->
<section>
<div class="flex items-center justify-between mb-8 border-l-4 border-[#2563eb] pl-4">
<h3 class="text-xl font-black text-white uppercase tracking-tighter geist-mono">Autogenerated Collections</h3>
</div>
<div class="flex gap-6 overflow-x-auto no-scrollbar pb-4">
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBX4MjWOaUI2OT7G-1EH4iX8fwmchgRIyNRL7agFG5GxTi-RWqKUM2cD8qL_8kZS17opkEzmt0Z5pb7U90EzpaHtbYZxHsxP05NPSXm1qsDamkV4vtb-wovTv4W5CuQqINNI2TtUAbSKcjnlANabj_NfmrfjRjcjEjxsBVzt_25lrsJKa-PEgVhvF_stJeAlL-DVVw0uOO87ZyQ0ZLlQcVRx4St4BfA8WIRyY6UnL5Jtj03fa8hO-mqEsyARLPQ0wGBX_nZOfPao4Y"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Arcade Revival</h4>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBE_Bwp3p2xwGRJbX0mMY130beG6FoRAeWcKQjFJQ1TemSkfm9oUpgoWokReYnkA8xlGeEKLPfSe6IS3KAMwk7uOIZ-W7lNFxuVRguT5HQsKMeIFNBwNAdzX6Dv-zB0UcR9MrZGeh-P937ZVdZlacyWKhd9UlrYYXKWs0WrlfzCwyih9iuOdBghRwGUPCSMZYl4GbaktvJvYaR4umqCssOOgxo4MJK2qMu_9MNT6TBPyxQAGAnMih8bz3c37weOAsALioyqUH_bjiY"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Deep Space Strategy</h4>
</div>
<div class="w-56 shrink-0 cursor-pointer group">
<div class="aspect-[2/3] bg-white/5 overflow-hidden mb-4 ring-1 ring-white/10 rounded-[6px]">
<img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCdZ6xp9ZMjTyJAWKmRuJFddxg7y2QXMYoQ9LeHyylQzDzJrnhJ9AtMH09t2IagtwxKIlyOIEHjYKsXZ-oxOr0HIGPSQhfbqi18wFk_4GBQWixpT6DxIA49JS-Nc2Aaka7-duczcRYIisv650f40n_HxzapPIgrdgWYKY03ykvHBYF4LIvfA6IyJgSyesR90E8vb-LUcNjcoExS5QD3IHV-pmbMeIikXzUN9FsUnkNUK66mCjRb7YuZ69g1UpU4JDfpv6r-drxrJ-0"/>
</div>
<h4 class="text-sm font-black text-white truncate geist-mono uppercase">Kinetic Combat</h4>
</div>
</div>
</section>
</div>
</main>
<script>
const gamesData = [
{
title: "SUNSET OVERDRIVE",
platform: "XBOX ONE / PC",
dev: "Fizz Co",
year: "2014",
rating: "8.8",
desc: "Style over everything. Grind, jump, and wall-run through a colorful post-apocalyptic city in the most energetic shooter ever made.",
tag: "Action Gold",
img: "https://lh3.googleusercontent.com/aida-public/AB6AXuBUhUkSXQHbjcUk0giuzo4xLtdCITpVPtDFEdrvea97_1irhgudxYgcyJQ3LOdTW1noAKV8wsN-ezHOrz9rerGXMsc8d-6lNiwf7ls7rihej6nuVloixoBODwfXdC9YnTeFuhoHmXnPySvjIZl9fR7B2dmUqWQS9HGWOiCzynYCDsh5MwikbBsg8GUSpcQLaEGigeJmABNTqjGEUTLSUzaJ_VArKU1pYQkTpc6gXSiMkC_AVb5ccr6YJp40LS7svN0PQ9G5vpkSHJw"
},
{
title: "Cyberpunk Reclamation",
platform: "PC / ARCHIVE",
dev: "Project Orion",
year: "2077",
rating: "9.8",
desc: "Experience the definitive edition of the neon-noir classic. Fully restored assets and curated archival content from the deepest vaults of the Digital Atelier.",
tag: "Masterpiece Collection",
img: "https://lh3.googleusercontent.com/aida-public/AB6AXuCg7VYbc9iGTxKmei73JUyk9phRdk_vhe8hLzpky0tU8YefgFG_WwZE21o-gGr2IWPZ58fE5a4sYVL2mdzcmyqQdDemL_erw6Ql0KB4jthOnvSAUCO5VTfmB11yD-sbjSFHOKKEqI4eiBileSBNLjdEewVvpvUNpx2zUvFhRtJl2Y5T7q06eMDtNh3phArXk8CFWDPymoa5H5YwKyhtwknjFQsxNIxQfdLALzyDCuAn4I5HeafBZiMhcuOVYM8egHBbW2raYd7xyqU"
},
{
title: "Lunar Colony",
platform: "PC / VR",
dev: "Stellar Ops",
year: "2042",
rating: "8.5",
desc: "Command the first permanent lunar settlement in this intricate survival strategy simulation. Manage oxygen, power, and human fragile spirits.",
tag: "Editor's Choice",
img: "https://lh3.googleusercontent.com/aida-public/AB6AXuBE_Bwp3p2xwGRJbX0mMY130beG6FoRAeWcKQjFJQ1TemSkfm9oUpgoWokReYnkA8xlGeEKLPfSe6IS3KAMwk7uOIZ-W7lNFxuVRguT5HQsKMeIFNBwNAdzX6Dv-zB0UcR9MrZGeh-P937ZVdZlacyWKhd9UlrYYXKWs0WrlfzCwyih9iuOdBghRwGUPCSMZYl4GbaktvJvYaR4umqCssOOgxo4MJK2qMu_9MNT6TBPyxQAGAnMih8bz3c37weOAsALioyqUH_bjiY"
},
{
title: "The Golem Project",
platform: "CONSOLE / NEXT-GEN",
dev: "Anima Works",
year: "2024",
rating: "9.2",
desc: "A fast-paced kinetic action RPG where you craft your own mechanical guardian from the scrap of a fallen civilization.",
tag: "Must Play",
img: "https://lh3.googleusercontent.com/aida-public/AB6AXuCdZ6xp9ZMjTyJAWKmRuJFddxg7y2QXMYoQ9LeHyylQzDzJrnhJ9AtMH09t2IagtwxKIlyOIEHjYKsXZ-oxOr0HIGPSQhfbqi18wFk_4GBQWixpT6DxIA49JS-Nc2Aaka7-duczcRYIisv650f40n_HxzapPIgrdgWYKY03ykvHBYF4LIvfA6IyJgSyesR90E8vb-LUcNjcoExS5QD3IHV-pmbMeIikXzUN9FsUnkNUK66mCjRb7YuZ69g1UpU4JDfpv6r-drxrJ-0"
},
{
title: "Vector Prime",
platform: "RETRO ARCADE",
dev: "Neo Geo",
year: "1998",
rating: "7.9",
desc: "The definitive retro arcade shooter experience. Battle through 32 levels of vector-based chaos with a pulsing synthwave soundtrack.",
tag: "Retro Vault",
img: "https://lh3.googleusercontent.com/aida-public/AB6AXuBX4MjWOaUI2OT7G-1EH4iX8fwmchgRIyNRL7agFG5GxTi-RWqKUM2cD8qL_8kZS17opkEzmt0Z5pb7U90EzpaHtbYZxHsxP05NPSXm1qsDamkV4vtb-wovTv4W5CuQqINNI2TtUAbSKcjnlANabj_NfmrfjRjcjEjxsBVzt_25lrsJKa-PEgVhvF_stJeAlL-DVVw0uOO87ZyQ0ZLlQcVRx4St4BfA8WIRyY6UnL5Jtj03fa8hO-mqEsyARLPQ0wGBX_nZOfPao4Y"
}
];
const carouselItems = [];
for(let i=0; i<25; i++) {
carouselItems.push(gamesData[i % gamesData.length]);
}
const container = document.getElementById('carousel-container');
const heroPoster = document.getElementById('hero-poster');
const heroTitle = document.getElementById('hero-title');
const heroPlatform = document.getElementById('hero-platform');
const heroDev = document.getElementById('hero-dev');
const heroYear = document.getElementById('hero-year');
const heroRating = document.getElementById('hero-rating');
const heroDesc = document.getElementById('hero-desc');
const heroTag = document.getElementById('hero-tag');
let currentIndex = 0;
let autoScrollInterval;
let isPaused = false;
function updateFeatured(index) {
currentIndex = index;
const game = carouselItems[index];
heroPoster.src = game.img;
heroTitle.textContent = game.title.toUpperCase();
heroPlatform.textContent = game.platform;
heroDev.textContent = game.dev;
heroYear.textContent = game.year;
heroRating.innerHTML = `<span class="material-symbols-outlined text-[10px] text-yellow-500" style="font-variation-settings: 'FILL' 1;">star</span> ${game.rating}`;
heroDesc.textContent = game.desc;
heroTag.textContent = game.tag.toUpperCase();
document.querySelectorAll('.carousel-thumb').forEach((thumb, i) => {
if(i === index) {
thumb.classList.add('thumbnail-active');
} else {
thumb.classList.remove('thumbnail-active');
}
});
const targetThumb = container.children[index];
if (targetThumb) {
container.scrollTo({
left: targetThumb.offsetLeft - (container.clientWidth / 2) + (targetThumb.clientWidth / 2),
behavior: 'smooth'
});
}
}
carouselItems.forEach((game, i) => {
const thumb = document.createElement('div');
thumb.className = `carousel-thumb w-[90px] shrink-0 aspect-[2/3] overflow-hidden cursor-pointer transition-all duration-300 ring-1 ring-white/10 opacity-60 hover:opacity-100 bg-white/5 rounded-[6px]`;
thumb.innerHTML = `<img class="w-full h-full object-cover" src="${game.img}"/>`;
thumb.onclick = () => {
updateFeatured(i);
stopAutoScroll();
startAutoScroll();
};
container.appendChild(thumb);
});
function startAutoScroll() {
autoScrollInterval = setInterval(() => {
if (!isPaused) {
currentIndex = (currentIndex + 1) % carouselItems.length;
updateFeatured(currentIndex);
}
}, 5000);
}
function stopAutoScroll() {
clearInterval(autoScrollInterval);
}
const featuredSection = document.getElementById('featured-section');
featuredSection.onmouseenter = () => isPaused = true;
featuredSection.onmouseleave = () => isPaused = false;
updateFeatured(0);
startAutoScroll();
</script>
</body></html>

View File

@@ -1,310 +0,0 @@
<!DOCTYPE html>
<html class="dark" lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"primary-container": "#2563eb",
"surface-container": "#1c1f2c",
"surface-container-lowest": "#0b0d1a",
"secondary-fixed-dim": "#d2bbff",
"primary-fixed": "#dbe1ff",
"primary": "#b4c5ff",
"on-error-container": "#ffdad6",
"outline-variant": "#434655",
"surface-container-high": "#272937",
"on-tertiary-fixed": "#181b24",
"on-primary": "#002a78",
"surface-container-low": "#181b28",
"primary-fixed-dim": "#b4c5ff",
"on-secondary-fixed-variant": "#5a00c6",
"on-secondary-fixed": "#25005a",
"on-primary-fixed-variant": "#003ea8",
"surface-bright": "#363847",
"inverse-primary": "#0053db",
"outline": "#8d90a0",
"on-secondary-container": "#c9aeff",
"surface": "#10131f",
"surface-tint": "#b4c5ff",
"surface-dim": "#10131f",
"on-error": "#690005",
"error-container": "#93000a",
"secondary-fixed": "#eaddff",
"surface-variant": "#323442",
"on-surface": "#e0e1f4",
"on-background": "#e0e1f4",
"secondary": "#d2bbff",
"inverse-surface": "#e0e1f4",
"surface-container-highest": "#323442",
"on-secondary": "#3f008e",
"on-primary-container": "#eeefff",
"tertiary-fixed-dim": "#c4c6d2",
"tertiary-fixed": "#e0e2ee",
"secondary-container": "#6001d1",
"inverse-on-surface": "#2d303e",
"tertiary": "#c4c6d2",
"on-tertiary-fixed-variant": "#444650",
"on-tertiary": "#2d3039",
"on-primary-fixed": "#00174b",
"tertiary-container": "#6a6d78",
"on-tertiary-container": "#eef0fc",
"error": "#ffb4ab",
"on-surface-variant": "#c3c6d7",
"background": "#10131f"
},
fontFamily: {
"headline": ["Inter"],
"body": ["Inter"],
"label": ["Inter"]
},
borderRadius: {"DEFAULT": "1rem", "lg": "2rem", "xl": "3rem", "full": "9999px"},
},
},
}
</script>
<style>
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
body {
background-color: #10131f;
color: #e0e1f4;
font-family: 'Inter', sans-serif;
}
.hide-scrollbar::-webkit-scrollbar { display: none; }
.glass-panel {
background: rgba(28, 31, 44, 0.7);
backdrop-filter: blur(20px);
}
</style>
</head>
<body class="antialiased">
<!-- TopNavBar -->
<nav class="fixed top-0 w-full z-50 h-16 bg-[#10131f] bg-opacity-90 backdrop-blur-xl flex items-center justify-between px-6 shadow-2xl shadow-black/50 font-['Inter'] antialiased tracking-tight">
<div class="flex items-center gap-8 w-full max-w-7xl mx-auto">
<!-- Brand -->
<div class="text-xl font-bold tracking-tighter text-slate-100 shrink-0">
The Digital Atelier
</div>
<!-- Search Bar (on_left) -->
<div class="relative max-w-md w-full ml-4">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
<input class="w-full bg-white/5 border-none rounded-full py-2 pl-10 pr-4 text-sm text-slate-200 focus:ring-2 focus:ring-blue-600 transition-all placeholder:text-slate-500" placeholder="Search the vault..." type="text"/>
</div>
<!-- Centered Stats -->
<div class="hidden lg:flex items-center gap-6 mx-auto">
<div class="flex flex-col items-center">
<span class="text-[10px] uppercase tracking-widest text-slate-500 font-bold">Games</span>
<span class="text-blue-500 font-bold">12,482</span>
</div>
<div class="w-px h-6 bg-white/10"></div>
<div class="flex flex-col items-center">
<span class="text-[10px] uppercase tracking-widest text-slate-500 font-bold">Storage</span>
<span class="text-blue-500 font-bold">4.2 TB</span>
</div>
</div>
<!-- Trailing Icons -->
<div class="flex items-center gap-4 shrink-0">
<button class="p-2 text-slate-400 hover:text-slate-100 hover:bg-white/5 rounded-full transition-all duration-200 active:scale-95 cursor-pointer">
<span class="material-symbols-outlined">notifications</span>
</button>
<button class="p-2 text-blue-500 font-semibold hover:bg-white/5 rounded-full transition-all duration-200 active:scale-95 cursor-pointer">
<span class="material-symbols-outlined">settings</span>
</button>
<div class="w-8 h-8 rounded-full bg-gradient-to-br from-blue-600 to-purple-600 p-[1px] cursor-pointer">
<img alt="User profile" class="w-full h-full rounded-full object-cover" data-alt="Close up of a professional male user avatar" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCfAtckX2He7yTMWIUG51cWyX7GKRwshh3bKOee22jc3QzbdJC3fx8P3sJ82Jb7qA6YmxFeMPKy2Ej82Lw1Y8HBRrEAJQl7TafEV-Avp0SblYlSfrIGczUawU4ArCXZ7X2dl4Auk0fHxsmVLNCj1BZKGO_JMIThDgLGF4EPpl64PdTNF0P5UAk-thHYvZ0xJ5sOK-sbfUO_HLhu100AKc4Py-DZAXMUSqrm9ozK2yO2xErKKqkOZC_m6_iyED4mk5rtRb4vaZ07TEg"/>
</div>
</div>
</div>
</nav>
<!-- SideNavBar -->
<aside class="fixed left-0 top-0 h-full w-64 z-[60] bg-[#181b28] shadow-[10px_0_30px_rgba(0,0,0,0.3)] flex flex-col p-4 gap-y-4 font-['Inter'] text-sm font-medium uppercase tracking-widest hidden md:flex">
<div class="mt-4 mb-8 px-4">
<div class="text-lg font-black bg-gradient-to-br from-blue-600 to-purple-600 bg-clip-text text-transparent">
The Vault
</div>
<div class="text-[10px] text-slate-500 font-bold -mt-1">Digital Preservation</div>
</div>
<nav class="flex-1 space-y-2">
<a class="flex items-center gap-3 text-slate-500 hover:text-slate-200 px-4 py-3 hover:bg-white/5 rounded-full transition-all hover:translate-x-1 duration-300" href="#">
<span class="material-symbols-outlined">home</span>
<span>Home</span>
</a>
<a class="flex items-center gap-3 text-slate-500 hover:text-slate-200 px-4 py-3 hover:bg-white/5 rounded-full transition-all hover:translate-x-1 duration-300" href="#">
<span class="material-symbols-outlined">grid_view</span>
<span>Platforms</span>
</a>
<a class="flex items-center gap-3 text-slate-500 hover:text-slate-200 px-4 py-3 hover:bg-white/5 rounded-full transition-all hover:translate-x-1 duration-300" href="#">
<span class="material-symbols-outlined">library_books</span>
<span>Collections</span>
</a>
<a class="flex items-center gap-3 text-slate-500 hover:text-slate-200 px-4 py-3 hover:bg-white/5 rounded-full transition-all hover:translate-x-1 duration-300" href="#">
<span class="material-symbols-outlined">videogame_asset</span>
<span>Console</span>
</a>
</nav>
<div class="pt-4 border-t border-white/5 space-y-2">
<a class="flex items-center gap-3 bg-white/10 backdrop-blur-md text-blue-400 rounded-full shadow-[0_0_15px_rgba(37,99,235,0.2)] px-4 py-3" href="#">
<span class="material-symbols-outlined">settings</span>
<span>Settings</span>
</a>
<a class="flex items-center gap-3 text-slate-500 hover:text-slate-200 px-4 py-3 hover:bg-white/5 rounded-full transition-all hover:translate-x-1 duration-300" href="#">
<span class="material-symbols-outlined">help</span>
<span>Support</span>
</a>
</div>
</aside>
<!-- Main Content Area -->
<main class="md:ml-64 pt-24 px-8 pb-12">
<div class="max-w-5xl mx-auto">
<!-- Page Header -->
<header class="mb-10">
<h1 class="text-4xl font-extrabold tracking-tight text-on-surface mb-2">Vault Console</h1>
<p class="text-on-surface-variant font-medium">Configure your library experience and server connections.</p>
</header>
<!-- Horizontal Tab Bar -->
<div class="flex items-center gap-2 mb-10 overflow-x-auto hide-scrollbar bg-surface-container-low p-1.5 rounded-full w-fit">
<button class="px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:text-on-surface transition-all">Profile</button>
<button class="px-6 py-2.5 rounded-full text-sm font-bold bg-primary-container text-on-primary-container shadow-lg shadow-primary-container/20 transition-all">User Interface</button>
<button class="px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:text-on-surface transition-all">Library Management</button>
<button class="px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:text-on-surface transition-all">Metadata Sources</button>
<button class="px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:text-on-surface transition-all">Administration</button>
<button class="px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:text-on-surface transition-all whitespace-nowrap">Server Stats</button>
</div>
<!-- Settings Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
<!-- Left Column: Visual Identity -->
<section class="lg:col-span-7 space-y-6">
<div class="bg-surface-container-high rounded-lg p-8 shadow-xl">
<div class="flex items-center gap-3 mb-8">
<span class="material-symbols-outlined text-primary" style="font-variation-settings: 'FILL' 1;">palette</span>
<h2 class="text-xl font-bold tracking-tight text-on-surface">Visual Identity</h2>
</div>
<div class="space-y-8">
<!-- Toggle: Dark Mode -->
<div class="flex items-center justify-between group">
<div>
<p class="font-bold text-on-surface">Enable Dark Mode</p>
<p class="text-sm text-on-surface-variant">Switch between obsidian and slate themes.</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input checked="" class="sr-only peer" type="checkbox"/>
<div class="w-12 h-6 bg-surface-bright rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-container"></div>
</label>
</div>
<!-- Toggle: Notifications -->
<div class="flex items-center justify-between group">
<div>
<p class="font-bold text-on-surface">Show Notifications</p>
<p class="text-sm text-on-surface-variant">Get alerts for scan completions and updates.</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input class="sr-only peer" type="checkbox"/>
<div class="w-12 h-6 bg-surface-bright rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-container"></div>
</label>
</div>
<!-- Toggle: Glassmorphism -->
<div class="flex items-center justify-between group">
<div>
<p class="font-bold text-on-surface">Glassmorphism Effects</p>
<p class="text-sm text-on-surface-variant">Enable translucent blurs and layered depth.</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input checked="" class="sr-only peer" type="checkbox"/>
<div class="w-12 h-6 bg-surface-bright rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-container"></div>
</label>
</div>
</div>
</div>
<!-- Layout Preview Card (Editorial Touch) -->
<div class="bg-gradient-to-br from-primary-container/20 to-secondary-container/20 rounded-lg p-8 border border-white/5 relative overflow-hidden group">
<div class="relative z-10">
<h3 class="text-lg font-bold text-white mb-2">Interface Preview</h3>
<p class="text-sm text-blue-200/70 mb-6">Current: Ultra-Modern / Editorial</p>
<div class="flex gap-2">
<div class="w-12 h-2 bg-white/20 rounded-full"></div>
<div class="w-24 h-2 bg-white/40 rounded-full"></div>
<div class="w-8 h-2 bg-white/20 rounded-full"></div>
</div>
</div>
<span class="material-symbols-outlined absolute -right-4 -bottom-4 text-9xl text-white/5 rotate-12 transition-transform group-hover:rotate-0 duration-700">dashboard_customize</span>
</div>
</section>
<!-- Right Column: Connection & API -->
<section class="lg:col-span-5">
<div class="bg-surface-container-high rounded-lg p-8 shadow-xl h-full border border-white/5">
<div class="flex items-center gap-3 mb-8">
<span class="material-symbols-outlined text-secondary" style="font-variation-settings: 'FILL' 1;">cloud_sync</span>
<h2 class="text-xl font-bold tracking-tight text-on-surface">Connection &amp; API</h2>
</div>
<div class="space-y-6">
<!-- Input: Server Address -->
<div>
<label class="block text-xs font-bold uppercase tracking-widest text-on-surface-variant mb-2 ml-1">Server Address</label>
<div class="relative">
<input class="w-full bg-surface-container-highest border-none rounded-md px-4 py-3 text-on-surface focus:ring-2 focus:ring-primary transition-all font-mono text-sm" type="text" value="romm.vault-local.io"/>
</div>
</div>
<!-- Input: API Port -->
<div>
<label class="block text-xs font-bold uppercase tracking-widest text-on-surface-variant mb-2 ml-1">API Port</label>
<div class="relative">
<input class="w-full bg-surface-container-highest border-none rounded-md px-4 py-3 text-on-surface focus:ring-2 focus:ring-primary transition-all font-mono text-sm" type="text" value="8080"/>
</div>
</div>
<!-- Input: IGDB Master API Key -->
<div>
<label class="block text-xs font-bold uppercase tracking-widest text-on-surface-variant mb-2 ml-1">IGDB Master API Key</label>
<div class="relative">
<input class="w-full bg-surface-container-highest border-none rounded-md px-4 py-3 text-on-surface focus:ring-2 focus:ring-primary transition-all font-mono text-sm" type="password" value="••••••••••••••••"/>
<button class="absolute right-3 top-1/2 -translate-y-1/2 text-on-surface-variant hover:text-on-surface">
<span class="material-symbols-outlined text-lg">visibility</span>
</button>
</div>
<p class="mt-2 text-[10px] text-on-surface-variant/60 italic leading-relaxed">Required for fetching high-resolution box art and historical metadata.</p>
</div>
<!-- Action Buttons -->
<div class="pt-6 flex flex-col gap-3">
<button class="w-full bg-gradient-to-br from-primary-container to-secondary-container text-white py-3 rounded-xl font-bold text-sm shadow-lg shadow-primary-container/30 hover:shadow-primary-container/50 transition-all active:scale-[0.98]">
Save Connections
</button>
<button class="w-full bg-white/5 text-on-surface-variant py-3 rounded-xl font-bold text-sm hover:bg-white/10 transition-all">
Test Latency
</button>
</div>
</div>
</div>
</section>
</div>
<!-- Footer Section: System Status -->
<footer class="mt-12 flex flex-col md:flex-row items-center justify-between gap-6 p-8 bg-surface-container-low rounded-lg border border-white/5">
<div class="flex items-center gap-4">
<div class="w-3 h-3 bg-emerald-500 rounded-full shadow-[0_0_10px_rgba(16,185,129,0.5)]"></div>
<div>
<p class="text-sm font-bold text-on-surface">System Status: Optimal</p>
<p class="text-xs text-on-surface-variant">Last library scan: 42 minutes ago</p>
</div>
</div>
<div class="flex items-center gap-6">
<div class="text-center">
<p class="text-xs font-bold uppercase tracking-widest text-on-surface-variant mb-1">Cores</p>
<p class="text-lg font-black text-on-surface">16</p>
</div>
<div class="text-center">
<p class="text-xs font-bold uppercase tracking-widest text-on-surface-variant mb-1">Latency</p>
<p class="text-lg font-black text-on-surface">12ms</p>
</div>
</div>
</footer>
</div>
</main>
</body></html>

View File

@@ -14,6 +14,11 @@ export default defineConfig({
target: 'https://retro.chieflix.com',
changeOrigin: true,
secure: false,
},
'/api': {
target: 'https://retro.chieflix.com',
changeOrigin: true,
secure: false,
}
}
},