Finished theming

This commit is contained in:
2025-09-06 22:28:11 -04:00
parent dae849bb90
commit bda6f999b7
5 changed files with 110 additions and 122 deletions

View File

@@ -55,17 +55,17 @@
<h2 class="text-2xl font-bold mb-6">System Management</h2> <h2 class="text-2xl font-bold mb-6">System Management</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
<!-- ROM Scanning --> <!-- Game Scanning -->
<div class="bg-secondary rounded-lg p-6 border border-theme"> <div class="bg-secondary rounded-lg p-6 border border-theme">
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<svg class="w-8 h-8 text-accent mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-8 h-8 text-accent mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg> </svg>
<h3 class="text-lg font-semibold">ROM Scanner</h3> <h3 class="text-lg font-semibold">Game Scanner</h3>
</div> </div>
<p class="text-secondary text-sm mb-4">Scan directories for new ROM files and add them to the database</p> <p class="text-secondary text-sm mb-4">Scan directories for new Game files and add them to the database</p>
<button onclick="triggerRomScan()" class="w-full bg-accent hover:bg-accent px-4 py-2 rounded font-medium transition-colors" id="scan-btn"> <button onclick="triggerGameScan()" class="w-full bg-accent hover:bg-accent px-4 py-2 rounded font-medium transition-colors" id="scan-btn">
<span class="scan-text">Start ROM Scan</span> <span class="scan-text">Start Game Scan</span>
<span class="scan-loading hidden">Scanning...</span> <span class="scan-loading hidden">Scanning...</span>
</button> </button>
<div id="scan-status" class="mt-2 text-sm text-secondary"></div> <div id="scan-status" class="mt-2 text-sm text-secondary"></div>
@@ -318,8 +318,8 @@
<h4 class="text-lg font-semibold text-accent">Paths</h4> <h4 class="text-lg font-semibold text-accent">Paths</h4>
<div> <div>
<label class="block text-sm font-medium text-secondary mb-1">ROM Directory</label> <label class="block text-sm font-medium text-secondary mb-1">Game Directory</label>
<input type="text" id="config-rom-path" class="w-full px-3 py-2 bg-tertiary border border-theme rounded text-primary" placeholder="/path/to/roms"> <input type="text" id="config-rom-path" class="w-full px-3 py-2 bg-tertiary border border-theme rounded text-primary" placeholder="/path/to/games">
</div> </div>
<div> <div>
@@ -415,7 +415,7 @@
</div> </div>
<script> <script>
async function triggerRomScan() { async function triggerGameScan() {
const btn = document.getElementById('scan-btn'); const btn = document.getElementById('scan-btn');
const status = document.getElementById('scan-status'); const status = document.getElementById('scan-status');
const scanText = btn.querySelector('.scan-text'); const scanText = btn.querySelector('.scan-text');
@@ -426,7 +426,7 @@ async function triggerRomScan() {
scanLoading.classList.remove('hidden'); scanLoading.classList.remove('hidden');
try { try {
const response = await fetch('/api/admin/rom-scan', { const response = await fetch('/api/admin/game-scan', {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': `Bearer ${getCookie('auth_token')}` 'Authorization': `Bearer ${getCookie('auth_token')}`
@@ -436,17 +436,17 @@ async function triggerRomScan() {
const result = await response.json(); const result = await response.json();
if (result.status === 'started') { if (result.status === 'started') {
status.textContent = 'ROM scan started...'; status.textContent = 'Game scan started...';
status.className = 'mt-2 text-sm text-accent'; status.className = 'mt-2 text-sm text-accent';
// Poll for completion // Poll for completion
pollTaskStatus('rom_scan', status); pollTaskStatus('game_scan', status);
} else if (result.status === 'already_running') { } else if (result.status === 'already_running') {
status.textContent = 'ROM scan already in progress'; status.textContent = 'Game scan already in progress';
status.className = 'mt-2 text-sm text-warning-color'; status.className = 'mt-2 text-sm text-warning-color';
} }
} catch (error) { } catch (error) {
status.textContent = 'Failed to start ROM scan'; status.textContent = 'Failed to start Game scan';
status.className = 'mt-2 text-sm text-danger-color'; status.className = 'mt-2 text-sm text-danger-color';
} finally { } finally {
btn.disabled = false; btn.disabled = false;
@@ -674,8 +674,8 @@ async function showSystemStats() {
<h4 class="text-lg font-semibold mb-3 text-primary">Running Tasks</h4> <h4 class="text-lg font-semibold mb-3 text-primary">Running Tasks</h4>
<div class="space-y-2 text-secondary"> <div class="space-y-2 text-secondary">
<div class="flex justify-between"> <div class="flex justify-between">
<span>ROM Scan:</span> <span>Game Scan:</span>
<span class="${stats.running_tasks.rom_scan ? 'text-accent' : 'text-secondary'}">${stats.running_tasks.rom_scan ? 'Running' : 'Idle'}</span> <span class="${stats.running_tasks.game_scan ? 'text-accent' : 'text-secondary'}">${stats.running_tasks.game_scan ? 'Running' : 'Idle'}</span>
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<span>Metadata Refresh:</span> <span>Metadata Refresh:</span>
@@ -1307,7 +1307,7 @@ function populateConfigForm(config) {
// Populate form fields // Populate form fields
document.getElementById('config-host').value = config.host || ''; document.getElementById('config-host').value = config.host || '';
document.getElementById('config-port').value = config.port || ''; document.getElementById('config-port').value = config.port || '';
document.getElementById('config-rom-path').value = config.rom_path || ''; document.getElementById('config-rom-path').value = config.game_path || '';
document.getElementById('config-images-path').value = config.images_path || ''; document.getElementById('config-images-path').value = config.images_path || '';
document.getElementById('config-igdb-client-id').value = config.igdb_client_id || ''; document.getElementById('config-igdb-client-id').value = config.igdb_client_id || '';
document.getElementById('config-igdb-secret').value = config.igdb_api_key || ''; document.getElementById('config-igdb-secret').value = config.igdb_api_key || '';
@@ -1320,7 +1320,7 @@ function collectConfigFromForm() {
const config = { const config = {
host: document.getElementById('config-host').value, host: document.getElementById('config-host').value,
port: parseInt(document.getElementById('config-port').value) || 8080, port: parseInt(document.getElementById('config-port').value) || 8080,
rom_path: document.getElementById('config-rom-path').value, game_path: document.getElementById('config-rom-path').value,
images_path: document.getElementById('config-images-path').value, images_path: document.getElementById('config-images-path').value,
igdb_client_id: document.getElementById('config-igdb-client-id').value, igdb_client_id: document.getElementById('config-igdb-client-id').value,
igdb_api_key: document.getElementById('config-igdb-secret').value igdb_api_key: document.getElementById('config-igdb-secret').value

View File

@@ -197,6 +197,8 @@
.bg-tertiary { background-color: var(--tertiary-bg); } .bg-tertiary { background-color: var(--tertiary-bg); }
.bg-accent { background-color: var(--accent-bg); } .bg-accent { background-color: var(--accent-bg); }
.bg-accent:hover { background-color: var(--accent-hover); } .bg-accent:hover { background-color: var(--accent-hover); }
.bg-accent-hover { background-color: var(--accent-hover); }
.bg-warning { background-color: var(--warning-color) !important; }
.text-primary { color: var(--text-primary); } .text-primary { color: var(--text-primary); }
.text-secondary { color: var(--text-secondary); } .text-secondary { color: var(--text-secondary); }
.text-accent { color: var(--text-accent); } .text-accent { color: var(--text-accent); }
@@ -259,7 +261,7 @@
<div class="hidden md:block"> <div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4"> <div class="ml-10 flex items-baseline space-x-4">
<a href="/" class="hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"> <a href="/" class="hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
Browse ROMs Browse Games
</a> </a>
{% if current_user and current_user.role != "demo" %} {% if current_user and current_user.role != "demo" %}
<a href="/favorites" class="hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"> <a href="/favorites" class="hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
@@ -317,28 +319,23 @@
</div> </div>
{% if current_user %} {% if current_user %}
<span class="text-sm"> <span class="text-sm text-primary">
Welcome, {{ current_user.username }} Welcome, {{ current_user.username }}
{% if current_user.role == "demo" %}
<span class="bg-yellow-600 px-2 py-1 rounded text-xs">DEMO</span>
{% elif current_user.role == "super" %}
<span class="bg-red-600 px-2 py-1 rounded text-xs">ADMIN</span>
{% endif %}
</span> </span>
<button onclick="logout()" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded text-sm"> <button onclick="logout()" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded text-sm transition-colors">
Logout Logout
</button> </button>
{% else %} {% else %}
<button onclick="showLogin()" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded text-sm"> <button onclick="showLogin()" class="bg-accent hover:bg-accent-hover px-4 py-2 rounded text-sm text-primary transition-colors">
Login Login
</button> </button>
<span class="bg-yellow-600 px-2 py-1 rounded text-xs">DEMO MODE</span> <span class="bg-warning px-2 py-1 rounded text-xs text-black font-medium">DEMO MODE</span>
{% endif %} {% endif %}
</div> </div>
<!-- Mobile menu button --> <!-- Mobile menu button -->
<div class="md:hidden"> <div class="md:hidden">
<button onclick="toggleMobileMenu()" class="bg-gray-700 inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"> <button onclick="toggleMobileMenu()" class="bg-tertiary inline-flex items-center justify-center p-2 rounded-md text-secondary hover:text-primary hover:bg-accent focus:outline-none focus:ring-2 focus:ring-inset focus:ring-accent transition-colors">
<svg id="hamburger-icon" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg id="hamburger-icon" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg> </svg>
@@ -354,7 +351,7 @@
<div id="mobile-menu" class="md:hidden hidden"> <div id="mobile-menu" class="md:hidden hidden">
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3"> <div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">
<a href="/" class="hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"> <a href="/" class="hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">
Browse ROMs Browse Games
</a> </a>
{% if current_user and current_user.role != "demo" %} {% if current_user and current_user.role != "demo" %}
<a href="/favorites" class="hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"> <a href="/favorites" class="hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">
@@ -406,24 +403,15 @@
</div> </div>
{% if current_user %} {% if current_user %}
<div class="text-base font-medium text-white">{{ current_user.username }}</div> <div class="text-base font-medium text-primary">{{ current_user.username }}</div>
<div class="text-sm text-gray-400 mb-3"> <button onclick="logout()" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded text-sm w-full mt-3 transition-colors">
{% if current_user.role == "demo" %}
<span class="bg-yellow-600 px-2 py-1 rounded text-xs">DEMO USER</span>
{% elif current_user.role == "super" %}
<span class="bg-red-600 px-2 py-1 rounded text-xs">ADMIN</span>
{% else %}
<span class="bg-green-600 px-2 py-1 rounded text-xs">USER</span>
{% endif %}
</div>
<button onclick="logout()" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded text-sm w-full">
Logout Logout
</button> </button>
{% else %} {% else %}
<button onclick="showLogin()" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded text-sm w-full mb-2"> <button onclick="showLogin()" class="bg-accent hover:bg-accent-hover px-4 py-2 rounded text-sm w-full mb-2 text-primary transition-colors">
Login Login
</button> </button>
<span class="bg-yellow-600 px-2 py-1 rounded text-xs">DEMO MODE</span> <span class="bg-warning px-2 py-1 rounded text-xs text-black font-medium">DEMO MODE</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@@ -431,14 +419,14 @@
</nav> </nav>
<!-- Genres Sidebar --> <!-- Genres Sidebar -->
<div id="genresSidebar" class="fixed inset-y-0 left-0 w-80 bg-gray-800 border-r border-gray-700 transform -translate-x-full transition-transform duration-300 ease-in-out z-50"> <div id="genresSidebar" class="fixed inset-y-0 left-0 w-80 bg-secondary border-r border-theme transform -translate-x-full transition-transform duration-300 ease-in-out z-50">
<div class="h-full flex flex-col p-4"> <div class="h-full flex flex-col p-4">
<div class="flex justify-between items-center mb-4 flex-shrink-0"> <div class="flex justify-between items-center mb-4 flex-shrink-0">
<h3 class="text-lg font-medium text-white">Browse by Genre</h3> <h3 class="text-lg font-medium text-primary">Browse by Genre</h3>
<button onclick="toggleGenresSidebar()" class="text-gray-400 hover:text-white">&times;</button> <button onclick="toggleGenresSidebar()" class="text-secondary hover:text-primary">&times;</button>
</div> </div>
<div id="genresContainer" class="space-y-2 flex-1 overflow-y-auto"> <div id="genresContainer" class="space-y-2 flex-1 overflow-y-auto">
<p class="text-gray-400 text-sm">Loading genres...</p> <p class="text-secondary text-sm">Loading genres...</p>
</div> </div>
</div> </div>
</div> </div>
@@ -451,27 +439,27 @@
</main> </main>
<!-- Login Modal --> <!-- Login Modal -->
<div id="loginModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"> <div id="loginModal" class="hidden fixed inset-0 bg-black bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-gray-800 border-gray-700"> <div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-secondary border-theme">
<div class="mt-3"> <div class="mt-3">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-white">Login</h3> <h3 class="text-lg font-medium text-primary">Login</h3>
<button onclick="hideLogin()" class="text-gray-400 hover:text-white">&times;</button> <button onclick="hideLogin()" class="text-secondary hover:text-primary">&times;</button>
</div> </div>
<form onsubmit="handleLogin(event)" class="space-y-4"> <form onsubmit="handleLogin(event)" class="space-y-4">
<div> <div>
<input type="text" id="username" placeholder="Username" required <input type="text" id="username" placeholder="Username" required
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"> class="w-full px-3 py-2 bg-tertiary border border-theme rounded-md text-primary placeholder-secondary focus:outline-none focus:ring-2 focus:ring-accent">
</div> </div>
<div> <div>
<input type="password" id="password" placeholder="Password" required <input type="password" id="password" placeholder="Password" required
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"> class="w-full px-3 py-2 bg-tertiary border border-theme rounded-md text-primary placeholder-secondary focus:outline-none focus:ring-2 focus:ring-accent">
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<button type="button" onclick="hideLogin()" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md"> <button type="button" onclick="hideLogin()" class="px-4 py-2 bg-tertiary hover:bg-accent rounded-md text-primary transition-colors">
Cancel Cancel
</button> </button>
<button type="submit" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md"> <button type="submit" class="px-4 py-2 bg-accent hover:bg-accent-hover rounded-md text-primary transition-colors">
Login Login
</button> </button>
</div> </div>
@@ -613,7 +601,7 @@
const container = document.getElementById('genresContainer'); const container = document.getElementById('genresContainer');
if (genres.length === 0) { if (genres.length === 0) {
container.innerHTML = '<p class="text-gray-400 text-sm">No genres found</p>'; container.innerHTML = '<p class="text-secondary text-sm">No genres found</p>';
return; return;
} }
@@ -621,8 +609,8 @@
.sort((a, b) => b.count - a.count) .sort((a, b) => b.count - a.count)
.map(genre => ` .map(genre => `
<a href="/browse/genres/${encodeURIComponent(genre.name)}" <a href="/browse/genres/${encodeURIComponent(genre.name)}"
class="block p-2 bg-gray-700 hover:bg-gray-600 rounded text-sm text-white"> class="block p-2 bg-tertiary hover:bg-accent rounded text-sm text-primary transition-colors">
${genre.name} <span class="text-gray-400">(${genre.count})</span> ${genre.name} <span class="text-secondary">(${genre.count})</span>
</a> </a>
`).join(''); `).join('');

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="mb-8"> <div class="mb-8">
<h1 class="text-3xl font-bold mb-2">My Favorites</h1> <h1 class="text-3xl font-bold mb-2">My Favorites</h1>
<p class="text-gray-400">Your personally selected ROM collection</p> <p class="text-gray-400">Your personally selected game collection</p>
</div> </div>
{% if games %} {% if games %}
@@ -86,7 +86,7 @@
<h2 class="text-2xl font-bold mb-2">No favorites yet</h2> <h2 class="text-2xl font-bold mb-2">No favorites yet</h2>
<p class="text-gray-400 mb-6">Start browsing and add games to your favorites collection!</p> <p class="text-gray-400 mb-6">Start browsing and add games to your favorites collection!</p>
<a href="/" class="bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded-lg text-white"> <a href="/" class="bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded-lg text-white">
Browse ROMs Browse Games
</a> </a>
</div> </div>
{% endif %} {% endif %}
@@ -130,7 +130,10 @@
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';
a.href = url; a.href = url;
a.download = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip'; // Get filename from Content-Disposition header, removing quotes and underscores
let filename = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip';
filename = filename.replace(/^["_]+|["_]+$/g, ''); // Remove quotes and underscores from start and end
a.download = filename;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);

View File

@@ -6,7 +6,7 @@
<div class="max-w-4xl mx-auto"> <div class="max-w-4xl mx-auto">
<div class="mb-6"> <div class="mb-6">
<nav class="text-sm text-gray-400 mb-4"> <nav class="text-sm text-gray-400 mb-4">
<a href="/" class="hover:text-white">Browse ROMs</a> <a href="/" class="hover:text-white">Browse Games</a>
<span class="mx-2">/</span> <span class="mx-2">/</span>
<span class="text-white">{{ game.metadata_obj.title or game.title }}</span> <span class="text-white">{{ game.metadata_obj.title or game.title }}</span>
</nav> </nav>
@@ -36,7 +36,7 @@
{% if can_download %} {% if can_download %}
<button onclick="downloadGame({{ game.id }})" <button onclick="downloadGame({{ game.id }})"
class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded"> class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded">
Download ROM Download Game
</button> </button>
{% else %} {% else %}
<span class="bg-gray-600 px-4 py-2 rounded cursor-not-allowed"> <span class="bg-gray-600 px-4 py-2 rounded cursor-not-allowed">
@@ -268,7 +268,10 @@
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';
a.href = url; a.href = url;
a.download = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip'; // Get filename from Content-Disposition header, removing quotes and underscores
let filename = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip';
filename = filename.replace(/^["_]+|["_]+$/g, ''); // Remove quotes and underscores from start and end
a.download = filename;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);

View File

@@ -1,11 +1,11 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}ROM Library - DOS Frontend{% endblock %} {% block title %}Game Library - DOS Frontend{% endblock %}
{% block content %} {% block content %}
<div class="mb-6"> <div class="mb-6">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4"> <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4">
<h1 class="text-3xl font-bold mb-2 sm:mb-0">ROM Library</h1> <h1 class="text-3xl font-bold mb-2 sm:mb-0">Game Library</h1>
<div class="flex flex-col sm:flex-row gap-2"> <div class="flex flex-col sm:flex-row gap-2">
<!-- Search Form --> <!-- Search Form -->
<form method="GET" class="flex gap-2"> <form method="GET" class="flex gap-2">
@@ -15,26 +15,26 @@
<div class="relative"> <div class="relative">
<input type="text" name="search" placeholder="Search games..." <input type="text" name="search" placeholder="Search games..."
value="{% if search and not search.startswith('genre:') and not search.startswith('tag:') %}{{ search }}{% endif %}" value="{% if search and not search.startswith('genre:') and not search.startswith('tag:') %}{{ search }}{% endif %}"
class="w-full sm:w-64 px-4 py-2 pl-10 bg-gray-700 border border-gray-600 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"> class="w-full sm:w-64 px-4 py-2 pl-10 bg-tertiary border border-theme rounded-lg text-primary placeholder-secondary focus:outline-none focus:ring-2 focus:ring-accent">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg> </svg>
</div> </div>
</div> </div>
<button type="submit" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg text-white">Search</button> <button type="submit" class="px-4 py-2 bg-accent hover:bg-accent-hover rounded-lg text-primary transition-colors">Search</button>
</form> </form>
</div> </div>
</div> </div>
<!-- Controls Bar --> <!-- Controls Bar -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4"> <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4">
<div class="text-gray-400"> <div class="text-secondary">
{% if is_demo %} {% if is_demo %}
<span class="text-yellow-400">Demo Mode:</span> You can browse ROMs but cannot download or favorite them. <span class="text-yellow-400">Demo Mode:</span> You can browse games but cannot download or favorite them.
<button onclick="showLogin()" class="text-blue-400 hover:text-blue-300 underline">Login</button> for full access. <button onclick="showLogin()" class="text-blue-400 hover:text-blue-300 underline">Login</button> for full access.
{% else %} {% else %}
Showing {{ games|length }} of {{ total_games }} ROMs Showing {{ games|length }} of {{ total_games }} games
{% if search %} for "{{ search }}"{% endif %} {% if search %} for "{{ search }}"{% endif %}
{% endif %} {% endif %}
</div> </div>
@@ -42,8 +42,8 @@
<div class="flex flex-col sm:flex-row gap-2 sm:gap-4 items-center"> <div class="flex flex-col sm:flex-row gap-2 sm:gap-4 items-center">
<!-- Results per page --> <!-- Results per page -->
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<label class="text-gray-400 text-sm">Show:</label> <label class="text-secondary text-sm">Show:</label>
<select onchange="changePerPage(this.value)" class="bg-gray-700 border border-gray-600 rounded px-2 py-1 text-white text-sm min-w-16"> <select onchange="changePerPage(this.value)" class="bg-tertiary border border-theme rounded px-2 py-1 text-primary text-sm min-w-16">
<option value="20" {% if per_page == 20 %}selected{% endif %}>20</option> <option value="20" {% if per_page == 20 %}selected{% endif %}>20</option>
<option value="50" {% if per_page == 50 %}selected{% endif %}>50</option> <option value="50" {% if per_page == 50 %}selected{% endif %}>50</option>
<option value="100" {% if per_page == 100 %}selected{% endif %}>100</option> <option value="100" {% if per_page == 100 %}selected{% endif %}>100</option>
@@ -51,16 +51,16 @@
</div> </div>
<!-- View Toggle --> <!-- View Toggle -->
<div class="flex bg-gray-700 rounded-lg p-1"> <div class="flex bg-tertiary rounded-lg p-1">
<button onclick="changeView('grid')" <button onclick="changeView('grid')"
class="px-4 py-2 rounded text-sm touch-manipulation {{ 'bg-blue-600 text-white' if view == 'grid' else 'text-gray-400 hover:text-white active:bg-gray-600' }}"> class="px-4 py-2 rounded text-sm touch-manipulation transition-colors {{ 'bg-accent text-primary' if view == 'grid' else 'text-secondary hover:text-primary active:bg-secondary' }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path> <path d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path>
</svg> </svg>
<span class="ml-1 hidden sm:inline">Grid</span> <span class="ml-1 hidden sm:inline">Grid</span>
</button> </button>
<button onclick="changeView('list')" <button onclick="changeView('list')"
class="px-4 py-2 rounded text-sm touch-manipulation {{ 'bg-blue-600 text-white' if view == 'list' else 'text-gray-400 hover:text-white active:bg-gray-600' }}"> class="px-4 py-2 rounded text-sm touch-manipulation transition-colors {{ 'bg-accent text-primary' if view == 'list' else 'text-secondary hover:text-primary active:bg-secondary' }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path> <path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 4a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path>
</svg> </svg>
@@ -75,29 +75,29 @@
{% if view == 'grid' %} {% if view == 'grid' %}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{% for game in games %} {% for game in games %}
<div class="bg-gray-800 rounded-lg border border-gray-700 hover:border-gray-600 transition-colors overflow-hidden hover:shadow-lg transform hover:-translate-y-1 transition-all duration-200 relative group"> <div class="bg-secondary rounded-lg border border-theme hover:border-accent transition-colors overflow-hidden hover:shadow-lg transform hover:-translate-y-1 transition-all duration-200 relative group">
<!-- Clickable overlay for the card --> <!-- Clickable overlay for the card -->
<div onclick="showGameDetail({{ game.id }})" class="absolute inset-0 z-10 cursor-pointer" aria-label="View {{ game.metadata_obj.title or game.title }} details"></div> <div onclick="showGameDetail({{ game.id }})" class="absolute inset-0 z-10 cursor-pointer" aria-label="View {{ game.metadata_obj.title or game.title }} details"></div>
<!-- Cover Image --> <!-- Cover Image -->
<div class="aspect-[3/4] bg-gray-900 relative overflow-hidden"> <div class="aspect-[3/4] bg-primary relative overflow-hidden">
{% if game.metadata_obj and (game.metadata_obj.cover_image_path or (game.metadata_obj.cover_image and game.metadata_obj.cover_image.startswith('http'))) %} {% if game.metadata_obj and (game.metadata_obj.cover_image_path or (game.metadata_obj.cover_image and game.metadata_obj.cover_image.startswith('http'))) %}
<img data-game-id="{{ game.id }}" <img data-game-id="{{ game.id }}"
{% if game.metadata_obj.cover_image_path %}src="/images/{{ game.metadata_obj.cover_image_path.name }}"{% else %}src="{{ game.metadata_obj.cover_image }}"{% endif %} {% if game.metadata_obj.cover_image_path %}src="/images/{{ game.metadata_obj.cover_image_path.name }}"{% else %}src="{{ game.metadata_obj.cover_image }}"{% endif %}
alt="{{ game.metadata_obj.title or game.title }}" alt="{{ game.metadata_obj.title or game.title }}"
class="w-full h-full object-cover transition-transform duration-200 group-hover:scale-105" class="w-full h-full object-cover transition-transform duration-200 group-hover:scale-105"
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="w-full h-full bg-gradient-to-br from-gray-800 to-gray-900 hidden items-center justify-center"> <div class="w-full h-full bg-gradient-theme hidden items-center justify-center absolute inset-0">
<div class="text-gray-400 text-center p-4"> <div class="text-secondary text-center p-4">
<svg class="w-16 h-16 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-16 h-16 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2h2a2 2 0 002-2z"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2h2a2 2 0 002-2z"></path>
</svg> </svg>
<p class="text-sm font-medium">{{ (game.metadata_obj.title or game.title)[:20] }}{% if (game.metadata_obj.title or game.title)|length > 20 %}...{% endif %}</p> <p class="text-sm font-medium">{{ (game.metadata_obj.title or game.title)[:20] }}{% if (game.metadata_obj.title or game.title)|length > 20 %}...{% endif %}</p>
<p class="text-xs text-gray-500 mt-1">DOS Game</p> <p class="text-xs text-secondary opacity-75 mt-1">DOS Game</p>
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="w-full h-full bg-gradient-theme flex items-center justify-center relative overflow-hidden"> <div class="w-full h-full bg-gradient-theme flex items-center justify-center relative overflow-hidden absolute inset-0">
<!-- Background Pattern --> <!-- Background Pattern -->
<div class="absolute inset-0 opacity-10"> <div class="absolute inset-0 opacity-10">
<svg width="100%" height="100%" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="100%" height="100%" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -111,7 +111,7 @@
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div class="text-center p-3 relative z-10"> <div class="text-center p-3 relative z-10 w-full h-full flex flex-col justify-center items-center">
<!-- DosVault Logo --> <!-- DosVault Logo -->
<div class="mb-4"> <div class="mb-4">
<svg class="w-20 h-20 mx-auto text-accent" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="w-20 h-20 mx-auto text-accent" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -188,23 +188,23 @@
</div> </div>
<!-- Game Info --> <!-- Game Info -->
<div class="p-3"> <div class="p-3 bg-tertiary border-t border-theme">
<h3 class="font-semibold text-blue-400 truncate mb-1 text-sm"> <h3 class="font-semibold text-accent truncate mb-1 text-sm">
{{ game.metadata_obj.title or game.title }} {{ game.metadata_obj.title or game.title }}
</h3> </h3>
{% if game.metadata_obj and game.metadata_obj.year %} {% if game.metadata_obj and game.metadata_obj.year %}
<p class="text-xs text-gray-400 mb-2">{{ game.metadata_obj.year }}</p> <p class="text-xs text-secondary mb-2">{{ game.metadata_obj.year }}</p>
{% endif %} {% endif %}
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-xs text-gray-500">Click to view details</span> <span class="text-xs text-secondary">Click to view details</span>
{% if not is_demo %} {% if not is_demo %}
<button onclick="event.stopPropagation(); downloadGame({{ game.id }})" <button onclick="event.stopPropagation(); downloadGame({{ game.id }})"
class="bg-green-600 hover:bg-green-700 px-2 py-1 rounded text-xs z-20 relative"> class="bg-green-600 hover:bg-green-700 px-2 py-1 rounded text-xs z-20 relative transition-colors">
Download Download
</button> </button>
{% else %} {% else %}
<span class="bg-gray-600 px-2 py-1 rounded text-xs cursor-not-allowed"> <span class="bg-tertiary border border-theme px-2 py-1 rounded text-xs cursor-not-allowed text-secondary">
Login Login
</span> </span>
{% endif %} {% endif %}
@@ -219,20 +219,20 @@
{% if view == 'list' %} {% if view == 'list' %}
<div class="space-y-2"> <div class="space-y-2">
{% for game in games %} {% for game in games %}
<div class="bg-gray-800 rounded-lg border border-gray-700 hover:border-gray-600 transition-colors hover:shadow-lg transform hover:-translate-y-1 transition-all duration-200 relative group"> <div class="bg-secondary rounded-lg border border-theme hover:border-accent transition-colors hover:shadow-lg transform hover:-translate-y-1 transition-all duration-200 relative group">
<!-- Clickable overlay for the card --> <!-- Clickable overlay for the card -->
<div onclick="showGameDetail({{ game.id }})" class="absolute inset-0 z-10 cursor-pointer" aria-label="View {{ game.metadata_obj.title or game.title }} details"></div> <div onclick="showGameDetail({{ game.id }})" class="absolute inset-0 z-10 cursor-pointer" aria-label="View {{ game.metadata_obj.title or game.title }} details"></div>
<div class="p-4 flex items-center gap-4"> <div class="p-4 flex items-center gap-4">
<!-- Cover Image --> <!-- Cover Image -->
<div class="w-16 h-20 bg-gray-900 rounded overflow-hidden flex-shrink-0 relative"> <div class="w-16 h-20 bg-primary rounded overflow-hidden flex-shrink-0 relative">
{% if game.metadata_obj and (game.metadata_obj.cover_image_path or (game.metadata_obj.cover_image and game.metadata_obj.cover_image.startswith('http'))) %} {% if game.metadata_obj and (game.metadata_obj.cover_image_path or (game.metadata_obj.cover_image and game.metadata_obj.cover_image.startswith('http'))) %}
<img data-game-id="{{ game.id }}" <img data-game-id="{{ game.id }}"
{% if game.metadata_obj.cover_image_path %}src="/images/{{ game.metadata_obj.cover_image_path.name }}"{% else %}src="{{ game.metadata_obj.cover_image }}"{% endif %} {% if game.metadata_obj.cover_image_path %}src="/images/{{ game.metadata_obj.cover_image_path.name }}"{% else %}src="{{ game.metadata_obj.cover_image }}"{% endif %}
alt="{{ game.metadata_obj.title or game.title }}" alt="{{ game.metadata_obj.title or game.title }}"
class="w-full h-full object-cover" class="w-full h-full object-cover"
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="w-full h-full bg-gradient-theme hidden items-center justify-center relative overflow-hidden"> <div class="w-full h-full bg-gradient-theme hidden items-center justify-center relative overflow-hidden absolute inset-0">
<!-- Background Pattern --> <!-- Background Pattern -->
<div class="absolute inset-0 opacity-10"> <div class="absolute inset-0 opacity-10">
<svg width="100%" height="100%" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="100%" height="100%" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -246,10 +246,10 @@
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div class="text-center p-3 relative z-10"> <div class="text-center p-1 relative z-10 w-full h-full flex flex-col justify-center items-center">
<!-- DosVault Logo --> <!-- DosVault Logo -->
<div class="mb-4"> <div class="mb-1">
<svg class="w-20 h-20 mx-auto text-accent" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="w-6 h-6 mx-auto text-accent" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Vault door with enhanced styling --> <!-- Vault door with enhanced styling -->
<circle cx="16" cy="16" r="15" fill="currentColor" opacity="0.15" stroke="currentColor" stroke-width="0.5"/> <circle cx="16" cy="16" r="15" fill="currentColor" opacity="0.15" stroke="currentColor" stroke-width="0.5"/>
<circle cx="16" cy="16" r="12" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.8"/> <circle cx="16" cy="16" r="12" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.8"/>
@@ -273,18 +273,9 @@
</svg> </svg>
</div> </div>
<!-- Game Title -->
<div class="mb-2">
<h3 class="text-accent font-bold text-sm leading-tight mb-1">
{{ (game.metadata_obj.title or game.title)[:18] }}{% if (game.metadata_obj.title or game.title)|length > 18 %}...{% endif %}
</h3>
<div class="w-12 h-0.5 bg-accent mx-auto opacity-60"></div>
</div>
<!-- Branding --> <!-- Branding -->
<div class="text-xs text-secondary opacity-75 font-medium"> <div class="text-[6px] text-accent font-bold tracking-wide opacity-75">
<div class="mb-1">CLASSIC DOS GAME</div> DOSVAULT
<div class="text-accent text-[10px] font-bold tracking-wider">DOSVAULT</div>
</div> </div>
<!-- Decorative Corner Elements --> <!-- Decorative Corner Elements -->
@@ -311,9 +302,9 @@
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="w-full h-full bg-gradient-theme flex items-center justify-center relative overflow-hidden"> <div class="w-full h-full bg-gradient-theme flex items-center justify-center relative overflow-hidden absolute inset-0">
<!-- Compact Background Pattern --> <!-- Compact Background Pattern -->
<div class="absolute inset-0 opacity-8"> <div class="absolute inset-0 opacity-10">
<svg width="100%" height="100%" viewBox="0 0 32 32" fill="none"> <svg width="100%" height="100%" viewBox="0 0 32 32" fill="none">
<defs> <defs>
<pattern id="pixel-grid-small" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse"> <pattern id="pixel-grid-small" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse">
@@ -325,8 +316,8 @@
</div> </div>
<!-- Compact Logo --> <!-- Compact Logo -->
<div class="text-center relative z-10"> <div class="text-center relative z-10 w-full h-full flex flex-col justify-center items-center p-1">
<svg class="w-8 h-8 mx-auto text-accent mb-1" viewBox="0 0 32 32" fill="none"> <svg class="w-6 h-6 mx-auto text-accent mb-1" viewBox="0 0 32 32" fill="none">
<!-- Simplified vault door --> <!-- Simplified vault door -->
<circle cx="16" cy="16" r="12" stroke="currentColor" stroke-width="1.5" fill="currentColor" opacity="0.1"/> <circle cx="16" cy="16" r="12" stroke="currentColor" stroke-width="1.5" fill="currentColor" opacity="0.1"/>
<circle cx="16" cy="16" r="8" stroke="currentColor" stroke-width="1" opacity="0.6"/> <circle cx="16" cy="16" r="8" stroke="currentColor" stroke-width="1" opacity="0.6"/>
@@ -342,7 +333,7 @@
<rect x="20" y="14" width="6" height="4" rx="2" fill="currentColor" opacity="0.7"/> <rect x="20" y="14" width="6" height="4" rx="2" fill="currentColor" opacity="0.7"/>
<circle cx="22" cy="16" r="0.8" fill="var(--primary-bg)"/> <circle cx="22" cy="16" r="0.8" fill="var(--primary-bg)"/>
</svg> </svg>
<div class="text-accent text-[8px] font-bold tracking-wide opacity-75">DOSVAULT</div> <div class="text-accent text-[6px] font-bold tracking-wide opacity-75">DOSVAULT</div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@@ -401,7 +392,7 @@
<nav class="flex items-center space-x-1"> <nav class="flex items-center space-x-1">
{% if current_page > 1 %} {% if current_page > 1 %}
<a href="javascript:void(0)" onclick="goToPage({{ current_page - 1 }})" <a href="javascript:void(0)" onclick="goToPage({{ current_page - 1 }})"
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm"> class="px-3 py-2 bg-tertiary hover:bg-accent rounded text-sm transition-colors">
Previous Previous
</a> </a>
{% endif %} {% endif %}
@@ -409,21 +400,21 @@
<!-- First page --> <!-- First page -->
{% if current_page > 6 %} {% if current_page > 6 %}
<a href="javascript:void(0)" onclick="goToPage(1)" <a href="javascript:void(0)" onclick="goToPage(1)"
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm"> class="px-3 py-2 bg-tertiary hover:bg-accent rounded text-sm transition-colors">
1 1
</a> </a>
{% if current_page > 7 %} {% if current_page > 7 %}
<span class="px-3 py-2 text-gray-400">...</span> <span class="px-3 py-2 text-secondary">...</span>
{% endif %} {% endif %}
{% endif %} {% endif %}
<!-- Page numbers around current page --> <!-- Page numbers around current page -->
{% for page_num in range(max(1, current_page - 4), min(total_pages + 1, current_page + 5)) %} {% for page_num in range(max(1, current_page - 4), min(total_pages + 1, current_page + 5)) %}
{% if page_num == current_page %} {% if page_num == current_page %}
<span class="px-3 py-2 bg-blue-600 rounded text-sm">{{ page_num }}</span> <span class="px-3 py-2 bg-accent rounded text-sm text-primary">{{ page_num }}</span>
{% else %} {% else %}
<a href="javascript:void(0)" onclick="goToPage({{ page_num }})" <a href="javascript:void(0)" onclick="goToPage({{ page_num }})"
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm"> class="px-3 py-2 bg-tertiary hover:bg-accent rounded text-sm transition-colors">
{{ page_num }} {{ page_num }}
</a> </a>
{% endif %} {% endif %}
@@ -432,17 +423,17 @@
<!-- Last page --> <!-- Last page -->
{% if current_page < total_pages - 5 %} {% if current_page < total_pages - 5 %}
{% if current_page < total_pages - 6 %} {% if current_page < total_pages - 6 %}
<span class="px-3 py-2 text-gray-400">...</span> <span class="px-3 py-2 text-secondary">...</span>
{% endif %} {% endif %}
<a href="javascript:void(0)" onclick="goToPage({{ total_pages }})" <a href="javascript:void(0)" onclick="goToPage({{ total_pages }})"
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm"> class="px-3 py-2 bg-tertiary hover:bg-accent rounded text-sm transition-colors">
{{ total_pages }} {{ total_pages }}
</a> </a>
{% endif %} {% endif %}
{% if current_page < total_pages %} {% if current_page < total_pages %}
<a href="javascript:void(0)" onclick="goToPage({{ current_page + 1 }})" <a href="javascript:void(0)" onclick="goToPage({{ current_page + 1 }})"
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm"> class="px-3 py-2 bg-tertiary hover:bg-accent rounded text-sm transition-colors">
Next Next
</a> </a>
{% endif %} {% endif %}
@@ -454,7 +445,7 @@
<div id="gameDetailOverlay" class="hidden fixed inset-0 bg-black bg-opacity-75 z-50 flex items-center justify-center p-4 overflow-y-auto backdrop-blur-sm"> <div id="gameDetailOverlay" class="hidden fixed inset-0 bg-black bg-opacity-75 z-50 flex items-center justify-center p-4 overflow-y-auto backdrop-blur-sm">
<div class="bg-secondary rounded-lg max-w-6xl max-h-full w-full overflow-hidden relative shadow-2xl border border-theme animate-fade-in"> <div class="bg-secondary rounded-lg max-w-6xl max-h-full w-full overflow-hidden relative shadow-2xl border border-theme animate-fade-in">
<!-- Close Button --> <!-- Close Button -->
<button onclick="closeGameDetail()" class="absolute top-4 right-4 text-primary hover:text-secondary text-2xl z-20 bg-primary bg-opacity-20 hover:bg-opacity-30 rounded-full w-10 h-10 flex items-center justify-center transition-all"> <button onclick="closeGameDetail()" class="absolute top-4 right-4 text-primary hover:text-secondary text-2xl z-30 bg-primary bg-opacity-20 hover:bg-opacity-30 rounded-full w-10 h-10 flex items-center justify-center transition-all">
&times; &times;
</button> </button>
@@ -486,7 +477,7 @@
<button id="gameDownloadBtn" onclick="downloadGameFromOverlay()" <button id="gameDownloadBtn" onclick="downloadGameFromOverlay()"
class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded hidden"> class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded hidden">
Download ROM Download Game
</button> </button>
<span id="gameDownloadDisabled" class="bg-gray-600 px-4 py-2 rounded cursor-not-allowed hidden"> <span id="gameDownloadDisabled" class="bg-gray-600 px-4 py-2 rounded cursor-not-allowed hidden">
@@ -648,7 +639,10 @@
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';
a.href = url; a.href = url;
a.download = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip'; // Get filename from Content-Disposition header, removing quotes and underscores
let filename = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip';
filename = filename.replace(/^["\_]+|["\_]+$/g, ''); // Remove quotes and underscores from start and end
a.download = filename;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);