Spaces:
Running
Running
| class GeneratorControls extends HTMLElement { | |
| constructor() { | |
| super(); | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| } | |
| .panel { | |
| background: rgba(30, 41, 59, 0.4); | |
| backdrop-filter: blur(12px); | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: 1rem; | |
| padding: 1.5rem; | |
| } | |
| .input-group { | |
| margin-bottom: 1rem; | |
| } | |
| .input-label { | |
| display: block; | |
| color: #94a3b8; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| margin-bottom: 0.5rem; | |
| } | |
| .cyber-input { | |
| width: 100%; | |
| background: rgba(15, 23, 42, 0.8); | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: 0.5rem; | |
| padding: 0.75rem 1rem; | |
| color: #f1f5f9; | |
| font-size: 0.875rem; | |
| transition: all 0.3s; | |
| resize: vertical; | |
| font-family: inherit; | |
| } | |
| .cyber-input:focus { | |
| outline: none; | |
| border-color: rgba(139, 92, 246, 0.6); | |
| box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1); | |
| } | |
| .cyber-input::placeholder { | |
| color: #475569; | |
| } | |
| .ratio-grid { | |
| display: grid; | |
| grid-template-columns: repeat(5, 1fr); | |
| gap: 0.5rem; | |
| margin-top: 0.5rem; | |
| } | |
| .ratio-btn { | |
| padding: 0.5rem; | |
| background: rgba(30, 41, 59, 0.8); | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: 0.375rem; | |
| color: #94a3b8; | |
| font-size: 0.75rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| text-align: center; | |
| } | |
| .ratio-btn:hover { | |
| border-color: rgba(139, 92, 246, 0.4); | |
| color: #c4b5fd; | |
| } | |
| .ratio-btn.active { | |
| background: #7c3aed; | |
| border-color: #7c3aed; | |
| color: white; | |
| } | |
| .generate-btn { | |
| width: 100%; | |
| padding: 0.875rem; | |
| background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); | |
| border: none; | |
| border-radius: 0.5rem; | |
| color: white; | |
| font-weight: 600; | |
| font-size: 0.875rem; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| position: relative; | |
| overflow: hidden; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 0.5rem; | |
| } | |
| .generate-btn:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 10px 25px -5px rgba(124, 58, 237, 0.4); | |
| } | |
| .generate-btn:active { | |
| transform: translateY(0); | |
| } | |
| .enhance-btn { | |
| position: absolute; | |
| right: 0.5rem; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| padding: 0.25rem 0.75rem; | |
| background: rgba(6, 182, 212, 0.1); | |
| border: 1px solid rgba(6, 182, 212, 0.3); | |
| border-radius: 0.25rem; | |
| color: #06b6d4; | |
| font-size: 0.75rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .enhance-btn:hover { | |
| background: rgba(6, 182, 212, 0.2); | |
| } | |
| .settings-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 1rem; | |
| margin-top: 1rem; | |
| } | |
| .setting-item label { | |
| display: block; | |
| color: #64748b; | |
| font-size: 0.75rem; | |
| margin-bottom: 0.25rem; | |
| } | |
| .setting-item input { | |
| width: 100%; | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: 0.25rem; | |
| padding: 0.375rem; | |
| color: #94a3b8; | |
| font-size: 0.75rem; | |
| } | |
| .slider-container { | |
| margin-top: 0.5rem; | |
| } | |
| .slider-label { | |
| display: flex; | |
| justify-content: space-between; | |
| color: #64748b; | |
| font-size: 0.75rem; | |
| margin-bottom: 0.25rem; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| height: 4px; | |
| background: rgba(139, 92, 246, 0.2); | |
| border-radius: 2px; | |
| outline: none; | |
| -webkit-appearance: none; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| width: 16px; | |
| height: 16px; | |
| background: #7c3aed; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| box-shadow: 0 0 10px rgba(124, 58, 237, 0.5); | |
| } | |
| .tag-suggestions { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.5rem; | |
| margin-top: 0.5rem; | |
| } | |
| .tag { | |
| padding: 0.25rem 0.5rem; | |
| background: rgba(139, 92, 246, 0.1); | |
| border: 1px solid rgba(139, 92, 246, 0.3); | |
| border-radius: 0.25rem; | |
| color: #a78bfa; | |
| font-size: 0.75rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .tag:hover { | |
| background: rgba(139, 92, 246, 0.2); | |
| transform: translateY(-1px); | |
| } | |
| </style> | |
| <div class="panel"> | |
| <div class="input-group"> | |
| <label class="input-label flex justify-between"> | |
| Prompt | |
| <span id="prompt-length" class="text-xs text-slate-500">0 chars</span> | |
| </label> | |
| <div style="position: relative;"> | |
| <textarea | |
| id="prompt-input" | |
| class="cyber-input" | |
| rows="4" | |
| placeholder="Describe what you want to see... (e.g., 'a cyberpunk street at night, neon lights, rain, 8k photorealistic')" | |
| ></textarea> | |
| <button class="enhance-btn" title="Auto-enhance prompt"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: inline; margin-right: 4px;"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg> | |
| Enhance | |
| </button> | |
| </div> | |
| <div class="tag-suggestions"> | |
| <span class="tag" onclick="document.getElementById('prompt-input').value += (document.getElementById('prompt-input').value ? ', ' : '') + '8k photorealistic'">+ 8k</span> | |
| <span class="tag" onclick="document.getElementById('prompt-input').value += (document.getElementById('prompt-input').value ? ', ' : '') + 'cinematic lighting'">+ cinematic</span> | |
| <span class="tag" onclick="document.getElementById('prompt-input').value += (document.getElementById('prompt-input').value ? ', ' : '') + 'unreal engine 5'">+ UE5</span> | |
| <span class="tag" onclick="document.getElementById('prompt-input').value += (document.getElementById('prompt-input').value ? ', ' : '') + 'highly detailed'">+ detailed</span> | |
| </div> | |
| </div> | |
| <div class="input-group"> | |
| <label class="input-label">Negative Prompt</label> | |
| <textarea | |
| id="negative-prompt" | |
| class="cyber-input" | |
| rows="2" | |
| placeholder="What to avoid... (e.g., 'blurry, low quality, distorted, ugly')" | |
| ></textarea> | |
| </div> | |
| <div class="input-group"> | |
| <label class="input-label">Aspect Ratio</label> | |
| <div class="ratio-grid"> | |
| <button class="ratio-btn active" data-ratio="1:1">1:1</button> | |
| <button class="ratio-btn" data-ratio="16:9">16:9</button> | |
| <button class="ratio-btn" data-ratio="9:16">9:16</button> | |
| <button class="ratio-btn" data-ratio="4:3">4:3</button> | |
| <button class="ratio-btn" data-ratio="3:4">3:4</button> | |
| </div> | |
| </div> | |
| <div class="slider-container"> | |
| <div class="slider-label"> | |
| <span>CFG Scale</span> | |
| <span id="cfg-value">7.5</span> | |
| </div> | |
| <input type="range" min="1" max="20" step="0.5" value="7.5" data-setting="cfgScale" oninput="document.getElementById('cfg-value').textContent = this.value"> | |
| </div> | |
| <div class="slider-container"> | |
| <div class="slider-label"> | |
| <span>Steps</span> | |
| <span id="steps-value">30</span> | |
| </div> | |
| <input type="range" min="10" max="50" step="1" value="30" data-setting="steps" oninput="document.getElementById('steps-value').textContent = this.value"> | |
| </div> | |
| <div class="settings-grid"> | |
| <div class="setting-item"> | |
| <label>Seed (-1 for random)</label> | |
| <input type="number" value="-1" data-setting="seed"> | |
| </div> | |
| <div class="setting-item"> | |
| <label>Model</label> | |
| <select class="cyber-input" style="padding: 0.375rem; font-size: 0.75rem; height: auto;"> | |
| <option>SDXL Photorealistic</option> | |
| <option>Uncensored V3</option> | |
| <option>Realistic Vision</option> | |
| <option>Epic Realism</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button id="generate-btn" class="generate-btn" style="margin-top: 1.5rem;"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg> | |
| Generate Image | |
| </button> | |
| </div> | |
| `; | |
| // Handle ratio button clicks | |
| this.shadowRoot.querySelectorAll('.ratio-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| this.shadowRoot.querySelectorAll('.ratio-btn').forEach(b => b.classList.remove('active')); | |
| e.target.classList.add('active'); | |
| }); | |
| }); | |
| } | |
| } | |
| class GeneratorPreview extends HTMLElement { | |
| constructor() { | |
| super(); | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| } | |
| .preview-container { | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: 1rem; | |
| min-height: 500px; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| .preview-header { | |
| padding: 1rem 1.5rem; | |
| border-bottom: 1px solid rgba(139, 92, 246, 0.1); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .preview-title { | |
| color: #e2e8f0; | |
| font-weight: 600; | |
| font-size: 0.875rem; | |
| } | |
| .preview-content { | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 1.5rem; | |
| position: relative; | |
| } | |
| .empty-state { | |
| text-align: center; | |
| color: #475569; | |
| } | |
| .empty-icon { | |
| width: 64px; | |
| height: 64px; | |
| margin: 0 auto 1rem; | |
| opacity: 0.3; | |
| } | |
| .video-controls { | |
| padding: 1rem 1.5rem; | |
| background: rgba(15, 23, 42, 0.8); | |
| border-top: 1px solid rgba(6, 182, 212, 0.2); | |
| display: none; | |
| } | |
| .video-controls.active { | |
| display: block; | |
| } | |
| .control-row { | |
| display: flex; | |
| gap: 1rem; | |
| margin-bottom: 0.75rem; | |
| } | |
| .control-group { | |
| flex: 1; | |
| } | |
| .control-label { | |
| display: block; | |
| color: #64748b; | |
| font-size: 0.75rem; | |
| margin-bottom: 0.25rem; | |
| } | |
| </style> | |
| <div class="preview-container" id="preview-container"> | |
| <div class="preview-header"> | |
| <span class="preview-title">Preview</span> | |
| <div class="flex gap-2"> | |
| <span class="text-xs text-slate-500 px-2 py-1 bg-slate-800 rounded">1024 × 1024</span> | |
| </div> | |
| </div> | |
| <div class="preview-content"> | |
| <div class="empty-state"> | |
| <svg class="empty-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg> | |
| <p class="text-sm">Enter a prompt and click generate</p> | |
| <p class="text-xs mt-2 opacity-50">Your creation will appear here</p> | |
| </div> | |
| </div> | |
| <div class="video-controls" id="video-controls"> | |
| <div class="control-row"> | |
| <div class="control-group"> | |
| <label class="control-label">Motion Strength</label> | |
| <input type="range" min="1" max="10" value="5" class="w-full" data-setting="motionStrength"> | |
| </div> | |
| <div class="control-group"> | |
| <label class="control-label">Duration (seconds)</label> | |
| <input type="range" min="2" max="10" value="4" class="w-full" data-setting="videoDuration"> | |
| </div> | |
| </div> | |
| <button id="generate-video-btn" class="w-full py-2 bg-secondary-600 hover:bg-secondary-500 text-white rounded-lg text-sm font-medium transition-colors flex items-center justify-center gap-2"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg> | |
| Generate Video from Image | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| } | |
| customElements.define('generator-controls', GeneratorControls); | |
| customElements.define('generator-preview', GeneratorPreview); |