Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>HiDream-O1-Image | Premium AI Generation</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-color: #0f172a; | |
| --accent-color: #3b82f6; | |
| --accent-glow: rgba(59, 130, 246, 0.5); | |
| --glass-bg: rgba(255, 255, 255, 0.03); | |
| --glass-border: rgba(255, 255, 255, 0.1); | |
| --text-primary: #f8fafc; | |
| --text-secondary: #94a3b8; | |
| --sidebar-width: 380px; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| -webkit-font-smoothing: antialiased; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: var(--bg-color); | |
| color: var(--text-primary); | |
| height: 100vh; | |
| overflow: hidden; | |
| display: flex; | |
| } | |
| /* Abstract background glow */ | |
| .glow { | |
| position: fixed; | |
| width: 600px; | |
| height: 600px; | |
| background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%); | |
| z-index: -1; | |
| filter: blur(80px); | |
| opacity: 0.4; | |
| } | |
| .glow-1 { top: -200px; right: -200px; } | |
| .glow-2 { bottom: -200px; left: -200px; } | |
| /* Sidebar */ | |
| aside { | |
| width: var(--sidebar-width); | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| border-right: 1px solid var(--glass-border); | |
| padding: 2rem; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 2rem; | |
| overflow-y: auto; | |
| z-index: 10; | |
| } | |
| .logo { | |
| font-family: 'Outfit', sans-serif; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| letter-spacing: -1px; | |
| background: linear-gradient(135deg, #fff 0%, #94a3b8 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| margin-bottom: 1rem; | |
| } | |
| .input-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.75rem; | |
| } | |
| label { | |
| font-size: 0.85rem; | |
| font-weight: 500; | |
| color: var(--text-secondary); | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| } | |
| textarea, select, input { | |
| background: rgba(0, 0, 0, 0.2); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 12px; | |
| padding: 1rem; | |
| color: var(--text-primary); | |
| font-family: inherit; | |
| font-size: 0.95rem; | |
| transition: all 0.3s ease; | |
| outline: none; | |
| } | |
| textarea:focus, select:focus, input:focus { | |
| border-color: var(--accent-color); | |
| box-shadow: 0 0 0 4px var(--accent-glow); | |
| } | |
| .slider-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .slider-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| accent-color: var(--accent-color); | |
| } | |
| .checkbox-group { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| cursor: pointer; | |
| } | |
| .checkbox-group input { | |
| width: 20px; | |
| height: 20px; | |
| } | |
| button#generate-btn { | |
| background: var(--accent-color); | |
| color: white; | |
| border: none; | |
| border-radius: 14px; | |
| padding: 1.25rem; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 0.75rem; | |
| margin-top: 1rem; | |
| box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3); | |
| } | |
| button#generate-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 30px rgba(59, 130, 246, 0.5); | |
| } | |
| button#generate-btn:active { | |
| transform: translateY(0); | |
| } | |
| button#generate-btn:disabled { | |
| background: #475569; | |
| cursor: not-allowed; | |
| box-shadow: none; | |
| transform: none; | |
| } | |
| /* Main Content */ | |
| main { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 2rem; | |
| gap: 2rem; | |
| position: relative; | |
| } | |
| .preview-container { | |
| flex: 1; | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 24px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| #result-image { | |
| max-width: 100%; | |
| max-height: 100%; | |
| object-fit: contain; | |
| border-radius: 12px; | |
| transition: all 0.5s ease; | |
| } | |
| .placeholder { | |
| text-align: center; | |
| color: var(--text-secondary); | |
| } | |
| .placeholder svg { | |
| width: 64px; | |
| height: 64px; | |
| margin-bottom: 1rem; | |
| opacity: 0.5; | |
| } | |
| /* Status / Loader */ | |
| .status-overlay { | |
| position: absolute; | |
| inset: 0; | |
| background: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(8px); | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 1.5rem; | |
| z-index: 20; | |
| } | |
| .loader { | |
| width: 48px; | |
| height: 48px; | |
| border: 3px solid var(--glass-border); | |
| border-top: 3px solid var(--accent-color); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .status-text { | |
| font-size: 1.1rem; | |
| font-weight: 500; | |
| color: var(--text-primary); | |
| } | |
| /* History */ | |
| .history-panel { | |
| height: 180px; | |
| display: flex; | |
| gap: 1rem; | |
| overflow-x: auto; | |
| padding-bottom: 1rem; | |
| } | |
| .history-item { | |
| min-width: 150px; | |
| height: 150px; | |
| background: var(--glass-bg); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 16px; | |
| cursor: pointer; | |
| overflow: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .history-item:hover { | |
| transform: scale(1.05); | |
| border-color: var(--accent-color); | |
| } | |
| .history-item img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 1024px) { | |
| body { | |
| flex-direction: column; | |
| overflow-y: auto; | |
| height: auto; | |
| } | |
| aside { | |
| width: 100%; | |
| height: auto; | |
| border-right: none; | |
| border-bottom: 1px solid var(--glass-border); | |
| } | |
| main { | |
| height: 800px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="glow glow-1"></div> | |
| <div class="glow glow-2"></div> | |
| <aside> | |
| <div class="logo">HiDream O1</div> | |
| <div class="input-group"> | |
| <label for="prompt">Prompt</label> | |
| <textarea id="prompt" rows="4" placeholder="A futuristic cityscape at sunset, neon lights, highly detailed..."></textarea> | |
| </div> | |
| <div class="input-group"> | |
| <label for="negative_prompt">Negative Prompt</label> | |
| <textarea id="negative_prompt" rows="2" placeholder="Blurry, distorted, low quality..."></textarea> | |
| </div> | |
| <div class="input-group"> | |
| <label for="wh_ratio">Aspect Ratio</label> | |
| <select id="wh_ratio"> | |
| <option value="1:1">1:1 Square</option> | |
| <option value="16:9">16:9 Cinematic</option> | |
| <option value="9:16">9:16 Portrait</option> | |
| <option value="4:3">4:3 Desktop</option> | |
| <option value="3:2">3:2 Photography</option> | |
| <option value="21:9">21:9 Ultrawide</option> | |
| </select> | |
| </div> | |
| <div class="slider-container"> | |
| <div class="slider-header"> | |
| <label>Guidance Scale</label> | |
| <span id="guidance-val">5.0</span> | |
| </div> | |
| <input type="range" id="guidance_scale" min="1" max="20" step="0.1" value="5.0"> | |
| </div> | |
| <div class="input-group"> | |
| <label for="seed">Seed</label> | |
| <input type="number" id="seed" value="-1" placeholder="-1 for random"> | |
| </div> | |
| <div class="checkbox-group"> | |
| <input type="checkbox" id="enable_prompt_refine" checked> | |
| <label for="enable_prompt_refine">Refine Prompt</label> | |
| </div> | |
| <button id="generate-btn"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 14 6-6 3 3 5-5"/><path d="m14 4 6 6-6 6-6-6Z"/></svg> | |
| Generate Image | |
| </button> | |
| </aside> | |
| <main> | |
| <div class="preview-container"> | |
| <div id="status-overlay" class="status-overlay"> | |
| <div class="loader"></div> | |
| <div id="status-text" class="status-text">Waking up the GPUs...</div> | |
| </div> | |
| <div id="placeholder" class="placeholder"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg> | |
| <p>Your masterpiece will appear here</p> | |
| </div> | |
| <img id="result-image" style="display: none;"> | |
| </div> | |
| <div class="history-panel" id="history-panel"> | |
| <!-- History items will be added here --> | |
| </div> | |
| </main> | |
| <script type="module"> | |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const promptInput = document.getElementById('prompt'); | |
| const negPromptInput = document.getElementById('negative_prompt'); | |
| const ratioInput = document.getElementById('wh_ratio'); | |
| const guidanceInput = document.getElementById('guidance_scale'); | |
| const guidanceVal = document.getElementById('guidance-val'); | |
| const seedInput = document.getElementById('seed'); | |
| const refineInput = document.getElementById('enable_prompt_refine'); | |
| const statusOverlay = document.getElementById('status-overlay'); | |
| const statusText = document.getElementById('status-text'); | |
| const resultImage = document.getElementById('result-image'); | |
| const placeholder = document.getElementById('placeholder'); | |
| const historyPanel = document.getElementById('history-panel'); | |
| guidanceInput.addEventListener('input', (e) => { | |
| guidanceVal.textContent = e.target.value; | |
| }); | |
| async function generate() { | |
| const prompt = promptInput.value.trim(); | |
| if (!prompt) { | |
| alert("Please enter a prompt!"); | |
| return; | |
| } | |
| try { | |
| generateBtn.disabled = true; | |
| statusOverlay.style.display = 'flex'; | |
| statusText.textContent = "Connecting to queue..."; | |
| const client = await Client.connect(window.location.origin); | |
| statusText.textContent = "In queue... please wait"; | |
| const result = await client.predict("/generate", { | |
| prompt: prompt, | |
| wh_ratio: ratioInput.value, | |
| negative_prompt: negPromptInput.value, | |
| enable_prompt_refine: refineInput.checked, | |
| seed: parseInt(seedInput.value), | |
| guidance_scale: parseFloat(guidanceInput.value), | |
| }); | |
| const imageUrl = result.data[0].url; | |
| // Update UI | |
| placeholder.style.display = 'none'; | |
| resultImage.src = imageUrl; | |
| resultImage.style.display = 'block'; | |
| // Add to history | |
| addToHistory(imageUrl); | |
| } catch (error) { | |
| console.error(error); | |
| alert("Error generating image: " + error.message); | |
| } finally { | |
| generateBtn.disabled = false; | |
| statusOverlay.style.display = 'none'; | |
| } | |
| } | |
| function addToHistory(url) { | |
| const div = document.createElement('div'); | |
| div.className = 'history-item'; | |
| div.innerHTML = `<img src="${url}" alt="Generated History">`; | |
| div.onclick = () => { | |
| resultImage.src = url; | |
| placeholder.style.display = 'none'; | |
| resultImage.style.display = 'block'; | |
| }; | |
| historyPanel.prepend(div); | |
| } | |
| generateBtn.addEventListener('click', generate); | |
| // Support Ctrl+Enter to generate | |
| promptInput.addEventListener('keydown', (e) => { | |
| if (e.ctrlKey && e.key === 'Enter') generate(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |