diff --git a/AUDIO_SYSTEM_CHANGES.md b/AUDIO_SYSTEM_CHANGES.md deleted file mode 100644 index 2318420..0000000 --- a/AUDIO_SYSTEM_CHANGES.md +++ /dev/null @@ -1,80 +0,0 @@ -# Simplified Audio System Documentation - -## Overview -The audio system has been completely refactored to use a simple, dynamic approach with a streamlined directory structure. - -## Simplified Directory Structure - -### Current Structure: -``` -audio/ -├── background/ (Contains all background music/audio clips) -└── ambient/ (Empty for now, reserved for future ambient sounds) -``` - -### Previous Complex Structure (REMOVED): -- ❌ `audio/effects/` -- ❌ `audio/tasks/teasing/` -- ❌ `audio/tasks/intense/` -- ❌ `audio/tasks/instructions/` -- ❌ `audio/punishments/denial/` -- ❌ `audio/punishments/mocking/` -- ❌ `audio/rewards/completion/` - -## How It Works - -### Single Background Audio Category -- **Before**: Multiple hardcoded categories (tasks, punishments, rewards, teasing, intense, etc.) -- **After**: One dynamic "background" category that uses user-managed audio files - -### Dynamic Audio Discovery -- Audio files are loaded from the existing `customAudio` storage system -- Combines all user audio (background, ambient, effects) into one pool -- No more hardcoded file paths that can break -- Automatically refreshes when users add new audio files - -### User Experience -1. **Add Audio**: Use "Manage Audio" menu to import audio files -2. **Auto-Play**: Audio automatically plays during tasks, rewards, and consequences -3. **Single Control**: All audio settings now control the same background audio -4. **No Errors**: No more "file not found" errors from missing hardcoded files - -## Technical Changes - -### AudioManager Simplification -- `playTaskAudio()` → `playBackgroundAudio()` -- `playPunishmentAudio()` → `playBackgroundAudio()` -- `playRewardAudio()` → `playBackgroundAudio()` -- Single audio category instead of multiple complex categories - -### Game Integration -- All game events (task start, completion, skip) play from the same audio pool -- Audio automatically refreshes when users add files via "Manage Audio" -- Settings UI still shows separate controls but they all control the same audio - -### Benefits -- ✅ No more audio file path errors -- ✅ User has full control over audio content -- ✅ Simple, reliable system -- ✅ Integrates with existing UI -- ✅ No hardcoded dependencies - -## For Users - -### To Add Background Audio: -1. Place your audio files in the `audio/background/` directory -2. Open the main menu and click "Manage Audio" -3. Click "🔍 Scan Directories" to discover the files -4. Audio will automatically play during the game - -### Audio Controls: -- All volume sliders now control the same background audio -- Enable/disable toggles all control the same background audio -- Preview buttons all preview your background audio - -## Migration Notes -- Complex directory structure has been simplified to just two folders -- Existing user audio files in "Manage Audio" will automatically work -- All audio files should be placed in `audio/background/` for now -- `audio/ambient/` is reserved for future ambient sounds (leave empty) -- System is now completely user-controlled and dynamic \ No newline at end of file diff --git a/DIRECTORY_SCANNING.md b/DIRECTORY_SCANNING.md deleted file mode 100644 index a296777..0000000 --- a/DIRECTORY_SCANNING.md +++ /dev/null @@ -1,113 +0,0 @@ -# Audio Directory Scanning Feature - -## Overview -The directory scanning feature automatically discovers audio files that are already present in your app's audio directory structure and adds them to your audio library. - -## How It Works - -### Automatic Scanning -- **On Startup**: The app automatically scans for audio files when it launches (desktop mode only) -- **Manual Scanning**: Use the "🔍 Scan Directories" button in the "Manage Audio" menu - -### Scanned Directories -The scanner looks for audio files in these directories: -``` -audio/ -├── background/ -└── ambient/ -``` - -### Supported Audio Formats -- **MP3** (most common) -- **WAV** (high quality) -- **OGG** (open source) -- **M4A** (Apple format) -- **AAC** (compressed) -- **FLAC** (lossless) - -## Using Directory Scanning - -### Desktop Mode (Recommended) -1. **Place Files**: Copy your audio files into the appropriate audio subdirectories -2. **Scan**: Click "🔍 Scan Directories" in the "Manage Audio" menu -3. **Auto-Use**: Scanned files are automatically added to your library and enabled - -### File Organization -- **Background Music** → `audio/background/` -- **Ambient Sounds** → `audio/ambient/` - -## Category Mapping -Due to the simplified audio system, all scanned files are categorized as follows: -- Files from `audio/ambient/` → **Ambient** category -- Files from `audio/background/` → **Background** category - -## Benefits - -### Easy Bulk Import -- Add many files at once by copying them to directories -- No need to import files one-by-one through the UI -- Perfect for organizing large audio collections - -### Automatic Discovery -- New files are found automatically on app restart -- Manual scan button for immediate discovery -- No configuration required - -### File Management -- Files stay in organized directory structure -- Easy to add/remove files outside the app -- Clear organization by purpose/category - -## Troubleshooting - -### No Files Found -- **Check Directories**: Ensure audio files are in the correct subdirectories -- **File Formats**: Verify files are in supported formats (MP3, WAV, OGG, M4A, AAC, FLAC) -- **Permissions**: Make sure the app has read access to the audio directories -- **File Names**: Avoid special characters in file names - -### Duplicate Files -- The scanner checks for existing files before adding -- Files already in your library won't be duplicated -- If you see duplicates, use the "🧹 Cleanup" button - -### Web Mode Limitations -- Directory scanning only works in desktop mode (Electron) -- Web browsers can't access file system directories -- Use the Import buttons for manual file upload in web mode - -## Tips - -### Organizing Your Audio -1. **Create Themed Folders**: Group similar audio files together -2. **Descriptive Names**: Use clear, descriptive file names -3. **Test Files**: Place a few test files first, then scan to verify - -### Performance -- Scanning is fast for hundreds of files -- Large files (>100MB) may take longer to process -- The app shows progress messages during scanning - -### Backup Strategy -- Keep original files in the audio directories -- The app creates references, not copies -- Easy to backup/restore entire audio collection - -## Integration - -### With Dynamic Audio System -- Scanned files integrate seamlessly with the dynamic audio system -- All audio management features work with scanned files -- Enable/disable scanned files individually - -### With Existing Audio -- Scanned files are added to existing audio library -- No conflicts with manually imported files -- Use both methods together for maximum flexibility - -## Next Steps - -1. **Organize Files**: Place your audio files in the appropriate directories -2. **Scan**: Use the "🔍 Scan Directories" button -3. **Test**: Try playing audio during a game session -4. **Manage**: Use the audio management UI to enable/disable specific files \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md index df0746e..9b9b31d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -25,6 +25,12 @@ - Enhanced user experience improvements - Bug fixes and stability enhancements - Performance optimizations +- **✅ 🎬 Porn Cinema Video Loading Bug Fix (October 31, 2025)** + - ✅ **Critical Bug Resolution**: Fixed video loading issue where Porn Cinema couldn't access videos from linked directories + - ✅ **Data Preservation Logic**: Implemented safeguards to prevent DesktopFileManager from overwriting existing video data + - ✅ **Storage System Enhancement**: Enhanced unified video library fallback mechanisms for reliable video access + - ✅ **Console Cleanup**: Removed excessive debug logging for cleaner user experience + - ✅ **Cross-Component Synchronization**: Fixed timing issues between main game and Porn Cinema video data access - **🎬 Porn Cinema Media Player** *(✅ Major Progress - October 30-31, 2025)* - ✅ **Complete Layout Implementation**: Professional two-column design with main content area and right sidebar - ✅ **Header Navigation**: Slim, modern header with Home, Settings, Theater, and Fullscreen controls @@ -34,6 +40,7 @@ - ✅ **Auto-Hide Controls**: Smart control visibility with 3-second timeout during playback - ✅ **Video Library Integration**: Minimal, clean library section with grid/list views - ✅ **Responsive Design**: Clean mockup-matching layout with proper CSS architecture + - ✅ **Video Loading System**: Robust video loading from unified video library with fallback mechanisms - 🚧 **Next Steps**: Video library population, playlist functionality, search implementation - **🔧 Base Video Player Extraction** *(✅ COMPLETED - October 31, 2025)* - ✅ **Extract Reusable Components**: Created BaseVideoPlayer class with full video control functionality (400+ lines) @@ -44,14 +51,22 @@ - ✅ **Script Integration**: Added baseVideoPlayer.js and focusVideoPlayer.js to index.html loading sequence - ✅ **Global Export**: Properly exported classes to window object for browser compatibility - ✅ **Syntax Validation**: Clean JavaScript validation with no errors -- **🎬 Porn Cinema Refactoring** *(✅ CORE COMPLETE - October 31, 2025)* +- **🎬 Porn Cinema Refactoring** *(✅ COMPLETED - October 31, 2025)* - ✅ **Legacy Code Analysis**: Analyzed existing pornCinema.js for BaseVideoPlayer integration points - ✅ **Architecture Planning**: Identified cinema-specific features (playlist, theater mode, navigation) - ✅ **Code Backup**: Created pornCinema-backup.js to preserve original implementation - ✅ **Class Refactoring**: Created clean PornCinema class extending BaseVideoPlayer - ✅ **Core Inheritance**: PornCinema now properly extends BaseVideoPlayer for shared functionality - - 📋 **Feature Migration**: Future enhancement to migrate advanced playlist and cinema UI features - - 📋 **Testing & Validation**: Comprehensive testing in cinema mode environment + - ✅ **Method Implementation**: Added initialize(), playVideo(), addToPlaylist() methods + - ✅ **Error Handling**: Proper TypeError resolution and method validation + - ✅ **Integration Testing**: Successfully tested in Option A architecture +- **🎮 Focus Interruption Video Integration** *(✅ COMPLETED - October 31, 2025)* + - ✅ **Video Library Access**: Fixed FocusVideoPlayer video library initialization + - ✅ **Volume Control Integration**: Added ultra-compact volume slider (60px width) + - ✅ **CSS Override System**: Resolved CSS conflicts with focus-volume-slider class + - ✅ **UI Refinement**: Achieved ultra-compact volume control design per user specs + - ✅ **Video Collection**: 34 videos successfully integrated from punishment/task/background categories + - ✅ **Testing Complete**: Focus interruption videos playing successfully with proper controls - **NEW XP System Implementation:** - **Main Game** - User gains 1 XP per task diff --git a/audio-compatibility-checker.js b/audio-compatibility-checker.js deleted file mode 100644 index aa5773d..0000000 --- a/audio-compatibility-checker.js +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Audio Compatibility Checker and Browser Mode Handler - * This script helps detect and handle audio limitations in browser vs Electron mode - */ - -class AudioCompatibilityChecker { - constructor() { - this.isElectron = window.electronAPI !== undefined; - this.isFileProtocol = window.location.protocol === 'file:'; - this.browserAudioSupported = this.checkBrowserAudioSupport(); - } - - checkBrowserAudioSupport() { - // Test if we can create audio elements - try { - const testAudio = new Audio(); - return testAudio !== null; - } catch (error) { - console.error('Browser does not support Audio API:', error); - return false; - } - } - - async testSingleAudioFile(path) { - return new Promise((resolve) => { - const audio = new Audio(); - let resolved = false; - - const cleanup = () => { - if (!resolved) { - resolved = true; - audio.pause(); - audio.src = ''; - audio.remove(); - } - }; - - audio.addEventListener('canplay', () => { - if (!resolved) { - cleanup(); - resolve({ success: true, path, error: null }); - } - }); - - audio.addEventListener('error', (e) => { - if (!resolved) { - const errorInfo = { - code: e.target.error?.code, - message: e.target.error?.message, - networkState: e.target.networkState, - readyState: e.target.readyState, - src: e.target.src - }; - cleanup(); - resolve({ success: false, path, error: errorInfo }); - } - }); - - // Timeout after 3 seconds - setTimeout(() => { - if (!resolved) { - cleanup(); - resolve({ success: false, path, error: 'timeout' }); - } - }, 3000); - - // Try to load the audio - try { - audio.src = path; - audio.load(); - } catch (error) { - if (!resolved) { - cleanup(); - resolve({ success: false, path, error: error.message }); - } - } - }); - } - - async quickAudioTest() { - console.log('🎵 Audio Compatibility Test Starting...'); - console.log(`🎵 Environment: ${this.isElectron ? 'Electron' : 'Browser'}`); - console.log(`🎵 Protocol: ${window.location.protocol}`); - console.log(`🎵 Audio API Support: ${this.browserAudioSupported}`); - - // Test a few known audio files - const testFiles = [ - 'audio/tasks/teasing/u.mp3', - 'audio/rewards/completion/u.mp3' - ]; - - const results = []; - for (const file of testFiles) { - console.log(`🎵 Testing: ${file}`); - const result = await this.testSingleAudioFile(file); - results.push(result); - - if (result.success) { - console.log(`✅ ${file} - Working`); - } else { - console.log(`❌ ${file} - Failed:`, result.error); - } - - // Small delay between tests - await new Promise(resolve => setTimeout(resolve, 100)); - } - - return results; - } - - getRecommendations() { - const recommendations = []; - - if (!this.isElectron && this.isFileProtocol) { - recommendations.push({ - type: 'warning', - message: 'Running in browser with file:// protocol', - suggestion: 'Use "npm start" to run the Electron desktop version for full audio support' - }); - } - - if (!this.browserAudioSupported) { - recommendations.push({ - type: 'error', - message: 'Browser does not support HTML5 Audio API', - suggestion: 'Try a different browser or update your current browser' - }); - } - - if (this.isElectron) { - recommendations.push({ - type: 'success', - message: 'Running in Electron desktop mode', - suggestion: 'Audio should work without restrictions' - }); - } - - return recommendations; - } - - async diagnoseAudioIssues() { - console.log('🎵 Audio Diagnostic Report'); - console.log('=' .repeat(50)); - - const recommendations = this.getRecommendations(); - recommendations.forEach(rec => { - const icon = rec.type === 'error' ? '❌' : rec.type === 'warning' ? '⚠️' : '✅'; - console.log(`${icon} ${rec.message}`); - console.log(` 💡 ${rec.suggestion}`); - }); - - console.log('\n🎵 Testing Audio Files...'); - const testResults = await this.quickAudioTest(); - - const workingFiles = testResults.filter(r => r.success).length; - const totalFiles = testResults.length; - - console.log('\n🎵 Summary:'); - console.log(`✅ Working: ${workingFiles}/${totalFiles}`); - console.log(`❌ Failed: ${totalFiles - workingFiles}/${totalFiles}`); - - if (workingFiles === 0) { - console.log('\n🎵 No audio files are working. This suggests:'); - console.log(' - Browser CORS restrictions (use Electron app)'); - console.log(' - Audio files are corrupted or missing'); - console.log(' - Browser audio support issues'); - } else if (workingFiles < totalFiles) { - console.log('\n🎵 Some audio files are working. Issues may be:'); - console.log(' - Specific file corruption'); - console.log(' - Filename/path issues'); - console.log(' - Audio format compatibility'); - } else { - console.log('\n🎵 All tested files are working! 🎉'); - } - - return { - environment: { - isElectron: this.isElectron, - isFileProtocol: this.isFileProtocol, - audioSupported: this.browserAudioSupported - }, - testResults, - recommendations - }; - } -} - -// Auto-run diagnostic if audio issues are detected -window.audioCompatibilityChecker = new AudioCompatibilityChecker(); - -// Quick check for common issues -if (!window.audioCompatibilityChecker.isElectron && window.audioCompatibilityChecker.isFileProtocol) { - console.log('🎵 Audio issues detected! Run window.audioCompatibilityChecker.diagnoseAudioIssues() for full report'); -} - -console.log('🎵 Audio Compatibility Checker loaded'); -console.log('🎵 Run: window.audioCompatibilityChecker.diagnoseAudioIssues()'); \ No newline at end of file diff --git a/audio-validator.js b/audio-validator.js deleted file mode 100644 index f00d283..0000000 --- a/audio-validator.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Audio File Validator - * This script tests which audio files can actually be loaded by the browser - */ - -async function validateAudioFiles() { - console.log('🎵 Starting audio file validation...'); - - const audioStructure = { - tasks: { - teasing: [ - 'enjoying-a-giant-cock.mp3', - 'horny-japanese-babe-japanese-sex.mp3', - 'long-and-hard-moan.mp3', - 'playing-her-pussy-with-a-dildo-japanese-sex.mp3', - 'u.mp3' - ], - intense: [ - 'bree-olson-screaming-orgasm-sound.mp3', - 'carmela-bing-screaming-orgasm-sound.mp3', - 'moaning-ringtone.mp3', - 'multiple-orgasms-with-creamy-pussy.mp3' - ], - instructions: [ - 'addict-to-my-tits-854x480p.mp3', - 'deeper.mp3', - 'you-love-to-goon.mp3' - ] - }, - punishments: { - denial: [ - 'addicted-to-my-tits-tit-worship-mesmerize-joi-mov.mp3', - 'dumb-gooner-bitch-4-big-tits.mp3', - 'you-will-be-left-thoughtless.mp3' - ], - mocking: [ - 'precise-denial.mp3', - 'punishment-episode-cockring-causes-cumshots-cumshots-gameplay-only.mp3', - 'quick-jerk-to-tits-in-pink-bra-1080p-ellie-idol.mp3', - 'stroke-your-goon-bud.mp3' - ] - }, - rewards: { - completion: ['u.mp3'] - } - }; - - const results = { - working: [], - failed: [], - total: 0 - }; - - for (const [category, subcategories] of Object.entries(audioStructure)) { - for (const [subcategory, files] of Object.entries(subcategories)) { - for (const file of files) { - const audioPath = `audio/${category}/${subcategory}/${file}`; - results.total++; - - try { - const canLoad = await testAudioFile(audioPath); - if (canLoad) { - results.working.push(audioPath); - console.log(`✅ ${audioPath} - OK`); - } else { - results.failed.push(audioPath); - console.log(`❌ ${audioPath} - FAILED`); - } - } catch (error) { - results.failed.push(audioPath); - console.log(`❌ ${audioPath} - ERROR: ${error.message}`); - } - - // Small delay to prevent browser overload - await new Promise(resolve => setTimeout(resolve, 100)); - } - } - } - - console.log('\n🎵 Audio Validation Results:'); - console.log(`✅ Working files: ${results.working.length}/${results.total}`); - console.log(`❌ Failed files: ${results.failed.length}/${results.total}`); - - if (results.failed.length > 0) { - console.log('\n❌ Failed files:'); - results.failed.forEach(file => console.log(` - ${file}`)); - } - - if (results.working.length > 0) { - console.log('\n✅ Working files:'); - results.working.forEach(file => console.log(` - ${file}`)); - } - - return results; -} - -async function testAudioFile(audioPath) { - return new Promise((resolve) => { - const audio = new Audio(audioPath); - let resolved = false; - - const cleanup = () => { - if (!resolved) { - resolved = true; - audio.pause(); - audio.src = ''; - } - }; - - audio.addEventListener('canplay', () => { - if (!resolved) { - cleanup(); - resolve(true); - } - }); - - audio.addEventListener('error', () => { - if (!resolved) { - cleanup(); - resolve(false); - } - }); - - // Timeout after 5 seconds - setTimeout(() => { - if (!resolved) { - cleanup(); - resolve(false); - } - }, 5000); - - // Start loading - audio.load(); - }); -} - -// Make functions available globally -window.audioValidator = { - validateAudioFiles, - testAudioFile -}; - -console.log('🎵 Audio validator loaded. Run: window.audioValidator.validateAudioFiles()'); \ No newline at end of file diff --git a/balanced-blue-theme.css b/balanced-blue-theme.css deleted file mode 100644 index 79e9156..0000000 --- a/balanced-blue-theme.css +++ /dev/null @@ -1,57 +0,0 @@ -/* Balanced Blue Theme - Saved Color Scheme */ -:root { - /* Color system - Blue accents with neutral backgrounds */ - --color-primary: #1e90ff; - --color-secondary: #0066cc; - --color-accent: #4da6ff; - --color-success: #28a745; - --color-warning: #ffc107; - --color-danger: #dc3545; - - /* Text colors */ - --text-primary: #ffffff; - --text-secondary: #e0e0e0; - --text-tertiary: #b0b0b0; - --text-muted: #808080; - - /* Background colors - Neutrals */ - --bg-primary: #0a0a0a; - --bg-secondary: #1a1a1a; - --bg-tertiary: #2a2a2a; - --bg-card: rgba(42, 42, 42, 0.8); - --bg-modal: rgba(26, 26, 26, 0.95); - - /* Border and shadow - Dark grays with blue accents */ - --border-color: rgba(255, 255, 255, 0.1); - --border-accent: rgba(30, 144, 255, 0.3); - --shadow-primary: 0 4px 12px rgba(0, 0, 0, 0.3); - --glow-primary: 0 0 20px rgba(30, 144, 255, 0.4); - - /* Font system */ - --font-xs: 0.75rem; - --font-sm: 0.875rem; - --font-base: 1rem; - --font-lg: 1.125rem; - --font-xl: 1.25rem; - --font-xxl: 1.5rem; - --font-xxxl: 2rem; - --font-xxxxl: 3rem; - - /* Spacing system */ - --space-xs: 0.25rem; - --space-sm: 0.5rem; - --space-base: 1rem; - --space-lg: 1.5rem; - --space-xl: 2rem; - --space-xxl: 3rem; -} - -/* Key styling notes for Balanced Blue Theme: - * - Backgrounds: Pure blacks and dark grays for excellent readability - * - Borders: Subtle white borders for clean separation - * - Blue accents: Professional tech blues - dodger blue, dark blue, light blue - * - Hover effects: Blue accent borders and subtle glows - * - Title: Clean blue gradient (dodger blue to dark blue) with animated glow - * - Progress bars: Professional blue gradient fills - * - Overall feel: Tech-sophisticated, clean, trustworthy, modern - */ \ No newline at end of file diff --git a/balanced-forest-green-theme.css b/balanced-forest-green-theme.css deleted file mode 100644 index 9ef7cd4..0000000 --- a/balanced-forest-green-theme.css +++ /dev/null @@ -1,57 +0,0 @@ -/* Balanced Forest Green Theme - Saved Color Scheme */ -:root { - /* Color system - Forest green accents with neutral backgrounds */ - --color-primary: #228b22; - --color-secondary: #006400; - --color-accent: #32cd32; - --color-success: #28a745; - --color-warning: #ffc107; - --color-danger: #dc3545; - - /* Text colors */ - --text-primary: #ffffff; - --text-secondary: #e0e0e0; - --text-tertiary: #b0b0b0; - --text-muted: #808080; - - /* Background colors - Neutrals */ - --bg-primary: #0a0a0a; - --bg-secondary: #1a1a1a; - --bg-tertiary: #2a2a2a; - --bg-card: rgba(42, 42, 42, 0.8); - --bg-modal: rgba(26, 26, 26, 0.95); - - /* Border and shadow - Dark grays with forest green accents */ - --border-color: rgba(255, 255, 255, 0.1); - --border-accent: rgba(34, 139, 34, 0.3); - --shadow-primary: 0 4px 12px rgba(0, 0, 0, 0.3); - --glow-primary: 0 0 20px rgba(34, 139, 34, 0.4); - - /* Font system */ - --font-xs: 0.75rem; - --font-sm: 0.875rem; - --font-base: 1rem; - --font-lg: 1.125rem; - --font-xl: 1.25rem; - --font-xxl: 1.5rem; - --font-xxxl: 2rem; - --font-xxxxl: 3rem; - - /* Spacing system */ - --space-xs: 0.25rem; - --space-sm: 0.5rem; - --space-base: 1rem; - --space-lg: 1.5rem; - --space-xl: 2rem; - --space-xxl: 3rem; -} - -/* Key styling notes for Balanced Forest Green Theme: - * - Backgrounds: Pure blacks and dark grays for excellent readability - * - Borders: Subtle white borders for clean separation - * - Forest green accents: Natural, earthy greens - forest green, dark green, lime green - * - Hover effects: Forest green accent borders and subtle glows - * - Title: Natural forest green gradient with animated glow - * - Progress bars: Organic green gradient fills - * - Overall feel: Natural, organic, earthy, calming forest environment - */ \ No newline at end of file diff --git a/balanced-purple-theme.css b/balanced-purple-theme.css deleted file mode 100644 index 97ed2cd..0000000 --- a/balanced-purple-theme.css +++ /dev/null @@ -1,57 +0,0 @@ -/* Balanced Purple Theme - Saved Color Scheme */ -:root { - /* Color system - Purple accents with neutral backgrounds */ - --color-primary: #8a2be2; - --color-secondary: #da70d6; - --color-accent: #ba55d3; - --color-success: #28a745; - --color-warning: #ffc107; - --color-danger: #dc3545; - - /* Text colors */ - --text-primary: #ffffff; - --text-secondary: #e0e0e0; - --text-tertiary: #b0b0b0; - --text-muted: #808080; - - /* Background colors - Neutrals with subtle purple hints */ - --bg-primary: #0a0a0a; - --bg-secondary: #1a1a1a; - --bg-tertiary: #2a2a2a; - --bg-card: rgba(42, 42, 42, 0.8); - --bg-modal: rgba(26, 26, 26, 0.95); - - /* Border and shadow - Dark grays with purple accents */ - --border-color: rgba(255, 255, 255, 0.1); - --border-accent: rgba(138, 43, 226, 0.3); - --shadow-primary: 0 4px 12px rgba(0, 0, 0, 0.3); - --glow-primary: 0 0 20px rgba(138, 43, 226, 0.4); - - /* Font system */ - --font-xs: 0.75rem; - --font-sm: 0.875rem; - --font-base: 1rem; - --font-lg: 1.125rem; - --font-xl: 1.25rem; - --font-xxl: 1.5rem; - --font-xxxl: 2rem; - --font-xxxxl: 3rem; - - /* Spacing system */ - --space-xs: 0.25rem; - --space-sm: 0.5rem; - --space-base: 1rem; - --space-lg: 1.5rem; - --space-xl: 2rem; - --space-xxl: 3rem; -} - -/* Key styling notes for Balanced Purple Theme: - * - Backgrounds: Pure blacks and dark grays for excellent readability - * - Borders: Subtle white borders for clean separation - * - Purple accents: Used strategically for buttons, highlights, and interactive elements - * - Hover effects: Purple accent borders and subtle glows - * - Title: Purple gradient with animated glow - * - Progress bars: Purple gradient fills - * - Overall feel: Sophisticated, readable, with strategic purple branding - */ \ No newline at end of file diff --git a/balanced-red-theme.css b/balanced-red-theme.css deleted file mode 100644 index 19a0b12..0000000 --- a/balanced-red-theme.css +++ /dev/null @@ -1,57 +0,0 @@ -/* Balanced Red/Crimson Theme - Saved Color Scheme (Cool Reds) */ -:root { - /* Color system - Red accents with neutral backgrounds */ - --color-primary: #dc143c; - --color-secondary: #b91c3c; - --color-accent: #e53e5a; - --color-success: #28a745; - --color-warning: #ffc107; - --color-danger: #dc3545; - - /* Text colors */ - --text-primary: #ffffff; - --text-secondary: #e0e0e0; - --text-tertiary: #b0b0b0; - --text-muted: #808080; - - /* Background colors - Neutrals */ - --bg-primary: #0a0a0a; - --bg-secondary: #1a1a1a; - --bg-tertiary: #2a2a2a; - --bg-card: rgba(42, 42, 42, 0.8); - --bg-modal: rgba(26, 26, 26, 0.95); - - /* Border and shadow - Dark grays with red accents */ - --border-color: rgba(255, 255, 255, 0.1); - --border-accent: rgba(220, 20, 60, 0.3); - --shadow-primary: 0 4px 12px rgba(0, 0, 0, 0.3); - --glow-primary: 0 0 20px rgba(220, 20, 60, 0.4); - - /* Font system */ - --font-xs: 0.75rem; - --font-sm: 0.875rem; - --font-base: 1rem; - --font-lg: 1.125rem; - --font-xl: 1.25rem; - --font-xxl: 1.5rem; - --font-xxxl: 2rem; - --font-xxxxl: 3rem; - - /* Spacing system */ - --space-xs: 0.25rem; - --space-sm: 0.5rem; - --space-base: 1rem; - --space-lg: 1.5rem; - --space-xl: 2rem; - --space-xxl: 3rem; -} - -/* Key styling notes for Balanced Red/Crimson Theme: - * - Backgrounds: Pure blacks and dark grays for excellent readability - * - Borders: Subtle white borders for clean separation - * - Red accents: Cool reds without orange tones - crimson, dark red, rose red - * - Hover effects: Red accent borders and subtle glows - * - Title: Cool red gradient (crimson to dark red) with animated glow - * - Progress bars: Cool red gradient fills - * - Overall feel: Bold, commanding, but balanced with neutral backgrounds - */ \ No newline at end of file diff --git a/clear-audio-debug.js b/clear-audio-debug.js deleted file mode 100644 index 250d2e4..0000000 --- a/clear-audio-debug.js +++ /dev/null @@ -1,55 +0,0 @@ -// Audio Debug Script - Clear corrupted audio data and trigger fresh scan - -console.log('🧹 Starting comprehensive audio cleanup...'); - -// Clear existing customAudio data completely and restart fresh -if (window.game && window.game.dataManager) { - console.log('🗑️ Clearing all existing audio data to start fresh...'); - - // Completely reset audio storage - window.game.dataManager.set('customAudio', { background: [], ambient: [] }); - console.log('✅ Cleared all customAudio storage'); - - // Clear any cached audio library - if (window.game.audioManager) { - window.game.audioManager.audioLibrary = { - background: { general: [] } - }; - console.log('✅ Cleared audio library cache'); - } - - // Now trigger a fresh directory scan if available - if (window.game.audioManager && window.game.audioManager.scanAudioDirectories) { - console.log('🔍 Triggering fresh directory scan...'); - window.game.audioManager.scanAudioDirectories().then((scannedFiles) => { - console.log(`✅ Fresh scan completed - found ${scannedFiles.length} files`); - - // Refresh the audio manager - return window.game.audioManager.refreshAudioLibrary(); - }).then(() => { - console.log('✅ Audio library refreshed with clean data'); - - // Refresh the audio gallery - if (window.game.loadAudioGallery) { - window.game.loadAudioGallery(); - console.log('✅ Audio gallery refreshed'); - } - }).catch(error => { - console.error('❌ Error during fresh scan:', error); - }); - } else { - console.log('⚠️ Directory scanning not available - try the "Scan Directories" button in Manage Audio'); - - // Just refresh what we have - if (window.game.audioManager) { - window.game.audioManager.refreshAudioLibrary().then(() => { - console.log('✅ Audio library refreshed'); - }); - } - } - -} else { - console.error('❌ Game or dataManager not available'); -} - -console.log('🎯 Cleanup script completed. Check the console for results and try playing audio.'); \ No newline at end of file diff --git a/clear-audio-storage.js b/clear-audio-storage.js deleted file mode 100644 index 117c158..0000000 --- a/clear-audio-storage.js +++ /dev/null @@ -1,33 +0,0 @@ -// Clear Audio Storage Script - Reset all audio data to start fresh - -console.log('🧹 Clearing all audio storage data...'); - -// Clear all audio-related storage -if (window.game && window.game.dataManager) { - console.log('🗑️ Clearing customAudio storage...'); - window.game.dataManager.set('customAudio', { background: [], ambient: [] }); - - console.log('🗑️ Clearing audio library cache...'); - if (window.game.audioManager) { - window.game.audioManager.audioLibrary = { - background: { general: [] } - }; - } - - console.log('🔄 Refreshing audio library...'); - if (window.game.audioManager) { - window.game.audioManager.refreshAudioLibrary().then(() => { - console.log('✅ Audio library refreshed'); - - // Refresh the UI - if (window.game.loadAudioGallery) { - window.game.loadAudioGallery(); - console.log('✅ Audio gallery refreshed'); - } - }); - } - - console.log('✅ All audio storage cleared successfully'); -} else { - console.error('❌ Game or dataManager not available'); -} \ No newline at end of file diff --git a/debug-audio-test.js b/debug-audio-test.js deleted file mode 100644 index 40fb96f..0000000 --- a/debug-audio-test.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Audio Overlap Debug Test Script - * - * This script helps test that background sounds stop properly when progressing through tasks. - * - * To use: - * 1. Start the game and begin playing - * 2. Open the browser console (F12) - * 3. Copy and paste this script into the console - * 4. Press Enter to run the test - * - * The test will simulate rapid task progression and monitor for audio overlaps. - */ - -console.log('🎵 Starting Audio Overlap Test...'); - -// Function to test audio stopping -function testAudioStopping() { - if (!window.game || !window.game.audioManager) { - console.error('❌ Game or AudioManager not available'); - return; - } - - const audioManager = window.game.audioManager; - - console.log('🎵 Testing audio category stopping...'); - - // Test rapid audio switching - let testCount = 0; - const maxTests = 5; - - const testInterval = setInterval(() => { - testCount++; - console.log(`🎵 Test ${testCount}/${maxTests}: Playing task audio...`); - - // Stop all audio first - audioManager.stopAllImmediate(); - - // Wait a bit, then play new audio - setTimeout(() => { - audioManager.playTaskAudio('teasing', { fadeIn: 500, loop: true }); - - // Check how many audio elements are playing after a short delay - setTimeout(() => { - const currentlyPlaying = audioManager.getCurrentlyPlaying(); - const playingCount = Object.keys(currentlyPlaying).length; - - console.log(`🎵 Test ${testCount}: ${playingCount} audio tracks currently playing`); - console.log('🎵 Playing audio details:', currentlyPlaying); - - if (playingCount > 1) { - console.warn(`⚠️ WARNING: ${playingCount} tracks playing simultaneously!`); - } else { - console.log('✅ Audio overlap test passed'); - } - }, 100); - }, 50); - - if (testCount >= maxTests) { - clearInterval(testInterval); - console.log('🎵 Audio overlap test completed'); - - // Clean up - setTimeout(() => { - audioManager.stopAllImmediate(); - console.log('🎵 Test cleanup completed'); - }, 2000); - } - }, 1000); -} - -// Function to monitor audio during actual gameplay -function monitorGameplayAudio() { - if (!window.game || !window.game.audioManager) { - console.error('❌ Game or AudioManager not available'); - return; - } - - console.log('🎵 Starting gameplay audio monitoring...'); - console.log('🎵 Quickly complete or skip several tasks to test audio overlap prevention'); - - // Monitor audio every 500ms - const monitorInterval = setInterval(() => { - const currentlyPlaying = window.game.audioManager.getCurrentlyPlaying(); - const playingCount = Object.keys(currentlyPlaying).length; - - if (playingCount > 1) { - console.warn(`⚠️ AUDIO OVERLAP DETECTED: ${playingCount} tracks playing!`); - console.log('🎵 Overlapping audio details:', currentlyPlaying); - } else if (playingCount === 1) { - console.log(`✅ Single audio track playing (normal)`); - } - }, 500); - - // Stop monitoring after 30 seconds - setTimeout(() => { - clearInterval(monitorInterval); - console.log('🎵 Audio monitoring stopped'); - }, 30000); - - return monitorInterval; -} - -// Function to test rapid task progression simulation -function simulateRapidProgression() { - if (!window.game || !window.game.gameState || !window.game.gameState.isRunning) { - console.error('❌ Game not running - start a game first'); - return; - } - - console.log('🎵 Simulating rapid task progression...'); - - let progressCount = 0; - const maxProgress = 3; - - const progressInterval = setInterval(() => { - progressCount++; - console.log(`🎵 Rapid progression test ${progressCount}/${maxProgress}`); - - // Simulate completing a task (this should stop audio and start new audio) - if (window.game.gameState.isRunning) { - window.game.completeTask(); - } - - if (progressCount >= maxProgress) { - clearInterval(progressInterval); - console.log('🎵 Rapid progression test completed'); - } - }, 200); // Very fast progression to test overlap -} - -// Run the tests -console.log('🎵 Audio Debug Test Menu:'); -console.log('🎵 1. Run testAudioStopping() - Tests basic audio stopping'); -console.log('🎵 2. Run monitorGameplayAudio() - Monitors for overlaps during gameplay'); -console.log('🎵 3. Run simulateRapidProgression() - Simulates rapid task progression'); -console.log('🎵 Example: testAudioStopping()'); - -// Make functions available globally for manual testing -window.audioDebugTest = { - testAudioStopping, - monitorGameplayAudio, - simulateRapidProgression -}; - -console.log('🎵 Functions available as window.audioDebugTest.*'); -console.log('🎵 Quick test: window.audioDebugTest.testAudioStopping()'); \ No newline at end of file diff --git a/debug-video.js b/debug-video.js deleted file mode 100644 index 1fb8766..0000000 --- a/debug-video.js +++ /dev/null @@ -1,49 +0,0 @@ -// Debug script to test video player integration -console.log('🔍 Testing video player integration...'); - -// Test if button exists -const videoBtn = document.getElementById('manage-video-btn'); -console.log('Video button exists:', !!videoBtn); - -// Test if video management screen exists -const videoScreen = document.getElementById('video-management-screen'); -console.log('Video management screen exists:', !!videoScreen); - -// Test if VideoPlayerManager class is available -console.log('VideoPlayerManager class available:', typeof window.VideoPlayerManager !== 'undefined'); - -// Test if showScreen function is available -console.log('showScreen function available:', typeof window.game?.showScreen === 'function'); - -// Manual button click test -if (videoBtn) { - console.log('✅ Button found, adding click test...'); - videoBtn.addEventListener('click', () => { - console.log('🎬 Video button clicked!'); - - // Try to show the video management screen - if (window.game && typeof window.game.showScreen === 'function') { - console.log('Calling showScreen...'); - window.game.showScreen('video-management-screen'); - } else { - console.error('❌ showScreen function not available'); - } - }); -} else { - console.error('❌ Video button not found'); -} - -// Test function directly -function testVideoScreen() { - console.log('🧪 Testing direct screen switch...'); - if (window.game && typeof window.game.showScreen === 'function') { - window.game.showScreen('video-management-screen'); - } else { - console.error('❌ Game instance or showScreen not available'); - } -} - -// Make test function available globally -window.testVideoScreen = testVideoScreen; - -console.log('✅ Debug script loaded. Try: window.testVideoScreen()'); \ No newline at end of file diff --git a/electron-audio-debug.js b/electron-audio-debug.js deleted file mode 100644 index 4ad16fd..0000000 --- a/electron-audio-debug.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Electron Audio Path Debug Script - * Run this in the console to debug audio path issues in Electron - */ - -function debugElectronAudioPaths() { - console.log('🔧 Electron Audio Path Debug'); - console.log('=' .repeat(50)); - - console.log('Environment Info:'); - console.log('- Is Electron:', window.electronAPI !== undefined); - console.log('- Current URL:', window.location.href); - console.log('- Current Protocol:', window.location.protocol); - console.log('- Current Directory:', window.location.href.replace('/index.html', '')); - - // Test basic audio creation - console.log('\n🎵 Testing Basic Audio Creation:'); - try { - const testAudio = new Audio(); - console.log('✅ Audio element created successfully'); - console.log('- Initial src:', testAudio.src); - - // Test setting a simple relative path - testAudio.src = 'audio/tasks/teasing/u.mp3'; - console.log('- After setting relative path:', testAudio.src); - - // Test setting an absolute file path - const absolutePath = window.location.href.replace('/index.html', '') + '/audio/tasks/teasing/u.mp3'; - testAudio.src = absolutePath; - console.log('- After setting absolute path:', testAudio.src); - - testAudio.src = ''; - } catch (error) { - console.error('❌ Error creating audio element:', error); - } - - // Test actual file access - console.log('\n📁 Testing File Access:'); - - const testPaths = [ - 'audio/tasks/teasing/u.mp3', - './audio/tasks/teasing/u.mp3', - window.location.href.replace('/index.html', '') + '/audio/tasks/teasing/u.mp3' - ]; - - testPaths.forEach((path, index) => { - console.log(`\nTest ${index + 1}: ${path}`); - - const audio = new Audio(); - - audio.addEventListener('loadstart', () => { - console.log(` ✅ Load started for: ${path}`); - }); - - audio.addEventListener('canplay', () => { - console.log(` ✅ Can play: ${path}`); - audio.src = ''; // Clean up - }); - - audio.addEventListener('error', (e) => { - console.log(` ❌ Error loading: ${path}`); - console.log(` Error details:`, { - error: e.target.error, - src: e.target.src, - networkState: e.target.networkState, - readyState: e.target.readyState - }); - }); - - try { - audio.src = path; - setTimeout(() => { - if (audio.readyState === 0 && audio.networkState !== 2) { - console.log(` ⏰ Timeout for: ${path}`); - } - }, 2000); - } catch (error) { - console.log(` ❌ Exception setting src: ${error.message}`); - } - }); - - // Test if electronAPI is available and working - if (window.electronAPI) { - console.log('\n⚡ Testing Electron API:'); - - window.electronAPI.fileExists('audio/tasks/teasing/u.mp3').then(exists => { - console.log('- File exists (relative):', exists); - }).catch(err => { - console.log('- Error checking file existence:', err); - }); - - window.electronAPI.getAppPath().then(appPath => { - console.log('- App path:', appPath); - }).catch(err => { - console.log('- Error getting app path:', err); - }); - } -} - -// Auto-run the debug -console.log('🔧 Electron Audio Debug loaded. Running automatic test...'); -debugElectronAudioPaths(); - -// Make function available globally -window.debugElectronAudioPaths = debugElectronAudioPaths; \ No newline at end of file diff --git a/index.html b/index.html index 91af99e..0df3021 100644 --- a/index.html +++ b/index.html @@ -486,13 +486,28 @@
-

🎥 Import Video Files

-
- - - - - +

🎥 Video Library Management

+

+ Link external directories to build your video library. All videos are scanned recursively (including subdirectories). +

+ + +
+

� Linked Directories

+
+ + + +
+
+ +
+
+ + +
+ 0 videos total + 0 directories linked
💻 Desktop: Native file dialogs • Supports MP4, WebM, OGV, MOV formats @@ -511,39 +526,20 @@
diff --git a/src/features/video/videoPlayerManager.js b/src/features/video/videoPlayerManager.js index feb5bbf..ceecb2e 100644 --- a/src/features/video/videoPlayerManager.js +++ b/src/features/video/videoPlayerManager.js @@ -148,24 +148,21 @@ class VideoPlayerManager { try { // In Electron environment, use the desktop file manager if (window.electronAPI && window.desktopFileManager) { - // Map category names to match file manager - const dirCategory = category === 'task' ? 'tasks' : - category === 'reward' ? 'rewards' : - category === 'punishment' ? 'punishments' : category; - const videos = await window.desktopFileManager.scanDirectoryForVideos(dirCategory); - this.videoLibrary[category] = videos; + // Use unified video library - get all videos and filter by category if needed + const allVideos = window.desktopFileManager.getAllVideos(); + + // For now, just use all videos for any category since we removed categories + // In the future, we could add tags or other filtering mechanisms + this.videoLibrary[category] = allVideos; } else { - // In browser environment, try to load from localStorage - const storedVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}'); - const dirCategory = category === 'task' ? 'tasks' : - category === 'reward' ? 'rewards' : - category === 'punishment' ? 'punishments' : category; - this.videoLibrary[category] = storedVideos[dirCategory] || []; + // In browser environment, try to load from unified storage + const unifiedData = JSON.parse(localStorage.getItem('unifiedVideoLibrary') || '{}'); + this.videoLibrary[category] = unifiedData.allVideos || []; } - console.log(`📹 Found ${this.videoLibrary[category].length} ${category} videos`); + console.log(`📹 Found ${this.videoLibrary[category].length} ${category} videos (from unified library)`); } catch (error) { - console.warn(`⚠️ Could not scan ${category} videos:`, error); + console.warn(`⚠️ Could not load ${category} videos:`, error); this.videoLibrary[category] = []; } } diff --git a/src/styles/styles-original-backup.css b/src/styles/styles-original-backup.css deleted file mode 100644 index 3a4d7a7..0000000 --- a/src/styles/styles-original-backup.css +++ /dev/null @@ -1,2931 +0,0 @@ -/* Reset and base styles */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: 'Arial', sans-serif; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; -} - -.game-container { - background: white; - border-radius: 15px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); - width: 95%; - max-width: 1400px; /* Increased from 800px for larger screens */ - min-width: 780px; /* Ensure minimum usable width */ - min-height: 600px; - overflow: hidden; - margin: 10px auto; /* Center and add some margin */ -} - -/* Header */ -.game-header { - background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); - color: white; - padding: 20px; - text-align: center; - position: relative; - display: flex; - align-items: center; - justify-content: center; -} - -.game-header h1 { - font-size: 2em; - margin: 0; -} - -.tagline { - font-size: 1.2em; - color: #666; - font-style: italic; - margin: 10px 0 20px 0; - opacity: 0.9; -} - -/* Compact Timer (top-right corner) */ -.timer-compact { - position: absolute; - top: 10px; - right: 80px; - display: flex; - align-items: center; - gap: 5px; - font-size: 0.9em; - background: rgba(0, 0, 0, 0.1); - padding: 5px 10px; - border-radius: 15px; - backdrop-filter: blur(5px); -} - -.timer { - font-weight: bold; - font-family: 'Courier New', monospace; -} - -.timer-status { - font-size: 0.8em; - opacity: 0.8; -} - -/* Compact Music Controls (expandable on hover) */ -.music-controls-compact { - position: absolute; - top: 10px; - right: 20px; -} - -.music-icon-btn { - background: rgba(0, 0, 0, 0.1); - border: none; - color: white; - font-size: 1.5em; - padding: 8px; - border-radius: 50%; - cursor: pointer; - transition: all 0.3s ease; - backdrop-filter: blur(5px); - width: 45px; - height: 45px; - display: flex; - align-items: center; - justify-content: center; -} - -.music-icon-btn:hover { - background: rgba(255, 255, 255, 0.2); - transform: scale(1.1); -} - -/* Expandable Music Panel */ -.music-panel-expanded { - position: absolute; - top: 55px; - right: 0; - background: rgba(255, 255, 255, 0.95); - color: #333; - border-radius: 10px; - padding: 15px; - box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2); - backdrop-filter: blur(10px); - opacity: 0; - visibility: hidden; - transform: translateY(-10px); - transition: all 0.3s ease; - min-width: 200px; - z-index: 1000; -} - -.music-controls-compact:hover .music-panel-expanded { - opacity: 1; - visibility: visible; - transform: translateY(0); -} - -.music-row { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - margin-bottom: 10px; -} - -.music-row:last-child { - margin-bottom: 0; -} - -.music-btn-small { - background: #007bff; - border: none; - color: white; - font-size: 1em; - padding: 6px 10px; - border-radius: 5px; - cursor: pointer; - transition: all 0.2s ease; - min-width: 35px; -} - -.music-btn-small:hover { - background: #0056b3; - transform: scale(1.05); -} - -.track-dropdown-compact { - background: white; - border: 1px solid #ddd; - border-radius: 5px; - padding: 5px 8px; - font-size: 0.9em; - width: 100%; - cursor: pointer; -} - -.volume-control-compact { - display: flex; - align-items: center; - gap: 8px; - width: 100%; -} - -.volume-icon { - font-size: 1em; -} - -.volume-slider-compact { - flex: 1; - height: 4px; - background: #ddd; - border-radius: 2px; - outline: none; - cursor: pointer; -} - -.volume-slider-compact::-webkit-slider-thumb { - appearance: none; - width: 16px; - height: 16px; - background: #007bff; - border-radius: 50%; - cursor: pointer; -} - -.volume-percent { - font-size: 0.8em; - min-width: 30px; - text-align: center; -} - -.music-status-compact { - text-align: center; - font-size: 0.8em; - color: #666; - font-style: italic; -} - -/* Data Management Controls */ -.data-controls { - display: flex; - gap: 10px; - margin: 10px 0; - flex-wrap: wrap; - justify-content: center; -} - -.data-btn, .btn-secondary { - background: rgba(255, 255, 255, 0.2); - border: 2px solid rgba(255, 255, 255, 0.3); - color: white; - padding: 8px 16px; - border-radius: 8px; - cursor: pointer; - font-size: 0.9em; - transition: all 0.3s ease; -} - -.data-btn:hover, .btn-secondary:hover { - background: rgba(255, 255, 255, 0.3); - transform: translateY(-2px); -} - -/* Statistics Modal */ -.modal { - position: fixed; - z-index: 1000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(5px); -} - -.modal-content { - background: var(--card-bg); - margin: 5% auto; - padding: 0; - border-radius: 16px; - width: 90%; - max-width: 800px; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4); - animation: modalSlideIn 0.3s ease; -} - -@keyframes modalSlideIn { - from { transform: translateY(-50px); opacity: 0; } - to { transform: translateY(0); opacity: 1; } -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 30px; - border-bottom: 2px solid var(--primary-color); - background: linear-gradient(135deg, var(--primary-color), var(--primary-dark)); - color: white; - border-radius: 16px 16px 0 0; -} - -.modal-header h2 { - margin: 0; - font-size: 1.5em; -} - -.close { - font-size: 28px; - font-weight: bold; - cursor: pointer; - opacity: 0.8; - transition: opacity 0.3s ease; -} - -.close:hover { - opacity: 1; - transform: scale(1.1); -} - -.modal-body { - padding: 30px; -} - -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 20px; - margin-bottom: 30px; -} - -.stat-card { - background: rgba(255, 255, 255, 0.05); - border: 2px solid rgba(255, 255, 255, 0.1); - border-radius: 12px; - padding: 20px; - text-align: center; - transition: all 0.3s ease; -} - -.stat-card:hover { - border-color: var(--primary-color); - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2); -} - -.stat-card h3 { - margin: 0 0 10px 0; - font-size: 1em; - color: var(--text-color); - opacity: 0.8; -} - -.stat-value { - font-size: 2.5em; - font-weight: bold; - color: var(--primary-color); - line-height: 1; -} - -.stats-actions { - display: flex; - gap: 15px; - justify-content: center; - flex-wrap: wrap; -} - -.btn-warning { - background: #ff6b35; - border-color: #ff6b35; -} - -.btn-warning:hover { - background: #e55a2e; -} - -/* Notification System */ -.notification { - position: fixed; - top: 20px; - right: 20px; - background: var(--card-bg); - border: 2px solid var(--primary-color); - color: var(--text-color); - padding: 15px 20px; - border-radius: 8px; - font-weight: 500; - transform: translateX(400px); - opacity: 0; - transition: all 0.3s ease; - z-index: 1001; - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); -} - -.notification.show { - transform: translateX(0); - opacity: 1; -} - -.notification.success { - border-color: #28a745; - background: rgba(40, 167, 69, 0.1); -} - -.notification.error { - border-color: #dc3545; - background: rgba(220, 53, 69, 0.1); -} - -.notification.info { - border-color: var(--primary-color); - background: rgba(var(--primary-color-rgb), 0.1); -} - -/* Streak Bonus Notification */ -.streak-bonus-notification { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(0.8); - opacity: 0; - background: linear-gradient(135deg, #ff6b35, #f7931e); - color: white; - padding: 20px; - border-radius: 15px; - text-align: center; - box-shadow: 0 10px 30px rgba(255, 107, 53, 0.4); - z-index: 1002; - transition: all 0.3s ease; - backdrop-filter: blur(10px); - border: 2px solid rgba(255, 255, 255, 0.2); -} - -.streak-bonus-notification.show { - transform: translate(-50%, -50%) scale(1); - opacity: 1; -} - -.streak-bonus-content { - display: flex; - align-items: center; - gap: 15px; -} - -.streak-icon { - font-size: 2.5em; - animation: fireFlicker 1s ease-in-out infinite alternate; -} - -.streak-text { - text-align: left; -} - -.streak-title { - font-size: 1.2em; - font-weight: bold; - margin-bottom: 5px; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); -} - -.streak-bonus { - font-size: 1em; - opacity: 0.9; - font-weight: 500; -} - -@keyframes fireFlicker { - 0% { transform: rotate(-2deg) scale(1); } - 100% { transform: rotate(2deg) scale(1.05); } -} - -/* Streak Milestone Styling */ -.streak-stat.streak-milestone { - background: linear-gradient(45deg, rgba(255, 107, 53, 0.1), rgba(247, 147, 30, 0.1)); - border-radius: 8px; - padding: 5px 8px; - border: 1px solid rgba(255, 107, 53, 0.3); - animation: streakGlow 2s ease-in-out infinite alternate; -} - -@keyframes streakGlow { - 0% { box-shadow: 0 0 5px rgba(255, 107, 53, 0.3); } - 100% { box-shadow: 0 0 15px rgba(255, 107, 53, 0.5); } -} -.audio-bar:nth-child(4) { animation-delay: 0.6s; } -.audio-bar:nth-child(5) { animation-delay: 0.8s; } - -@keyframes audioWave { - 0%, 100% { height: 4px; } - 50% { height: 16px; } -} - -/* Desktop Application Styles */ -.desktop-mode .upload-controls { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin-bottom: 10px; -} - -.desktop-mode .upload-controls .btn { - flex: 1; - min-width: 180px; -} - -.desktop-only { - display: none; -} - -.desktop-mode .desktop-only { - display: inline-block; -} - -.desktop-feature { - display: none; -} - -.desktop-mode .desktop-feature { - display: inline; -} - -.web-feature { - display: inline; -} - -.desktop-mode .web-feature { - display: none; -} - -/* Enhanced button styling for desktop */ -.desktop-mode .btn { - padding: 12px 20px; - font-size: 14px; - font-weight: 500; - border-radius: 6px; - transition: all 0.2s ease; -} - -.desktop-mode .btn:hover { - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.desktop-mode .btn-primary { - background: linear-gradient(135deg, #007bff, #0056b3); - border: none; - color: white; -} - -.desktop-mode .btn-warning { - background: linear-gradient(135deg, #ffc107, #e0a800); - border: none; - color: #212529; -} - -/* Desktop-specific upload section */ -.desktop-mode .upload-section h3 { - color: var(--primary-color); - margin-bottom: 15px; - font-size: 18px; - display: flex; - align-items: center; - gap: 8px; -} - -.desktop-mode .upload-info { - font-size: 13px; - color: var(--text-secondary); - font-style: italic; - margin-top: 8px; -} - -/* Responsive design for music controls */ -@media (max-width: 768px) { - .game-header { - flex-direction: column; - gap: 15px; - } - - .game-header h1 { - font-size: 1.6em; - } - - .timer-container { - position: static; - transform: none; - } - - .music-controls { - justify-content: center; - gap: 10px; - } - - .track-dropdown { - min-width: 120px; - font-size: 0.8em; - } - - .volume-control { - padding: 4px 8px; - } - - .volume-slider { - width: 60px; - } - - .music-status { - display: none; /* Hide on mobile to save space */ - } -} - -@media (max-width: 480px) { - .music-controls { - flex-wrap: wrap; - justify-content: center; - } - - .track-dropdown { - order: 3; - flex-basis: 100%; - margin-top: 5px; - } -} - -.timer { - font-family: 'Courier New', monospace; - font-size: 1.5em; - font-weight: bold; -} - -.timer-status { - font-size: 0.9em; - opacity: 0.8; -} - -/* Screen management */ -.screen { - display: none; - padding: 20px; - text-align: center; - min-height: 500px; -} - -.screen.active { - display: block; -} - -/* Start screen */ -#start-screen h2 { - color: #333; - margin-bottom: 15px; -} - -#start-screen p { - color: #666; - margin-bottom: 25px; - font-size: 1.1em; -} - -/* Theme Selector */ -.theme-selector { - margin-bottom: 25px; -} - -.theme-label { - display: block; - margin-bottom: 10px; - font-size: 1.1em; - font-weight: 600; - color: #333; -} - -.theme-dropdown { - padding: 10px 15px; - font-size: 1em; - border: 2px solid #ddd; - border-radius: 8px; - background: white; - cursor: pointer; - min-width: 200px; - transition: border-color 0.3s ease; -} - -.theme-dropdown:hover { - border-color: #007bff; -} - -.theme-dropdown:focus { - outline: none; - border-color: #007bff; - box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); -} - -/* Task Management Screen */ -#task-management-screen h2 { - color: #333; - margin-bottom: 25px; -} - -.task-editor-section { - background: #f8f9fa; - border-radius: 10px; - padding: 20px; - margin-bottom: 25px; -} - -.task-editor-section h3 { - color: #333; - margin-bottom: 15px; - font-size: 1.2em; -} - -.task-input-group { - margin-bottom: 15px; -} - -.task-input-group label { - display: block; - margin-bottom: 5px; - font-weight: 600; - color: #555; -} - -.task-input-group textarea, -.task-input-group select { - width: 100%; - padding: 10px; - border: 2px solid #ddd; - border-radius: 6px; - font-size: 1em; - transition: border-color 0.3s ease; -} - -.task-input-group textarea:focus, -.task-input-group select:focus { - outline: none; - border-color: #007bff; - box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); -} - -.task-list-section { - margin-bottom: 25px; -} - -.task-list-section h3 { - color: #333; - margin-bottom: 15px; - font-size: 1.2em; -} - -.task-tabs { - display: flex; - margin-bottom: 15px; - border-bottom: 2px solid #ddd; -} - -.tab-btn { - padding: 10px 20px; - border: none; - background: none; - cursor: pointer; - font-size: 1em; - font-weight: 600; - color: #666; - border-bottom: 3px solid transparent; - transition: all 0.3s ease; -} - -.tab-btn.active { - color: #007bff; - border-bottom-color: #007bff; -} - -.tab-btn:hover { - color: #007bff; -} - -.task-list { - display: none; - max-height: 300px; - overflow-y: auto; - border: 1px solid #ddd; - border-radius: 6px; - background: white; -} - -.task-list.active { - display: block; -} - -.task-item { - padding: 15px; - border-bottom: 1px solid #eee; - display: flex; - justify-content: space-between; - align-items: flex-start; -} - -.task-item:last-child { - border-bottom: none; -} - -.task-text-display { - flex: 1; - margin-right: 15px; - line-height: 1.4; -} - -.task-difficulty-display { - font-size: 0.85em; - color: #666; - margin: 5px 15px 5px 0; - font-weight: bold; -} - -.task-actions { - display: flex; - gap: 8px; -} - -.btn-small { - padding: 5px 10px; - font-size: 0.85em; - min-width: auto; -} - -.management-buttons { - display: flex; - gap: 15px; - justify-content: center; -} - -.empty-list { - padding: 40px; - text-align: center; - color: #666; - font-style: italic; -} - -/* Task container */ -.task-container { - background: #f8f9fa; - border-radius: 15px; - padding: 25px; - margin-bottom: 20px; -} - -.task-type { - margin-bottom: 15px; -} - -#task-type-indicator { - background: #28a745; - color: white; - padding: 5px 15px; - border-radius: 20px; - font-size: 0.9em; - font-weight: bold; -} - -#task-type-indicator.consequence { - background: #dc3545; -} - -.task-image-container { - margin-bottom: 20px; - display: flex; - justify-content: center; - align-items: center; - min-height: max(200px, 25vh); /* Dynamic minimum height based on viewport */ - max-height: 85vh; /* Allow container to take up most of screen */ - width: 100%; /* Full width for centering */ -} - -.task-image { - /* Hybrid responsive sizing - scales with viewport but has reasonable limits */ - max-width: min(90vw, 1200px); /* 90% of viewport width, capped at 1200px */ - max-height: min(70vh, 800px); /* 70% of viewport height, capped at 800px */ - min-height: max(200px, 20vh); /* Minimum 200px or 20% of viewport height */ - min-width: 250px; /* Prevent images from being too narrow */ - - /* Maintain aspect ratio and image quality */ - width: auto; - height: auto; - object-fit: contain; /* Preserve aspect ratio, fit within bounds */ - - /* Visual enhancements */ - border-radius: 15px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); - transition: all 0.3s ease; /* Smooth transitions when resizing */ - object-fit: cover; -} - -.task-text-container { - margin-bottom: 25px; -} - -.task-text { - color: #333; - font-size: 1.2em; - line-height: 1.4; -} - -/* Task difficulty and points display */ -.task-difficulty { - background: rgba(255, 255, 255, 0.8); - padding: 8px 16px; - border-radius: 20px; - font-size: 0.9em; - font-weight: bold; - margin-bottom: 15px; - text-align: center; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.task-points { - background: #ffd700; - color: #333; - padding: 6px 12px; - border-radius: 15px; - font-size: 0.8em; - font-weight: bold; - margin-left: 10px; - box-shadow: 0 1px 3px rgba(0,0,0,0.2); -} - -/* Buttons */ -.action-buttons { - display: flex; - gap: 15px; - justify-content: center; - margin-bottom: 20px; -} - -.btn { - padding: 12px 24px; - border: none; - border-radius: 6px; - font-size: 1em; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - min-width: 100px; -} - -.btn:hover { - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); -} - -.btn-primary { - background: #007bff; - color: white; -} - -.btn-primary:hover { - background: #0056b3; -} - -.btn-success { - background: #28a745; - color: white; -} - -.btn-success:hover { - background: #1e7e34; -} - -.btn-warning { - background: #ffc107; - color: #212529; -} - -.btn-warning:hover { - background: #e0a800; -} - -.btn-info { - background: #17a2b8; - color: white; -} - -.btn-info:hover { - background: #117a8b; -} - -.btn-danger { - background: #dc3545; - color: white; - position: relative; -} - -.btn-danger:hover { - background: #c82333; -} - -.btn-danger:disabled { - background: #6c757d; - color: #ffffff; - opacity: 0.65; - cursor: not-allowed; -} - -.btn-danger:disabled:hover { - background: #6c757d; - transform: none; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -/* Options Menu Styles */ -.options-section { - margin-top: 20px; - position: relative; -} - -.btn-tertiary { - background: #6c757d; - color: white; - margin-bottom: 10px; -} - -.btn-tertiary:hover { - background: #545b62; -} - -.options-menu { - background: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 8px; - padding: 20px; - margin-top: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); -} - -.option-item { - margin-bottom: 15px; - padding-bottom: 15px; - border-bottom: 1px solid #e9ecef; -} - -.option-item:last-child { - margin-bottom: 0; - padding-bottom: 0; - border-bottom: none; -} - -.option-label { - display: block; - font-weight: 600; - margin-bottom: 8px; - color: #495057; -} - -.theme-dropdown { - width: 100%; - padding: 8px 12px; - border: 1px solid #ced4da; - border-radius: 4px; - font-size: 0.95em; -} - -.data-controls, .other-controls { - display: flex; - gap: 10px; - flex-wrap: wrap; -} - -.btn-option { - background: #e9ecef; - color: #495057; - padding: 8px 16px; - font-size: 0.9em; - min-width: auto; -} - -.btn-option:hover { - background: #dee2e6; - transform: translateY(-1px); -} - -.main-actions { - display: flex; - flex-direction: column; - gap: 15px; - margin-bottom: 20px; -} - -.main-actions .btn { - width: 100%; - max-width: 300px; - margin: 0 auto; -} - -/* Game Mode Selection */ -.game-mode-selection { - background: #f8f9fa; - border-radius: 10px; - padding: 20px; - margin: 20px 0; - border: 2px solid #e0e0e0; -} - -.game-mode-selection h3 { - color: #333; - margin-bottom: 15px; - text-align: center; - font-size: 1.2em; -} - -.game-mode-options { - display: flex; - flex-direction: column; - gap: 15px; -} - -.game-mode-option { - position: relative; -} - -.game-mode-option input[type="radio"] { - position: absolute; - opacity: 0; - cursor: pointer; -} - -.game-mode-option label { - display: block; - background: white; - border: 2px solid #ddd; - border-radius: 8px; - padding: 15px; - cursor: pointer; - transition: all 0.3s ease; - position: relative; -} - -.game-mode-option label:hover { - border-color: #4facfe; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(79, 172, 254, 0.2); -} - -.game-mode-option input[type="radio"]:checked + label { - border-color: #4facfe; - background: linear-gradient(135deg, #4facfe22, #00f2fe11); - box-shadow: 0 4px 12px rgba(79, 172, 254, 0.3); -} - -.game-mode-option label strong { - color: #333; - font-size: 1.1em; - display: block; - margin-bottom: 5px; -} - -.game-mode-option label p { - color: #666; - font-size: 0.9em; - margin: 0; - line-height: 1.4; -} - -.mode-config { - margin-top: 15px; - padding-top: 15px; - border-top: 1px solid #eee; -} - -.mode-config label { - font-size: 0.9em; - color: #555; - font-weight: 500; -} - -.mode-config select { - margin-left: 10px; - padding: 5px 10px; - border: 1px solid #ddd; - border-radius: 4px; - background: white; - font-size: 0.9em; -} - -.custom-time-input, -.custom-score-input { - margin-left: 10px; - padding: 5px 10px; - border: 1px solid #ddd; - border-radius: 4px; - background: white; - font-size: 0.9em; - width: 80px; - display: none; -} - -.custom-time-input:focus, -.custom-score-input:focus { - border-color: #007bff; - outline: none; - box-shadow: 0 0 3px rgba(0, 123, 255, 0.3); -} - -.mercy-cost { - display: block; - font-size: 0.8em; - opacity: 0.9; - font-weight: bold; -} - -.btn-secondary { - background: #6c757d; - color: white; -} - -.btn-secondary:hover { - background: #545b62; -} - -/* Loading indicator styles */ -.btn:disabled { - opacity: 0.7; - cursor: not-allowed; -} - -.btn:disabled:hover { - transform: none; - box-shadow: none; -} - -.btn-loading { - animation: pulse 1.5s ease-in-out infinite; -} - -@keyframes pulse { - 0% { opacity: 1; } - 50% { opacity: 0.6; } - 100% { opacity: 1; } -} - -/* Game stats */ -.game-stats { - display: flex; - justify-content: space-around; - background: #e9ecef; - padding: 15px; - border-radius: 8px; -} - -.stat { - text-align: center; -} - -#score { - color: #ffd700; - font-weight: bold; - font-size: 1.3em; - text-shadow: 0 1px 3px rgba(0,0,0,0.3); - background: rgba(255, 215, 0, 0.1); - padding: 4px 8px; - border-radius: 6px; - border: 1px solid rgba(255, 215, 0, 0.3); -} - -.stat-label { - display: block; - font-size: 0.9em; - color: #666; - margin-bottom: 5px; -} - -.stat-value { - display: block; - font-size: 1.5em; - font-weight: bold; - color: #333; -} - -/* Paused screen */ -#paused-screen h2 { - color: #333; - margin-bottom: 15px; -} - -#paused-screen p { - color: #666; - margin-bottom: 25px; -} - -#paused-screen .btn { - margin: 0 10px; -} - -/* Game over screen */ -#game-over-screen h2 { - color: #333; - margin-bottom: 20px; -} - -.final-stats { - background: #f8f9fa; - padding: 20px; - border-radius: 8px; - margin-bottom: 25px; -} - -.final-stats p { - margin-bottom: 10px; - font-size: 1.1em; - color: #333; -} - -.final-stats span { - font-weight: bold; - color: #007bff; -} - -/* Responsive design */ -@media (max-width: 480px) { - .game-container { - width: 98%; - } - - .task-image-container { - min-height: 250px; - } - - .task-image { - max-width: 95%; - max-height: 250px; - min-height: 200px; - } - - .action-buttons { - flex-direction: column; - align-items: center; - } - - .btn { - width: 200px; - } - - .game-stats { - flex-direction: column; - gap: 10px; - } - - .timer-container { - flex-direction: column; - gap: 5px; - } - - .screen { - padding: 15px; - } -} - -/* THEME SYSTEM */ -/* Ocean Theme (Default) - Already defined above */ - -/* Sunset Theme */ -body.theme-sunset { - background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 100%); -} -.theme-sunset .game-header { - background: linear-gradient(135deg, #ff6b35 0%, #f7931e 100%); -} -.theme-sunset .btn-primary { background: #ff6b35; } -.theme-sunset .btn-primary:hover { background: #e55a2b; } -.theme-sunset .btn-success { background: #ff8c00; } -.theme-sunset .btn-success:hover { background: #e67e00; } - -/* Forest Theme */ -body.theme-forest { - background: linear-gradient(135deg, #134e5e 0%, #71b280 100%); -} -.theme-forest .game-header { - background: linear-gradient(135deg, #2d5016 0%, #a8e6cf 100%); -} -.theme-forest .btn-primary { background: #2d5016; } -.theme-forest .btn-primary:hover { background: #1e3510; } -.theme-forest .btn-success { background: #4caf50; } -.theme-forest .btn-success:hover { background: #45a049; } - -/* Midnight Theme */ -body.theme-midnight { - background: linear-gradient(135deg, #0c0c0c 0%, #434343 100%); -} -.theme-midnight .game-container { - background: #1a1a1a; - color: white; -} -.theme-midnight .game-header { - background: linear-gradient(135deg, #000000 0%, #434343 100%); -} -.theme-midnight .task-container { - background: #2a2a2a; - color: white; -} -.theme-midnight .task-text { - color: white; -} -.theme-midnight .game-stats { - background: #2a2a2a; -} -.theme-midnight .btn-primary { background: #6c757d; } -.theme-midnight .btn-primary:hover { background: #545b62; } - -/* Pastel Theme */ -body.theme-pastel { - background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); -} -.theme-pastel .game-header { - background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); -} -.theme-pastel .btn-primary { background: #ff9a9e; } -.theme-pastel .btn-primary:hover { background: #ff8a8e; } -.theme-pastel .btn-success { background: #a8e6cf; } -.theme-pastel .btn-success:hover { background: #98d6bf; } -.theme-pastel .btn-warning { background: #ffd3a5; color: #333; } -.theme-pastel .btn-warning:hover { background: #ffc385; } - -/* Neon Theme */ -body.theme-neon { - background: linear-gradient(135deg, #0f0f0f 0%, #2a0845 100%); -} -.theme-neon .game-container { - background: #1a1a1a; - color: #00ff00; - border: 2px solid #00ff00; - box-shadow: 0 0 30px rgba(0, 255, 0, 0.3); -} -.theme-neon .game-header { - background: linear-gradient(135deg, #000000 0%, #ff006e 100%); - border-bottom: 2px solid #00ff00; -} -.theme-neon .task-container { - background: #0a0a0a; - color: #00ff00; - border: 1px solid #00ff00; -} -.theme-neon .task-text { - color: #00ff00; -} -.theme-neon .btn-primary { - background: #ff006e; - border: 2px solid #00ff00; - box-shadow: 0 0 10px rgba(255, 0, 110, 0.5); -} -.theme-neon .btn-success { - background: #00ff00; - color: black; - box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); -} - -/* Autumn Theme */ -body.theme-autumn { - background: linear-gradient(135deg, #d2691e 0%, #8b4513 100%); -} -.theme-autumn .game-header { - background: linear-gradient(135deg, #cd853f 0%, #a0522d 100%); -} -.theme-autumn .btn-primary { background: #8b4513; } -.theme-autumn .btn-primary:hover { background: #7a3f12; } -.theme-autumn .btn-success { background: #daa520; } -.theme-autumn .btn-success:hover { background: #b8941b; } -.theme-autumn .btn-warning { background: #ff8c00; } -.theme-autumn .btn-warning:hover { background: #e67e00; } - -/* Monochrome Theme */ -body.theme-monochrome { - background: linear-gradient(135deg, #000000 0%, #434343 100%); -} -.theme-monochrome .game-container { - background: white; - border: 3px solid black; -} -.theme-monochrome .game-header { - background: black; - border-bottom: 3px solid white; -} -.theme-monochrome .task-container { - background: #f8f8f8; - border: 2px solid black; -} -.theme-monochrome .btn-primary { - background: black; - color: white; - border: 2px solid black; -} -.theme-monochrome .btn-primary:hover { - background: white; - color: black; -} -.theme-monochrome .btn-success { - background: white; - color: black; - border: 2px solid black; -} -.theme-monochrome .btn-success:hover { - background: black; - color: white; -} - -/* Help Menu Styles */ -.help-section { - margin-bottom: 25px; -} - -.help-section h3 { - color: #333; - margin-bottom: 15px; - padding-bottom: 8px; - border-bottom: 2px solid #e9ecef; - font-size: 1.1em; -} - -.shortcut-list { - display: flex; - flex-direction: column; - gap: 10px; -} - -.shortcut-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 12px; - background: #f8f9fa; - border-radius: 6px; - border-left: 4px solid #007bff; -} - -.shortcut-key { - background: #343a40; - color: white; - padding: 4px 12px; - border-radius: 4px; - font-family: monospace; - font-weight: bold; - font-size: 0.9em; - min-width: 60px; - text-align: center; -} - -.shortcut-action { - flex: 1; - margin-left: 15px; - color: #555; - font-weight: 500; -} - -.help-tips { - background: #e3f2fd; - padding: 15px; - border-radius: 8px; - border-left: 4px solid #2196f3; - margin: 0; -} - -.help-tips li { - margin-bottom: 8px; - color: #1565c0; - line-height: 1.4; -} - -.help-tips li:last-child { - margin-bottom: 0; -} - -/* Image Management Styles */ -.upload-section { - background: #f8f9fa; - padding: 20px; - border-radius: 8px; - margin-bottom: 30px; - border: 2px dashed #dee2e6; -} - -.upload-controls { - display: flex; - align-items: center; - gap: 15px; -} - -.upload-info { - font-size: 0.9em; - color: #6c757d; - font-style: italic; -} - -.gallery-section { - margin-bottom: 30px; -} - -.gallery-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - flex-wrap: wrap; - gap: 15px; -} - -.gallery-controls { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.image-count { - font-size: 0.9em; - color: #6c757d; - font-weight: 500; -} - -.image-gallery { - display: none; /* Hide galleries by default */ - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 15px; - max-height: 400px; - overflow-y: auto; - padding: 10px; - border: 1px solid #dee2e6; - border-radius: 8px; -} - -.image-gallery.active { - display: grid; /* Show active gallery as grid */ -} - -.image-item { - position: relative; - background: white; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; -} - -.image-item:hover { - transform: translateY(-2px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); -} - -.image-item.selected { - border: 3px solid #007bff; - transform: scale(0.95); -} - -.image-preview { - width: 100%; - height: 150px; - object-fit: cover; - cursor: pointer; -} - -.image-info { - padding: 10px; - background: white; -} - -.image-name { - font-size: 0.8em; - color: #333; - margin-bottom: 5px; - word-break: break-word; -} - -.image-controls { - display: flex; - justify-content: space-between; - align-items: center; -} - -.image-checkbox { - margin: 0; -} - -.image-status { - font-size: 0.7em; - padding: 2px 6px; - border-radius: 4px; - font-weight: bold; -} - -.image-enabled { - background: #d4edda; - color: #155724; -} - -.image-disabled { - background: #f8d7da; - color: #721c24; -} - -.loading, .no-images { - text-align: center; - padding: 40px; - color: #6c757d; - font-style: italic; -} - -.no-images { - background: #f8f9fa; - border-radius: 8px; - border: 2px dashed #dee2e6; -} - -/* Audio Management Styles */ -.audio-tabs { - display: flex; - margin-bottom: 15px; - border-bottom: 2px solid #dee2e6; -} - -.audio-tabs .tab-btn { - flex: 1; - padding: 12px 16px; - border: none; - background: #f8f9fa; - color: #6c757d; - cursor: pointer; - border-bottom: 3px solid transparent; - transition: all 0.3s ease; - font-weight: 600; -} - -.audio-tabs .tab-btn:hover { - background: #e9ecef; - color: #495057; -} - -.audio-tabs .tab-btn.active { - background: white; - color: #007bff; - border-bottom-color: #007bff; -} - -.audio-count { - font-size: 0.9em; - color: #6c757d; - font-weight: 500; -} - -.audio-gallery { - display: none; /* Hide galleries by default */ - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 15px; - max-height: 400px; - overflow-y: auto; - padding: 10px; - border: 1px solid #dee2e6; - border-radius: 8px; -} - -.audio-gallery.active { - display: grid; /* Show active gallery as grid */ -} - -.audio-item { - position: relative; - background: white; - border-radius: 8px; - overflow: hidden; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; - padding: 15px; -} - -.audio-item:hover { - transform: translateY(-2px); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); -} - -.audio-item.selected { - border: 3px solid #007bff; - transform: scale(0.98); - background: #f8f9ff; -} - -.audio-icon { - font-size: 2.5em; - text-align: center; - margin-bottom: 10px; - color: #007bff; -} - -.audio-title { - font-size: 0.9em; - color: #333; - margin-bottom: 8px; - font-weight: 600; - word-break: break-word; - text-align: center; -} - -.audio-filename { - font-size: 0.7em; - color: #6c757d; - margin-bottom: 10px; - text-align: center; - word-break: break-word; -} - -.audio-controls { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 10px; -} - -.audio-preview-btn { - background: #28a745; - color: white; - border: none; - padding: 4px 8px; - border-radius: 4px; - cursor: pointer; - font-size: 0.8em; - transition: background 0.3s ease; -} - -.audio-preview-btn:hover { - background: #218838; -} - -.audio-checkbox { - margin: 0; -} - -.audio-status { - font-size: 0.7em; - padding: 2px 6px; - border-radius: 4px; - font-weight: bold; -} - -.audio-enabled { - background: #d4edda; - color: #155724; -} - -.audio-disabled { - background: #f8d7da; - color: #721c24; -} - -.audio-preview-section { - background: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 8px; - padding: 20px; - margin: 20px 0; -} - -.audio-preview-section h4 { - margin-bottom: 15px; - color: #333; -} - -.preview-controls { - display: flex; - flex-direction: column; - gap: 10px; -} - -.preview-info { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: 10px; -} - -.preview-info span { - font-size: 0.9em; - color: #6c757d; - font-weight: 500; -} - -.no-audio { - text-align: center; - padding: 40px; - color: #6c757d; - font-style: italic; - background: #f8f9fa; - border-radius: 8px; - border: 2px dashed #dee2e6; -} - -/* Audio category icons */ -.audio-item[data-category="background"] .audio-icon::before { - content: "🎵"; -} - -.audio-item[data-category="ambient"] .audio-icon::before { - content: "🌿"; -} - -.audio-item[data-category="effects"] .audio-icon::before { - content: "🔊"; -} - -/* =========================== - RESPONSIVE DESIGN ENHANCEMENTS - =========================== */ - -/* Large screens - take advantage of extra space */ -@media (min-width: 1200px) { - .game-container { - width: 90%; - max-width: 1400px; - } - - .game-header h1 { - font-size: 2.2em; - } - - /* Make better use of horizontal space */ - .main-actions { - flex-direction: row; - justify-content: center; - flex-wrap: wrap; - gap: 20px; - } - - .main-actions .btn { - width: auto; - min-width: 200px; - max-width: 250px; - margin: 0; - } - - /* Multi-column layouts for management screens */ - .image-gallery, .audio-gallery { - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 15px; - } - - /* Game mode selection in columns */ - .game-mode-options { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; - } -} - -/* Medium screens - balanced layout */ -@media (min-width: 900px) and (max-width: 1199px) { - .game-container { - width: 95%; - } - - .main-actions { - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - gap: 15px; - } - - .main-actions .btn { - width: auto; - min-width: 180px; - max-width: 220px; - margin: 0; - } -} - -/* Small screens - compact layout */ -@media (max-width: 899px) { - .game-container { - width: 98%; - margin: 5px auto; - border-radius: 10px; - min-width: 760px; /* Ensure minimum usability */ - } - - .game-header { - padding: 15px; - } - - .game-header h1 { - font-size: 1.8em; - } - - .main-actions { - flex-direction: column; - gap: 12px; - } - - .main-actions .btn { - width: 100%; - max-width: 280px; - } - - /* Compact game mode selection */ - .game-mode-selection { - padding: 15px; - } - - .game-mode-option label { - padding: 12px; - } - - /* Adjust image and audio galleries */ - .image-gallery, .audio-gallery { - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - gap: 10px; - } -} - -/* Handle very wide screens */ -@media (min-width: 1600px) { - .game-container { - max-width: 1600px; - } - - .game-content { - padding: 30px; - } - - /* Use more columns for galleries on wide screens */ - .image-gallery, .audio-gallery { - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 20px; - } -} - -/* Ensure game screen task display scales well */ -@media (min-width: 1400px) { - .task-display { - max-width: 900px; - margin: 0 auto; - } - - .task-image { - max-height: min(75vh, 900px); /* Much larger images on very large screens */ - max-width: min(85vw, 1400px); /* Can be much wider on large screens */ - } - - .task-image-container { - min-height: max(300px, 35vh); /* Taller container for larger images */ - } -} - -@media (min-width: 1000px) and (max-width: 1399px) { - .task-display { - max-width: 700px; - margin: 0 auto; - } - - .task-image { - max-height: min(65vh, 700px); /* Bigger images for medium-large screens */ - max-width: min(88vw, 1000px); /* More width utilization */ - } -} - -/* Compact sizing for smaller windows */ -@media (max-width: 999px) { - .task-image { - max-height: min(55vh, 500px); /* Still quite large on compact screens */ - max-width: min(92vw, 600px); /* Take up most of the width */ - min-height: max(180px, 18vh); /* Smaller minimum height */ - } - - .task-image-container { - min-height: max(180px, 20vh); /* Compact container */ - margin-bottom: 15px; - } -} - -/* Very small windows - still allow decent image size */ -@media (max-width: 800px) { - .task-image { - max-height: min(50vh, 400px); /* Half the viewport height on small windows */ - max-width: min(95vw, 500px); /* Nearly full width */ - min-height: max(150px, 15vh); /* Smaller minimum */ - min-width: 200px; /* Smaller minimum width */ - } - - .task-image-container { - min-height: max(150px, 18vh); - margin-bottom: 10px; - } -} - -/* High DPI / Retina display adjustments */ -@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { - .game-container { - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); - } - - .btn { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - } -} - -/* =========================== - DYNAMIC WINDOW SIZE CLASSES - =========================== */ - -/* Extra fine-tuning based on JavaScript window size detection */ -.game-container.window-xl .task-image { - max-width: min(85vw, 1500px); /* Huge images on ultra-wide screens */ - max-height: min(80vh, 1000px); /* Take up most of the vertical space */ - min-height: max(400px, 30vh); /* Large minimum size */ -} - -.game-container.window-large .task-image { - max-width: min(88vw, 1200px); /* Very large images */ - max-height: min(75vh, 800px); /* Most of the screen height */ - min-height: max(300px, 25vh); /* Good minimum size */ -} - -.game-container.window-medium .task-image { - max-width: min(90vw, 800px); /* Large images on medium screens */ - max-height: min(65vh, 600px); /* Good portion of screen height */ - min-height: max(250px, 22vh); /* Reasonable minimum */ -} - -.game-container.window-small .task-image { - max-width: min(95vw, 500px); /* Nearly full width on small screens */ - max-height: min(55vh, 400px); /* More than half the screen height */ - min-height: max(200px, 20vh); /* Decent minimum size */ - min-width: 180px; -} - -/* Smooth transitions between window size changes */ -.task-image { - transition: max-width 0.3s ease, max-height 0.3s ease, min-height 0.3s ease; -} - -.task-image-container { - transition: min-height 0.3s ease; -} - -/* =========================== - FLASH MESSAGE SYSTEM - =========================== */ - -/* Flash message overlay */ -.flash-message-overlay { - position: fixed; - display: none; - pointer-events: none; /* Don't block clicks */ - font-family: 'Arial', sans-serif; - user-select: none; /* Prevent text selection */ - opacity: 0; - transition: opacity 0.5s ease-in-out, transform 0.5s ease-in-out; -} - -/* Flash message animations */ -@keyframes flashBounceIn { - 0% { - opacity: 0; - transform: scale(0.3) translate(-50%, -50%); - } - 50% { - opacity: 1; - transform: scale(1.05) translate(-50%, -50%); - } - 70% { - transform: scale(0.9) translate(-50%, -50%); - } - 100% { - opacity: 1; - transform: scale(1) translate(-50%, -50%); - } -} - -@keyframes flashPulseIn { - 0% { - opacity: 0; - transform: scale(1) translate(-50%, -50%); - } - 20% { - opacity: 0.8; - transform: scale(1.1) translate(-50%, -50%); - } - 40% { - opacity: 1; - transform: scale(0.95) translate(-50%, -50%); - } - 60% { - opacity: 1; - transform: scale(1.05) translate(-50%, -50%); - } - 80% { - opacity: 1; - transform: scale(0.98) translate(-50%, -50%); - } - 100% { - opacity: 1; - transform: scale(1) translate(-50%, -50%); - } -} - -@keyframes flashFadeOut { - 0% { - opacity: 1; - transform: scale(1); - } - 100% { - opacity: 0; - transform: scale(0.95); - } -} - -/* Flash message responsive styles */ -@media (max-width: 768px) { - .flash-message-overlay { - max-width: 90% !important; - padding: 15px 20px !important; - font-size: 18px !important; - } -} - -@media (max-width: 480px) { - .flash-message-overlay { - max-width: 95% !important; - padding: 12px 16px !important; - font-size: 16px !important; - border-radius: 10px !important; - } -} - -/* =========================== - ANNOYANCE MANAGEMENT SCREEN - =========================== */ - -/* Tab Navigation */ -.annoyance-tabs { - display: flex; - gap: 2px; - margin-bottom: 20px; - background: #e9ecef; - border-radius: 8px; - padding: 4px; -} - -.annoyance-tab { - flex: 1; - background: transparent; - border: none; - padding: 12px 16px; - border-radius: 6px; - cursor: pointer; - font-weight: 500; - transition: all 0.2s ease; - color: #666; -} - -.annoyance-tab:hover { - background: #dee2e6; - color: #333; -} - -.annoyance-tab.active { - background: #007bff; - color: white; - box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3); -} - -/* Tab Content */ -.annoyance-tab-content { - display: none; -} - -.annoyance-tab-content.active { - display: block; - animation: fadeIn 0.3s ease-in-out; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -} - -/* Section Styling */ -.annoyance-section { - background: #f8f9fa; - border-radius: 10px; - padding: 20px; - margin-bottom: 20px; -} - -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - padding-bottom: 10px; - border-bottom: 2px solid #e9ecef; -} - -.header-controls { - display: flex; - align-items: center; - gap: 15px; -} - -/* Message Editor */ -.message-editor { - background: white; - border: 2px solid #007bff; - border-radius: 10px; - padding: 20px; - margin-bottom: 20px; - box-shadow: 0 4px 12px rgba(0, 123, 255, 0.1); -} - -.editor-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 15px; - padding-bottom: 10px; - border-bottom: 1px solid #e9ecef; -} - -.editor-header h4 { - margin: 0; - color: #007bff; -} - -.editor-form { - display: flex; - flex-direction: column; - gap: 15px; -} - -.form-group { - display: flex; - flex-direction: column; - gap: 5px; -} - -.form-group label { - font-weight: 600; - color: #333; -} - -.form-group textarea { - padding: 12px; - border: 2px solid #ddd; - border-radius: 6px; - font-size: 14px; - resize: vertical; - min-height: 80px; - transition: border-color 0.2s; -} - -.form-group textarea:focus { - border-color: #007bff; - outline: none; -} - -.form-group select { - padding: 10px 12px; - border: 2px solid #ddd; - border-radius: 6px; - background: white; - font-size: 14px; - cursor: pointer; - transition: border-color 0.2s; -} - -.form-group select:focus { - border-color: #007bff; - outline: none; -} - -.form-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 15px; -} - -.char-counter { - font-size: 12px; - color: #666; - text-align: right; -} - -.char-counter.warning { - color: #ffc107; -} - -.char-counter.error { - color: #dc3545; -} - -.editor-actions { - display: flex; - gap: 10px; - justify-content: flex-start; -} - -/* Message List */ -.message-list-section { - background: white; - border-radius: 8px; - overflow: hidden; -} - -.list-header { - background: #f8f9fa; - padding: 15px 20px; - border-bottom: 1px solid #e9ecef; - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 10px; -} - -.list-filters { - display: flex; - align-items: center; - gap: 15px; - flex-wrap: wrap; -} - -.list-filters label { - font-size: 14px; - color: #666; - display: flex; - align-items: center; - gap: 5px; -} - -.list-filters select { - padding: 6px 10px; - border: 1px solid #ddd; - border-radius: 4px; - background: white; - font-size: 13px; -} - -.list-stats { - font-size: 14px; - color: #666; - font-weight: 500; -} - -.message-list { - max-height: 400px; - overflow-y: auto; -} - -.message-item { - display: flex; - align-items: center; - padding: 15px 20px; - border-bottom: 1px solid #f1f3f4; - transition: background-color 0.2s; -} - -.message-item:hover { - background-color: #f8f9fa; -} - -.message-item.disabled { - opacity: 0.6; - background-color: #f8f9fa; -} - -.message-content { - flex: 1; - display: flex; - flex-direction: column; - gap: 5px; -} - -.message-text { - font-size: 14px; - color: #333; - line-height: 1.4; -} - -.message-meta { - display: flex; - gap: 15px; - font-size: 12px; - color: #666; -} - -.message-category { - display: inline-flex; - align-items: center; - gap: 3px; - padding: 2px 8px; - background: #e9ecef; - border-radius: 12px; - font-size: 11px; - font-weight: 500; -} - -.message-category.motivational { background: #d4edda; color: #155724; } -.message-category.encouraging { background: #d1ecf1; color: #0c5460; } -.message-category.achievement { background: #fff3cd; color: #856404; } -.message-category.persistence { background: #f8d7da; color: #721c24; } -.message-category.custom { background: #e2e3e5; color: #383d41; } - -.message-actions { - display: flex; - gap: 8px; - align-items: center; -} - -.message-toggle { - width: 40px; - height: 20px; - background: #ccc; - border-radius: 10px; - position: relative; - cursor: pointer; - transition: background-color 0.3s; -} - -.message-toggle.enabled { - background: #28a745; -} - -.message-toggle::after { - content: ''; - position: absolute; - top: 2px; - left: 2px; - width: 16px; - height: 16px; - background: white; - border-radius: 50%; - transition: transform 0.3s; -} - -.message-toggle.enabled::after { - transform: translateX(20px); -} - -/* Control Layouts */ -.control-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; -} - -.control-group { - display: flex; - flex-direction: column; - gap: 8px; -} - -.control-group label { - font-weight: 600; - color: #333; - display: flex; - align-items: center; - gap: 8px; -} - -.control-group input[type="range"] { - width: 100%; - height: 6px; - border-radius: 3px; - background: #ddd; - outline: none; - opacity: 0.7; - transition: opacity 0.2s; - cursor: pointer; -} - -.control-group input[type="range"]:hover { - opacity: 1; -} - -.control-group input[type="range"]::-webkit-slider-thumb { - appearance: none; - width: 18px; - height: 18px; - border-radius: 50%; - background: #007bff; - cursor: pointer; - transition: background 0.2s; -} - -.control-group input[type="range"]::-webkit-slider-thumb:hover { - background: #0056b3; -} - -.control-group input[type="checkbox"] { - width: 18px; - height: 18px; - cursor: pointer; -} - -.control-group input[type="color"] { - width: 60px; - height: 40px; - border: 2px solid #ddd; - border-radius: 6px; - cursor: pointer; - transition: border-color 0.2s; -} - -.control-group input[type="color"]:focus { - border-color: #007bff; - outline: none; -} - -.help-text { - font-size: 12px; - color: #666; - font-style: italic; - margin-top: 2px; -} - -.help-text.danger { - color: #dc3545; -} - -/* Import/Export Sections */ -.control-section { - background: white; - border-radius: 8px; - padding: 20px; - margin-bottom: 15px; - border: 1px solid #e9ecef; -} - -.control-section h4 { - margin: 0 0 15px 0; - color: #333; - font-size: 16px; -} - -.export-options, .import-options, .reset-options { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin-bottom: 10px; -} - -.import-mode { - margin-top: 10px; -} - -.radio-group { - display: flex; - gap: 15px; - margin-top: 5px; -} - -.radio-group label { - display: flex; - align-items: center; - gap: 5px; - font-size: 14px; - cursor: pointer; -} - -/* Responsive Design */ -@media (max-width: 768px) { - .annoyance-tabs { - flex-direction: column; - } - - .section-header { - flex-direction: column; - align-items: flex-start; - gap: 10px; - } - - .form-row, .control-row { - grid-template-columns: 1fr; - gap: 10px; - } - - .list-header { - flex-direction: column; - align-items: flex-start; - } - - .message-item { - flex-direction: column; - align-items: flex-start; - gap: 10px; - } - - .message-actions { - align-self: stretch; - justify-content: space-between; - } - - .export-options, .import-options, .reset-options { - flex-direction: column; - } - - .annoyance-section { - padding: 15px; - } -} - -/* ====================================== - Punishment Popup System Styles - ====================================== */ - -/* Background blur for punishment popups */ -.punishment-popup-blur { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(3px); - z-index: 9999; - pointer-events: none; -} - -/* Individual punishment popup */ -.punishment-popup { - position: fixed; - z-index: 10000; - background: white; - border: 3px solid #dc3545; - border-radius: 10px; - box-shadow: 0 8px 32px rgba(220, 53, 69, 0.4); - overflow: hidden; - display: flex; - flex-direction: column; - font-family: var(--font-family); - min-width: 200px; - min-height: 150px; - max-width: 500px; - max-height: 400px; -} - -/* Popup header with timer and title */ -.punishment-popup-header { - background: #dc3545; - color: white; - padding: 8px 12px; - font-size: 12px; - font-weight: bold; - display: flex; - justify-content: space-between; - align-items: center; - user-select: none; -} - -.punishment-popup-timer { - background: rgba(255, 255, 255, 0.2); - padding: 2px 6px; - border-radius: 4px; - font-family: monospace; - font-size: 11px; - min-width: 30px; - text-align: center; -} - -/* Image container within popup */ -.punishment-popup img { - max-width: 100%; - max-height: 100%; - object-fit: contain; - border-radius: 5px; -} - -/* Popup Images Tab specific styles */ -.range-inputs { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 15px; - margin-top: 10px; -} - -.range-inputs > div { - display: flex; - flex-direction: column; - gap: 5px; -} - -.range-inputs input[type="number"] { - padding: 8px; - border: 1px solid #ddd; - border-radius: 5px; - font-size: 14px; - background: white; - color: #333; -} - -.range-inputs input[type="number"]:focus { - outline: none; - border-color: #007bff; - box-shadow: 0 0 5px rgba(0, 123, 255, 0.3); -} - -.warning-text { - background: #fff3cd; - color: #856404; - padding: 10px; - border: 1px solid #ffeaa7; - border-radius: 5px; - margin-top: 10px; - font-size: 14px; - font-weight: 500; -} - -.test-buttons { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin-top: 10px; -} - -.test-buttons .btn { - flex: 1; - min-width: 120px; - padding: 10px 15px; - font-size: 14px; - font-weight: 500; - border: none; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - text-align: center; -} - -.test-buttons .btn-info { - background: #17a2b8; - color: white; -} - -.test-buttons .btn-info:hover { - background: #138496; - transform: translateY(-1px); -} - -.test-buttons .btn-primary { - background: #007bff; - color: white; -} - -.test-buttons .btn-primary:hover { - background: #0056b3; - transform: translateY(-1px); -} - -.test-buttons .btn-danger { - background: #dc3545; - color: white; -} - -.test-buttons .btn-danger:hover { - background: #c82333; - transform: translateY(-1px); -} - -.info-display { - background: #f8f9fa; - padding: 15px; - border-radius: 8px; - border-left: 4px solid #007bff; - margin-top: 10px; -} - -.info-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 5px 0; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); -} - -.info-item:last-child { - border-bottom: none; -} - -.info-label { - font-weight: 500; - color: #495057; -} - -.info-item span:last-child { - font-weight: bold; - color: #007bff; - background: rgba(0, 123, 255, 0.1); - padding: 2px 8px; - border-radius: 12px; - font-size: 12px; - min-width: 30px; - text-align: center; -} - -/* Responsive styles for popup images tab */ -@media (max-width: 768px) { - .range-inputs { - grid-template-columns: 1fr; - gap: 10px; - } - - .test-buttons { - flex-direction: column; - } - - .test-buttons .btn { - min-width: 100%; - } - - .punishment-popup { - max-width: 90vw; - max-height: 80vh; - } - - .info-item { - flex-direction: column; - align-items: flex-start; - gap: 5px; - } - - .info-item span:last-child { - align-self: flex-end; - } -} - -/* Animation for popup appearance */ -@keyframes popupFadeIn { - from { - opacity: 0; - transform: scale(0.8); - } - to { - opacity: 1; - transform: scale(1); - } -} - -@keyframes popupFadeOut { - from { - opacity: 1; - transform: scale(1); - } - to { - opacity: 0; - transform: scale(0.9); - } -} - -.punishment-popup { - animation: popupFadeIn 0.3s ease-out; -} - -.punishment-popup.fade-out { - animation: popupFadeOut 0.3s ease-in; -} \ No newline at end of file diff --git a/src/styles/styles.css b/src/styles/styles.css index 2925b41..d8d4b4d 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -2289,12 +2289,35 @@ body.theme-monochrome { /* Video Management Styles */ .video-gallery { display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + grid-auto-rows: minmax(320px, auto); gap: var(--space-lg); padding: var(--space-lg); background: var(--bg-secondary); border-radius: 12px; border: 1px solid var(--border-color); + align-content: start; + max-height: 80vh; + overflow-y: auto; +} + +/* Custom scrollbar styling for video gallery */ +.video-gallery::-webkit-scrollbar { + width: 12px; +} + +.video-gallery::-webkit-scrollbar-track { + background: #333; + border-radius: 6px; +} + +.video-gallery::-webkit-scrollbar-thumb { + background: #666; + border-radius: 6px; +} + +.video-gallery::-webkit-scrollbar-thumb:hover { + background: #888; } .video-gallery.active { @@ -2309,6 +2332,7 @@ body.theme-monochrome { overflow: hidden; transition: transform 0.2s ease, box-shadow 0.2s ease; cursor: pointer; + min-height: 320px; } .video-item:hover { @@ -2326,9 +2350,10 @@ body.theme-monochrome { .video-thumbnail { position: relative; width: 100%; - height: 120px; + height: 140px; overflow: hidden; background: var(--bg-tertiary); + border-radius: 8px; } .video-thumbnail video { @@ -3400,6 +3425,56 @@ body.theme-monochrome { background: #0056b3; } +/* Focus video player volume slider override */ +.focus-volume-slider { + width: 60px !important; + max-width: 60px !important; + min-width: 60px !important; + height: 3px !important; + background: #666 !important; +} + +/* Performance optimizations for large video galleries */ +.video-galleries-container { + height: 60vh; + max-height: 800px; + overflow-y: auto; + overflow-x: hidden; +} + +#unified-video-gallery { + height: 60vh; + max-height: 800px; +} + +.video-grid-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 15px; + padding: 15px; +} + +.video-item { + transform: translateZ(0); /* Force hardware acceleration */ + will-change: transform; /* Hint to browser for optimization */ + contain: layout style paint; /* CSS containment for better performance */ +} + +.lazy-thumbnail { + background: #2a2a2a; + border-radius: 4px; +} + +.thumbnail-placeholder { + font-size: 24px; + transition: opacity 0.3s ease; +} + +/* Virtual scrolling optimization */ +.video-gallery.active { + contain: strict; /* Strict containment for performance */ +} + .control-group input[type="checkbox"] { width: 18px; height: 18px; diff --git a/src/utils/desktop-file-manager.js b/src/utils/desktop-file-manager.js index 3a2f509..5c51180 100644 --- a/src/utils/desktop-file-manager.js +++ b/src/utils/desktop-file-manager.js @@ -18,6 +18,10 @@ class DesktopFileManager { punishments: null }; + // External video directories (linked, not copied) + this.externalVideoDirectories = []; // Array of linked directory objects + this.allLinkedVideos = []; // Cached array of all videos from all directories + this.init(); } @@ -47,15 +51,16 @@ class DesktopFileManager { await window.electronAPI.createDirectory(this.audioDirectories.background); await window.electronAPI.createDirectory(this.audioDirectories.ambient); - await window.electronAPI.createDirectory(this.videoDirectories.background); - await window.electronAPI.createDirectory(this.videoDirectories.tasks); - await window.electronAPI.createDirectory(this.videoDirectories.rewards); - await window.electronAPI.createDirectory(this.videoDirectories.punishments); + // Note: No longer creating/using local video directories + // All videos come from external linked directories only console.log('Desktop file manager initialized'); console.log('App path:', this.appPath); console.log('Image directories:', this.imageDirectories); console.log('Audio directories:', this.audioDirectories); + + // Load any previously linked external directories + await this.loadLinkedDirectories(); } else { console.error('Failed to get app path'); } @@ -276,6 +281,282 @@ class DesktopFileManager { } } + async addVideoDirectory(customName = null) { + if (!this.isElectron) { + this.showNotification('Directory linking only available in desktop version', 'warning'); + return null; + } + + try { + // Open directory dialog + const directoryPath = await window.electronAPI.selectDirectory(); + + if (!directoryPath) { + return null; + } + + // Check if directory is already linked + const existingDir = this.externalVideoDirectories.find(dir => dir.path === directoryPath); + if (existingDir) { + this.showNotification('Directory is already linked!', 'warning'); + return existingDir; + } + + // Show scanning notification + this.showNotification('🔍 Scanning directory for videos... This may take a moment for large collections.', 'info'); + + // Scan the directory recursively for video files + console.log(`🔍 Scanning directory recursively: ${directoryPath}`); + const videoFiles = await window.electronAPI.readVideoDirectoryRecursive(directoryPath); + + console.log(`Found ${videoFiles.length} video files in directory:`, directoryPath); + + if (videoFiles.length === 0) { + this.showNotification('No video files found in selected directory', 'warning'); + return null; + } + + // Create directory object + const directoryName = customName || this.getDirectoryName(directoryPath); + const directoryObj = { + id: Date.now(), // Unique ID + name: directoryName, + path: directoryPath, + videoCount: videoFiles.length, + dateAdded: new Date().toISOString(), + isRecursive: true + }; + + // Process videos in chunks to avoid blocking UI + const linkedVideos = await this.processVideosInChunks(videoFiles, directoryObj); + + // Add to linked directories + this.externalVideoDirectories.push(directoryObj); + + // Update cached video list + this.allLinkedVideos.push(...linkedVideos); + + // Save to persistent storage + await this.saveLinkedDirectories(); + + // Update video storage + await this.updateUnifiedVideoStorage(); + + this.showNotification( + `✅ Linked directory "${directoryName}"!\nFound ${videoFiles.length} videos`, + 'success' + ); + + console.log(`🔗 Linked directory: ${directoryName} (${videoFiles.length} videos)`); + + return { + directory: directoryObj, + videoCount: videoFiles.length, + videos: linkedVideos + }; + + } catch (error) { + console.error('Error linking video directory:', error); + this.showNotification('Failed to link video directory', 'error'); + return null; + } + } + + async processVideosInChunks(videoFiles, directoryObj) { + const chunkSize = 100; // Process 100 videos at a time + const linkedVideos = []; + + for (let i = 0; i < videoFiles.length; i += chunkSize) { + const chunk = videoFiles.slice(i, i + chunkSize); + const progress = Math.round(((i + chunk.length) / videoFiles.length) * 100); + + console.log(`Processing videos ${i + 1}-${i + chunk.length} of ${videoFiles.length} (${progress}%)`); + + const chunkProcessed = chunk.map(video => ({ + id: `${directoryObj.id}_${video.name}`, + name: video.name, + path: video.path, + url: `file:///${video.path.replace(/\\/g, '/')}`, + title: this.getVideoTitle(video.name), + size: video.size || 0, + directoryId: directoryObj.id, + directoryName: directoryObj.name, + isExternal: true, + relativePath: video.path.replace(directoryObj.path, '').replace(/^[\\/]/, '') + })); + + linkedVideos.push(...chunkProcessed); + + // Small delay to keep UI responsive + if (i + chunkSize < videoFiles.length) { + await new Promise(resolve => setTimeout(resolve, 10)); + } + } + + return linkedVideos; + } + + async removeVideoDirectory(directoryId) { + if (!this.isElectron) { + return false; + } + + try { + // Find directory + const dirIndex = this.externalVideoDirectories.findIndex(dir => dir.id === directoryId); + if (dirIndex === -1) { + this.showNotification('Directory not found', 'error'); + return false; + } + + const directory = this.externalVideoDirectories[dirIndex]; + + // Remove from arrays + this.externalVideoDirectories.splice(dirIndex, 1); + this.allLinkedVideos = this.allLinkedVideos.filter(video => video.directoryId !== directoryId); + + // Save to persistent storage + await this.saveLinkedDirectories(); + + // Update video storage + await this.updateUnifiedVideoStorage(); + + this.showNotification(`Unlinked directory: ${directory.name}`, 'success'); + return true; + + } catch (error) { + console.error('Error unlinking directory:', error); + this.showNotification('Failed to unlink directory', 'error'); + return false; + } + } + + async refreshAllDirectories() { + if (!this.isElectron) { + return; + } + + console.log('🔄 Refreshing all linked directories...'); + this.allLinkedVideos = []; + + for (const directory of this.externalVideoDirectories) { + try { + console.log(`🔍 Rescanning: ${directory.name}`); + const videoFiles = await window.electronAPI.readVideoDirectoryRecursive(directory.path); + + const linkedVideos = videoFiles.map(video => ({ + id: `${directory.id}_${video.name}`, + name: video.name, + path: video.path, + url: `file:///${video.path.replace(/\\/g, '/')}`, + title: this.getVideoTitle(video.name), + size: video.size || 0, + directoryId: directory.id, + directoryName: directory.name, + isExternal: true, + relativePath: video.path.replace(directory.path, '').replace(/^[\\/]/, '') + })); + + this.allLinkedVideos.push(...linkedVideos); + + // Update directory video count + directory.videoCount = videoFiles.length; + + console.log(`✅ Found ${videoFiles.length} videos in ${directory.name}`); + + } catch (error) { + console.warn(`Could not access directory ${directory.name}:`, error); + // Directory might be unavailable (external drive, network, etc.) + } + } + + await this.saveLinkedDirectories(); + await this.updateUnifiedVideoStorage(); + + const totalVideos = this.allLinkedVideos.length; + this.showNotification(`🔄 Refreshed ${this.externalVideoDirectories.length} directories, found ${totalVideos} videos`, 'success'); + } + + async saveLinkedDirectories() { + const data = { + directories: this.externalVideoDirectories, + lastUpdated: new Date().toISOString() + }; + this.dataManager.set('linkedVideoDirectories', data); + } + + async loadLinkedDirectories() { + if (!this.isElectron) { + return; + } + + try { + const data = this.dataManager.get('linkedVideoDirectories'); + if (data && data.directories) { + this.externalVideoDirectories = data.directories; + console.log(`📁 Loaded ${this.externalVideoDirectories.length} linked directories`); + + // Refresh all directories to get current video lists + await this.refreshAllDirectories(); + } + } catch (error) { + console.error('Error loading linked directories:', error); + } + } + + async updateUnifiedVideoStorage() { + // Store all videos in a single unified list (no categories) + + // If we have no linked directories but there are existing videos in storage, + // don't overwrite the existing data + if (this.externalVideoDirectories.length === 0 && this.allLinkedVideos.length === 0) { + // Check if there are existing videos in storage + try { + const existingData = JSON.parse(localStorage.getItem('unifiedVideoLibrary') || '{}'); + if (existingData.allVideos && existingData.allVideos.length > 0) { + console.log(`📹 Preserving existing unified video library: ${existingData.allVideos.length} videos`); + // Don't overwrite - preserve existing data + return; + } + } catch (error) { + console.warn('Error checking existing unified video library:', error); + } + } + + const videoData = { + allVideos: this.allLinkedVideos, + directories: this.externalVideoDirectories, + lastUpdated: new Date().toISOString() + }; + + localStorage.setItem('unifiedVideoLibrary', JSON.stringify(videoData)); + console.log(`📹 Updated unified video library: ${this.allLinkedVideos.length} videos from ${this.externalVideoDirectories.length} directories`); + + // Trigger video manager reload if it exists + if (window.videoPlayerManager) { + window.videoPlayerManager.loadVideoFiles(); + } + } + + getDirectoryName(directoryPath) { + // Extract just the folder name from the full path + return directoryPath.split(/[\\/]/).pop() || 'Unknown Directory'; + } + + getAllVideos() { + return this.allLinkedVideos; + } + + getDirectoriesInfo() { + return this.externalVideoDirectories.map(dir => ({ + id: dir.id, + name: dir.name, + path: dir.path, + videoCount: dir.videoCount, + dateAdded: dir.dateAdded + })); + } + async scanDirectoryForImages(category = 'task') { if (!this.isElectron) { return []; @@ -303,51 +584,6 @@ class DesktopFileManager { } } - async scanAllDirectories() { - if (!this.isElectron) { - this.showNotification('Directory scanning only available in desktop version', 'warning'); - return { task: [], consequence: [], videos: { background: [], tasks: [], rewards: [], punishments: [] } }; - } - - // Scan image directories - const taskImages = await this.scanDirectoryForImages('task'); - const consequenceImages = await this.scanDirectoryForImages('consequence'); - - // Scan video directories - const backgroundVideos = await this.scanDirectoryForVideos('background'); - const taskVideos = await this.scanDirectoryForVideos('tasks'); - const rewardVideos = await this.scanDirectoryForVideos('rewards'); - const punishmentVideos = await this.scanDirectoryForVideos('punishments'); - - const results = { - task: taskImages, - consequence: consequenceImages, - videos: { - background: backgroundVideos, - tasks: taskVideos, - rewards: rewardVideos, - punishments: punishmentVideos - } - }; - - // Update image storage - if (taskImages.length > 0 || consequenceImages.length > 0) { - await this.updateImageStorage([...taskImages, ...consequenceImages]); - } - - // Update video storage - const allVideos = [...backgroundVideos, ...taskVideos, ...rewardVideos, ...punishmentVideos]; - if (allVideos.length > 0) { - await this.updateVideoStorage(allVideos); - } - - const totalImages = taskImages.length + consequenceImages.length; - const totalVideos = allVideos.length; - // this.showNotification(`Found ${totalImages} images (${taskImages.length} tasks, ${consequenceImages.length} consequences) and ${totalVideos} videos (${backgroundVideos.length} background, ${taskVideos.length} tasks, ${rewardVideos.length} rewards, ${punishmentVideos.length} punishments)`, 'success'); - - return results; - } - async updateImageStorage(images) { // Get existing images let customImages = this.dataManager.get('customImages') || { task: [], consequence: [] }; @@ -666,19 +902,28 @@ class DesktopFileManager { // Get existing videos from localStorage const existingVideos = JSON.parse(localStorage.getItem('videoFiles') || '{}'); + console.log('Updating video storage with', videoFiles.length, 'videos'); + // Add new videos videoFiles.forEach(video => { + console.log(`Adding video to category: ${video.category}, name: ${video.name}`); if (!existingVideos[video.category]) { existingVideos[video.category] = []; + console.log(`Created new category: ${video.category}`); } // Check if video already exists (prevent duplicates) const exists = existingVideos[video.category].some(existing => existing.name === video.name); if (!exists) { existingVideos[video.category].push(video); + console.log(`Added video: ${video.name} to ${video.category}`); + } else { + console.log(`Video already exists: ${video.name} in ${video.category}`); } }); + console.log('Final video storage:', existingVideos); + // Save back to localStorage localStorage.setItem('videoFiles', JSON.stringify(existingVideos)); diff --git a/test-cinema.js b/test-cinema.js deleted file mode 100644 index b899cf7..0000000 Binary files a/test-cinema.js and /dev/null differ diff --git a/theme-mockup-option-e.html b/theme-mockup-option-e.html deleted file mode 100644 index d296ad5..0000000 --- a/theme-mockup-option-e.html +++ /dev/null @@ -1,413 +0,0 @@ - - - - - - Theme Mockup - Balanced Green - - - - -
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
- -
- -
-

🌲 Balanced Forest Green Theme

-

Natural forest green accents with neutral black/gray backgrounds - Organic, earthy feel

-
- - -
-
-
-
42
-
Sessions Completed
-
-
-
18h 32m
-
Total Training Time
-
-
-
Level 7
-
Current Rank
-
-
-
95%
-
Dedication Score
-
-
-
- - - - - -
-

Current Progress

-
-
-
-

65% through Intermediate Training Module

-
- - -
-

Recent Activity

-
- Completed Focus Session: Advanced Techniques - 2 hours ago -
-
- Achievement Unlocked: Dedication Master - Yesterday -
-
- Training Session: Endurance Building - 2 days ago -
-
- Mirror Task: Self-Reflection Exercise - 3 days ago -
-
-
- - - - \ No newline at end of file diff --git a/title-color-variations.html b/title-color-variations.html deleted file mode 100644 index 69c3aa0..0000000 --- a/title-color-variations.html +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - Option 4 - Color Variations & Game Palettes - - - - -
-
-

Option 4: Gaming Aesthetic - Color Variations

-

Audiowide font with different color schemes and complete game palettes

-
- - -
-
-
A
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

🌈 Pink/Orange Gaming Palette

-
-
-
Primary
-
#ff0080
-
-
-
Secondary
-
#ff8000
-
-
-
Accent
-
#ff4d4d
-
-
-
Dark BG
-
#2d2d2d
-
-
-
Darker BG
-
#1a1a1a
-
-
-
Glow
-
rgba(255,0,128,0.1)
-
-
-
- Game Implementation: High-energy gaming vibe with pink primary for buttons/highlights, orange for progress bars/completion states, dark backgrounds for contrast. Perfect for an intense, exciting gaming experience. -
-
-
- - -
-
-
B
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

💙 Electric Blue/Cyan Tech Palette

-
-
-
Primary
-
#00bfff
-
-
-
Secondary
-
#1e90ff
-
-
-
Accent
-
#4db8ff
-
-
-
Dark BG
-
#0f2027
-
-
-
Mid BG
-
#203a43
-
-
-
Glow
-
rgba(0,255,255,0.15)
-
-
-
- Game Implementation: Professional tech aesthetic with blue primary for navigation/buttons, cyan for active states/progress, dark blue-grey backgrounds. Creates a sophisticated, high-tech training environment feel. -
-
-
- - -
-
-
C
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

💜 Purple/Violet Luxe Palette

-
-
-
Primary
-
#8a2be2
-
-
-
Secondary
-
#da70d6
-
-
-
Accent
-
#ba55d3
-
-
-
Dark BG
-
#1a0d26
-
-
-
Mid BG
-
#2d1b3d
-
-
-
Glow
-
rgba(138,43,226,0.12)
-
-
-
- Game Implementation: Luxurious, premium feel with purple primary for main actions, orchid for highlights/hover states, deep purple backgrounds. Creates an exclusive, high-end academy atmosphere. -
-
-
- - -
-
-
D
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

💚 Green/Lime Matrix Palette

-
-
-
Primary
-
#00ff00
-
-
-
Secondary
-
#32cd32
-
-
-
Accent
-
#7fff00
-
-
-
Dark BG
-
#0d1a0d
-
-
-
Mid BG
-
#1b2d1b
-
-
-
Glow
-
rgba(0,255,0,0.08)
-
-
-
- Game Implementation: Matrix/hacker aesthetic with bright green primary for success states/progress, lime for active elements, very dark green backgrounds. Perfect for a digital training simulation vibe. -
-
-
- - -
-
-
E
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

❤️ Red/Crimson Power Palette

-
-
-
Primary
-
#dc143c
-
-
-
Secondary
-
#ff6347
-
-
-
Accent
-
#ff4757
-
-
-
Dark BG
-
#1a0a0a
-
-
-
Mid BG
-
#2d1515
-
-
-
Glow
-
rgba(220,20,60,0.1)
-
-
-
- Game Implementation: Bold, intense atmosphere with crimson primary for warnings/important actions, coral for completion states, dark red backgrounds. Creates a powerful, commanding training environment. -
-
-
- - -
-
-
F
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
-
-

🏆 Gold/Amber Elite Palette

-
-
-
Primary
-
#ffd700
-
-
-
Secondary
-
#ffa500
-
-
-
Accent
-
#ffb347
-
-
-
Dark BG
-
#1a1a0d
-
-
-
Mid BG
-
#2d2d1b
-
-
-
Glow
-
rgba(255,215,0,0.08)
-
-
-
- Game Implementation: Premium, achievement-focused design with gold primary for rewards/completion, orange for progress indicators, warm dark backgrounds. Perfect for highlighting accomplishments and elite status. -
-
-
- -
-

🎨 Choose Your Academy's Identity!

-

Each color scheme creates a completely different atmosphere for your training academy. Consider what mood and personality best fits your vision:

-
    -
  • A - Pink/Orange: High-energy gaming excitement
  • -
  • B - Blue/Cyan: Professional tech sophistication
  • -
  • C - Purple/Violet: Luxurious premium experience
  • -
  • D - Green/Lime: Matrix-style digital simulation
  • -
  • E - Red/Crimson: Bold commanding intensity
  • -
  • F - Gold/Amber: Elite achievement-focused
  • -
-

Just tell me which letter you prefer!

-
-
- - \ No newline at end of file diff --git a/title-design-test.html b/title-design-test.html deleted file mode 100644 index 3cac846..0000000 --- a/title-design-test.html +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - Gooner Training Academy - Title Design Options - - - - -
-
-

Gooner Training Academy - Title Design Options

-

Choose your favorite design for the main header!

-
- -
-
1
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Futuristic Tech - Orbitron font with cyan gradients and glow effects
-
- -
-
2
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Military Academy - Bebas Neue with orange theme and 3D perspective
-
- -
-
3
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Sleek Modern - Exo 2 font with purple-blue gradients
-
- -
-
4
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Gaming Aesthetic - Audiowide font with pink-orange gradients and glow animation
-
- -
-
5
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Professional Bold - Russo One font with green theme and layered shadows
-
- -
-
6
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Elegant Minimalist - Saira Condensed with clean white text and subtle shadows
-
- -
-
7
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Neon Cyberpunk - Rajdhani font with red neon glow and flicker animation
-
- -
-
8
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Gold Luxury - Titillium Web with gold gradients for premium feel
-
- -
-
9
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Purple Power - Exo 2 font with purple gradients and ethereal glow
-
- -
-
10
-

GOONER TRAINING ACADEMY

-

Master Your Dedication

-
Fire Theme - Bebas Neue with red-orange fire gradients and pulse animation
-
- -
-

🎯 Your Choice Matters!

-

Each design option has its own personality and vibe. Consider which one best represents the sophisticated training platform you've built with interactive scenarios, TTS narration, and advanced focus challenges.

-

Just tell me which number(s) you prefer!

-
-
- - \ No newline at end of file diff --git a/tts-test.html b/tts-test.html deleted file mode 100644 index 0237260..0000000 --- a/tts-test.html +++ /dev/null @@ -1,535 +0,0 @@ - - - - - - Text-to-Speech Testing - - - -

🎙️ Electron Female Voice TTS Testing Lab

-

Test female voices available in your Electron app. Shows system voices and Chromium built-ins that will work in your desktop application.

- -
-

Electron Female Voice Selection & Settings

-

🚺 Female voices only - Filtered to show female voices available in Electron applications.

-

- ⚡ Electron Environment: Shows system voices (Windows SAPI, macOS System) and Chromium built-ins. These are the actual voices your users will hear. -

- -
- -
- -
Select a voice to see details...
- -
- - - - - -
-
- -
-

Sample Scenario Content

- -
- - - - -
- -

Custom Text Testing

- - -
- - - - -
-
- -
-

Sample Texts Being Tested

- -
-

Scenario Introduction:

-
-
- -
-

Task Instruction:

-
-
- -
-

Feedback Message:

-
-
- -
-

Scenario Ending:

-
-
-
- -
-

Electron Female Voices by Platform

-
-
-

🖥️ Windows in Electron:

-
    -
  • Microsoft Zira Desktop ⭐ (Default)
  • -
  • Microsoft Hazel Desktop
  • -
  • Microsoft Eva Desktop
  • -
  • Microsoft Aria (if updated)
  • -
  • Microsoft Jenny (if updated)
  • -
-
-
-

🍎 macOS in Electron:

-
    -
  • Samantha ⭐ (Default female)
  • -
  • Alex (Can be female-sounding)
  • -
  • Victoria
  • -
  • Karen
  • -
  • Fiona
  • -
-
-
-

⚡ Electron Notes:

-
    -
  • System voices only - No Google/Amazon voices
  • -
  • Quality varies - Depends on OS updates
  • -
  • Local processing - No internet required
  • -
  • Consistent - Same voice for all users on same OS
  • -
-
-
- -
-

💡 Electron TTS Best Practices:

-
    -
  • Windows: Zira is most reliable, Hazel for variety
  • -
  • macOS: Samantha is the go-to female voice
  • -
  • Fallback: Always provide text backup for accessibility
  • -
  • Testing: Test on actual target OS, not just browser
  • -
  • Chrome users: Online voices often sound better than local ones
  • -
  • Speed matters: 0.7-0.8x often sounds more natural and intimate
  • -
  • Pitch adjustment: Slightly lower pitch (0.9) can sound more mature
  • -
-
-
- - - - \ No newline at end of file diff --git a/video-player-test.html b/video-player-test.html deleted file mode 100644 index 6db9cca..0000000 --- a/video-player-test.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - Video Player Test - - - - -
-

🎬 Video Player Integration Test

- -
-

BaseVideoPlayer Test

-
- -
-
- - -
-
- -
-

FocusVideoPlayer Test

- -
- - -
-
- -
-

Test Log

-
-
-
- - - - - - - - \ No newline at end of file