training-academy/index.html

2623 lines
142 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: file: blob: http://localhost:* https:; connect-src 'self' http://localhost:* https: ws://localhost:*; img-src 'self' data: file: blob:; media-src 'self' data: file: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:;">
<title>Gooner Training Academy - Master Your Dedication</title>
<link rel="stylesheet" href="src/styles/styles.css">
<link href="https://fonts.googleapis.com/css2?family=Audiowide&display=swap" rel="stylesheet">
<script src="https://unpkg.com/jszip@3.10.1/dist/jszip.min.js" crossorigin="anonymous"></script>
<script>
// Test JSZip availability when page loads
window.addEventListener('load', function() {
if (typeof JSZip !== 'undefined') {
console.log('✅ JSZip loaded successfully from global scope');
} else if (typeof window.JSZip !== 'undefined') {
console.log('✅ JSZip loaded successfully from window scope');
} else {
console.error('❌ JSZip failed to load');
}
});
</script>
</head>
<body>
<!-- Loading Overlay -->
<div id="loading-overlay" class="loading-overlay">
<div class="loading-content">
<div class="loading-spinner"></div>
<h2>Initializing Game...</h2>
<p class="loading-status" id="loading-status">Loading components...</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>
<div class="game-container">
<!-- Game Header -->
<header class="game-header">
<h1>Gooner Training Academy</h1>
<p class="tagline">Master Your Dedication</p>
<!-- Compact Timer (top-right corner) -->
<div class="timer-compact">
<span id="timer" class="timer">00:00</span>
<span id="timer-status" class="timer-status"></span>
</div>
<!-- Compact Music Controls (expandable) -->
<div class="music-controls-compact">
<button id="music-toggle-compact" class="music-icon-btn" title="Music Controls">🎵</button>
<div class="music-panel-expanded">
<div class="music-row">
<button id="music-toggle" class="music-btn-small" title="Play/Pause">▶️</button>
<button id="loop-btn" class="music-btn-small" title="Loop">🔁</button>
<button id="shuffle-btn" class="music-btn-small" title="Shuffle">🔀</button>
</div>
<div class="music-row">
<select id="track-selector" class="track-dropdown-compact">
<option value="0">Colorful Flowers</option>
<option value="1">New Beginnings</option>
<option value="2">Storm Clouds</option>
<option value="3">Brunch For Two</option>
</select>
</div>
<div class="music-row">
<div class="volume-control-compact">
<span class="volume-icon">🔊</span>
<input type="range" id="volume-slider" min="0" max="100" value="30" class="volume-slider-compact">
<span class="volume-percent" id="volume-percent">30%</span>
</div>
</div>
<div class="music-status-compact" id="music-status">Music: Off</div>
</div>
</div>
</header>
<!-- Game Content -->
<main class="game-content">
<!-- Start Screen -->
<div id="start-screen" class="screen active">
<h2>Ready to Start?</h2>
<p>Complete tasks, but beware of skipping - there are consequences!</p>
<!-- Main Action Buttons -->
<div class="main-actions">
<button id="start-btn" class="btn btn-primary">Start Game</button>
<button id="manage-tasks-btn" class="btn btn-secondary">Manage Tasks</button>
<button id="manage-images-btn" class="btn btn-secondary">Manage Images</button>
<button id="manage-audio-btn" class="btn btn-secondary">🎵 Manage Audio</button>
<button id="manage-video-btn" class="btn btn-secondary">🎬 Manage Video</button>
<button id="photo-gallery-btn" class="btn btn-secondary">📸 Photo Gallery</button>
<button id="manage-annoyance-btn" class="btn btn-secondary">😈 Annoyance</button>
</div>
<!-- Game Mode Selection -->
<div class="game-mode-selection">
<h3>Game Mode</h3>
<div class="game-mode-options">
<div class="game-mode-option">
<input type="radio" id="mode-complete-all" name="gameMode" value="complete-all" checked>
<label for="mode-complete-all">
<strong>🎯 Complete All Tasks</strong>
<p>Finish every task in the game</p>
</label>
</div>
<div class="game-mode-option">
<input type="radio" id="mode-timed" name="gameMode" value="timed">
<label for="mode-timed">
<strong>⏱️ Timed Challenge</strong>
<p>Complete as many tasks as possible within the time limit</p>
<div class="mode-config" id="timed-config" style="display: none;">
<label>Time Limit:
<select id="time-limit-select">
<option value="300">5 minutes</option>
<option value="600">10 minutes</option>
<option value="900">15 minutes</option>
<option value="1200">20 minutes</option>
<option value="1800">30 minutes</option>
<option value="custom">Custom...</option>
</select>
</label>
<div id="custom-time-input" style="display: none; margin-top: 10px;">
<label>Custom time (minutes):
<input type="number" id="custom-time-value" min="1" max="180" value="15" style="width: 60px;">
</label>
</div>
</div>
</label>
</div>
<div class="game-mode-option">
<input type="radio" id="mode-xp-target" name="gameMode" value="xp-target">
<label for="mode-xp-target">
<strong>🏆 XP Target</strong>
<p>Reach the target XP to win</p>
<div class="mode-config" id="xp-target-config" style="display: none;">
<label>Target XP:
<select id="xp-target-select">
<option value="100">100 XP</option>
<option value="200">200 XP</option>
<option value="300">300 XP</option>
<option value="custom">Custom...</option>
</select>
</label>
<div id="custom-xp-input" style="display: none; margin-top: 10px;">
<label>Custom target XP:
<input type="number" id="custom-xp-value" min="50" max="10000" value="300" style="width: 80px;">
</label>
</div>
</div>
</label>
</div>
</div>
</div>
<!-- Options Menu -->
<div class="options-section">
<button id="options-menu-btn" class="btn btn-tertiary">⚙️ Options</button>
<div id="options-menu" class="options-menu" style="display: none;">
<!-- Theme Selector -->
<div class="option-item theme-selector">
<label for="theme-dropdown" class="option-label">Choose Your Vibe:</label>
<select id="theme-dropdown" class="theme-dropdown">
<option value="balanced-purple" selected>💜 Balanced Purple (Default)</option>
<option value="balanced-red">❤️ Balanced Red</option>
<option value="balanced-blue">💙 Balanced Blue</option>
<option value="balanced-green">💚 Balanced Forest Green</option>
</select>
</div>
<!-- Periodic Popup Controls -->
<div class="option-item periodic-popup-controls">
<div class="periodic-popup-header">
<label class="option-label">💫 Periodic Image Popups</label>
<button id="test-periodic-popup" class="btn btn-mini">🔍 Test Popup</button>
</div>
<div class="periodic-popup-group">
<div class="periodic-control">
<label><input type="checkbox" id="enable-periodic-popups" checked> Enable Periodic Popups</label>
</div>
<div class="periodic-control">
<label for="popup-min-interval">Min Interval (seconds):</label>
<input type="number" id="popup-min-interval" min="10" max="300" value="30" class="number-input">
</div>
<div class="periodic-control">
<label for="popup-max-interval">Max Interval (seconds):</label>
<input type="number" id="popup-max-interval" min="30" max="600" value="120" class="number-input">
</div>
<div class="periodic-control">
<label for="popup-display-duration">Display Duration (seconds):</label>
<input type="number" id="popup-display-duration" min="2" max="30" value="5" class="number-input">
</div>
</div>
</div>
<!-- Audio Controls -->
<div class="option-item audio-controls">
<div class="audio-control-header">
<label class="option-label">🎵 Audio Settings</label>
<button id="debug-audio" class="btn btn-mini">🔧 Debug Audio</button>
</div>
<div class="audio-control-group">
<div class="audio-control">
<label for="master-volume">Master Volume:</label>
<input type="range" id="master-volume" min="0" max="100" value="70" class="volume-slider">
<span id="master-volume-display">70%</span>
</div>
<div class="audio-control">
<label for="task-audio-volume">Task Audio:</label>
<input type="range" id="task-audio-volume" min="0" max="100" value="70" class="volume-slider">
<span id="task-audio-volume-display">70%</span>
<button id="preview-task-audio" class="btn btn-mini">▶️</button>
</div>
<div class="audio-control">
<label for="punishment-audio-volume">Punishment Audio:</label>
<input type="range" id="punishment-audio-volume" min="0" max="100" value="80" class="volume-slider">
<span id="punishment-audio-volume-display">80%</span>
<button id="preview-punishment-audio" class="btn btn-mini">▶️</button>
</div>
<div class="audio-control">
<label for="reward-audio-volume">Reward Audio:</label>
<input type="range" id="reward-audio-volume" min="0" max="100" value="60" class="volume-slider">
<span id="reward-audio-volume-display">60%</span>
<button id="preview-reward-audio" class="btn btn-mini">▶️</button>
</div>
</div>
<div class="audio-toggles">
<label><input type="checkbox" id="enable-task-audio" checked> Enable Task Audio</label>
<label><input type="checkbox" id="enable-punishment-audio" checked> Enable Punishment Audio</label>
<label><input type="checkbox" id="enable-reward-audio" checked> Enable Reward Audio</label>
</div>
</div>
<!-- Data Management -->
<div class="option-item data-controls">
<button id="import-btn" class="btn btn-option" title="Import Save File">
<span class="btn-text"><EFBFBD> Import</span>
<span class="btn-loading" style="display: none;">⏳ Importing...</span>
</button>
<button id="export-btn" class="btn btn-option" title="Export Save File">
<span class="btn-text"><EFBFBD> Export</span>
<span class="btn-loading" style="display: none;">⏳ Exporting...</span>
</button>
</div>
<!-- Other Options -->
<div class="option-item other-controls">
<button id="stats-btn" class="btn btn-option" title="View Statistics">📊 Stats</button>
<button id="help-btn" class="btn btn-option" title="Keyboard Shortcuts & Help">❓ Help</button>
</div>
</div>
<input type="file" id="import-file" accept=".json" style="display: none;">
</div>
</div>
<!-- Task Management Screen -->
<div id="task-management-screen" class="screen">
<h2>Manage Your Tasks</h2>
<!-- Add New Task Section -->
<div class="task-editor-section">
<h3>Add New Task</h3>
<div class="task-input-group">
<label for="new-task-text">Task Description:</label>
<textarea id="new-task-text" placeholder="Enter your task description..." rows="3"></textarea>
</div>
<div class="task-input-group">
<label for="new-task-type">Task Type:</label>
<select id="new-task-type">
<option value="main">Main Task</option>
<option value="consequence">Consequence Task</option>
</select>
</div>
<div class="task-input-group" id="difficulty-input-group">
<label for="new-task-difficulty">Difficulty:</label>
<select id="new-task-difficulty">
<option value="Easy">🟢 Easy (1 point)</option>
<option value="Medium" selected>🟡 Medium (3 points)</option>
<option value="Hard">🔴 Hard (5 points)</option>
</select>
</div>
<button id="add-task-btn" class="btn btn-success">Add Task</button>
</div>
<!-- Existing Tasks Section -->
<div class="task-list-section">
<h3>Your Tasks</h3>
<div class="task-tabs">
<button id="main-tasks-tab" class="tab-btn active">Main Tasks</button>
<button id="consequence-tasks-tab" class="tab-btn">Consequence Tasks</button>
</div>
<div id="main-tasks-list" class="task-list active">
<!-- Main tasks will be populated here -->
</div>
<div id="consequence-tasks-list" class="task-list">
<!-- Consequence tasks will be populated here -->
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-btn" class="btn btn-secondary">Back to Start</button>
<button id="reset-tasks-btn" class="btn btn-warning">Reset to Defaults</button>
</div>
</div>
<!-- Image Management Screen -->
<div id="image-management-screen" class="screen">
<h2>🖼️ Image Library Management</h2>
<p>Upload and organize image content to enhance your gaming experience</p>
<!-- Upload Section -->
<div class="upload-section">
<h3><EFBFBD> Import Image Files</h3>
<div class="upload-controls">
<button id="import-task-images-btn" class="btn btn-primary">🎯 Task Images</button>
<button id="import-consequence-images-btn" class="btn btn-warning">⚠️ Consequence Images</button>
<input type="file" id="image-upload-input" accept="image/*" multiple style="display: none;">
</div>
<div class="upload-info desktop-feature">
<span>💻 Desktop: Native file dialogs • Supports JPEG, PNG, GIF, WebP formats</span>
</div>
<div class="upload-info web-feature" style="display: none;">
<span>🌐 Web: Limited browser upload functionality</span>
</div>
<div class="directory-controls">
<button id="storage-info-btn" class="btn btn-outline">📊 Storage Info</button>
<button id="cleanup-invalid-images-btn" class="btn btn-warning">🧹 Cleanup</button>
<button id="clear-all-images-btn" class="btn btn-danger">🗑️ Clear All</button>
<span class="scan-info">📡 Auto-scan on startup</span>
</div>
</div>
<!-- Image Gallery Section -->
<div class="gallery-section">
<div class="gallery-header">
<h3>🖼️ Current Image Library</h3>
<div class="gallery-controls">
<button id="select-all-images-btn" class="btn btn-small btn-outline">Select All</button>
<button id="deselect-all-images-btn" class="btn btn-small btn-outline">Deselect All</button>
<button id="delete-selected-btn" class="btn btn-danger btn-small">Delete Selected</button>
<button id="refresh-images-btn" class="btn btn-secondary btn-small">🔄 Refresh</button>
<span class="image-count">Loading images...</span>
</div>
</div>
<!-- Image Tabs -->
<div class="image-tabs">
<button id="task-images-tab" class="tab-btn active">🎯 Tasks</button>
<button id="consequence-images-tab" class="tab-btn">⚠️ Consequences</button>
</div>
<div class="image-galleries-container">
<div id="task-images-gallery" class="image-gallery active">
<!-- Task images will be populated here -->
</div>
<div id="consequence-images-gallery" class="image-gallery">
<!-- Consequence images will be populated here -->
</div>
</div>
</div>
<!-- Image Preview Section -->
<div class="image-preview-section" id="image-preview-section" style="display: none;">
<h4>🖼️ Image Preview</h4>
<div class="preview-controls">
<div class="image-preview-container">
<img id="image-preview-img" src="" alt="Image Preview" style="max-width: 100%; max-height: 600px; border-radius: 8px;">
</div>
<div class="preview-info">
<span id="preview-image-name">No image selected</span>
<div class="preview-buttons">
<button id="close-image-preview-btn" class="btn btn-small btn-outline">✕ Close</button>
</div>
</div>
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-from-images-btn" class="btn btn-secondary">← Back to Start</button>
</div>
</div>
<!-- Audio Management Screen -->
<div id="audio-management-screen" class="screen">
<h2>🎵 Audio Library Management</h2>
<p>Upload and organize audio content to enhance your gaming experience</p>
<!-- Upload Section -->
<div class="upload-section">
<h3>🎵 Import Audio Files</h3>
<div class="upload-controls">
<button id="import-background-audio-btn" class="btn btn-primary">🎶 Background Music</button>
<button id="import-ambient-audio-btn" class="btn btn-success">🌿 Ambient Sounds</button>
<input type="file" id="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, AAC, FLAC formats</span>
</div>
<div class="upload-info web-feature" style="display: none;">
<span>🌐 Web: Limited browser upload functionality</span>
</div>
<div class="directory-controls">
<button id="scan-audio-directories-btn" class="btn btn-primary">🔍 Scan Directories</button>
<button id="audio-storage-info-btn" class="btn btn-outline">📊 Storage Info</button>
<button id="cleanup-invalid-audio-btn" class="btn btn-warning">🧹 Cleanup</button>
<button id="clear-all-audio-btn" class="btn btn-danger">🗑️ Clear All</button>
<span class="scan-info">📡 Auto-scan on startup</span>
</div>
</div>
<!-- Audio Gallery Section -->
<div class="gallery-section">
<div class="gallery-header">
<h3>🎧 Current Audio Library</h3>
<div class="gallery-controls">
<button id="select-all-audio-btn" class="btn btn-small btn-outline">Select All</button>
<button id="deselect-all-audio-btn" class="btn btn-small btn-outline">Deselect All</button>
<button id="delete-selected-audio-btn" class="btn btn-danger btn-small">Delete Selected</button>
<button id="preview-selected-audio-btn" class="btn btn-info btn-small">🎧 Preview</button>
<span class="audio-count">Loading audio files...</span>
</div>
</div>
<!-- Audio Tabs -->
<div class="audio-tabs">
<button id="background-audio-tab" class="tab-btn active">🎶 Background</button>
<button id="ambient-audio-tab" class="tab-btn">🌿 Ambient</button>
</div>
<div class="audio-galleries-container">
<div id="background-audio-gallery" class="audio-gallery active">
<!-- Background audio files will be populated here -->
</div>
<div id="ambient-audio-gallery" class="audio-gallery">
<!-- Ambient audio files will be populated here -->
</div>
</div>
</div>
<!-- Audio Preview Section -->
<div class="audio-preview-section" id="audio-preview-section" style="display: none;">
<h4>🎧 Audio Preview</h4>
<div class="preview-controls">
<div class="audio-preview-container">
<audio id="audio-preview-player" controls style="width: 100%; max-width: 600px;">
Your browser does not support the audio element.
</audio>
</div>
<div class="preview-info">
<span id="preview-file-name">No file selected</span>
<div class="preview-buttons">
<button id="close-preview-btn" class="btn btn-small btn-outline">✕ Close</button>
</div>
</div>
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-from-audio-btn" class="btn btn-secondary">← Back to Start</button>
</div>
</div>
<!-- Video Management Screen -->
<div id="video-management-screen" class="screen">
<h2>🎬 Video Library Management</h2>
<p>Upload and organize video content to enhance your gaming experience</p>
<!-- Upload Section -->
<div class="upload-section">
<h3>🎥 Import Video Files</h3>
<div class="upload-controls">
<button id="import-background-video-btn" class="btn btn-primary">🌄 Background Videos</button>
<button id="import-task-video-btn" class="btn btn-success">🎯 Task Videos</button>
<button id="import-reward-video-btn" class="btn btn-info">🏆 Reward Videos</button>
<button id="import-punishment-video-btn" class="btn btn-warning">⚠️ Punishment Videos</button>
<input type="file" id="video-upload-input" accept="video/*" multiple style="display: none;">
</div>
<div class="upload-info desktop-feature">
<span>💻 Desktop: Native file dialogs • Supports MP4, WebM, OGV, MOV formats</span>
</div>
<div class="upload-info web-feature" style="display: none;">
<span>🌐 Web: Limited browser upload functionality</span>
</div>
<div class="directory-controls">
<button id="video-storage-info-btn" class="btn btn-outline">📊 Storage Info</button>
<button id="cleanup-invalid-videos-btn" class="btn btn-warning">🧹 Cleanup</button>
<button id="clear-all-videos-btn" class="btn btn-danger">🗑️ Clear All</button>
<span class="scan-info">📡 Auto-scan on startup</span>
</div>
</div>
<!-- Video Gallery Section -->
<div class="gallery-section">
<div class="gallery-header">
<h3>📹 Current Video Library</h3>
<div class="gallery-controls">
<button id="select-all-video-btn" class="btn btn-small btn-outline">Select All</button>
<button id="deselect-all-video-btn" class="btn btn-small btn-outline">Deselect All</button>
<button id="delete-selected-video-btn" class="btn btn-danger btn-small">Delete Selected</button>
<button id="refresh-videos-btn" class="btn btn-secondary btn-small">🔄 Refresh</button>
<span class="video-count">Loading video files...</span>
</div>
</div>
<!-- Video Tabs -->
<div class="video-tabs">
<button id="background-video-tab" class="tab-btn active">🌄 Background</button>
<button id="task-video-tab" class="tab-btn">🎯 Tasks</button>
<button id="reward-video-tab" class="tab-btn">🏆 Rewards</button>
<button id="punishment-video-tab" class="tab-btn">⚠️ Punishments</button>
</div>
<div class="video-galleries-container">
<div id="background-video-gallery" class="video-gallery active">
<!-- Background video files will be populated here -->
</div>
<div id="task-video-gallery" class="video-gallery">
<!-- Task video files will be populated here -->
</div>
<div id="reward-video-gallery" class="video-gallery">
<!-- Reward video files will be populated here -->
</div>
<div id="punishment-video-gallery" class="video-gallery">
<!-- Punishment video files will be populated here -->
</div>
</div>
</div>
<!-- Video Preview Section -->
<div class="video-preview-section" id="video-preview-section" style="display: none;">
<h4>🎬 Video Preview</h4>
<div class="preview-controls">
<div class="video-preview-container">
<video id="video-preview-player" controls style="width: 100%; max-width: 800px; max-height: 450px; border-radius: 8px;">
Your browser does not support the video element.
</video>
</div>
<div class="preview-info">
<span id="preview-video-name">No video selected</span>
<div class="preview-buttons">
<button id="toggle-video-loop-btn" class="btn btn-small btn-outline">🔁 Loop</button>
<button id="close-video-preview-btn" class="btn btn-small btn-outline">✕ Close</button>
</div>
</div>
</div>
</div>
<!-- Video Settings Section -->
<div class="video-settings-section">
<h4>⚙️ Video Player Settings</h4>
<div class="settings-grid">
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="enable-video-player" checked>
<span class="switch"></span>
Enable Video Player
</label>
</div>
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="enable-background-videos" checked>
<span class="switch"></span>
Background Videos
</label>
</div>
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="enable-task-videos" checked>
<span class="switch"></span>
Task Videos
</label>
</div>
<div class="setting-group volume-setting">
<label for="video-volume">Video Volume:</label>
<div class="volume-control">
<input type="range" id="video-volume" min="0" max="100" value="30" class="volume-slider">
<span id="video-volume-display">30%</span>
</div>
</div>
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="video-autoplay" checked>
<span class="switch"></span>
Autoplay Videos
</label>
</div>
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="video-show-controls">
<span class="switch"></span>
Show Video Controls
</label>
</div>
<div class="setting-group">
<label class="switch-label">
<input type="checkbox" id="video-fade-transitions" checked>
<span class="switch"></span>
Fade Transitions
</label>
</div>
</div>
<div class="test-buttons">
<button id="test-background-video" class="btn btn-info">Test Background Video</button>
<button id="test-overlay-video" class="btn btn-primary">Test Overlay Video</button>
<button id="stop-all-videos" class="btn btn-danger">Stop All Videos</button>
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-from-video-btn" class="btn btn-secondary">Back to Start</button>
</div>
</div>
<!-- Photo Gallery Screen -->
<div id="photo-gallery-screen" class="screen">
<h2>📸 Photo Gallery Management</h2>
<p>View and manage your captured photos from photography sessions</p>
<!-- Upload Section -->
<div class="upload-section">
<h3>📸 Photo Management</h3>
<div class="upload-controls">
<button id="download-all-photos-btn" class="btn btn-primary">📥 Download All Photos</button>
<button id="download-selected-photos-btn" class="btn btn-info" disabled>📥 Download Selected</button>
<button id="clear-all-photos-btn" class="btn btn-danger">🗑️ Clear All Photos</button>
<button id="photo-storage-settings-btn" class="btn btn-secondary">⚙️ Storage Settings</button>
</div>
<div class="upload-info desktop-feature">
<span>📷 Desktop: Photos automatically saved from sessions • JPEG, PNG formats</span>
</div>
<div class="directory-controls">
<div class="photo-stats">
<span id="photo-count-display">Loading photos...</span>
<span id="storage-size-display"></span>
</div>
<span class="scan-info">📷 Photos saved from gameplay sessions</span>
</div>
</div>
<!-- Photo Gallery Section -->
<div class="gallery-section">
<div class="gallery-header">
<h3>📷 Photo Collection</h3>
<div class="gallery-controls">
<button id="select-all-photos-btn" class="btn btn-small btn-outline">Select All</button>
<button id="deselect-all-photos-btn" class="btn btn-small btn-outline">Deselect All</button>
<div class="filter-controls">
<label>Filter by Session:
<select id="photo-session-filter" class="filter-select">
<option value="all">All Photos</option>
<option value="dress-up-photo">Dress-up Photos</option>
<option value="photography-studio">Photography Studio</option>
<option value="custom">Custom Sessions</option>
</select>
</label>
<label>Sort by:
<select id="photo-sort-order" class="filter-select">
<option value="newest">Newest First</option>
<option value="oldest">Oldest First</option>
<option value="session">By Session Type</option>
</select>
</label>
</div>
</div>
</div>
<!-- Photo Tabs -->
<div class="photo-tabs">
<button id="all-photos-tab" class="tab-btn active">📸 All Photos</button>
<button id="dress-up-photos-tab" class="tab-btn">👗 Dress-up</button>
<button id="studio-photos-tab" class="tab-btn">🏠 Studio</button>
<button id="custom-photos-tab" class="tab-btn">🎨 Custom</button>
</div>
<div class="photo-galleries-container">
<div id="all-photos-gallery" class="photo-gallery active">
<div id="photo-grid" class="photo-grid">
<!-- All photos will be populated here -->
</div>
</div>
<div id="dress-up-photos-gallery" class="photo-gallery">
<div class="photo-grid">
<!-- Dress-up photos will be populated here -->
</div>
</div>
<div id="studio-photos-gallery" class="photo-gallery">
<div class="photo-grid">
<!-- Studio photos will be populated here -->
</div>
</div>
<div id="custom-photos-gallery" class="photo-gallery">
<div class="photo-grid">
<!-- Custom photos will be populated here -->
</div>
</div>
<div id="no-photos-message" class="no-photos-message" style="display: none;">
<h4>📷 No Photos Yet</h4>
<p>Take some photos during photography sessions and they'll appear here!</p>
<p>You can enable photo saving in the settings when you take your first photo.</p>
</div>
</div>
</div>
<!-- Photo Preview Section -->
<div class="photo-preview-section" id="photo-preview-section" style="display: none;">
<h4>📸 Photo Preview</h4>
<div class="preview-controls">
<div class="photo-preview-container">
<img id="photo-preview-image" src="" alt="Photo Preview" style="max-width: 100%; max-height: 600px; border-radius: 8px;">
</div>
<div class="preview-info">
<span id="preview-photo-name">No photo selected</span>
<div class="preview-buttons">
<button id="download-photo-btn" class="btn btn-small btn-primary">💾 Download</button>
<button id="close-photo-preview-btn" class="btn btn-small btn-outline">✕ Close</button>
</div>
</div>
</div>
</div>
<!-- Photo Detail Modal -->
<div id="photo-detail-modal" class="modal photo-modal" style="display: none;">
<div class="modal-content photo-detail-content">
<div class="modal-header">
<h3 id="photo-detail-title">Photo Details</h3>
<span class="close" id="close-photo-detail">&times;</span>
</div>
<div class="modal-body photo-detail-body">
<div class="photo-display">
<img id="photo-detail-image" src="" alt="Photo" />
</div>
<div class="photo-info">
<div class="info-row">
<label>Captured:</label>
<span id="photo-detail-date"></span>
</div>
<div class="info-row">
<label>Session:</label>
<span id="photo-detail-session"></span>
</div>
<div class="info-row">
<label>Task:</label>
<span id="photo-detail-task"></span>
</div>
<div class="info-row">
<label>Size:</label>
<span id="photo-detail-size"></span>
</div>
</div>
<div class="photo-actions">
<button id="download-photo-btn" class="btn btn-primary">📥 Download</button>
<button id="delete-photo-btn" class="btn btn-danger">🗑️ Delete</button>
</div>
</div>
</div>
</div>
<!-- Photo Storage Settings Modal -->
<div id="photo-settings-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>📸 Photo Storage Settings</h3>
<span class="close" id="close-photo-settings">&times;</span>
</div>
<div class="modal-body">
<div class="setting-group">
<h4>Storage Consent</h4>
<div class="consent-controls">
<label>
<input type="radio" name="photo-consent" value="true" id="consent-enable">
Enable photo storage (save photos for later viewing)
</label>
<label>
<input type="radio" name="photo-consent" value="false" id="consent-disable">
Disable photo storage (session only)
</label>
</div>
<p class="help-text">
Photos are stored locally on your device only. No photos are sent to any servers.
</p>
</div>
<div class="setting-group">
<h4>Storage Information</h4>
<div class="storage-info">
<div class="info-item">
<span class="info-label">Total Photos:</span>
<span id="settings-photo-count">0</span>
</div>
<div class="info-item">
<span class="info-label">Storage Used:</span>
<span id="settings-storage-size">0 KB</span>
</div>
<div class="info-item">
<span class="info-label">Oldest Photo:</span>
<span id="settings-oldest-photo">None</span>
</div>
</div>
</div>
<div class="modal-actions">
<button id="save-photo-settings-btn" class="btn btn-primary">Save Settings</button>
</div>
</div>
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-from-photos-btn" class="btn btn-secondary">← Back to Start</button>
</div>
</div>
<!-- Annoyance Management Screen -->
<div id="annoyance-management-screen" class="screen">
<h2>😈 Annoyance Management</h2>
<p>Configure flash messages and motivational features to enhance your experience!</p>
<!-- Tab Navigation -->
<div class="annoyance-tabs">
<button id="messages-tab" class="annoyance-tab active">💬 Messages</button>
<button id="appearance-tab" class="annoyance-tab">🎨 Appearance</button>
<button id="behavior-tab" class="annoyance-tab">⚡ Behavior</button>
<button id="popup-images-tab" class="annoyance-tab">🖼️ Popup Images</button>
<button id="ai-tasks-tab" class="annoyance-tab">🤖 AI Tasks</button>
<button id="import-export-tab" class="annoyance-tab">📁 Import/Export</button>
</div>
<!-- Messages Tab -->
<div id="messages-tab-content" class="annoyance-tab-content active">
<div class="annoyance-section">
<div class="section-header">
<h3>💬 Message Management</h3>
<div class="header-controls">
<label>
<input type="checkbox" id="flash-messages-enabled" checked>
Enable Flash Messages
</label>
<button id="add-new-message-btn" class="btn btn-success btn-small">+ Add Message</button>
</div>
</div>
<!-- Message Editor -->
<div id="message-editor" class="message-editor" style="display: none;">
<div class="editor-header">
<h4 id="editor-title">Add New Message</h4>
<button id="close-editor-btn" class="btn btn-outline btn-small">✕ Close</button>
</div>
<div class="editor-form">
<div class="form-group">
<label>Message Text:</label>
<textarea id="message-text" placeholder="Enter your motivational message..." maxlength="200" rows="3"></textarea>
<div class="char-counter">
<span id="char-count">0</span>/200 characters
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Category:</label>
<select id="message-category">
<option value="motivational">💪 Motivational</option>
<option value="encouraging">🌟 Encouraging</option>
<option value="achievement">🏆 Achievement</option>
<option value="persistence">🔥 Persistence</option>
<option value="custom">✨ Custom</option>
</select>
</div>
<div class="form-group">
<label>Priority:</label>
<select id="message-priority">
<option value="normal">Normal</option>
<option value="high">High</option>
<option value="low">Low</option>
</select>
</div>
</div>
<div class="editor-actions">
<button id="save-message-btn" class="btn btn-primary">Save Message</button>
<button id="preview-current-message-btn" class="btn btn-info">Preview</button>
<button id="cancel-edit-btn" class="btn btn-secondary">Cancel</button>
</div>
</div>
</div>
<!-- Message List -->
<div class="message-list-section">
<div class="list-header">
<div class="list-filters">
<label>Filter by Category:
<select id="category-filter">
<option value="all">All Categories</option>
<option value="motivational">💪 Motivational</option>
<option value="encouraging">🌟 Encouraging</option>
<option value="achievement">🏆 Achievement</option>
<option value="persistence">🔥 Persistence</option>
<option value="custom">✨ Custom</option>
</select>
</label>
<label>
<input type="checkbox" id="show-disabled-messages"> Show Disabled
</label>
</div>
<div class="list-stats">
<span id="message-stats">20 messages (18 enabled, 2 disabled)</span>
</div>
</div>
<div id="message-list" class="message-list">
<!-- Messages will be populated here -->
</div>
</div>
</div>
</div>
<!-- Appearance Tab -->
<div id="appearance-tab-content" class="annoyance-tab-content">
<div class="annoyance-section">
<h3>🎨 Visual Appearance</h3>
<div class="appearance-controls">
<div class="control-row">
<div class="control-group">
<label>Position:</label>
<select id="message-position">
<option value="center">Center</option>
<option value="top-center">Top Center</option>
<option value="bottom-center">Bottom Center</option>
<option value="top-left">Top Left</option>
<option value="top-right">Top Right</option>
<option value="bottom-left">Bottom Left</option>
<option value="bottom-right">Bottom Right</option>
<option value="center-left">Center Left</option>
<option value="center-right">Center Right</option>
</select>
</div>
<div class="control-group">
<label>Animation:</label>
<select id="animation-style">
<option value="fade">Fade</option>
<option value="slide">Slide</option>
<option value="bounce">Bounce</option>
<option value="pulse">Pulse</option>
</select>
</div>
</div>
<div class="control-row">
<div class="control-group">
<label>Font Size: <span id="font-size-display">24px</span></label>
<input type="range" id="font-size" min="16" max="48" value="24" step="2">
</div>
<div class="control-group">
<label>Opacity: <span id="opacity-display">90%</span></label>
<input type="range" id="message-opacity" min="50" max="100" value="90" step="5">
</div>
</div>
<div class="control-row">
<div class="control-group">
<label>Text Color:</label>
<input type="color" id="text-color" value="#ffffff">
</div>
<div class="control-group">
<label>Background Color:</label>
<input type="color" id="background-color" value="#007bff">
</div>
</div>
<div class="control-group">
<button id="reset-appearance-btn" class="btn btn-outline">Reset to Defaults</button>
<button id="preview-appearance-btn" class="btn btn-info">Preview Style</button>
</div>
</div>
</div>
</div>
<!-- Behavior Tab -->
<div id="behavior-tab-content" class="annoyance-tab-content">
<div class="annoyance-section">
<h3>⚡ Behavior Settings</h3>
<div class="behavior-controls">
<div class="control-group">
<label>🧘 Focus Interruption Chance: <span id="focus-interruption-display">0%</span></label>
<input type="range" id="focus-interruption-chance" min="0" max="50" value="0" step="5">
<small class="help-text">Chance for focus-hold interruptions during scenario adventures (0% = disabled, max 50%)</small>
</div>
<div class="control-group">
<label>Display Duration: <span id="duration-display">3.0s</span></label>
<input type="range" id="display-duration" min="1000" max="10000" value="3000" step="500">
</div>
<div class="control-group">
<label>Interval Between Messages: <span id="interval-display">45s</span></label>
<input type="range" id="interval-delay" min="10000" max="300000" value="45000" step="5000">
</div>
<div class="control-group">
<label>Random Variation: <span id="variation-display">±5s</span></label>
<input type="range" id="time-variation" min="0" max="30000" value="5000" step="1000">
<small class="help-text">Adds random time variation to prevent predictability</small>
</div>
<div class="control-row">
<div class="control-group">
<label>
<input type="checkbox" id="event-based-messages" checked>
Enable Event-Based Messages
</label>
<small class="help-text">Show special messages for task completion, streaks, etc.</small>
</div>
<div class="control-group">
<label>
<input type="checkbox" id="pause-on-hover">
Pause Timer on Message Hover
</label>
<small class="help-text">Pause message fade when hovering (useful for reading)</small>
</div>
</div>
<div class="control-group">
<button id="test-behavior-btn" class="btn btn-success">Test Current Settings</button>
</div>
</div>
</div>
</div>
<!-- Popup Images Tab -->
<div id="popup-images-tab-content" class="annoyance-tab-content">
<div class="annoyance-section">
<h3>🖼️ Punishment Popups</h3>
<p class="help-text">Configure consequence images that appear when tasks are skipped</p>
<!-- Enable/Disable -->
<div class="control-section">
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-images-enabled" />
<span class="switch"></span>
Enable Punishment Popups
</label>
</div>
</div>
<!-- Image Count Settings -->
<div class="control-section">
<h4>📊 Number of Images</h4>
<div class="control-group">
<label for="popup-count-mode">Count Mode:</label>
<select id="popup-count-mode">
<option value="fixed">Fixed Amount</option>
<option value="random">Random (1-10)</option>
<option value="range">Custom Range</option>
</select>
</div>
<div id="popup-fixed-count" class="control-group">
<label for="popup-image-count">Number of Images:</label>
<input type="range" id="popup-image-count" min="1" max="40" value="3" />
<span id="popup-image-count-value">3</span>
</div>
<div id="popup-range-count" class="control-group" style="display: none;">
<div class="range-inputs">
<div>
<label for="popup-min-count">Minimum:</label>
<input type="number" id="popup-min-count" min="1" max="20" value="2" />
</div>
<div>
<label for="popup-max-count">Maximum:</label>
<input type="number" id="popup-max-count" min="2" max="40" value="5" />
</div>
</div>
</div>
</div>
<!-- Display Duration Settings -->
<div class="control-section">
<h4>⏱️ Display Duration</h4>
<div class="control-group">
<label for="popup-duration-mode">Duration Mode:</label>
<select id="popup-duration-mode">
<option value="fixed">Fixed Duration</option>
<option value="random">Random (5-15s)</option>
<option value="range">Custom Range</option>
</select>
</div>
<div id="popup-fixed-duration" class="control-group">
<label for="popup-display-duration">Duration (seconds):</label>
<input type="range" id="popup-display-duration" min="3" max="30" value="8" />
<span id="popup-display-duration-value">8s</span>
</div>
<div id="popup-range-duration" class="control-group" style="display: none;">
<div class="range-inputs">
<div>
<label for="popup-min-duration">Min (seconds):</label>
<input type="number" id="popup-min-duration" min="2" max="20" value="5" />
</div>
<div>
<label for="popup-max-duration">Max (seconds):</label>
<input type="number" id="popup-max-duration" min="5" max="60" value="15" />
</div>
</div>
</div>
</div>
<!-- Positioning & Appearance -->
<div class="control-section">
<h4>🎯 Positioning</h4>
<div class="control-group">
<label for="popup-positioning">Layout Style:</label>
<select id="popup-positioning">
<option value="random">Random Positions</option>
<option value="cascade">Cascading</option>
<option value="grid">Grid Layout</option>
<option value="center">Centered (stacked)</option>
</select>
</div>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-allow-overlap" />
<span class="switch"></span>
Allow Overlapping
</label>
</div>
</div>
<!-- Size Settings -->
<div class="control-section">
<h4>📏 Size Settings</h4>
<p class="help-text">Popups automatically size to match image proportions within these limits</p>
<div class="control-group">
<label for="popup-viewport-width">Max Viewport Width:</label>
<input type="range" id="popup-viewport-width" min="20" max="60" value="35" />
<span id="popup-viewport-width-value">35%</span>
</div>
<div class="control-group">
<label for="popup-viewport-height">Max Viewport Height:</label>
<input type="range" id="popup-viewport-height" min="20" max="60" value="40" />
<span id="popup-viewport-height-value">40%</span>
</div>
<div class="control-group">
<div class="range-inputs">
<div>
<label for="popup-min-width">Min Width (px):</label>
<input type="number" id="popup-min-width" min="150" max="400" value="200" />
</div>
<div>
<label for="popup-max-width">Max Width (px):</label>
<input type="number" id="popup-max-width" min="300" max="800" value="500" />
</div>
</div>
</div>
<div class="control-group">
<div class="range-inputs">
<div>
<label for="popup-min-height">Min Height (px):</label>
<input type="number" id="popup-min-height" min="100" max="300" value="150" />
</div>
<div>
<label for="popup-max-height">Max Height (px):</label>
<input type="number" id="popup-max-height" min="200" max="600" value="400" />
</div>
</div>
</div>
</div>
<!-- Visual Effects -->
<div class="control-section">
<h4>✨ Visual Effects</h4>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-fade-animation" />
<span class="switch"></span>
Fade In/Out Animation
</label>
</div>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-blur-background" />
<span class="switch"></span>
Blur Background
</label>
</div>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-show-timer" />
<span class="switch"></span>
Show Countdown Timer
</label>
</div>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="popup-prevent-close" />
<span class="switch"></span>
Prevent Manual Close
</label>
</div>
</div>
<!-- Test & Preview -->
<div class="control-section">
<h4>🧪 Testing</h4>
<div class="test-buttons">
<button id="test-popup-single" class="btn btn-info">Test 1 Popup</button>
<button id="test-popup-multiple" class="btn btn-primary">Test Multiple</button>
<button id="clear-all-popups" class="btn btn-danger">Clear All</button>
</div>
<p class="help-text">Test your popup settings to see how they look</p>
<div id="popup-warning" class="warning-text" style="display: none;">
⚠️ High popup counts (>20) may impact performance and visibility
</div>
</div>
<!-- Status & Info -->
<div class="control-section">
<div class="info-display">
<div class="info-item">
<span class="info-label">Available Images:</span>
<span id="available-images-count">0</span>
</div>
<div class="info-item">
<span class="info-label">Active Popups:</span>
<span id="active-popups-count">0</span>
</div>
</div>
</div>
</div>
</div>
<!-- AI Tasks Tab -->
<div id="ai-tasks-tab-content" class="annoyance-tab-content">
<div class="annoyance-section">
<h3>🤖 AI Task Generation</h3>
<p class="help-text">Let AI create personalized edging tasks using your local Ollama installation</p>
<!-- Connection Status -->
<div class="control-section">
<h4>📡 Connection Status</h4>
<div class="ai-status-display">
<div class="status-item">
<span class="status-label">Ollama Service:</span>
<span id="ollama-status" class="status-value">Checking...</span>
</div>
<div class="status-item">
<span class="status-label">Available Models:</span>
<span id="models-count" class="status-value">0</span>
</div>
<div class="status-item">
<span class="status-label">Current Model:</span>
<span id="current-model" class="status-value">None</span>
</div>
</div>
<button id="test-ai-connection" class="btn btn-info">Test Connection</button>
</div>
<!-- AI Configuration -->
<div class="control-section">
<h4>⚙️ AI Configuration</h4>
<div class="control-group">
<label class="switch-label">
<input type="checkbox" id="ai-tasks-enabled" />
<span class="switch"></span>
Enable AI Task Generation
</label>
</div>
<div class="control-group">
<label for="ai-model-select">Model Selection:</label>
<select id="ai-model-select">
<option value="">Select a model...</option>
</select>
</div>
<div class="control-group">
<label for="ai-temperature">Creativity (Temperature):</label>
<input type="range" id="ai-temperature" min="0.1" max="2.0" step="0.1" value="0.8" />
<span id="ai-temperature-value">0.8</span>
</div>
<div class="control-group">
<label for="ai-max-tokens">Max Response Length:</label>
<input type="range" id="ai-max-tokens" min="100" max="500" step="50" value="300" />
<span id="ai-max-tokens-value">300</span>
</div>
</div>
<!-- User Preferences -->
<div class="control-section">
<h4>👤 Your Preferences</h4>
<div class="control-group">
<label for="ai-experience">Experience Level:</label>
<select id="ai-experience">
<option value="beginner">Beginner</option>
<option value="intermediate" selected>Intermediate</option>
<option value="advanced">Advanced</option>
<option value="expert">Expert</option>
</select>
</div>
<div class="control-group">
<label for="ai-intensity">Default Intensity:</label>
<select id="ai-intensity">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
<option value="extreme">Extreme</option>
</select>
</div>
<div class="control-group">
<label for="ai-duration">Preferred Duration (minutes):</label>
<input type="range" id="ai-duration" min="3" max="30" value="5" />
<span id="ai-duration-value">5</span>
</div>
<div class="control-group">
<label for="ai-style">Instruction Style:</label>
<select id="ai-style">
<option value="instructional" selected>Instructional</option>
<option value="descriptive">Descriptive</option>
<option value="commanding">Commanding</option>
<option value="encouraging">Encouraging</option>
</select>
</div>
</div>
<!-- Testing & Preview -->
<div class="control-section">
<h4>🧪 Testing</h4>
<div class="ai-test-buttons">
<button id="generate-test-task" class="btn btn-primary">Generate Test Task</button>
<button id="generate-consequence-task" class="btn btn-danger">Generate Consequence</button>
<button id="clear-test-output" class="btn btn-secondary">Clear Output</button>
</div>
<div class="control-group">
<label for="test-task-output">Generated Task Preview:</label>
<div id="test-task-output" class="task-preview">
Click "Generate Test Task" to see AI-generated content...
</div>
</div>
</div>
<!-- Installation Help -->
<div class="control-section">
<h4>📚 Setup Help</h4>
<div class="help-content">
<p><strong>Need to install Ollama?</strong></p>
<ol>
<li>Download from <a href="https://ollama.ai" target="_blank">ollama.ai</a></li>
<li>Install recommended NSFW models:</li>
<ul>
<li><code>ollama pull dolphin-mistral:7b</code></li>
<li><code>ollama pull wizardlm-uncensored:7b</code></li>
</ul>
<li>Ensure Ollama service is running</li>
<li>Click "Test Connection" above</li>
</ol>
<p class="help-text">AI tasks are generated locally for complete privacy!</p>
</div>
</div>
</div>
</div>
<!-- Import/Export Tab -->
<div id="import-export-tab-content" class="annoyance-tab-content">
<div class="annoyance-section">
<h3><EFBFBD> Import & Export</h3>
<div class="import-export-controls">
<div class="control-section">
<h4>💾 Export Messages</h4>
<div class="export-options">
<button id="export-all-messages-btn" class="btn btn-primary">Export All Messages</button>
<button id="export-enabled-messages-btn" class="btn btn-secondary">Export Enabled Only</button>
<button id="export-custom-messages-btn" class="btn btn-info">Export Custom Only</button>
</div>
<p class="help-text">Export your messages as a JSON file for backup or sharing</p>
</div>
<div class="control-section">
<h4><EFBFBD> Import Messages</h4>
<div class="import-options">
<button id="import-messages-btn" class="btn btn-success">Import Messages</button>
<input type="file" id="import-messages-file" accept=".json" style="display: none;">
<div class="import-mode">
<label>Import Mode:</label>
<div class="radio-group">
<label><input type="radio" name="importMode" value="merge" checked> Merge with existing</label>
<label><input type="radio" name="importMode" value="replace"> Replace all messages</label>
</div>
</div>
</div>
<p class="help-text">Import messages from a JSON file</p>
</div>
<div class="control-section">
<h4>🔄 Reset Options</h4>
<div class="reset-options">
<button id="reset-to-defaults-btn" class="btn btn-warning">Reset to Default Messages</button>
<button id="clear-all-messages-btn" class="btn btn-danger">Clear All Messages</button>
</div>
<p class="help-text danger">⚠️ Reset operations cannot be undone!</p>
</div>
</div>
</div>
</div>
<div class="management-buttons">
<button id="back-to-start-from-annoyance-btn" class="btn btn-secondary">Back to Start</button>
<button id="save-annoyance-settings" class="btn btn-primary">Save All Settings</button>
</div>
</div>
<!-- Game Screen -->
<div id="game-screen" class="screen">
<div class="task-container">
<div class="task-image-container">
<img id="task-image" src="" alt="Task Image" class="task-image">
</div>
<div class="task-text-container">
<h3 id="task-text" class="task-text">Loading task...</h3>
</div>
<div class="action-buttons">
<button id="complete-btn" class="btn btn-success">Complete</button>
<button id="skip-btn" class="btn btn-warning">Skip</button>
<button id="mercy-skip-btn" class="btn btn-danger" style="display: none;">
<span id="mercy-skip-text">Mercy Skip</span>
<span id="mercy-skip-cost" class="mercy-cost"></span>
</button>
<button id="pause-btn" class="btn btn-info">Pause</button>
</div>
</div>
<div class="game-stats">
<div class="stat">
<span class="stat-label">Session XP:</span>
<span id="xp" class="stat-value">0</span>
</div>
<div class="stat">
<span class="stat-label">Total XP:</span>
<span id="overall-xp" class="stat-value">0</span>
</div>
<div class="stat">
<span class="stat-label">Completed:</span>
<span id="completed-count" class="stat-value">0</span>
</div>
<div class="stat streak-stat">
<span class="stat-label">Streak:</span>
<span id="current-streak" class="stat-value">0</span>🔥
</div>
<div class="stat">
<span class="stat-label">Skipped:</span>
<span id="skipped-count" class="stat-value">0</span>
</div>
<div class="stat">
<span class="stat-label">Consequences:</span>
<span id="consequence-count" class="stat-value">0</span>
</div>
</div>
</div>
<!-- Paused Screen -->
<div id="paused-screen" class="screen">
<h2>Game Paused</h2>
<p>Take a break! Click resume when ready.</p>
<button id="resume-btn" class="btn btn-primary">Resume</button>
<button id="quit-btn" class="btn btn-secondary">Quit Game</button>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" class="screen">
<h2>Game Complete!</h2>
<p>Congratulations! You've completed all available tasks!</p>
<div id="final-stats" class="final-stats">
<p><strong>Game Mode:</strong> <span id="final-game-mode"></span></p>
<p>Final XP: <span id="final-xp"></span> XP</p>
<p>Final Time: <span id="final-time"></span></p>
<p>Tasks Completed: <span id="final-completed"></span></p>
<p>Tasks Skipped: <span id="final-skipped"></span></p>
<p>Consequence Tasks: <span id="final-consequences"></span></p>
<p>Best Streak: <span id="final-best-streak"></span> 🔥</p>
<p>Streak Bonus Points: <span id="final-streak-bonuses"></span></p>
</div>
<button id="play-again-btn" class="btn btn-primary">Play Again</button>
</div>
</main>
</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>
<!-- Legacy Data (will be phased out) -->
<script src="src/data/gameData.js"></script>
<!-- Statistics Modal -->
<div id="stats-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h2>📊 Game Statistics</h2>
<span class="close" id="close-stats">&times;</span>
</div>
<div class="modal-body">
<div class="stats-grid">
<div class="stat-card">
<h3>Games Played</h3>
<div class="stat-value" id="stat-games">0</div>
</div>
<div class="stat-card">
<h3>Tasks Completed</h3>
<div class="stat-value" id="stat-completed">0</div>
</div>
<div class="stat-card">
<h3>Best Score</h3>
<div class="stat-value" id="stat-score">0</div>
</div>
<div class="stat-card">
<h3>Current Streak</h3>
<div class="stat-value" id="stat-streak">0</div>
</div>
<div class="stat-card">
<h3>Completion Rate</h3>
<div class="stat-value" id="stat-rate">0%</div>
</div>
<div class="stat-card">
<h3>Hours Played</h3>
<div class="stat-value" id="stat-hours">0.0</div>
</div>
</div>
<div class="stats-actions">
<button id="reset-stats-btn" class="btn btn-warning">Reset Statistics</button>
<button id="export-stats-btn" class="btn btn-secondary">
<span class="btn-text">Export Stats Only</span>
<span class="btn-loading" style="display: none;">⏳ Exporting...</span>
</button>
</div>
</div>
</div>
</div>
<!-- Help Menu Modal -->
<div id="help-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h2>⌨️ Keyboard Shortcuts & Help</h2>
<span class="close" id="close-help">&times;</span>
</div>
<div class="modal-body">
<div class="help-section">
<h3>🎮 Game Controls</h3>
<div class="shortcut-list">
<div class="shortcut-item">
<span class="shortcut-key">Enter</span>
<span class="shortcut-action">Complete Current Task</span>
</div>
<div class="shortcut-item">
<span class="shortcut-key">Ctrl</span>
<span class="shortcut-action">Skip Task (get consequence task)</span>
</div>
<div class="shortcut-item">
<span class="shortcut-key">Shift+Ctrl</span>
<span class="shortcut-action">Mercy Skip (spend points to avoid consequence)</span>
</div>
<div class="shortcut-item">
<span class="shortcut-key">Space</span>
<span class="shortcut-action">Pause/Resume Game</span>
</div>
<div class="shortcut-item">
<span class="shortcut-key">P</span>
<span class="shortcut-action">Pause/Resume Game</span>
</div>
</div>
</div>
<div class="help-section">
<h3>🎵 Music Controls</h3>
<div class="shortcut-list">
<div class="shortcut-item">
<span class="shortcut-key">M</span>
<span class="shortcut-action">Toggle Music On/Off</span>
</div>
</div>
</div>
<div class="help-section">
<h3>🧭 Navigation</h3>
<div class="shortcut-list">
<div class="shortcut-item">
<span class="shortcut-key">Escape</span>
<span class="shortcut-action">Close Modals / Back / Pause Game</span>
</div>
<div class="shortcut-item">
<span class="shortcut-key">H</span>
<span class="shortcut-action">Show/Hide This Help Menu</span>
</div>
</div>
</div>
<div class="help-section">
<h3>💡 Tips</h3>
<ul class="help-tips">
<li>Use Enter and Ctrl for lightning-fast task progression</li>
<li>Ctrl gives you a consequence task - complete it to save points!</li>
<li>Shift+Ctrl spends points for mercy skip - use strategically</li>
<li>Choose your skipping strategy: face the consequence or pay points</li>
<li>Shortcuts are disabled when typing in input fields</li>
<li>The game auto-saves when paused and offers to resume</li>
<li>Export your progress regularly to avoid data loss</li>
</ul>
</div>
</div>
</div>
</div>
</body>
<!-- Script Loading Order -->
<script src="src/features/ui/flashMessageManager.js"></script>
<script src="src/features/images/popupImageManager.js"></script>
<script src="src/features/tasks/aiTaskManager.js"></script>
<script src="src/features/audio/audioManager.js"></script>
<script src="src/features/images/image-discovery-fix.js"></script>
<script src="src/core/gameModeManager.js"></script>
<script src="src/features/webcam/webcamManager.js"></script>
<script src="src/features/tts/voiceManager.js"></script>
<script src="src/features/tasks/interactiveTaskManager.js"></script>
<script src="src/features/video/videoPlayerManager.js"></script>
<script src="src/utils/desktop-file-manager.js"></script>
<script src="src/core/game.js"></script>
<script>
// Force apply emergency fix
window.addEventListener('load', () => {
console.log('🚨 Page loaded - applying emergency fixes...');
setTimeout(() => {
if (window.forceFixGame) {
window.forceFixGame();
}
}, 2000);
});
// Video management UI handlers
let videoPlayerManager = null;
// Initialize video player when available
function initializeVideoPlayer() {
if (window.VideoPlayerManager && !videoPlayerManager) {
videoPlayerManager = new VideoPlayerManager();
window.videoPlayerManager = videoPlayerManager; // Expose globally
console.log('✅ Video player initialized');
}
}
// Video management screen handlers
let videoHandlersAttached = false;
function setupVideoManagementHandlers() {
// Prevent multiple attachments
if (videoHandlersAttached) {
console.log('Video handlers already attached, skipping...');
return;
}
console.log('Setting up video management handlers...');
videoHandlersAttached = true;
// Back button
const backBtn = document.getElementById('back-to-start-from-video-btn');
if (backBtn) {
backBtn.addEventListener('click', () => {
if (window.game && typeof window.game.showScreen === 'function') {
window.game.showScreen('start-screen');
}
});
}
function showVideoStorageInfo() {
const categories = ['background', 'task', 'reward', 'punishment'];
let totalVideos = 0;
let totalSize = 0;
let invalidCount = 0;
const details = categories.map(category => {
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const validVideos = videos.filter(v => !v.url.startsWith('blob:'));
const invalid = videos.length - validVideos.length;
const categorySize = validVideos.reduce((sum, v) => sum + (v.size || 0), 0);
totalVideos += validVideos.length;
totalSize += categorySize;
invalidCount += invalid;
return `${category}: ${validVideos.length} videos (${formatFileSize(categorySize)})${invalid > 0 ? ` + ${invalid} invalid` : ''}`;
}).join('\n');
// Calculate current localStorage usage
const currentStorageSize = new Blob([JSON.stringify(localStorage)]).size;
const storageLimit = 5 * 1024 * 1024; // 5MB conservative estimate
const usagePercent = Math.round((currentStorageSize / storageLimit) * 100);
const storageWarning = currentStorageSize > storageLimit * 0.8 ?
'\n⚠ Warning: Storage nearly full!' : '';
alert(`Video Storage Info:\n\n${details}\n\nTotal: ${totalVideos} videos (${formatFileSize(totalSize)})${invalidCount > 0 ? `\nInvalid entries: ${invalidCount}` : ''}\n\nLocalStorage Usage: ${formatFileSize(currentStorageSize)} / ~${formatFileSize(storageLimit)} (${usagePercent}%)${storageWarning}\n\nTip: Use videos under 10MB for best performance.`);
}
function manualCleanupInvalidVideos() {
const categories = ['background', 'task', 'reward', 'punishment'];
let totalCleaned = 0;
categories.forEach(category => {
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const validVideos = videos.filter(video => !video.url.startsWith('blob:'));
const cleaned = videos.length - validVideos.length;
if (cleaned > 0) {
localStorage.setItem(`${category}-videos`, JSON.stringify(validVideos));
totalCleaned += cleaned;
// Refresh the gallery if this category is currently active
loadVideoGalleryContent(category);
}
});
if (totalCleaned > 0) {
if (window.game && window.game.showNotification) {
window.game.showNotification(`🧹 Cleaned up ${totalCleaned} invalid video entries`, 'success');
}
// Refresh current gallery
const activeTab = document.querySelector('.video-tabs .tab-btn.active');
if (activeTab) {
const category = activeTab.id.replace('-video-tab', '');
loadVideoGalleryContent(category);
}
} else {
if (window.game && window.game.showNotification) {
window.game.showNotification('No invalid videos found to clean up', 'info');
}
}
}
function clearAllVideos() {
const categories = ['background', 'task', 'reward', 'punishment'];
let totalCleared = 0;
categories.forEach(category => {
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
totalCleared += videos.length;
localStorage.removeItem(`${category}-videos`);
// Refresh the gallery if this category is currently active
loadVideoGalleryContent(category);
});
if (window.game && window.game.showNotification) {
window.game.showNotification(`🗑️ Cleared ${totalCleared} videos and freed up storage space`, 'success');
}
// Refresh current gallery
const activeTab = document.querySelector('.video-tabs .tab-btn.active');
if (activeTab) {
const category = activeTab.id.replace('-video-tab', '');
loadVideoGalleryContent(category);
}
} // Video tab buttons
const videoTabs = [
{ id: 'background-video-tab', gallery: 'background-video-gallery', category: 'background' },
{ id: 'task-video-tab', gallery: 'task-video-gallery', category: 'task' },
{ id: 'reward-video-tab', gallery: 'reward-video-gallery', category: 'reward' },
{ id: 'punishment-video-tab', gallery: 'punishment-video-gallery', category: 'punishment' }
];
videoTabs.forEach(tab => {
const tabBtn = document.getElementById(tab.id);
if (tabBtn) {
tabBtn.addEventListener('click', () => {
console.log(`Switching to ${tab.category} videos`);
// Update active tab
document.querySelectorAll('.video-tabs .tab-btn').forEach(btn => {
btn.classList.remove('active');
});
tabBtn.classList.add('active');
// Hide all galleries first
document.querySelectorAll('.video-gallery').forEach(gallery => {
gallery.classList.remove('active');
gallery.style.display = 'none';
});
// Show the selected gallery
const targetGallery = document.getElementById(tab.gallery);
if (targetGallery) {
targetGallery.classList.add('active');
targetGallery.style.display = 'grid';
}
// Load videos for this category
loadVideoGalleryContent(tab.category);
});
}
});
// Import video buttons
const importButtons = [
{ id: 'import-background-video-btn', category: 'background' },
{ id: 'import-task-video-btn', category: 'task' },
{ id: 'import-reward-video-btn', category: 'reward' },
{ id: 'import-punishment-video-btn', category: 'punishment' }
];
importButtons.forEach(button => {
const btn = document.getElementById(button.id);
if (btn) {
btn.addEventListener('click', () => {
handleVideoImport(button.category);
});
}
});
// Gallery control buttons
const selectAllBtn = document.getElementById('select-all-video-btn');
if (selectAllBtn) {
selectAllBtn.addEventListener('click', () => {
selectAllVideos();
});
}
const deselectAllBtn = document.getElementById('deselect-all-video-btn');
if (deselectAllBtn) {
deselectAllBtn.addEventListener('click', () => {
deselectAllVideos();
});
}
const deleteSelectedBtn = document.getElementById('delete-selected-video-btn');
if (deleteSelectedBtn) {
deleteSelectedBtn.addEventListener('click', () => {
deleteSelectedVideos();
});
}
// Refresh videos button
const refreshVideosBtn = document.getElementById('refresh-videos-btn');
if (refreshVideosBtn) {
refreshVideosBtn.addEventListener('click', () => {
refreshVideoLibrary();
});
}
// Preview close button
const closePreviewBtn = document.getElementById('close-video-preview-btn');
if (closePreviewBtn) {
closePreviewBtn.addEventListener('click', () => {
closeVideoPreview();
});
}
// Video loop toggle button
const toggleLoopBtn = document.getElementById('toggle-video-loop-btn');
if (toggleLoopBtn) {
toggleLoopBtn.addEventListener('click', () => {
toggleVideoLoop();
});
}
// Test buttons
const testBackgroundBtn = document.getElementById('test-background-video');
if (testBackgroundBtn) {
testBackgroundBtn.addEventListener('click', () => {
testVideoPlayback('background');
});
}
const testOverlayBtn = document.getElementById('test-overlay-video');
if (testOverlayBtn) {
testOverlayBtn.addEventListener('click', () => {
testVideoPlayback('overlay');
});
}
const stopAllBtn = document.getElementById('stop-all-videos');
if (stopAllBtn) {
stopAllBtn.addEventListener('click', () => {
stopAllVideos();
});
}
// Storage info button
const storageInfoBtn = document.getElementById('video-storage-info-btn');
if (storageInfoBtn) {
storageInfoBtn.addEventListener('click', () => {
showVideoStorageInfo();
});
}
// Cleanup button
const cleanupBtn = document.getElementById('cleanup-invalid-videos-btn');
if (cleanupBtn) {
cleanupBtn.addEventListener('click', () => {
if (confirm('Clean up all invalid video entries? This will remove broken video references but keep valid videos.')) {
manualCleanupInvalidVideos();
}
});
}
// Clear all videos button
const clearAllBtn = document.getElementById('clear-all-videos-btn');
if (clearAllBtn) {
clearAllBtn.addEventListener('click', () => {
if (confirm('⚠️ Delete ALL videos from ALL categories? This cannot be undone!')) {
clearAllVideos();
}
});
}
// Settings handlers
setupVideoSettingsHandlers();
// Initial load - ensure only background tab is active
// Hide all galleries first
document.querySelectorAll('.video-gallery').forEach(gallery => {
gallery.classList.remove('active');
gallery.style.display = 'none';
});
// Show only background gallery
const backgroundGallery = document.getElementById('background-video-gallery');
if (backgroundGallery) {
backgroundGallery.classList.add('active');
backgroundGallery.style.display = 'grid';
}
// Scan for new videos if in desktop mode
if (window.electronAPI && window.desktopFileManager) {
console.log('🔍 Scanning for new videos...');
// Clear existing video storage to force fresh scan
localStorage.removeItem('videoFiles');
Promise.all([
window.desktopFileManager.scanDirectoryForVideos('background'),
window.desktopFileManager.scanDirectoryForVideos('tasks'),
window.desktopFileManager.scanDirectoryForVideos('rewards'),
window.desktopFileManager.scanDirectoryForVideos('punishments')
]).then(([backgroundVideos, taskVideos, rewardVideos, punishmentVideos]) => {
const allVideos = [...backgroundVideos, ...taskVideos, ...rewardVideos, ...punishmentVideos];
if (allVideos.length > 0) {
window.desktopFileManager.updateVideoStorage(allVideos);
console.log(`📹 Found ${allVideos.length} videos in directories`);
}
// Reload all galleries after scanning
loadVideoGalleryContent('background');
loadVideoGalleryContent('task');
loadVideoGalleryContent('reward');
loadVideoGalleryContent('punishment');
}).catch(error => {
console.error('Error scanning video directories:', error);
// Still load the galleries even if scanning fails
loadVideoGalleryContent('background');
});
} else {
loadVideoGalleryContent('background');
}
// Clean up any existing invalid blob URLs from previous sessions
cleanupInvalidVideoData();
}
function cleanupInvalidVideoData() {
const categories = ['background', 'task', 'reward', 'punishment'];
let totalCleaned = 0;
categories.forEach(category => {
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const validVideos = videos.filter(video => {
if (video.url && video.url.startsWith('blob:')) {
totalCleaned++;
return false;
}
return true;
});
if (validVideos.length !== videos.length) {
localStorage.setItem(`${category}-videos`, JSON.stringify(validVideos));
}
});
if (totalCleaned > 0) {
console.log(`🧹 Cleaned up ${totalCleaned} invalid blob URL videos from localStorage`);
if (window.game && window.game.showNotification) {
window.game.showNotification(`🧹 Cleaned up ${totalCleaned} invalid video entries`, 'info');
}
}
}
function handleVideoImport(category) {
console.log(`Importing ${category} videos...`);
if (window.electronAPI && window.desktopFileManager) {
// Desktop version - use desktop file manager with native file dialog
window.desktopFileManager.selectAndImportVideos(category).then((importedVideos) => {
loadVideoGalleryContent(category);
if (window.game && window.game.showNotification && importedVideos.length > 0) {
window.game.showNotification(`${importedVideos.length} ${category} videos imported successfully!`, 'success');
}
}).catch(error => {
console.error('Video import failed:', error);
if (window.game && window.game.showNotification) {
window.game.showNotification('❌ Video import failed', 'error');
}
});
} else {
// Web version - use file input and process files
const input = document.createElement('input');
input.type = 'file';
input.accept = 'video/*';
input.multiple = true;
input.multiple = true;
input.onchange = async (e) => {
const files = Array.from(e.target.files);
console.log(`Selected ${files.length} ${category} video files`);
if (files.length === 0) return;
// Show progress notification
if (window.game && window.game.showNotification) {
window.game.showNotification(`Processing ${files.length} video files...`, 'info');
}
// Process each file
let processedCount = 0;
const videoList = [];
for (const file of files) {
try {
// Check file size (limit to 50MB for localStorage compatibility)
const maxSize = 50 * 1024 * 1024; // 50MB (will become ~67MB as base64)
if (file.size > maxSize) {
console.warn(`⚠️ Skipping ${file.name}: File too large (${formatFileSize(file.size)}). Maximum size: 50MB`);
if (window.game && window.game.showNotification) {
window.game.showNotification(`⚠️ ${file.name} is too large (max 50MB)`, 'warning');
}
continue;
}
// Check available localStorage space
const currentStorageSize = new Blob([JSON.stringify(localStorage)]).size;
const estimatedVideoSize = file.size * 1.37; // Base64 overhead estimate
const storageLimit = 5 * 1024 * 1024; // Conservative 5MB limit
if (currentStorageSize + estimatedVideoSize > storageLimit) {
console.warn(`⚠️ Skipping ${file.name}: Would exceed storage quota. Current: ${formatFileSize(currentStorageSize)}, Video: ${formatFileSize(estimatedVideoSize)}`);
if (window.game && window.game.showNotification) {
window.game.showNotification(`⚠️ Storage quota exceeded. Try smaller videos or clear some data.`, 'error');
}
continue;
}
console.log(`Processing video: ${file.name} (${formatFileSize(file.size)})`);
// Convert file to data URL for reliable storage and playback
const videoDataURL = await fileToDataURL(file);
const videoData = {
name: file.name,
url: videoDataURL,
size: file.size,
type: file.type,
category: category,
addedDate: new Date().toISOString()
};
videoList.push(videoData);
processedCount++;
console.log(`✅ Processed ${category} video: ${file.name}`);
} catch (error) {
console.error(`❌ Failed to process video ${file.name}:`, error);
}
}
if (processedCount > 0) {
try {
// Save videos to localStorage
const existingVideos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const updatedVideos = [...existingVideos, ...videoList];
// Try to save and handle quota exceeded error
localStorage.setItem(`${category}-videos`, JSON.stringify(updatedVideos));
// Refresh the gallery
loadVideoGalleryContent(category);
if (window.game && window.game.showNotification) {
window.game.showNotification(`${processedCount} ${category} videos uploaded successfully!`, 'success');
}
} catch (error) {
if (error.name === 'QuotaExceededError') {
console.error('❌ LocalStorage quota exceeded:', error);
if (window.game && window.game.showNotification) {
window.game.showNotification('❌ Storage quota exceeded. Try smaller videos or clear browser data.', 'error');
}
} else {
console.error('❌ Failed to save videos:', error);
if (window.game && window.game.showNotification) {
window.game.showNotification('❌ Failed to save videos', 'error');
}
}
}
}
};
input.click();
}
}
function loadVideoGalleryContent(category) {
const gallery = document.getElementById(`${category}-video-gallery`);
if (!gallery) return;
let videos = [];
// Check if we're in Electron environment and can use file system
if (window.electronAPI && window.desktopFileManager) {
// Load from file system storage
const storedVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}');
const dirCategory = category === 'task' ? 'tasks' :
category === 'reward' ? 'rewards' :
category === 'punishment' ? 'punishments' : category;
videos = storedVideos[dirCategory] || [];
} else {
// Fallback to localStorage
videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
// Clean up any invalid blob URLs from previous uploads
const validVideos = videos.filter(video => {
if (video.url && video.url.startsWith('blob:')) {
console.warn(`🧹 Removing invalid blob URL video: ${video.name}`);
return false;
}
return true;
});
// Save cleaned data if we removed any invalid entries
if (validVideos.length !== videos.length) {
localStorage.setItem(`${category}-videos`, JSON.stringify(validVideos));
videos = validVideos;
console.log(`🧹 Cleaned up ${videos.length - validVideos.length} invalid video entries for ${category}`);
}
}
if (videos.length === 0) {
// Show placeholder content
gallery.innerHTML = `
<div class="video-placeholder">
<h4>📁 ${category.charAt(0).toUpperCase() + category.slice(1)} Videos</h4>
<p>No ${category} videos found. Use the import button above to add video files.</p>
<div class="video-instructions">
<h5>Supported formats:</h5>
<ul>
<li>MP4 (recommended)</li>
<li>WebM</li>
<li>OGV</li>
<li>MOV</li>
</ul>
<h5>Size limits:</h5>
<ul>
<li>Maximum: 50MB per video</li>
<li>Recommended: Under 10MB for best performance</li>
<li>Total storage: ~5MB across all videos</li>
</ul>
</div>
</div>
`;
} else {
// Show video grid
gallery.innerHTML = videos.map((video, index) => `
<div class="video-item" data-video-id="${video.name}">
<div class="video-thumbnail">
<video src="${video.url}" preload="metadata" muted>
Your browser does not support the video element.
</video>
</div>
<div class="video-info">
<div class="video-name">${video.name}</div>
<div class="video-meta">
<small>${formatFileSize(video.size || 0)}${video.type || 'video/mp4'}</small>
</div>
<div class="video-actions">
<button onclick="previewVideo('${video.url}', '${video.name}')" class="btn-small">🎬 Preview</button>
<button onclick="deleteVideo('${category}', '${video.name}')" class="btn-small btn-danger">🗑️ Delete</button>
<input type="checkbox" class="video-select" value="${video.name}">
</div>
</div>
</div>
`).join('');
// Add click handlers to video items for selection
setTimeout(() => {
gallery.querySelectorAll('.video-item').forEach(item => {
const videoName = item.dataset.videoId;
const checkbox = item.querySelector('.video-select');
// Click anywhere on item to select/deselect (like image gallery)
item.addEventListener('click', (e) => {
// Don't toggle selection if clicking on checkbox or buttons
if (e.target.type !== 'checkbox' && e.target.tagName !== 'BUTTON') {
const isCurrentlySelected = checkbox.checked;
checkbox.checked = !isCurrentlySelected;
// Toggle visual selection state
if (checkbox.checked) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
}
});
// Checkbox click handler
checkbox.addEventListener('change', (e) => {
e.stopPropagation();
// Toggle visual selection state
if (checkbox.checked) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
});
});
}, 0);
}
// Update video count
const videoCount = document.querySelector('.video-count');
if (videoCount) {
videoCount.textContent = `${category}: ${videos.length} videos`;
}
}
function formatFileSize(bytes) {
if (!bytes || bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function fileToDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
console.log(`✅ Converted ${file.name} to data URL`);
resolve(e.target.result);
};
reader.onerror = (e) => {
console.error(`❌ Failed to read ${file.name}:`, e);
reject(e);
};
reader.onprogress = (e) => {
if (e.lengthComputable) {
const progress = Math.round((e.loaded / e.total) * 100);
if (progress % 25 === 0) { // Log every 25%
console.log(`📖 Reading ${file.name}: ${progress}%`);
}
}
};
reader.readAsDataURL(file);
});
}
function previewVideo(videoUrl, videoName) {
const previewSection = document.getElementById('video-preview-section');
const previewPlayer = document.getElementById('video-preview-player');
const previewNameElement = document.getElementById('preview-video-name');
const toggleLoopBtn = document.getElementById('toggle-video-loop-btn');
if (previewSection && previewPlayer && previewNameElement) {
// Stop any currently playing video
previewPlayer.pause();
previewPlayer.currentTime = 0;
// Reset loop state for new video
previewPlayer.loop = false;
if (toggleLoopBtn) {
toggleLoopBtn.textContent = '🔁 Loop';
toggleLoopBtn.classList.remove('btn-active');
}
// Set new video source
previewPlayer.src = videoUrl;
previewNameElement.textContent = videoName;
previewSection.style.display = 'block';
previewSection.scrollIntoView({ behavior: 'smooth' });
// Autoplay the video
previewPlayer.play().catch(error => {
console.warn('Autoplay failed:', error);
});
console.log(`🎬 Previewing video: ${videoName}`);
}
}
function deleteVideo(category, videoName) {
if (confirm(`Delete video "${videoName}"?`)) {
// Check if we're in Electron environment with file system storage
if (window.electronAPI && window.desktopFileManager) {
// Get videos from file system storage
const storedVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}');
const dirCategory = category === 'task' ? 'tasks' :
category === 'reward' ? 'rewards' :
category === 'punishment' ? 'punishments' : category;
if (storedVideos[dirCategory]) {
// Find the video to get its file path
const videoToDelete = storedVideos[dirCategory].find(video => video.name === videoName);
if (videoToDelete && videoToDelete.path) {
// Delete the actual file
window.electronAPI.deleteVideo(videoToDelete.path).then(success => {
if (success) {
console.log(`Successfully deleted video file: ${videoToDelete.path}`);
} else {
console.warn(`Failed to delete video file: ${videoToDelete.path}`);
}
}).catch(error => {
console.error('Error deleting video file:', error);
});
}
// Remove from storage
storedVideos[dirCategory] = storedVideos[dirCategory].filter(video => video.name !== videoName);
localStorage.setItem('videoFiles', JSON.stringify(storedVideos));
}
} else {
// Browser environment - use old localStorage method
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const updatedVideos = videos.filter(video => video.name !== videoName);
localStorage.setItem(`${category}-videos`, JSON.stringify(updatedVideos));
}
// Refresh the gallery
loadVideoGalleryContent(category);
if (window.game && window.game.showNotification) {
window.game.showNotification(`Video "${videoName}" deleted`, 'info');
}
}
}
function selectAllVideos() {
const activeGallery = document.querySelector('.video-gallery.active');
if (activeGallery) {
const checkboxes = activeGallery.querySelectorAll('.video-select');
checkboxes.forEach(checkbox => checkbox.checked = true);
console.log(`Selected ${checkboxes.length} videos in current tab`);
}
}
function deselectAllVideos() {
const activeGallery = document.querySelector('.video-gallery.active');
if (activeGallery) {
const checkboxes = activeGallery.querySelectorAll('.video-select');
checkboxes.forEach(checkbox => checkbox.checked = false);
console.log('Deselected all videos');
}
}
function deleteSelectedVideos() {
const activeGallery = document.querySelector('.video-gallery.active');
if (!activeGallery) return;
const selectedCheckboxes = activeGallery.querySelectorAll('.video-select:checked');
if (selectedCheckboxes.length === 0) {
if (window.game && window.game.showNotification) {
window.game.showNotification('No videos selected', 'warning');
}
return;
}
const videoNames = Array.from(selectedCheckboxes).map(cb => cb.value);
const category = activeGallery.id.replace('-video-gallery', '');
if (confirm(`Delete ${videoNames.length} selected videos?`)) {
// Check if we're in Electron environment with file system storage
if (window.electronAPI && window.desktopFileManager) {
// Get videos from file system storage
const storedVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}');
const dirCategory = category === 'task' ? 'tasks' :
category === 'reward' ? 'rewards' :
category === 'punishment' ? 'punishments' : category;
if (storedVideos[dirCategory]) {
// Find videos to delete and their file paths
const videosToDelete = storedVideos[dirCategory].filter(video => videoNames.includes(video.name));
// Delete actual files
videosToDelete.forEach(video => {
if (video.path) {
window.electronAPI.deleteVideo(video.path).then(success => {
if (success) {
console.log(`Successfully deleted video file: ${video.path}`);
} else {
console.warn(`Failed to delete video file: ${video.path}`);
}
}).catch(error => {
console.error('Error deleting video file:', error);
});
}
});
// Remove from storage
storedVideos[dirCategory] = storedVideos[dirCategory].filter(video => !videoNames.includes(video.name));
localStorage.setItem('videoFiles', JSON.stringify(storedVideos));
}
} else {
// Browser environment - use old localStorage method
const videos = JSON.parse(localStorage.getItem(`${category}-videos`) || '[]');
const updatedVideos = videos.filter(video => !videoNames.includes(video.name));
localStorage.setItem(`${category}-videos`, JSON.stringify(updatedVideos));
}
loadVideoGalleryContent(category);
if (window.game && window.game.showNotification) {
window.game.showNotification(`${videoNames.length} videos deleted`, 'success');
}
}
}
function refreshVideoLibrary() {
if (window.electronAPI && window.desktopFileManager) {
// Clear existing video storage
localStorage.removeItem('videoFiles');
if (window.game && window.game.showNotification) {
window.game.showNotification('🔄 Refreshing video library...', 'info');
}
// Rescan all directories
Promise.all([
window.desktopFileManager.scanDirectoryForVideos('background'),
window.desktopFileManager.scanDirectoryForVideos('tasks'),
window.desktopFileManager.scanDirectoryForVideos('rewards'),
window.desktopFileManager.scanDirectoryForVideos('punishments')
]).then(([backgroundVideos, taskVideos, rewardVideos, punishmentVideos]) => {
const allVideos = [...backgroundVideos, ...taskVideos, ...rewardVideos, ...punishmentVideos];
if (allVideos.length > 0) {
window.desktopFileManager.updateVideoStorage(allVideos);
}
// Reload all galleries
loadVideoGalleryContent('background');
loadVideoGalleryContent('task');
loadVideoGalleryContent('reward');
loadVideoGalleryContent('punishment');
if (window.game && window.game.showNotification) {
window.game.showNotification(`✅ Video library refreshed! Found ${allVideos.length} videos`, 'success');
}
}).catch(error => {
console.error('Error refreshing video library:', error);
if (window.game && window.game.showNotification) {
window.game.showNotification('❌ Failed to refresh video library', 'error');
}
});
} else {
if (window.game && window.game.showNotification) {
window.game.showNotification('Refresh is only available in desktop mode', 'warning');
}
}
}
function closeVideoPreview() {
const previewSection = document.getElementById('video-preview-section');
const previewPlayer = document.getElementById('video-preview-player');
if (previewSection && previewPlayer) {
// Stop playback and clear source
previewPlayer.pause();
previewPlayer.currentTime = 0;
previewPlayer.src = '';
previewSection.style.display = 'none';
console.log('🔇 Video preview closed');
}
}
function toggleVideoLoop() {
const previewPlayer = document.getElementById('video-preview-player');
const toggleLoopBtn = document.getElementById('toggle-video-loop-btn');
if (previewPlayer && toggleLoopBtn) {
// Toggle the loop attribute
previewPlayer.loop = !previewPlayer.loop;
// Update button appearance and text
if (previewPlayer.loop) {
toggleLoopBtn.textContent = '🔁 Loop ON';
toggleLoopBtn.classList.add('btn-active');
console.log('🔁 Video loop enabled');
} else {
toggleLoopBtn.textContent = '🔁 Loop';
toggleLoopBtn.classList.remove('btn-active');
console.log('🔁 Video loop disabled');
}
}
}
function testVideoPlayback(type) {
console.log(`Testing ${type} video playback`);
if (window.game && window.game.showNotification) {
window.game.showNotification(`${type} video test - feature coming soon!`, 'info');
}
}
function stopAllVideos() {
console.log('Stopping all video playback');
if (window.game && window.game.showNotification) {
window.game.showNotification('All videos stopped', 'info');
}
}
function setupVideoSettingsHandlers() {
const settings = [
'enable-video-player',
'enable-background-videos',
'enable-task-videos',
'video-autoplay',
'video-show-controls',
'video-fade-transitions'
];
settings.forEach(settingId => {
const element = document.getElementById(settingId);
if (element && element.type === 'checkbox') {
element.addEventListener('change', () => {
localStorage.setItem(settingId, element.checked);
console.log(`Video setting ${settingId}: ${element.checked}`);
});
// Load saved setting
const saved = localStorage.getItem(settingId);
if (saved !== null) {
element.checked = saved === 'true';
}
}
});
// Volume slider
const volumeSlider = document.getElementById('video-volume');
const volumeDisplay = document.getElementById('video-volume-display');
if (volumeSlider && volumeDisplay) {
volumeSlider.addEventListener('input', () => {
const volume = volumeSlider.value;
volumeDisplay.textContent = `${volume}%`;
localStorage.setItem('video-volume', volume);
});
// Load saved volume
const savedVolume = localStorage.getItem('video-volume') || '30';
volumeSlider.value = savedVolume;
volumeDisplay.textContent = `${savedVolume}%`;
}
}
// Initialize everything when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
initializeVideoPlayer();
// Initialize desktop file manager if in Electron environment
if (window.electronAPI && typeof DesktopFileManager !== 'undefined') {
window.desktopFileManager = new DesktopFileManager(window.game?.dataManager);
console.log('🖥️ Desktop File Manager initialized for video management');
}
// Set up video management button (only once)
const videoManageBtn = document.getElementById('manage-video-btn');
if (videoManageBtn && !videoManageBtn.hasAttribute('data-handler-attached')) {
videoManageBtn.setAttribute('data-handler-attached', 'true');
videoManageBtn.addEventListener('click', () => {
if (window.game && typeof window.game.showScreen === 'function') {
window.game.showScreen('video-management-screen');
// Set up handlers when screen is shown (with delay to ensure DOM is ready)
setTimeout(() => {
setupVideoManagementHandlers();
}, 100);
} else {
console.error('Game instance not available for video management');
}
});
}
}, 1000);
});
</script>
</html>