anycoder-acdb0139 / index.html
Knives567's picture
Upload folder using huggingface_hub
59e0592 verified
<!DOCTYPE html>
<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()">&times;</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>