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

@@ -1,11 +1,11 @@
{% extends "base.html" %}
{% block title %}ROM Library - DOS Frontend{% endblock %}
{% block title %}Game Library - DOS Frontend{% endblock %}
{% block content %}
<div class="mb-6">
<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">
<!-- Search Form -->
<form method="GET" class="flex gap-2">
@@ -15,26 +15,26 @@
<div class="relative">
<input type="text" name="search" placeholder="Search games..."
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">
<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>
</svg>
</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>
</div>
</div>
<!-- Controls Bar -->
<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 %}
<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.
{% else %}
Showing {{ games|length }} of {{ total_games }} ROMs
Showing {{ games|length }} of {{ total_games }} games
{% if search %} for "{{ search }}"{% endif %}
{% endif %}
</div>
@@ -42,8 +42,8 @@
<div class="flex flex-col sm:flex-row gap-2 sm:gap-4 items-center">
<!-- Results per page -->
<div class="flex items-center gap-2">
<label class="text-gray-400 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">
<label class="text-secondary text-sm">Show:</label>
<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="50" {% if per_page == 50 %}selected{% endif %}>50</option>
<option value="100" {% if per_page == 100 %}selected{% endif %}>100</option>
@@ -51,16 +51,16 @@
</div>
<!-- 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')"
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">
<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>
<span class="ml-1 hidden sm:inline">Grid</span>
</button>
<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">
<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>
@@ -75,29 +75,29 @@
{% 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">
{% 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 -->
<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 -->
<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'))) %}
<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 %}
alt="{{ game.metadata_obj.title or game.title }}"
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';">
<div class="w-full h-full bg-gradient-to-br from-gray-800 to-gray-900 hidden items-center justify-center">
<div class="text-gray-400 text-center p-4">
<div class="w-full h-full bg-gradient-theme hidden items-center justify-center absolute inset-0">
<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">
<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>
<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>
{% 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 -->
<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">
@@ -111,7 +111,7 @@
</div>
<!-- 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 -->
<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">
@@ -188,23 +188,23 @@
</div>
<!-- Game Info -->
<div class="p-3">
<h3 class="font-semibold text-blue-400 truncate mb-1 text-sm">
<div class="p-3 bg-tertiary border-t border-theme">
<h3 class="font-semibold text-accent truncate mb-1 text-sm">
{{ game.metadata_obj.title or game.title }}
</h3>
{% 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 %}
<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 %}
<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
</button>
{% 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
</span>
{% endif %}
@@ -219,20 +219,20 @@
{% if view == 'list' %}
<div class="space-y-2">
{% 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 -->
<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">
<!-- 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'))) %}
<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 %}
alt="{{ game.metadata_obj.title or game.title }}"
class="w-full h-full object-cover"
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 -->
<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">
@@ -246,10 +246,10 @@
</div>
<!-- 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 -->
<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">
<div class="mb-1">
<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 -->
<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"/>
@@ -273,18 +273,9 @@
</svg>
</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 -->
<div class="text-xs text-secondary opacity-75 font-medium">
<div class="mb-1">CLASSIC DOS GAME</div>
<div class="text-accent text-[10px] font-bold tracking-wider">DOSVAULT</div>
<div class="text-[6px] text-accent font-bold tracking-wide opacity-75">
DOSVAULT
</div>
<!-- Decorative Corner Elements -->
@@ -311,9 +302,9 @@
</div>
</div>
{% 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 -->
<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">
<defs>
<pattern id="pixel-grid-small" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse">
@@ -325,8 +316,8 @@
</div>
<!-- Compact Logo -->
<div class="text-center relative z-10">
<svg class="w-8 h-8 mx-auto text-accent mb-1" viewBox="0 0 32 32" fill="none">
<div class="text-center relative z-10 w-full h-full flex flex-col justify-center items-center p-1">
<svg class="w-6 h-6 mx-auto text-accent mb-1" viewBox="0 0 32 32" fill="none">
<!-- 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="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"/>
<circle cx="22" cy="16" r="0.8" fill="var(--primary-bg)"/>
</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>
{% endif %}
@@ -401,7 +392,7 @@
<nav class="flex items-center space-x-1">
{% if 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
</a>
{% endif %}
@@ -409,21 +400,21 @@
<!-- First page -->
{% if current_page > 6 %}
<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
</a>
{% 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 %}
<!-- Page numbers around current page -->
{% for page_num in range(max(1, current_page - 4), min(total_pages + 1, current_page + 5)) %}
{% 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 %}
<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 }}
</a>
{% endif %}
@@ -432,17 +423,17 @@
<!-- Last page -->
{% if current_page < total_pages - 5 %}
{% 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 %}
<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 }}
</a>
{% endif %}
{% if current_page < total_pages %}
<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
</a>
{% 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 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 -->
<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;
</button>
@@ -486,7 +477,7 @@
<button id="gameDownloadBtn" onclick="downloadGameFromOverlay()"
class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded hidden">
Download ROM
Download Game
</button>
<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');
a.style.display = 'none';
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);
a.click();
window.URL.revokeObjectURL(url);