// State Management const AppState = { currentImage: null, currentVideo: null, isGenerating: false, history: JSON.parse(localStorage.getItem('generationHistory') || '[]'), settings: { width: 1024, height: 1024, steps: 30, cfgScale: 7.5, seed: -1, motionStrength: 5, videoDuration: 4 } }; // Utility Functions const Utils = { generateId: () => Math.random().toString(36).substr(2, 9), async hashString(str) { const encoder = new TextEncoder(); const data = encoder.encode(str); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); }, getRandomSeed() { return Math.floor(Math.random() * 2147483647); }, formatDate(date) { return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(date); }, debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } }; // API Simulation (Mock) const APIService = { async generateImage(prompt, negativePrompt, settings) { // Simulate API delay await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 2000)); // Generate deterministic seed from prompt for consistent demo results const seed = settings.seed > 0 ? settings.seed : Math.abs(Utils.hashString(prompt).split('').reduce((a,b)=>a+b.charCodeAt(0),0)); // Use static.photos with random category based on prompt content const categories = ['technology', 'abstract', 'nature', 'people', 'cityscape', 'minimal']; const category = categories[seed % categories.length]; const dimensions = settings.width > settings.height ? '1200x630' : settings.width < settings.height ? '640x360' : '1024x576'; return { id: Utils.generateId(), url: `http://static.photos/${category}/${dimensions}/${seed % 999}`, prompt: prompt, negativePrompt: negativePrompt, settings: {...settings}, timestamp: new Date(), seed: seed, type: 'image' }; }, async generateVideo(imageUrl, prompt, settings) { await new Promise(resolve => setTimeout(resolve, 3000 + Math.random() * 3000)); const seed = Utils.getRandomSeed(); return { id: Utils.generateId(), imageUrl: imageUrl, videoUrl: `http://static.photos/technology/640x360/${seed % 999}`, // Simulated video thumbnail prompt: prompt, settings: {...settings}, timestamp: new Date(), duration: settings.videoDuration || 4, type: 'video' }; } }; // UI Components Logic const UI = { showLoading(element, text = 'Generating...') { element.innerHTML = `

${text}

`; }, showError(element, message) { element.innerHTML = `

${message}

`; // Fade in effect for image const img = container.querySelector('img'); if (img) { img.onload = () => { img.style.opacity = '1'; }; // Force load if cached if (img.complete) { img.style.opacity = '1'; } } if (typeof feather !== 'undefined') { feather.replace(); } }, createImageCard(item) { return `
${item.prompt}
${item.type === 'video' ? `
VIDEO
` : ''}

${item.prompt}

${Utils.formatDate(new Date(item.timestamp))} Seed: ${item.seed || 'N/A'}
`; } }; // Main Application Logic class DreamMachineApp { constructor() { this.init(); } init() { this.bindEvents(); this.loadHistory(); this.setupRealtimeValidation(); } bindEvents() { // Generate Image Button - handle both shadow DOM and regular DOM document.addEventListener('click', (e) => { const generateBtn = e.target.closest('#generate-btn'); if (generateBtn) { e.preventDefault(); e.stopPropagation(); this.generateImage(); } if (e.target.closest('#generate-video-btn')) { this.generateVideoFromCurrent(); } }); // Also bind to shadow DOM buttons directly this.bindShadowDomevents(); } bindShadowDomevents() { // Wait for components to be defined const controls = document.querySelector('generator-controls'); if (controls && controls.shadowRoot) { const btn = controls.shadowRoot.getElementById('generate-btn'); if (btn) { btn.addEventListener('click', (e) => { e.stopPropagation(); this.generateImage(); }); } } } // Settings changes document.addEventListener('change', (e) => { if (e.target.hasAttribute('data-setting')) { const key = e.target.getAttribute('data-setting'); AppState.settings[key] = parseFloat(e.target.value) || e.target.value; } }); // Aspect ratio presets document.addEventListener('click', (e) => { if (e.target.closest('[data-ratio]')) { const ratio = e.target.closest('[data-ratio]').getAttribute('data-ratio'); this.setAspectRatio(ratio); } }); // Prompt enhancement document.addEventListener('click', (e) => { if (e.target.closest('.enhance-btn')) { this.enhancePrompt(); } }); } setupRealtimeValidation() { const promptInput = document.getElementById('prompt-input'); if (promptInput) { promptInput.addEventListener('input', Utils.debounce(() => { const length = promptInput.value.length; const indicator = document.getElementById('prompt-length'); if (indicator) { indicator.textContent = `${length} chars`; indicator.className = `text-xs ${length > 200 ? 'text-yellow-400' : 'text-slate-500'}`; } }, 300)); } } setAspectRatio(ratio) { const ratios = { '1:1': [1024, 1024], '16:9': [1024, 576], '9:16': [576, 1024], '4:3': [1024, 768], '3:4': [768, 1024] }; const [w, h] = ratios[ratio] || ratios['1:1']; AppState.settings.width = w; AppState.settings.height = h; // Update UI indicators document.querySelectorAll('[data-ratio]').forEach(el => { el.classList.remove('bg-primary-600', 'text-white'); el.classList.add('bg-slate-800', 'text-slate-400'); }); const active = document.querySelector(`[data-ratio="${ratio}"]`); if (active) { active.classList.remove('bg-slate-800', 'text-slate-400'); active.classList.add('bg-primary-600', 'text-white'); } } async generateImage() { // Get elements from shadow DOM if needed const controls = document.querySelector('generator-controls'); let promptInput = document.getElementById('prompt-input'); let negativeInput = document.getElementById('negative-prompt'); // Use shadow DOM if regular DOM not found const shadowRoot = controls?.shadowRoot; if (shadowRoot) { if (!promptInput) promptInput = shadowRoot.getElementById('prompt-input'); if (!negativeInput) negativeInput = shadowRoot.getElementById('negative-prompt'); } const preview = document.querySelector('generator-preview'); const previewContainer = preview?.shadowRoot?.getElementById('preview-container') || document.getElementById('preview-container'); const promptValue = promptInput?.value?.trim(); if (!promptValue) { if (promptInput) { promptInput.style.borderColor = '#ef4444'; setTimeout(() => { promptInput.style.borderColor = ''; }, 2000); } return; } const prompt = promptValue; const negativePrompt = negativeInput ? negativeInput.value.trim() : ''; AppState.isGenerating = true; UI.showLoading(previewContainer, 'Generating Image...'); try { const result = await APIService.generateImage(prompt, negativePrompt, AppState.settings); AppState.currentImage = result; // Save to history AppState.history.unshift(result); localStorage.setItem('generationHistory', JSON.stringify(AppState.history)); this.renderPreview(result); this.updateGallery(); } catch (error) { console.error('Generation error:', error); this.showErrorInPreview(preview || previewContainer, 'Generation failed. Please try again.'); } finally { AppState.isGenerating = false; } } showErrorInPreview(previewElement, message) { const container = previewElement?.shadowRoot?.getElementById('preview-container') || previewElement; if (!container) return; const errorDiv = document.createElement('div'); errorDiv.className = 'error-container flex flex-col items-center justify-center space-y-3 p-8 text-red-400'; errorDiv.innerHTML = `

${message}

`; container.innerHTML = ''; container.appendChild(errorDiv); // Bind dismiss button errorDiv.querySelector('.dismiss-error')?.addEventListener('click', () => { errorDiv.remove(); }); if (typeof feather !== 'undefined') { feather.replace(); } } renderPreview(item) { const preview = document.querySelector('generator-preview'); const container = preview?.shadowRoot?.getElementById('preview-container') || document.getElementById('preview-container'); if (!container) return; container.innerHTML = `
Generated
${item.width || 1024}x${item.height || 1024}
Prompt: ${item.prompt.substring(0, 100)}${item.prompt.length > 100 ? '...' : ''}
`; if (typeof feather !== 'undefined') { feather.replace(); } } async generateVideoFromCurrent() { if (!AppState.currentImage) return; const preview = document.querySelector('generator-preview'); const container = preview?.shadowRoot?.getElementById('preview-container') || document.getElementById('preview-container'); this.showLoadingInContainer(container, 'Generating Video... This may take a minute'); try { const result = await APIService.generateVideo( AppState.currentImage.url, AppState.currentImage.prompt + ', cinematic motion, smooth animation', AppState.settings ); AppState.currentVideo = result; // Update history AppState.history.unshift(result); localStorage.setItem('generationHistory', JSON.stringify(AppState.history)); this.renderVideoPreview(result); this.updateGallery(); } catch (error) { console.error('Video generation error:', error); this.showErrorInPreview(preview, 'Video generation failed.'); } } showLoadingInContainer(container, text = 'Generating...') { if (!container) return; container.innerHTML = `

${text}

`; } renderVideoPreview(item) { const preview = document.querySelector('generator-preview'); const container = preview?.shadowRoot?.getElementById('preview-container') || document.getElementById('preview-container'); if (!container) return; container.innerHTML = `
Video thumbnail
MP4 ${item.duration}s @ 24fps
`; feather.replace(); } animateImage(id) { const item = AppState.history.find(h => h.id === id); if (item && item.type === 'image') { AppState.currentImage = item; this.generateVideoFromCurrent(); } } downloadImage(url) { const a = document.createElement('a'); a.href = url; a.download = `dream-machine-${Date.now()}.png`; a.target = '_blank'; a.click(); } deleteItem(id) { AppState.history = AppState.history.filter(h => h.id !== id); localStorage.setItem('generationHistory', JSON.stringify(AppState.history)); this.updateGallery(); } updateGallery() { const gallery = document.querySelector('gallery-grid'); const grid = gallery?.shadowRoot?.getElementById('gallery-grid') || document.getElementById('gallery-grid'); const countIndicator = gallery?.shadowRoot?.getElementById('gallery-count'); if (countIndicator) { countIndicator.textContent = `${AppState.history.length} items`; } if (!grid) return; if (AppState.history.length === 0) { grid.innerHTML = `

No generations yet

Your creations will be saved here automatically

`; } else { grid.innerHTML = AppState.history.map(item => this.createImageCard(item)).join(''); } if (typeof feather !== 'undefined') { feather.replace(); } } createImageCard(item) { const isVideo = item.type === 'video'; return `
${item.prompt}
${!isVideo ? `` : ''}
${isVideo ? `
VIDEO
` : ''}

${item.prompt}

${Utils.formatDate(new Date(item.timestamp))} Seed: ${item.seed || 'N/A'}
`; } loadHistory() { this.updateGallery(); } enhancePrompt() { const controls = document.querySelector('generator-controls'); const input = document.getElementById('prompt-input') || controls?.shadowRoot?.getElementById('prompt-input'); if (!input) return; const current = input.value; const enhancers = [ '8k resolution, photorealistic, highly detailed, professional photography', 'cinematic lighting, unreal engine 5, octane render', 'masterpiece, best quality, intricate details', 'trending on artstation, sharp focus, vivid colors' ]; const randomEnhancer = enhancers[Math.floor(Math.random() * enhancers.length)]; input.value = current ? `${current}, ${randomEnhancer}` : randomEnhancer; // Visual feedback input.classList.add('ring-2', 'ring-secondary-500'); setTimeout(() => input.classList.remove('ring-2', 'ring-secondary-500'), 500); } } // Gallery button event delegation document.addEventListener('click', (e) => { const animateBtn = e.target.closest('.animate-image-btn'); const downloadBtn = e.target.closest('.download-btn'); const deleteBtn = e.target.closest('.delete-btn'); if (animateBtn) { e.stopPropagation(); const id = animateBtn.getAttribute('data-id'); app.animateImage(id); } if (downloadBtn) { e.stopPropagation(); const url = downloadBtn.getAttribute('data-url'); app.downloadImage(url); } if (deleteBtn) { e.stopPropagation(); const id = deleteBtn.getAttribute('data-id'); app.deleteItem(id); } }); // Initialize const app = new DreamMachineApp(); // Expose to window for onclick handlers window.app = app;