Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Crimson Canvas Creations</title> | |
| <script src="https://unpkg.com/lucide@latest"></script> | |
| <!-- Google Fonts --> | |
| <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;600;700&family=Playfair+Display:wght@700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary-color: #8b0000; /* Crimson */ | |
| --primary-hover: #a52a2a; | |
| --bg-dark: #0f0f0f; | |
| --bg-card: #1a1a1a; | |
| --text-main: #f0f0f0; | |
| --text-muted: #a0a0a0; | |
| --accent-glow: rgba(139, 0, 0, 0.4); | |
| --border-color: #333; | |
| --font-heading: 'Playfair Display', serif; | |
| --font-body: 'Outfit', sans-serif; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| font-family: var(--font-body); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-x: hidden; | |
| } | |
| /* --- Header & Footer --- */ | |
| header { | |
| padding: 20px; | |
| border-bottom: 1px solid var(--border-color); | |
| background-color: var(--bg-dark); | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| backdrop-filter: blur(10px); | |
| } | |
| .nav-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .brand { | |
| font-family: var(--font-heading); | |
| font-size: 1.8rem; | |
| color: var(--text-main); | |
| text-decoration: none; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .brand span { | |
| color: var(--primary-color); | |
| } | |
| .footer-link { | |
| text-decoration: none; | |
| color: var(--text-muted); | |
| font-size: 0.85rem; | |
| transition: color 0.3s; | |
| } | |
| .footer-link:hover { | |
| color: var(--primary-color); | |
| text-decoration: underline; | |
| } | |
| /* --- Main Layout --- */ | |
| main { | |
| flex: 1; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 40px 20px; | |
| width: 100%; | |
| display: grid; | |
| grid-template-columns: 350px 1fr; | |
| gap: 40px; | |
| } | |
| /* --- Controls Sidebar --- */ | |
| .controls-panel { | |
| background: var(--bg-card); | |
| padding: 30px; | |
| border-radius: 16px; | |
| border: 1px solid var(--border-color); | |
| height: fit-content; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.5); | |
| } | |
| h2 { | |
| font-family: var(--font-heading); | |
| font-size: 2rem; | |
| margin-bottom: 25px; | |
| color: var(--text-main); | |
| } | |
| .form-group { | |
| margin-bottom: 25px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-size: 0.9rem; | |
| color: var(--text-muted); | |
| font-weight: 600; | |
| } | |
| textarea { | |
| width: 100%; | |
| background-color: #0a0a0a; | |
| border: 1px solid var(--border-color); | |
| color: var(--text-main); | |
| padding: 15px; | |
| border-radius: 8px; | |
| font-family: var(--font-body); | |
| font-size: 1rem; | |
| resize: vertical; | |
| min-height: 100px; | |
| transition: all 0.3s ease; | |
| } | |
| textarea:focus { | |
| outline: none; | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 10px var(--accent-glow); | |
| } | |
| .slider-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| accent-color: var(--primary-color); | |
| cursor: pointer; | |
| } | |
| .value-display { | |
| font-size: 0.85rem; | |
| color: var(--primary-color); | |
| font-weight: 700; | |
| text-align: right; | |
| } | |
| .btn-generate { | |
| width: 100%; | |
| padding: 18px; | |
| background-color: var(--primary-color); | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| font-family: var(--font-heading); | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| box-shadow: 0 4px 15px var(--accent-glow); | |
| } | |
| .btn-generate:hover { | |
| background-color: var(--primary-hover); | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px var(--accent-glow); | |
| } | |
| .btn-generate:active { | |
| transform: translateY(1px); | |
| } | |
| .btn-generate:disabled { | |
| background-color: #444; | |
| cursor: not-allowed; | |
| box-shadow: none; | |
| transform: none; | |
| } | |
| /* --- Canvas / Output Area --- */ | |
| .canvas-container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: flex-start; | |
| } | |
| .art-frame { | |
| width: 100%; | |
| aspect-ratio: 1 / 1; /* Square canvas */ | |
| background-color: #000; | |
| border-radius: 16px; | |
| overflow: hidden; | |
| position: relative; | |
| border: 4px solid var(--border-color); | |
| box-shadow: 0 20px 50px rgba(0,0,0,0.6); | |
| transition: border-color 0.5s ease; | |
| } | |
| .art-frame.active-border { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 30px var(--accent-glow); | |
| } | |
| #generated-image { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| display: block; | |
| } | |
| .placeholder-content { | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--text-muted); | |
| background: radial-gradient(circle at center, #1a1a1a 0%, #0a0a0a 100%); | |
| text-align: center; | |
| padding: 40px; | |
| } | |
| .placeholder-icon { | |
| font-size: 4rem; | |
| margin-bottom: 20px; | |
| color: #333; | |
| } | |
| .status-bar { | |
| margin-top: 20px; | |
| width: 100%; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| color: var(--text-muted); | |
| font-size: 0.9rem; | |
| padding: 0 10px; | |
| } | |
| .loader { | |
| display: none; | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid var(--border-color); | |
| border-top: 4px solid var(--primary-color); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin: 0 auto; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| /* --- Responsive Design --- */ | |
| @media (max-width: 900px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| } | |
| .art-frame { | |
| max-width: 500px; | |
| } | |
| } | |
| @media (max-width: 600px) { | |
| .nav-container { | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| .brand { | |
| font-size: 1.5rem; | |
| } | |
| .controls-panel { | |
| padding: 20px; | |
| } | |
| h2 { | |
| font-size: 1.5rem; | |
| } | |
| } | |
| /* Utility for the footer link */ | |
| .built-with-container { | |
| display: flex; | |
| align-items: center; | |
| gap: 5px; | |
| } | |
| .built-with-text { | |
| color: var(--text-muted); | |
| font-size: 0.8rem; | |
| } | |
| .built-with-link { | |
| color: var(--primary-color); | |
| text-decoration: none; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="nav-container"> | |
| <a href="#" class="brand"> | |
| Crimson <span>Canvas</span> | |
| </a> | |
| <div class="built-with-container"> | |
| <span class="built-with-text">Built with</span> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with-link"> | |
| anycoder | |
| </a> | |
| </div> | |
| </div> | |
| </header> | |
| <main> | |
| <!-- Left Side: Controls --> | |
| <aside class="controls-panel"> | |
| <h2>New Creation</h2> | |
| <div class="form-group"> | |
| <label for="prompt">Art Prompt</label> | |
| <textarea id="prompt" placeholder="Describe the masterpiece you want to create... e.g., 'A futuristic cyberpunk city with neon rain, cinematic lighting, 8k resolution'"></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="style-select">Art Style</label> | |
| <select id="style-select" style="width: 100%; padding: 12px; background: #0a0a0a; color: var(--text-main); border: 1px solid var(--border-color); border-radius: 8px; font-family: var(--font-body);"> | |
| <option value="realistic">Photorealistic</option> | |
| <option value="cyberpunk">Cyberpunk / Sci-Fi</option> | |
| <option value="fantasy">Fantasy / Epic</option> | |
| <option value="abstract">Abstract / Geometric</option> | |
| <option value="oil-painting">Oil Painting Style</option> | |
| <option value="anime">Anime / Manga</option> | |
| <option value="noir">Film Noir</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="aspect-ratio">Aspect Ratio</label> | |
| <div class="slider-container"> | |
| <input type="range" id="aspect-ratio" min="0.5" max="2.0" step="0.1" value="1.0"> | |
| <div class="value-display" id="ratio-display">Square (1:1)</div> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="creativity">Creativity / Nudging</label> | |
| <div class="slider-container"> | |
| <input type="range" id="creativity" min="0" max="100" step="5" value="50"> | |
| <div class="value-display" id="creativity-display">50%</div> | |
| </div> | |
| </div> | |
| <button id="generate-btn" class="btn-generate"> | |
| Generate Art | |
| </button> | |
| </aside> | |
| <!-- Right Side: Canvas --> | |
| <section class="canvas-container"> | |
| <div class="art-frame" id="art-frame"> | |
| <div class="placeholder-content" id="placeholder"> | |
| <div class="placeholder-icon"> | |
| <i data-lucide="palette"></i> | |
| </div> | |
| <h3>Ready to Create</h3> | |
| <p style="margin-top: 10px; max-width: 300px;"> | |
| Enter a prompt on the left and click "Generate Art" to see your vision come to life. | |
| </p> | |
| </div> | |
| <div class="loader" id="loader"></div> | |
| <img id="generated-image" src="" alt="Generated Art" style="display: none;"> | |
| </div> | |
| <div class="status-bar"> | |
| <span id="status-text">Waiting for input...</span> | |
| <span id="seed-display">Seed: -</span> | |
| </div> | |
| </section> | |
| </main> | |
| <script> | |
| // Initialize Lucide Icons | |
| lucide.createIcons(); | |
| // DOM Elements | |
| const generateBtn = document.getElementById('generate-btn'); | |
| const promptInput = document.getElementById('prompt'); | |
| const styleSelect = document.getElementById('style-select'); | |
| const aspectRatioInput = document.getElementById('aspect-ratio'); | |
| const creativityInput = document.getElementById('creativity'); | |
| const ratioDisplay = document.getElementById('ratio-display'); | |
| const creativityDisplay = document.getElementById('creativity-display'); | |
| const artFrame = document.getElementById('art-frame'); | |
| const placeholder = document.getElementById('placeholder'); | |
| const loader = document.getElementById('loader'); | |
| const generatedImage = document.getElementById('generated-image'); | |
| const statusText = document.getElementById('status-text'); | |
| const seedDisplay = document.getElementById('seed-display'); | |
| // Helper to generate random seeds | |
| function generateRandomSeed() { | |
| return Math.floor(Math.random() * 1000000); | |
| } | |
| // Helper to get placeholder images for demo purposes | |
| // Using Picsum with random seed to simulate different results | |
| function getDemoImageURL(seed, width, height) { | |
| return `https://picsum.photos/seed/${seed}/${width}/${height}?random=${seed}`; | |
| } | |
| // Update UI Sliders | |
| aspectRatioInput.addEventListener('input', (e) => { | |
| const val = e.target.value; | |
| ratioDisplay.textContent = val === '1' ? 'Square (1:1)' : `Custom (${val}:1)`; | |
| // Adjust aspect ratio of the frame dynamically | |
| if (val === '1') { | |
| artFrame.style.aspectRatio = '1 / 1'; | |
| } else { | |
| artFrame.style.aspectRatio = `${val} / 1`; | |
| } | |
| }); | |
| creativityInput.addEventListener('input', (e) => { | |
| creativityDisplay.textContent = e.target.value + '%'; | |
| }); | |
| // Main Generation Logic | |
| generateBtn.addEventListener('click', async () => { | |
| const prompt = promptInput.value.trim(); | |
| if (!prompt) { | |
| alert("Please enter a prompt to generate your artwork."); | |
| promptInput.focus(); | |
| return; | |
| } | |
| // 1. UI State: Loading | |
| generateBtn.disabled = true; | |
| generateBtn.innerHTML = "Dreaming..."; | |
| artFrame.classList.remove('active-border'); | |
| placeholder.style.display = 'none'; | |
| generatedImage.style.display = 'none'; | |
| loader.style.display = 'block'; | |
| statusText.textContent = "Processing prompt..."; | |
| // 2. Simulation of AI Processing | |
| const seed = generateRandomSeed(); | |
| const style = styleSelect.value; | |
| const creativity = creativityInput.value; | |
| // Update status based on inputs | |
| statusText.innerHTML = `Applying <strong>${style.replace('-', ' ')}</strong> style... (Seed: ${seed})`; | |
| // Calculate fake processing time based on creativity (higher creativity = longer time) | |
| const processingTime = 1500 + (creativity * 20); | |
| await new Promise(resolve => setTimeout(resolve, processingTime)); | |
| // 3. "Generate" Image (Using Picsum for Demo) | |
| // We construct a URL that roughly matches the aspect ratio | |
| const currentRatio = parseFloat(aspectRatioInput.value); | |
| let w = 800; | |
| let h = 800; | |
| if (currentRatio > 1.2) { w = 1000; h = 600; } // Landscape | |
| else if (currentRatio < 0.8) { w = 600; h = 1000; } // Portrait | |
| else { w = 800; h = 800; } // Square | |
| const imageUrl = getDemoImageURL(seed, w, h); | |
| // 4. UI State: Success | |
| loader.style.display = 'none'; | |
| generatedImage.src = imageUrl; | |
| generatedImage.onload = () => { | |
| generatedImage.style.display = 'block'; | |
| artFrame.classList.add('active-border'); | |
| statusText.innerHTML = `Generation complete! <br><small>Style: ${style} | Steps: ${15 + (creativity/2)}</small>`; | |
| seedDisplay.textContent = `Seed: ${seed}`; | |
| generateBtn.disabled = false; | |
| generateBtn.textContent = "Generate Art"; | |
| }; | |
| }); | |
| // Add a subtle animation to the frame on hover | |
| artFrame.addEventListener('mouseenter', () => { | |
| if (!generatedImage.style.display || generatedImage.style.display === 'none') { | |
| artFrame.style.borderColor = '#555'; | |
| } | |
| }); | |
| artFrame.addEventListener('mouseleave', () => { | |
| if (!generatedImage.style.display || generatedImage.style.display === 'none') { | |
| artFrame.style.borderColor = '#333'; | |
| } else { | |
| artFrame.classList.add('active-border'); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |