HiDream-O1-Image / index.html
akhaliq's picture
akhaliq HF Staff
feat: add ZeroGPU support and custom index page for image generation app
15c5eb9
<!DOCTYPE html>
<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>