training-academy/library.html

1001 lines
35 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">
<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;
}
/* 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: 1400px;
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;
}
.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;
}
</style>
</head>
<body>
<!-- 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="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</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="tasks">🎯 Task Images</option>
<option value="consequences">⚠️ Consequence Images</option>
<option value="rewards">🎁 Reward Images</option>
<option value="verification">📷 Verification Photos</option>
<option value="jpg">JPEG Images</option>
<option value="png">PNG Images</option>
<option value="gif">GIF Images</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>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</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 Categories</option>
<option value="training">Training Videos</option>
<option value="background">Background Videos</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', () => {
console.log('Library page loaded');
// Initialize all library tabs
setTimeout(() => {
setupLibraryImagesTab();
setupLibraryAudioTab();
setupLibraryVideoTab();
setupLibraryGalleryTab();
}, 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 || {};
// 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}`);
};
}
</script>
<!-- Library Manager Functions -->
<script src="src/utils/libraryManager.js"></script>
</body>
</html>