training-academy/library.html

1339 lines
48 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Media Library - Gooner Training</title>
<!-- Core Styles -->
<link rel="stylesheet" href="src/styles/color-variables.css">
<link rel="stylesheet" href="src/styles/styles.css">
<link rel="stylesheet" href="src/styles/styles-dark-edgy.css">
<link rel="stylesheet" href="src/styles/base-video-player.css">
<link rel="stylesheet" href="src/styles/media-tags.css">
<script src="src/utils/themeManager.js"></script>
<style>
/* Library Page Specific Styles */
/* Library Header - Matching Training Academy Style */
.library-header {
background: var(--gradient-primary);
border-bottom: 2px solid var(--header-border);
box-shadow: var(--shadow-md);
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
z-index: 1000;
backdrop-filter: blur(10px);
box-sizing: border-box;
}
.library-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
gap: 2rem;
width: 100%;
}
.library-nav .nav-left h1 {
color: var(--header-title-color);
font-size: 1.8rem;
margin: 0;
text-shadow: var(--shadow-glow-primary);
white-space: nowrap;
}
.library-nav .nav-center {
flex: 1;
text-align: center;
}
.library-subtitle {
color: var(--header-subtitle-color);
font-size: 1rem;
font-style: italic;
opacity: 0.9;
}
.library-nav .nav-right {
display: flex;
gap: 0.5rem;
align-items: center;
}
/* Button Styles */
.btn {
background: var(--btn-secondary-bg);
border: 1px solid var(--btn-secondary-border);
color: var(--btn-secondary-text);
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s ease;
font-family: inherit;
}
.btn:hover {
background: var(--btn-secondary-hover-bg);
border-color: var(--btn-secondary-hover-border);
transform: translateY(-1px);
}
.btn-secondary {
background: var(--bg-secondary-overlay-20);
border-color: var(--color-primary);
}
.btn-secondary:hover {
background: var(--bg-secondary-overlay-30);
border-color: var(--color-primary-light);
}
.btn-primary {
background: var(--btn-primary-bg);
border-color: var(--btn-primary-border);
color: var(--btn-primary-text);
}
.btn-primary:hover {
background: var(--btn-primary-hover-bg);
border-color: var(--btn-primary-hover-border);
}
.btn-danger {
background: var(--btn-danger-bg);
border-color: var(--btn-danger-border);
color: var(--btn-danger-text);
}
.btn-danger:hover {
background: var(--btn-danger-hover-bg);
border-color: var(--btn-danger-hover-border);
}
.btn-warning {
background: var(--color-warning);
border-color: var(--color-warning);
color: var(--text-primary);
}
.btn-warning:hover {
background: var(--color-accent-gold);
border-color: var(--color-accent-gold);
}
.btn-outline {
background: transparent;
border-color: var(--color-primary);
color: var(--color-primary);
}
.btn-outline:hover {
background: var(--bg-secondary-overlay-20);
border-color: var(--color-primary-light);
}
.btn-small {
padding: 0.3rem 0.6rem;
font-size: 0.85rem;
}
.btn-success {
background: var(--color-success);
border-color: var(--color-success);
color: var(--text-primary);
}
.btn-success:hover {
background: var(--color-success-light);
border-color: var(--color-success-light);
}
.btn-success:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-danger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Loading Overlay */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bg-primary);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
backdrop-filter: blur(10px);
}
.loading-content {
text-align: center;
}
.loading-content h2 {
font-family: 'Audiowide', cursive;
font-size: var(--font-xxxl);
background: linear-gradient(45deg, var(--color-primary), var(--color-secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: var(--space-lg);
animation: titleGlow 2s ease-in-out infinite alternate;
}
.loading-spinner {
width: 60px;
height: 60px;
border: 4px solid var(--border-color);
border-top: 4px solid var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto var(--space-lg);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-status {
color: var(--text-secondary);
font-size: var(--font-lg);
margin-bottom: var(--space-lg);
}
.loading-progress {
width: 300px;
height: 8px;
background: var(--bg-tertiary);
border-radius: 4px;
overflow: hidden;
margin: 0 auto var(--space-base);
}
.loading-progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary), var(--color-secondary));
border-radius: 4px;
width: 0%;
transition: width 0.3s ease;
}
.loading-percentage {
color: var(--text-primary);
font-size: var(--font-lg);
font-weight: 600;
}
/* Main Content Area */
body {
margin: 0;
padding: 0;
padding-top: 80px; /* Account for fixed header */
background: var(--bg-primary);
color: var(--text-primary);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.library-container {
max-width: 95vw;
width: 100%;
margin: 0 auto;
padding: 2rem;
}
/* Library Tabs */
.library-tabs {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.library-tab {
background: var(--bg-secondary);
border: 2px solid var(--border-color);
color: var(--text-primary);
padding: 0.75rem 1.5rem;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
}
.library-tab:hover {
background: var(--bg-secondary-overlay-30);
border-color: var(--color-primary);
transform: translateY(-2px);
}
.library-tab.active {
background: var(--gradient-primary);
border-color: var(--color-primary);
box-shadow: var(--shadow-glow-primary);
}
/* Library Content Sections */
.library-content {
display: none;
}
.library-content.active {
display: block;
}
.content-section {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 2rem;
box-shadow: var(--shadow-md);
}
.content-section h2,
.content-section h3 {
color: var(--color-primary);
margin-top: 0;
}
.content-section h4 {
color: var(--color-secondary);
}
.content-section p {
color: var(--text-secondary);
margin-bottom: 1.5rem;
}
/* Directory Management Section */
.directory-management-section,
.upload-section {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.directory-controls,
.upload-controls {
display: flex;
gap: 0.75rem;
margin: 1rem 0;
flex-wrap: wrap;
align-items: center;
}
.linked-directories-container {
margin-top: 1rem;
}
#linked-image-directories-list,
#linked-video-directories-list {
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 1rem;
min-height: 100px;
}
.no-directories {
color: var(--text-secondary);
text-align: center;
padding: 2rem;
font-style: italic;
}
.upload-info {
background: var(--bg-secondary-overlay-20);
border-left: 3px solid var(--color-primary);
padding: 0.75rem;
margin: 1rem 0;
border-radius: 4px;
color: var(--text-secondary);
}
/* Gallery Sections */
.gallery-section,
.audio-gallery-section,
.video-gallery-section {
margin-top: 2rem;
}
.gallery-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
flex-wrap: wrap;
gap: 1rem;
}
.gallery-controls {
display: flex;
gap: 1rem;
align-items: center;
}
.gallery-controls select {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 0.5rem;
border-radius: 6px;
font-family: inherit;
}
.image-gallery,
.audio-gallery,
.video-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
min-height: 200px;
max-height: 75vh;
overflow-y: auto;
}
.no-images-message,
.no-audio-message,
.no-video-message {
grid-column: 1 / -1;
text-align: center;
padding: 3rem;
color: var(--text-secondary);
}
.gallery-item {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 0.75rem;
transition: all 0.3s ease;
}
.gallery-item:hover {
border-color: var(--color-primary);
box-shadow: var(--shadow-glow-primary);
transform: translateY(-2px);
}
.gallery-item img {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 6px;
}
.gallery-item-info {
margin-top: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.85rem;
}
.gallery-item-type {
background: var(--color-primary);
color: var(--text-primary);
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
}
.gallery-item-name {
color: var(--text-secondary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.audio-item,
.video-item {
display: flex;
flex-direction: column;
justify-content: center;
}
.audio-info,
.video-info {
text-align: center;
}
.audio-icon,
.video-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.audio-name,
.video-name {
color: var(--text-primary);
font-weight: 500;
margin-bottom: 0.25rem;
}
.audio-type,
.video-type {
color: var(--text-secondary);
font-size: 0.85rem;
}
/* Photo Gallery Styles */
.gallery-categories {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.gallery-category-btn {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.gallery-category-btn:hover {
background: var(--bg-secondary-overlay-30);
border-color: var(--color-primary);
}
.gallery-category-btn.active {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--text-primary);
}
.photo-galleries {
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1.5rem;
}
.photo-gallery {
display: none;
}
.photo-gallery.active {
display: block;
}
.bulk-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding: 1rem;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
flex-wrap: wrap;
gap: 1rem;
}
.selection-controls,
.action-buttons {
display: flex;
gap: 0.5rem;
align-items: center;
}
.selected-count {
color: var(--text-secondary);
font-size: 0.9rem;
}
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 1rem;
}
.photo-item {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
}
.photo-item:hover {
border-color: var(--color-primary);
box-shadow: var(--shadow-glow-primary);
transform: translateY(-2px);
}
.photo-container {
position: relative;
}
.photo-container img {
width: 100%;
height: 180px;
object-fit: cover;
cursor: pointer;
}
.photo-checkbox {
position: absolute;
top: 0.5rem;
left: 0.5rem;
z-index: 10;
}
.photo-checkbox input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
}
.photo-actions {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
display: flex;
gap: 0.5rem;
}
.photo-download-btn,
.photo-delete-btn {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
padding: 0.4rem 0.6rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
}
.photo-download-btn:hover {
background: var(--color-success);
}
.photo-delete-btn:hover {
background: var(--color-danger);
}
.photo-info {
padding: 0.5rem;
background: var(--bg-tertiary);
}
.photo-date,
.photo-type {
display: block;
font-size: 0.75rem;
color: var(--text-secondary);
}
.photo-type {
margin-top: 0.25rem;
color: var(--color-primary);
}
.no-photos-message {
text-align: center;
padding: 3rem;
color: var(--text-secondary);
}
/* Management Buttons */
.management-buttons {
display: flex;
gap: 1rem;
margin-top: 2rem;
justify-content: center;
flex-wrap: wrap;
}
/* Desktop Feature Badge */
.desktop-feature {
background: var(--bg-secondary-overlay-20);
border-left: 3px solid var(--color-accent);
padding: 0.5rem 1rem;
border-radius: 4px;
font-size: 0.85rem;
}
/* Video Disabled Styles */
.video-disabled {
opacity: 0.6;
}
.video-disabled .video-thumbnail-container {
position: relative;
}
.disabled-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
color: #ff4444;
font-weight: bold;
font-size: 1.2rem;
border-radius: 6px;
}
.btn-warning {
background: var(--color-warning, #ff9800);
}
.btn-warning:hover {
background: var(--color-warning-hover, #f57c00);
}
</style>
</head>
<body>
<!-- Loading Overlay -->
<div id="library-loading" class="loading-overlay" style="display: flex;">
<div class="loading-content">
<div class="loading-spinner"></div>
<h2>📚 Loading Media Library...</h2>
<p class="loading-status" id="loading-status">Initializing file systems...</p>
<div class="loading-progress">
<div class="loading-progress-fill" id="loading-progress-fill"></div>
</div>
<div class="loading-percentage" id="loading-percentage">0%</div>
</div>
</div>
<!-- Library Header -->
<header class="library-header">
<nav class="library-nav">
<div class="nav-left">
<h1>📚 Media Library</h1>
</div>
<div class="nav-center">
<p class="library-subtitle">Manage all your media content in one place</p>
</div>
<div class="nav-right">
<button id="manage-tags-btn" class="btn btn-primary">🏷️ Manage Tags</button>
<button id="refresh-library-btn" class="btn btn-primary">🔄 Refresh</button>
<button id="back-to-home-btn" class="btn btn-secondary">🏠 Home</button>
</div>
</nav>
</header>
<!-- Main Library Content -->
<div class="library-container">
<!-- Library Navigation Tabs -->
<div class="library-tabs">
<button class="library-tab active" data-tab="images">🖼️ Images</button>
<button class="library-tab" data-tab="audio">🎵 Audio</button>
<button class="library-tab" data-tab="video">🎬 Video</button>
<button class="library-tab" data-tab="gallery">📸 Gallery</button>
</div>
<!-- Images Tab Content -->
<div id="library-images-content" class="library-content active">
<div class="content-section">
<h3>🖼️ Image Library Management</h3>
<p>Link directories from your computer to access image content</p>
<!-- Directory Management Section -->
<div class="directory-management-section">
<h4>📁 Linked Image Directories</h4>
<div class="directory-controls">
<button id="lib-add-image-directory-btn" class="btn btn-primary">📁 Add Directory(ies)</button>
<button id="lib-add-individual-images-btn" class="btn btn-primary">🖼️ Add Individual Images</button>
<button id="lib-refresh-image-directories-btn" class="btn btn-secondary">🔄 Refresh</button>
<button id="lib-clear-image-directories-btn" class="btn btn-danger">🗑️ Clear All</button>
<span id="lib-directories-count">0 directories linked</span>
</div>
<div class="linked-directories-container">
<div id="linked-image-directories-list">
<div class="no-directories">No image directories linked yet</div>
</div>
</div>
</div>
<!-- Image Gallery -->
<div class="gallery-section">
<div class="gallery-header">
<h4>🖼️ Current Image Library</h4>
<div class="gallery-controls">
<select id="lib-image-category-filter">
<option value="all">All Images</option>
<option value="untagged">📝 Untagged</option>
</select>
<span id="lib-image-count">0 images</span>
</div>
</div>
<div class="image-gallery active" id="lib-image-gallery">
<div class="no-images-message">
<p>🗃️ No images found in linked directories</p>
<p>Click "Add Directory" to link a folder containing images</p>
</div>
</div>
</div>
</div>
</div>
<!-- Audio Tab Content -->
<div id="library-audio-content" class="library-content">
<div class="content-section">
<h3>🎵 Audio Library Management</h3>
<p>WORK IN PROGRESS - FEATURE TO COME</p>
<p>Organize your background music and ambient sounds</p>
<!-- Audio Upload Section -->
<div class="upload-section">
<h4>🎵 Import Audio Files</h4>
<div class="upload-controls">
<button id="lib-import-background-music-btn" class="btn btn-primary">🎵 Background Music</button>
<button id="lib-import-ambient-audio-btn" class="btn btn-secondary">🌊 Ambient Sounds</button>
<input type="file" id="lib-audio-upload-input" accept="audio/*" multiple style="display: none;">
</div>
<div class="upload-info desktop-feature">
<span>💻 Desktop: Native file dialogs • Supports MP3, WAV, OGG, M4A formats</span>
</div>
<div class="directory-controls">
<button id="lib-audio-storage-info-btn" class="btn btn-outline">📊 Storage Info</button>
<button id="lib-cleanup-invalid-audio-btn" class="btn btn-warning">🧹 Cleanup</button>
<button id="lib-clear-all-audio-btn" class="btn btn-danger">🗑️ Clear All</button>
</div>
</div>
<!-- Audio Library -->
<div class="audio-gallery-section">
<div class="gallery-header">
<h4>🎵 Current Audio Library</h4>
<div class="gallery-controls">
<select id="lib-audio-category-filter">
<option value="all">All Categories</option>
<option value="background">Background Music</option>
<option value="ambient">Ambient Sounds</option>
</select>
<span id="lib-audio-count">0 files</span>
</div>
</div>
<div class="audio-gallery" id="lib-audio-gallery">
<div class="no-audio-message">
<p>🎵 No audio files found</p>
<p>Import audio to get started</p>
</div>
</div>
</div>
</div>
</div>
<!-- Video Tab Content -->
<div id="library-video-content" class="library-content">
<div class="content-section">
<h3>🎬 Video Library Management</h3>
<p>Manage your video content for enhanced training sessions</p>
<!-- Video Directory Management Section -->
<div class="directory-management-section">
<h4>📁 Linked Video Directories</h4>
<p>Link directories from your computer to access video content</p>
<div class="directory-controls">
<button id="lib-add-video-directory-btn" class="btn btn-primary">📁 Add Directory(ies)</button>
<button id="lib-add-individual-videos-btn" class="btn btn-primary">🎬 Add Individual Videos</button>
<button id="lib-refresh-video-directories-btn" class="btn btn-secondary">🔄 Refresh</button>
<button id="lib-clear-video-directories-btn" class="btn btn-danger">🗑️ Clear All</button>
<span id="lib-video-directories-count">0 directories linked</span>
</div>
<div class="linked-directories-container">
<div id="linked-video-directories-list">
<div class="no-directories">No video directories linked yet</div>
</div>
</div>
</div>
<!-- Video Library -->
<div class="video-gallery-section">
<div class="gallery-header">
<h4>🎬 Current Video Library</h4>
<div class="gallery-controls">
<select id="lib-video-category-filter">
<option value="all">All Videos</option>
<option value="untagged">📝 Untagged</option>
</select>
<span id="lib-video-count">0 files</span>
</div>
</div>
<div class="video-gallery active" id="lib-video-gallery">
<div class="no-video-message">
<p>🎬 No video files found</p>
<p>Import videos to get started</p>
</div>
</div>
</div>
</div>
</div>
<!-- Gallery Tab Content -->
<div id="library-gallery-content" class="library-content">
<div class="content-section">
<h3>📸 Photo Gallery</h3>
<p>Browse and organize your photo collections</p>
<!-- Gallery Categories -->
<div class="gallery-categories">
<button class="gallery-category-btn active" data-category="all">All Photos</button>
<button class="gallery-category-btn" data-category="dress-up">Dress Up</button>
<button class="gallery-category-btn" data-category="studio">Studio</button>
<button class="gallery-category-btn" data-category="custom">Custom</button>
</div>
<!-- Photo Gallery Display -->
<div class="photo-galleries">
<div id="lib-all-photos-gallery" class="photo-gallery active">
<div class="gallery-header">
<h4>📸 All Photos</h4>
<span class="photo-count" id="lib-all-photos-count">0 photos</span>
</div>
<div class="bulk-actions">
<div class="selection-controls">
<button id="select-all-photos" class="btn btn-small">☑️ Select All</button>
<button id="deselect-all-photos" class="btn btn-small">☐ Deselect All</button>
<span id="selected-count" class="selected-count">0 selected</span>
</div>
<div class="action-buttons">
<button id="download-selected-photos" class="btn btn-success" disabled>📥 Download Selected</button>
<button id="delete-selected-photos" class="btn btn-danger" disabled>🗑️ Delete Selected</button>
</div>
</div>
<div class="photo-grid" id="lib-all-photos-grid">
<div class="no-photos-message">
<p>📸 No photos found</p>
<p>Take some photos during gameplay to see them here</p>
</div>
</div>
</div>
<div id="lib-dress-up-photos-gallery" class="photo-gallery">
<div class="gallery-header">
<h4>👗 Dress Up Photos</h4>
<span class="photo-count" id="lib-dress-up-photos-count">0 photos</span>
</div>
<div class="bulk-actions">
<div class="selection-controls">
<button id="select-all-dress-up" class="btn btn-small">☑️ Select All</button>
<button id="deselect-all-dress-up" class="btn btn-small">☐ Deselect All</button>
<span id="selected-dress-up-count" class="selected-count">0 selected</span>
</div>
<div class="action-buttons">
<button id="download-selected-dress-up" class="btn btn-success" disabled>📥 Download Selected</button>
<button id="delete-selected-dress-up" class="btn btn-danger" disabled>🗑️ Delete Selected</button>
</div>
</div>
<div class="photo-grid" id="lib-dress-up-photos-grid">
<div class="no-photos-message">
<p>👗 No dress up photos found</p>
</div>
</div>
</div>
<div id="lib-studio-photos-gallery" class="photo-gallery">
<div class="gallery-header">
<h4>🎬 Studio Photos</h4>
<span class="photo-count" id="lib-studio-photos-count">0 photos</span>
</div>
<div class="photo-grid" id="lib-studio-photos-grid">
<div class="no-photos-message">
<p>🎬 No studio photos found</p>
</div>
</div>
</div>
<div id="lib-custom-photos-gallery" class="photo-gallery">
<div class="gallery-header">
<h4>⭐ Custom Photos</h4>
<span class="photo-count" id="lib-custom-photos-count">0 photos</span>
</div>
<div class="photo-grid" id="lib-custom-photos-grid">
<div class="no-photos-message">
<p>⭐ No custom photos found</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Game Data System -->
<script src="src/data/modes/mainGameData.js"></script>
<script src="src/data/modes/humiliationGameData.js"></script>
<script src="src/data/modes/trainingGameData.js"></script>
<script src="src/data/modes/enduranceGameData.js"></script>
<script src="src/data/modes/dressUpGameData.js"></script>
<script src="src/data/gameDataManager.js"></script>
<!-- Desktop File Manager -->
<script src="src/utils/desktop-file-manager.js"></script>
<!-- Library Management JavaScript -->
<script>
// Navigation
document.getElementById('back-to-home-btn').addEventListener('click', () => {
window.location.href = 'index.html';
});
// Tab Switching
document.querySelectorAll('.library-tab').forEach(tab => {
tab.addEventListener('click', () => {
const targetTab = tab.dataset.tab;
// Remove active class from all tabs and content
document.querySelectorAll('.library-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.library-content').forEach(c => c.classList.remove('active'));
// Add active class to clicked tab and corresponding content
tab.classList.add('active');
document.getElementById(`library-${targetTab}-content`).classList.add('active');
});
});
// Gallery Category Switching
document.querySelectorAll('.gallery-category-btn').forEach(btn => {
btn.addEventListener('click', () => {
const category = btn.dataset.category;
// Remove active class from all category buttons
document.querySelectorAll('.gallery-category-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Show corresponding gallery
document.querySelectorAll('.photo-gallery').forEach(g => g.classList.remove('active'));
if (category === 'all') {
document.getElementById('lib-all-photos-gallery').classList.add('active');
} else if (category === 'dress-up') {
document.getElementById('lib-dress-up-photos-gallery').classList.add('active');
} else if (category === 'studio') {
document.getElementById('lib-studio-photos-gallery').classList.add('active');
} else if (category === 'custom') {
document.getElementById('lib-custom-photos-gallery').classList.add('active');
}
});
});
// Refresh Library Button
document.getElementById('refresh-library-btn').addEventListener('click', () => {
console.log('Refreshing library...');
refreshAllLibraryContent();
});
// Initialize library on page load
window.addEventListener('DOMContentLoaded', async () => {
console.log('Library page loaded');
const loadingStatus = document.getElementById('loading-status');
const loadingProgressFill = document.getElementById('loading-progress-fill');
const loadingPercentage = document.getElementById('loading-percentage');
function updateProgress(percent, status) {
if (loadingProgressFill) loadingProgressFill.style.width = `${percent}%`;
if (loadingPercentage) loadingPercentage.textContent = `${percent}%`;
if (loadingStatus) loadingStatus.textContent = status;
}
updateProgress(10, 'Initializing desktop file manager...');
// Create a data manager wrapper that matches the real DataManager behavior
// This ensures linked directories are stored in the same place across all pages
const simpleDataManager = {
data: null,
storageKey: 'webGame-data',
loadData() {
try {
const stored = localStorage.getItem(this.storageKey);
if (stored) {
this.data = JSON.parse(stored);
} else {
this.data = {};
}
} catch (e) {
console.error('Error loading data:', e);
this.data = {};
}
},
saveData() {
try {
if (!this.data) this.data = {};
this.data.timestamp = Date.now();
localStorage.setItem(this.storageKey, JSON.stringify(this.data));
} catch (e) {
console.error('Error saving data:', e);
}
},
get(key) {
if (!this.data) this.loadData();
return this.data[key];
},
set(key, value) {
if (!this.data) this.loadData();
this.data[key] = value;
this.saveData();
}
};
// Load data initially
simpleDataManager.loadData();
// Check if running in Electron or browser mode
if (!window.electronAPI) {
console.warn('⚠️ Running in browser mode - linked directories will not work');
console.info('💡 To access local media files, run with: npm start');
// Show warning banner
const warningBanner = document.createElement('div');
warningBanner.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, #f39c12 0%, #e74c3c 100%);
color: white;
padding: 15px 20px;
text-align: center;
z-index: 10001;
font-weight: bold;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
`;
warningBanner.innerHTML = `
⚠️ Browser Mode: Cannot access local directories.
To use linked media libraries, run with <code style="background: rgba(0,0,0,0.3); padding: 2px 6px; border-radius: 3px;">npm start</code> in Electron.
<button onclick="this.parentElement.remove()" style="margin-left: 20px; background: rgba(0,0,0,0.3); border: none; color: white; padding: 5px 15px; border-radius: 4px; cursor: pointer;">Dismiss</button>
`;
document.body.insertBefore(warningBanner, document.body.firstChild);
}
// Initialize desktop file manager first
if (window.electronAPI) {
console.log('📸 Initializing desktop file manager for library...');
if (!window.desktopFileManager) {
window.desktopFileManager = new DesktopFileManager(simpleDataManager);
// Wait for initialization to complete (includes loading linked directories)
await window.desktopFileManager.init();
} else {
// If already exists, ensure it has dataManager and reload linked directories
if (!window.desktopFileManager.dataManager) {
window.desktopFileManager.dataManager = simpleDataManager;
}
await window.desktopFileManager.loadLinkedDirectories();
}
console.log('📸 Desktop file manager ready:', !!window.desktopFileManager);
console.log('📸 isElectron:', window.desktopFileManager?.isElectron);
console.log('📸 photoDirectory:', window.desktopFileManager?.photoDirectory);
console.log('📸 Linked video directories:', window.desktopFileManager?.externalVideoDirectories?.length);
// Clean up legacy localStorage video directories
const legacyDirs = localStorage.getItem('linkedVideoDirectories');
const legacyIndividual = localStorage.getItem('linkedIndividualVideos');
if (legacyDirs || legacyIndividual) {
console.log('🧹 Cleaning up legacy localStorage video directories...');
localStorage.removeItem('linkedVideoDirectories');
localStorage.removeItem('linkedIndividualVideos');
console.log('✅ Legacy video directory storage cleared');
}
}
updateProgress(30, 'Loading image library...');
await new Promise(resolve => setTimeout(resolve, 100));
// Initialize all library tabs
setupLibraryImagesTab();
updateProgress(50, 'Loading audio library...');
await new Promise(resolve => setTimeout(resolve, 100));
setupLibraryAudioTab();
updateProgress(70, 'Loading video library...');
await new Promise(resolve => setTimeout(resolve, 100));
setupLibraryVideoTab();
updateProgress(90, 'Loading photo gallery...');
await new Promise(resolve => setTimeout(resolve, 100));
setupLibraryGalleryTab();
updateProgress(100, 'Library ready!');
// Hide loading overlay after brief delay
setTimeout(() => {
const loadingOverlay = document.getElementById('library-loading');
if (loadingOverlay) {
loadingOverlay.style.display = 'none';
}
}, 500);
});
function refreshAllLibraryContent() {
console.log('Refreshing all library content...');
// Refresh based on currently active tab
const activeTab = document.querySelector('.library-tab.active');
if (activeTab) {
const tabType = activeTab.getAttribute('data-tab');
switch(tabType) {
case 'images':
setupLibraryImagesTab();
break;
case 'audio':
setupLibraryAudioTab();
break;
case 'video':
setupLibraryVideoTab();
break;
case 'gallery':
setupLibraryGalleryTab();
break;
}
}
console.log('Library content refreshed');
}
</script>
<!-- Library Compatibility Layer -->
<script>
// Create minimal game object for compatibility
window.game = window.game || {};
// Simple DataManager for library page
if (!window.game.dataManager) {
window.game.dataManager = {
get: function(key) {
try {
const data = localStorage.getItem('library-' + key);
return data ? JSON.parse(data) : null;
} catch (error) {
console.error('Error loading data:', error);
return null;
}
},
set: function(key, value) {
try {
localStorage.setItem('library-' + key, JSON.stringify(value));
return true;
} catch (error) {
console.error('Error saving data:', error);
return false;
}
},
remove: function(key) {
try {
localStorage.removeItem('library-' + key);
return true;
} catch (error) {
console.error('Error removing data:', error);
return false;
}
}
};
console.log('✅ DataManager initialized for library');
}
// Flash message manager placeholder
if (!window.game.flashMessageManager) {
window.game.flashMessageManager = {
show: function(message, type) {
console.log(`[${type}] ${message}`);
// Simple alert fallback
if (type === 'error') {
alert(message);
}
}
};
}
// Notification placeholder
if (!window.game.showNotification) {
window.game.showNotification = function(message, type) {
console.log(`[${type}] ${message}`);
// Create a simple notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 1rem 1.5rem;
background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : type === 'warning' ? '#ff9800' : '#2196F3'};
color: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
z-index: 10000;
animation: slideIn 0.3s ease-out;
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease-in';
setTimeout(() => notification.remove(), 300);
}, 3000);
};
}
</script>
<style>
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
</style>
<!-- Library Manager Functions -->
<script src="src/utils/libraryManager.js"></script>
<!-- Media Tagging System -->
<script src="src/features/media/mediaTagManager.js"></script>
<script src="src/features/media/mediaTagUI.js"></script>
<!-- Only use libraryTaggingIntegration.js for library.html, not globalTaggingInit.js -->
<script src="src/features/media/libraryTaggingIntegration.js"></script>
</body>
</html>