Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Image Generator</title> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| --primary: #6366f1; | |
| --primary-dark: #4f46e5; | |
| --secondary: #ec4899; | |
| --bg-dark: #0f0f1a; | |
| --bg-card: #1a1a2e; | |
| --bg-input: #252542; | |
| --text-primary: #ffffff; | |
| --text-secondary: #a0a0b8; | |
| --border: #2d2d4a; | |
| --success: #10b981; | |
| --warning: #f59e0b; | |
| --gradient: linear-gradient(135deg, #6366f1 0%, #ec4899 100%); | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: var(--bg-dark); | |
| color: var(--text-primary); | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| /* Animated Background */ | |
| .bg-animation { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| overflow: hidden; | |
| } | |
| .bg-animation::before { | |
| content: ''; | |
| position: absolute; | |
| width: 200%; | |
| height: 200%; | |
| background: | |
| radial-gradient(circle at 20% 50%, rgba(99, 102, 241, 0.15) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(236, 72, 153, 0.1) 0%, transparent 40%); | |
| animation: bgPulse 15s ease-in-out infinite; | |
| } | |
| @keyframes bgPulse { | |
| 0%, 100% { transform: translate(0, 0) scale(1); } | |
| 50% { transform: translate(-5%, -5%) scale(1.1); } | |
| } | |
| /* Header */ | |
| header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 1.5rem 2rem; | |
| background: rgba(26, 26, 46, 0.8); | |
| backdrop-filter: blur(10px); | |
| border-bottom: 1px solid var(--border); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| background: var(--gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .logo i { | |
| -webkit-text-fill-color: var(--primary); | |
| } | |
| .anycoder-link { | |
| color: var(--text-secondary); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| transition: color 0.3s ease; | |
| } | |
| .anycoder-link:hover { | |
| color: var(--primary); | |
| } | |
| .anycoder-link span { | |
| background: var(--gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| font-weight: 600; | |
| } | |
| /* Main Container */ | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 2rem; | |
| } | |
| /* Generator Section */ | |
| .generator-section { | |
| display: grid; | |
| grid-template-columns: 1fr 320px; | |
| gap: 2rem; | |
| margin-bottom: 3rem; | |
| } | |
| @media (max-width: 1024px) { | |
| .generator-section { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| /* Input Card */ | |
| .input-card { | |
| background: var(--bg-card); | |
| border-radius: 20px; | |
| padding: 2rem; | |
| border: 1px solid var(--border); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .input-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 3px; | |
| background: var(--gradient); | |
| } | |
| .input-label { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| font-size: 0.9rem; | |
| color: var(--text-secondary); | |
| margin-bottom: 1rem; | |
| } | |
| .prompt-input-container { | |
| position: relative; | |
| margin-bottom: 1.5rem; | |
| } | |
| .prompt-input { | |
| width: 100%; | |
| min-height: 120px; | |
| padding: 1.25rem; | |
| background: var(--bg-input); | |
| border: 2px solid var(--border); | |
| border-radius: 12px; | |
| color: var(--text-primary); | |
| font-size: 1rem; | |
| resize: vertical; | |
| transition: all 0.3s ease; | |
| } | |
| .prompt-input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 20px rgba(99, 102, 241, 0.2); | |
| } | |
| .prompt-input::placeholder { | |
| color: var(--text-secondary); | |
| } | |
| .char-count { | |
| position: absolute; | |
| bottom: 0.75rem; | |
| right: 1rem; | |
| font-size: 0.8rem; | |
| color: var(--text-secondary); | |
| } | |
| /* Settings Grid */ | |
| .settings-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| @media (max-width: 768px) { | |
| .settings-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| } | |
| .setting-item { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .setting-label { | |
| font-size: 0.85rem; | |
| color: var(--text-secondary); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .setting-select { | |
| padding: 0.75rem 1rem; | |
| background: var(--bg-input); | |
| border: 2px solid var(--border); | |
| border-radius: 8px; | |
| color: var(--text-primary); | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .setting-select:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| } | |
| /* Generate Button */ | |
| .generate-btn { | |
| width: 100%; | |
| padding: 1rem 2rem; | |
| background: var(--gradient); | |
| border: none; | |
| border-radius: 12px; | |
| color: white; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 0.75rem; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .generate-btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: left 0.5s ease; | |
| } | |
| .generate-btn:hover::before { | |
| left: 100%; | |
| } | |
| .generate-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 30px rgba(99, 102, 241, 0.4); | |
| } | |
| .generate-btn:active { | |
| transform: translateY(0); | |
| } | |
| .generate-btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .generate-btn .spinner { | |
| display: none; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| border-top-color: white; | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .generate-btn.loading .spinner { | |
| display: block; | |
| } | |
| .generate-btn.loading .btn-text { | |
| display: none; | |
| } | |
| /* Side Panel */ | |
| .side-panel { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .panel-card { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| padding: 1.5rem; | |
| border: 1px solid var(--border); | |
| } | |
| .panel-title { | |
| font-size: 1rem; | |
| font-weight: 600; | |
| margin-bottom: 1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .style-tags { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.5rem; | |
| } | |
| .style-tag { | |
| padding: 0.5rem 1rem; | |
| background: var(--bg-input); | |
| border: 2px solid var(--border); | |
| border-radius: 20px; | |
| font-size: 0.85rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| color: var(--text-secondary); | |
| } | |
| .style-tag:hover { | |
| border-color: var(--primary); | |
| color: var(--primary); | |
| } | |
| .style-tag.active { | |
| background: var(--primary); | |
| border-color: var(--primary); | |
| color: white; | |
| } | |
| /* Gallery Section */ | |
| .gallery-section { | |
| margin-top: 2rem; | |
| } | |
| .gallery-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1.5rem; | |
| } | |
| .gallery-title { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .gallery-actions { | |
| display: flex; | |
| gap: 0.75rem; | |
| } | |
| .gallery-btn { | |
| padding: 0.75rem 1.25rem; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| color: var(--text-primary); | |
| font-size: 0.9rem; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| transition: all 0.3s ease; | |
| } | |
| .gallery-btn:hover { | |
| border-color: var(--primary); | |
| color: var(--primary); | |
| } | |
| /* Gallery Grid */ | |
| .gallery-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); | |
| gap: 1.5rem; | |
| } | |
| .gallery-item { | |
| background: var(--bg-card); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| border: 1px solid var(--border); | |
| transition: all 0.3s ease; | |
| position: relative; | |
| } | |
| .gallery-item:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); | |
| border-color: var(--primary); | |
| } | |
| .gallery-image { | |
| width: 100%; | |
| aspect-ratio: 1; | |
| object-fit: cover; | |
| background: var(--bg-input); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--text-secondary); | |
| font-size: 3rem; | |
| } | |
| .gallery-image img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .gallery-info { | |
| padding: 1rem; | |
| } | |
| .gallery-prompt { | |
| font-size: 0.9rem; | |
| color: var(--text-secondary); | |
| margin-bottom: 0.75rem; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| } | |
| .gallery-meta { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-size: 0.8rem; | |
| color: var(--text-secondary); | |
| } | |
| .gallery-actions-small { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .action-icon { | |
| width: 32px; | |
| height: 32px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: var(--bg-input); | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| border: none; | |
| color: var(--text-secondary); | |
| } | |
| .action-icon:hover { | |
| background: var(--primary); | |
| color: white; | |
| } | |
| .action-icon.download:hover { | |
| background: var(--success); | |
| } | |
| .action-icon.delete:hover { | |
| background: #ef4444; | |
| } | |
| /* Loading Overlay */ | |
| .loading-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(15, 15, 26, 0.9); | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 1000; | |
| flex-direction: column; | |
| gap: 1.5rem; | |
| } | |
| .loading-overlay.active { | |
| display: flex; | |
| } | |
| .loading-progress { | |
| width: 300px; | |
| height: 6px; | |
| background: var(--bg-input); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| } | |
| .loading-progress-bar { | |
| height: 100%; | |
| background: var(--gradient); | |
| border-radius: 3px; | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| } | |
| .loading-text { | |
| font-size: 1.1rem; | |
| color: var(--text-secondary); | |
| } | |
| /* Toast Notification */ | |
| .toast-container { | |
| position: fixed; | |
| bottom: 2rem; | |
| right: 2rem; | |
| z-index: 1000; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.75rem; | |
| } | |
| .toast { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 1rem 1.5rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| animation: slideIn 0.3s ease; | |
| min-width: 300px; | |
| } | |
| @keyframes slideIn { | |
| from { | |
| transform: translateX(100%); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateX(0); | |
| opacity: 1; | |
| } | |
| } | |
| .toast.success { | |
| border-left: 4px solid var(--success); | |
| } | |
| .toast.error { | |
| border-left: 4px solid #ef4444; | |
| } | |
| .toast.info { | |
| border-left: 4px solid var(--primary); | |
| } | |
| /* Empty State */ | |
| .empty-state { | |
| text-align: center; | |
| padding: 4rem 2rem; | |
| color: var(--text-secondary); | |
| } | |
| .empty-state i { | |
| font-size: 4rem; | |
| margin-bottom: 1rem; | |
| opacity: 0.5; | |
| } | |
| .empty-state h3 { | |
| font-size: 1.25rem; | |
| margin-bottom: 0.5rem; | |
| color: var(--text-primary); | |
| } | |
| /* Responsive Adjustments */ | |
| @media (max-width: 768px) { | |
| header { | |
| padding: 1rem; | |
| } | |
| .logo { | |
| font-size: 1.2rem; | |
| } | |
| .container { | |
| padding: 1rem; | |
| } | |
| .input-card, .panel-card { | |
| padding: 1.5rem; | |
| } | |
| .gallery-header { | |
| flex-direction: column; | |
| gap: 1rem; | |
| align-items: flex-start; | |
| } | |
| .toast-container { | |
| left: 1rem; | |
| right: 1rem; | |
| bottom: 1rem; | |
| } | |
| .toast { | |
| min-width: auto; | |
| } | |
| } | |
| /* Image Preview Modal */ | |
| .modal { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.9); | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 2000; | |
| padding: 2rem; | |
| } | |
| .modal.active { | |
| display: flex; | |
| } | |
| .modal-content { | |
| max-width: 90%; | |
| max-height: 90%; | |
| position: relative; | |
| } | |
| .modal-content img { | |
| max-width: 100%; | |
| max-height: 80vh; | |
| border-radius: 12px; | |
| } | |
| .modal-close { | |
| position: absolute; | |
| top: -50px; | |
| right: 0; | |
| background: none; | |
| border: none; | |
| color: white; | |
| font-size: 2rem; | |
| cursor: pointer; | |
| transition: transform 0.3s ease; | |
| } | |
| .modal-close:hover { | |
| transform: scale(1.1); | |
| } | |
| .modal-actions { | |
| display: flex; | |
| justify-content: center; | |
| gap: 1rem; | |
| margin-top: 1.5rem; | |
| } | |
| .modal-btn { | |
| padding: 0.75rem 2rem; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| color: white; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| transition: all 0.3s ease; | |
| } | |
| .modal-btn:hover { | |
| border-color: var(--primary); | |
| } | |
| .modal-btn.primary { | |
| background: var(--primary); | |
| border-color: var(--primary); | |
| } | |
| .modal-btn.primary:hover { | |
| background: var(--primary-dark); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="bg-animation"></div> | |
| <header> | |
| <div class="logo"> | |
| <i class="fas fa-cube"></i> | |
| <span>DreamGen</span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with <span>anycoder</span> | |
| </a> | |
| </header> | |
| <div class="container"> | |
| <section class="generator-section"> | |
| <div class="input-card"> | |
| <div class="input-label"> | |
| <i class="fas fa-wand-magic-sparkles"></i> | |
| Describe your image | |
| </div> | |
| <div class="prompt-input-container"> | |
| <textarea | |
| class="prompt-input" | |
| id="promptInput" | |
| placeholder="A futuristic city with flying cars, neon lights, and towering skyscrapers at sunset..." | |
| ></textarea> | |
| <span class="char-count"><span id="charCount">0</span>/500</span> | |
| </div> | |
| <div class="settings-grid"> | |
| <div class="setting-item"> | |
| <label class="setting-label"> | |
| <i class="fas fa-expand"></i> Aspect Ratio | |
| </label> | |
| <select class="setting-select" id="aspectRatio"> | |
| <option value="1:1">Square (1:1)</option> | |
| <option value="3:4">Portrait (3:4)</option> | |
| <option value="4:3">Landscape (4:3)</option> | |
| <option value="16:9">Wide (16:9)</option> | |
| </select> | |
| </div> | |
| <div class="setting-item"> | |
| <label class="setting-label"> | |
| <i class="fas fa-palette"></i> Style | |
| </label> | |
| <select class="setting-select" id="imageStyle"> | |
| <option value="realistic">Realistic</option> | |
| <option value="anime">Anime</option> | |
| <option value="oil">Oil Painting</option> | |
| <option value="watercolor">Watercolor</option> | |
| <option value="3d">3D Render</option> | |
| <option value="cyberpunk">Cyberpunk</option> | |
| </select> | |
| </div> | |
| <div class="setting-item"> | |
| <label class="setting-label"> | |
| <i class="fas fa-layer-group"></i> Quality | |
| </label> | |
| <select class="setting-select" id="imageQuality"> | |
| <option value="standard">Standard</option> | |
| <option value="hd">HD</option> | |
| <option value="ultra">Ultra HD</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button class="generate-btn" id="generateBtn" onclick="generateImage()"> | |
| <span class="btn-text"> | |
| <i class="fas fa-bolt"></i> | |
| Generate Image | |
| </span> | |
| <div class="spinner"></div> | |
| </button> | |
| </div> | |
| <div class="side-panel"> | |
| <div class="panel-card"> | |
| <h3 class="panel-title"> | |
| <i class="fas fa-paintbrush"></i> | |
| Quick Styles | |
| </h3> | |
| <div class="style-tags"> | |
| <span class="style-tag active" onclick="selectStyle(this, 'photorealistic')">Photorealistic</span> | |
| <span class="style-tag" onclick="selectStyle(this, 'digital art')">Digital Art</span> | |
| <span class="style-tag" onclick="selectStyle(this, 'minimalist')">Minimalist</span> | |
| <span class="style-tag" onclick="selectStyle(this, 'fantasy')">Fantasy</span> | |
| <span class="style-tag" onclick="selectStyle(this, 'sci-fi')">Sci-Fi</span> | |
| <span class="style-tag" onclick="selectStyle(this, 'vintage')">Vintage</span> | |
| </div> | |
| </div> | |
| <div class="panel-card"> | |
| <h3 class="panel-title"> | |
| <i class="fas fa-lightbulb"></i> | |
| Tips | |
| </h3> | |
| <ul style="color: var(--text-secondary); font-size: 0.9rem; line-height: 1.8; padding-left: 1.25rem;"> | |
| <li>Be specific about lighting</li> | |
| <li>Mention camera angles</li> | |
| <li>Include color palettes</li> | |
| <li>Describe textures</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="gallery-section"> | |
| <div class="gallery-header"> | |
| <h2 class="gallery-title"> | |
| <i class="fas fa-images"></i> | |
| Generated Images | |
| </h2> | |
| <div class="gallery-actions"> | |
| <button class="gallery-btn" onclick="clearGallery()"> | |
| <i class="fas fa-trash-can"></i> | |
| Clear All | |
| </button> | |
| <button class="gallery-btn" onclick="downloadAll()"> | |
| <i class="fas fa-download"></i> | |
| Download All | |
| </button> | |
| </div> | |
| </div> | |
| <div class="gallery-grid" id="galleryGrid"> | |
| <div class="empty-state"> | |
| <i class="fas fa-palette"></i> | |
| <h3>No images yet</h3> | |
| <p>Start generating amazing images with your prompts!</p> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| <!-- Loading Overlay --> | |
| <div class="loading-overlay" id="loadingOverlay"> | |
| <div class="loading-text" id="loadingText">Generating your image...</div> | |
| <div class="loading-progress"> | |
| <div class="loading-progress-bar" id="progressBar"></div> | |
| </div> | |
| <div style="color: var(--text-secondary); font-size: 0.9rem;">This may take a moment</div> | |
| </div> | |
| <!-- Modal --> | |
| <div class="modal" id="imageModal"> | |
| <div class="modal-content"> | |
| <button class="modal-close" onclick="closeModal()">×</button> | |
| <img id="modalImage" src="" alt="Preview"> | |
| <div class="modal-actions"> | |
| <button class="modal-btn" onclick="closeModal()"> | |
| <i class="fas fa-xmark"></i> Close | |
| </button> | |
| <button class="modal-btn primary" id="modalDownloadBtn" onclick="downloadCurrentImage()"> | |
| <i class="fas fa-download"></i> Download | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Toast Container --> | |
| <div class="toast-container" id="toastContainer"></div> | |
| <script> | |
| // State management | |
| let images = []; | |
| let currentImageId = null; | |
| let selectedStyle = 'photorealistic'; | |
| // DOM Elements | |
| const promptInput = document.getElementById('promptInput'); | |
| const charCount = document.getElementById('charCount'); | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const galleryGrid = document.getElementById('galleryGrid'); | |
| const loadingOverlay = document.getElementById('loadingOverlay'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const loadingText = document.getElementById('loadingText'); | |
| const imageModal = document.getElementById('imageModal'); | |
| const modalImage = document.getElementById('modalImage'); | |
| // Character count | |
| promptInput.addEventListener('input', function() { | |
| const count = this.value.length; | |
| charCount.textContent = count; | |
| if (count > 500) { | |
| charCount.style.color = '#ef4444'; | |
| } else { | |
| charCount.style.color = 'var(--text-secondary)'; | |
| } | |
| }); | |
| // Style selection | |
| function selectStyle(element, style) { | |
| document.querySelectorAll('.style-tag').forEach(tag => tag.classList.remove('active')); | |
| element.classList.add('active'); | |
| selectedStyle = style; | |
| } | |
| // Add style to prompt | |
| document.querySelectorAll('.style-tag').forEach(tag => { | |
| tag.addEventListener('click', function() { | |
| if (promptInput.value && !promptInput.value.includes(this.textContent)) { | |
| promptInput.value += `, ${this.textContent} style`; | |
| promptInput.dispatchEvent(new Event('input')); | |
| } | |
| }); | |
| }); | |
| // Generate Image | |
| async function generateImage() { | |
| const prompt = promptInput.value.trim(); | |
| if (!prompt) { | |
| showToast('Please enter a prompt', 'error'); | |
| return; | |
| } | |
| if (prompt.length > 500) { | |
| showToast('Prompt is too long (max 500 characters)', 'error'); | |
| return; | |
| } | |
| generateBtn.classList.add('loading'); | |
| generateBtn.disabled = true; | |
| loadingOverlay.classList.add('active'); | |
| const aspectRatio = document.getElementById('aspectRatio').value; | |
| const style = document.getElementById('imageStyle').value; | |
| const quality = document.getElementById('imageQuality').value; | |
| // Simulate generation progress | |
| let progress = 0; | |
| const progressInterval = setInterval(() => { | |
| progress += Math.random() * 15; | |
| if (progress > 90) progress = 90; | |
| progressBar.style.width = progress + '%'; | |
| loadingText.textContent = getLoadingMessage(progress); | |
| }, 200); | |
| // Simulate API call delay | |
| await new Promise(resolve => setTimeout(resolve, 3000 + Math.random() * 2000)); | |
| clearInterval(progressInterval); | |
| progressBar.style.width = '100%'; | |
| loadingText.textContent = 'Complete!'; | |
| // Generate placeholder image | |
| const seed = Date.now(); | |
| const width = aspectRatio === '16:9' ? 768 : aspectRatio === '3:4' ? 480 : aspectRatio === '4:3' ? 640 : 512; | |
| const height = aspectRatio === '16:9' ? 432 : aspectRatio === '3:4' ? 640 : aspectRatio === '4:3' ? 480 : 512; | |
| // Create a colored placeholder with text | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = width; | |
| canvas.height = height; | |
| const ctx = canvas.getContext('2d'); | |
| // Generate gradient based on prompt hash | |
| const hue = Math.abs(hashString(prompt)) % 360; | |
| const gradient = ctx.createLinearGradient(0, 0, width, height); | |
| gradient.addColorStop(0, `hsl(${hue}, 60%, 20%)`); | |
| gradient.addColorStop(1, `hsl(${(hue + 60) % 360}, 60%, 30%)`); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, width, height); | |
| // Add decorative elements | |
| for (let i = 0; i < 20; i++) { | |
| ctx.beginPath(); | |
| ctx.arc( | |
| Math.random() * width, | |
| Math.random() * height, | |
| Math.random() * 50 + 10, | |
| 0, | |
| Math.PI * 2 | |
| ); | |
| ctx.fillStyle = `hsla(${(hue + Math.random() * 60) % 360}, 70%, 50%, 0.1)`; | |
| ctx.fill(); | |
| } | |
| // Add text | |
| ctx.font = 'bold 24px Arial'; | |
| ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('AI Generated', width / 2, height / 2 - 20); | |
| ctx.font = '16px Arial'; | |
| ctx.fillText(`Style: ${style}`, width / 2, height / 2 + 20); | |
| const imageData = canvas.toDataURL('image/png'); | |
| const newImage = { | |
| id: seed, | |
| prompt: prompt, | |
| style: style, | |
| aspectRatio: aspectRatio, | |
| quality: quality, | |
| timestamp: new Date(), | |
| src: imageData | |
| }; | |
| images.unshift(newImage); | |
| // Reset UI | |
| await new Promise(resolve => setTimeout(resolve, 500)); | |
| loadingOverlay.classList.remove('active'); | |
| generateBtn.classList.remove('loading'); | |
| generateBtn.disabled = false; | |
| progressBar.style.width = '0%'; | |
| renderGallery(); | |
| showToast('Image generated successfully!', 'success'); | |
| } | |
| function hashString(str) { | |
| let hash = 0; | |
| for (let i = 0; i < str.length; i++) { | |
| const char = str.charCodeAt(i); | |
| hash = ((hash << 5) - hash) + char; | |
| hash = hash & hash; | |
| } | |
| return Math.abs(hash); | |
| } | |
| function getLoadingMessage(progress) { | |
| if (progress < 25) return 'Analyzing prompt...'; | |
| if (progress < 50) return 'Creating composition...'; | |
| if (progress < 75) return 'Adding details...'; | |
| if (progress < 90) return 'Refining image...'; | |
| return 'Finalizing...'; | |
| } | |
| // Render Gallery | |
| function renderGallery() { | |
| if (images.length === 0) { | |
| galleryGrid.innerHTML = ` | |
| <div class="empty-state"> | |
| <i class="fas fa-palette"></i> | |
| <h3>No images yet</h3> | |
| <p>Start generating amazing images with your prompts!</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| galleryGrid.innerHTML = images.map(img => ` | |
| <div class="gallery-item" data-id="${img.id}"> | |
| <div class="gallery-image" onclick="openModal(${img.id})"> | |
| <img src="${img.src}" alt="${img.prompt}" loading="lazy"> | |
| </div> | |
| <div class="gallery-info"> | |
| <p class="gallery-prompt">${img.prompt}</p> | |
| <div class="gallery-meta"> | |
| <span><i class="fas fa-clock"></i> ${formatTime(img.timestamp)}</span> | |
| <div class="gallery-actions-small"> | |
| <button class="action-icon" onclick="openModal(${img.id})" title="Preview"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="action-icon download" onclick="downloadImage(${img.id})" title="Download"> | |
| <i class="fas fa-download"></i> | |
| </button> | |
| <button class="action-icon delete" onclick="deleteImage(${img.id})" title="Delete"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| function formatTime(date) { | |
| const now = new Date(); | |
| const diff = now - date; | |
| const minutes = Math.floor(diff / 60000); | |
| if (minutes < 1) return 'Just now'; | |
| if (minutes < 60) return `${minutes}m ago`; | |
| return `${Math.floor(minutes / 60)}h ago`; | |
| } | |
| // Image Actions | |
| function openModal(id) { | |
| const img = images.find(i => i.id === id); | |
| if (img) { | |
| currentImageId = id; | |
| modalImage.src = img.src; | |
| imageModal.classList.add('active'); | |
| } | |
| } | |
| function closeModal() { | |
| imageModal.classList.remove('active'); | |
| currentImageId = null; | |
| } | |
| function downloadImage(id) { | |
| const img = images.find(i => i.id === id); | |
| if (img) { | |
| const link = document.createElement('a'); | |
| link.download = `dreamgen_${id}.png`; | |
| link.href = img.src; | |
| link.click(); | |
| showToast('Image downloaded!', 'success'); | |
| } | |
| } | |
| function downloadCurrentImage() { | |
| if (currentImageId) { | |
| downloadImage(currentImageId); | |
| } | |
| } | |
| function deleteImage(id) { | |
| images = images.filter(i => i.id !== id); | |
| renderGallery(); | |
| showToast('Image deleted', 'info'); | |
| } | |
| function clearGallery() { | |
| if (images.length > 0 && confirm('Are you sure you want to delete all images?')) { | |
| images = []; | |
| renderGallery(); | |
| showToast('All images cleared', 'info'); | |
| } | |
| } | |
| function downloadAll() { | |
| if (images.length === 0) { | |
| showToast('No images to download', 'error'); | |
| return; | |
| } | |
| images.forEach((img, index) => { | |
| setTimeout(() => { | |
| const link = document.createElement('a'); | |
| link.download = `dreamgen_${img.id}.png`; | |
| link.href = img.src; | |
| link.click(); | |
| }, index * 500); | |
| }); | |
| showToast('Downloading all images...', 'success'); | |
| } | |
| // Toast Notifications | |
| function showToast(message, type = 'info') { | |
| const container = document.getElementById('toastContainer'); | |
| const toast = document.createElement('div'); | |
| toast.className = `toast ${type}`; | |
| const icon = type === 'success' ? 'fa-check-circle' : | |
| type === 'error' ? 'fa-exclamation-circle' : | |
| 'fa-info-circle'; | |
| toast.innerHTML = ` | |
| <i class="fas ${icon}"></i> | |
| <span>${message}</span> | |
| `; | |
| container.appendChild(toast); | |
| setTimeout(() => { | |
| toast.style.animation = 'slideIn 0.3s ease reverse'; | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| // Close modal on outside click | |
| imageModal.addEventListener('click', function(e) { | |
| if (e.target === imageModal) { | |
| closeModal(); | |
| } | |
| }); | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', function(e) { | |
| if (e.key === 'Escape' && imageModal.classList.contains('active')) { | |
| closeModal(); | |
| } | |
| if (e.ctrlKey && e.key === 'Enter') { | |
| generateImage(); | |
| } | |
| }); | |
| // Initial render | |
| renderGallery(); | |
| // Add some demo images on first load | |
| window.addEventListener('load', function() { | |
| setTimeout(() => { | |
| const demoPrompts = [ | |
| 'A majestic dragon flying over misty mountains at golden hour', | |
| 'Cyberpunk city street at night with neon signs and rain', | |
| 'A cozy coffee shop interior with warm lighting and plants', | |
| 'Abstract geometric patterns in vibrant colors' | |
| ]; | |
| demoPrompts.forEach((prompt, i) => { | |
| setTimeout(() => { | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = 512; | |
| canvas.height = 512; | |
| const ctx = canvas.getContext('2d'); | |
| const hue = (i * 90) % 360; | |
| const gradient = ctx.createLinearGradient(0, 0, 512, 512); | |
| gradient.addColorStop(0, `hsl(${hue}, 50%, 15%)`); | |
| gradient.addColorStop(1, `hsl(${(hue + 60) % 360}, 50%, 25%)`); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, 512, 512); | |
| for (let j = 0; j < 15; j++) { | |
| ctx.beginPath(); | |
| ctx.arc( | |
| Math.random() * 512, | |
| Math.random() * 512, | |
| Math.random() * 80 + 20, | |
| 0, | |
| Math.PI * 2 | |
| ); | |
| ctx.fillStyle = `hsla(${(hue + Math.random() * 60) % 360}, 70%, 50%, 0.15)`; | |
| ctx.fill(); | |
| } | |
| ctx.font = 'bold 20px Arial'; | |
| ctx.fillStyle = 'rgba(255, 255, 255, 0.4)'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('Demo Image', 256, 256); | |
| images.push({ | |
| id: Date.now() + i, | |
| prompt: prompt, | |
| style: 'realistic', | |
| aspectRatio: '1:1', | |
| quality: 'standard', | |
| timestamp: new Date(Date.now() - i * 60000), | |
| src: canvas.toDataURL('image/png') | |
| }); | |
| renderGallery(); | |
| }, i * 200); | |
| }); | |
| }, 1000); | |
| }); | |
| </script> | |
| </body> | |
| </html> |