| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>soc.io | AI 기반 소셜 콘텐츠 생성기</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Inter:wght@300;400;500;600&display=swap'); |
| |
| :root { |
| --primary: #6d28d9; |
| --primary-light: #8b5cf6; |
| --secondary: #10b981; |
| --dark: #0f172a; |
| --light: #f8fafc; |
| --accent: #f59e0b; |
| } |
| |
| body { |
| font-family: 'Inter', sans-serif; |
| background-color: var(--dark); |
| color: var(--light); |
| overflow-x: hidden; |
| } |
| |
| .tech-font { |
| font-family: 'Orbitron', sans-serif; |
| } |
| |
| .gradient-text { |
| background: linear-gradient(90deg, var(--primary-light), var(--secondary)); |
| -webkit-background-clip: text; |
| background-clip: text; |
| color: transparent; |
| } |
| |
| .glow-box { |
| box-shadow: 0 0 15px rgba(139, 92, 246, 0.5); |
| } |
| |
| .glow-box:hover { |
| box-shadow: 0 0 25px rgba(139, 92, 246, 0.7); |
| } |
| |
| .neon-border { |
| border: 1px solid rgba(139, 92, 246, 0.3); |
| position: relative; |
| } |
| |
| .neon-border::before { |
| content: ''; |
| position: absolute; |
| top: -2px; |
| left: -2px; |
| right: -2px; |
| bottom: -2px; |
| border: 2px solid transparent; |
| background: linear-gradient(135deg, var(--primary-light), var(--secondary)) border-box; |
| -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); |
| -webkit-mask-composite: destination-out; |
| mask-composite: exclude; |
| border-radius: inherit; |
| pointer-events: none; |
| } |
| |
| .grid-pattern { |
| background-image: |
| linear-gradient(rgba(139, 92, 246, 0.1) 1px, transparent 1px), |
| linear-gradient(90deg, rgba(139, 92, 246, 0.1) 1px, transparent 1px); |
| background-size: 30px 30px; |
| } |
| |
| .pulse-animation { |
| animation: pulse 2s infinite; |
| } |
| |
| @keyframes pulse { |
| 0% { opacity: 0.7; } |
| 50% { opacity: 1; } |
| 100% { opacity: 0.7; } |
| } |
| |
| .slide-in { |
| animation: slideIn 0.5s ease-out forwards; |
| } |
| |
| @keyframes slideIn { |
| from { transform: translateY(20px); opacity: 0; } |
| to { transform: translateY(0); opacity: 1; } |
| } |
| |
| .terminal-text { |
| font-family: 'Courier New', monospace; |
| color: #50fa7b; |
| } |
| |
| .loading-spinner { |
| width: 24px; |
| height: 24px; |
| border: 3px solid rgba(255,255,255,0.3); |
| border-radius: 50%; |
| border-top-color: var(--primary-light); |
| animation: spin 1s ease-in-out infinite; |
| } |
| |
| @keyframes spin { |
| to { transform: rotate(360deg); } |
| } |
| |
| .api-key-modal { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background-color: rgba(0,0,0,0.8); |
| z-index: 100; |
| justify-content: center; |
| align-items: center; |
| } |
| |
| .api-key-modal.active { |
| display: flex; |
| } |
| |
| .generated-image { |
| max-width: 100%; |
| max-height: 400px; |
| border-radius: 0.5rem; |
| object-fit: contain; |
| } |
| </style> |
| </head> |
| <body class="min-h-screen grid-pattern"> |
| |
| <nav class="bg-black bg-opacity-80 backdrop-blur-md border-b border-purple-900 fixed w-full z-50"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <div class="flex items-center justify-between h-16"> |
| <div class="flex items-center"> |
| <div class="flex-shrink-0 flex items-center"> |
| <i class="fas fa-robot text-purple-500 text-2xl mr-2"></i> |
| <span class="tech-font text-xl font-bold gradient-text">soc.io</span> |
| </div> |
| <div class="hidden md:block"> |
| <div class="ml-10 flex items-baseline space-x-4"> |
| <a href="#" class="text-purple-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Dashboard</a> |
| <a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Templates</a> |
| <a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Gallery</a> |
| <a href="#" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Analytics</a> |
| </div> |
| </div> |
| </div> |
| <div class="hidden md:block"> |
| <div class="ml-4 flex items-center md:ml-6"> |
| <button id="generate-btn" class="bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white px-4 py-2 rounded-md text-sm font-medium mr-3"> |
| <i class="fas fa-bolt mr-1"></i> Generate |
| </button> |
| <div class="ml-3 relative"> |
| <div> |
| <button class="max-w-xs flex items-center text-sm rounded-full focus:outline-none" id="user-menu"> |
| <img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="-mr-2 flex md:hidden"> |
| <button class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none"> |
| <span class="sr-only">Open main menu</span> |
| <i class="fas fa-bars"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| </nav> |
|
|
| |
| <div id="api-key-modal" class="api-key-modal"> |
| <div class="bg-gray-900 rounded-xl p-8 max-w-md w-full neon-border glow-box"> |
| <div class="flex justify-between items-center mb-6"> |
| <h3 class="tech-font text-2xl gradient-text">Hugging Face API 키</h3> |
| <button id="close-modal" class="text-gray-400 hover:text-white"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <p class="text-gray-300 mb-4"> |
| 이미지를 생성하려면 Stable Diffusion 모델에 액세스할 수 있는 Hugging Face API 키가 필요합니다. |
| </p> |
| <div class="mb-6"> |
| <label class="block text-sm font-medium text-gray-300 mb-2">당신의 API 키</label> |
| <input |
| type="password" |
| id="api-key-input" |
| class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent" |
| placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> |
| <p class="text-xs text-gray-500 mt-2"> |
| API 키가 없으신가요? <a href="https://huggingface.co/settings/tokens" target="_blank" class="text-purple-400 hover:underline">여기에서 받으세요</a> |
| </p> |
| </div> |
| <div class="flex justify-end gap-3"> |
| <button id="cancel-api-key" class="px-4 py-2 rounded-lg border border-gray-700 text-gray-300 hover:bg-gray-800"> |
| 취소 |
| </button> |
| <button id="save-api-key" class="bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white px-6 py-2 rounded-lg"> |
| 키 저장 |
| </button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <main class="pt-20 pb-12 px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto"> |
| |
| <div class="text-center py-12 slide-in"> |
| <h1 class="tech-font text-4xl md:text-6xl font-bold mb-6"> |
| <span class="gradient-text">AI 기반</span> 소셜 콘텐츠 |
| </h1> |
| <p class="text-xl text-gray-300 max-w-3xl mx-auto"> |
| <span class="text-purple-300 font-medium">ControlNet</span>과 <span class="text-green-300 font-medium">SDXL</span>의 힘으로 소셜 미디어를 위한 놀라운 시각 자료를 만드세요. 원하는 것을 설명하거나 템플릿을 사용하세요. |
| </p> |
| |
| <div class="mt-10 flex flex-col sm:flex-row justify-center gap-4"> |
| <button id="start-creating" class="glow-box bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white px-8 py-4 rounded-lg text-lg font-medium flex items-center justify-center"> |
| <i class="fas fa-magic mr-3"></i> 만들기 시작 |
| </button> |
| <button class="neon-border bg-gray-900 hover:bg-gray-800 text-white px-8 py-4 rounded-lg text-lg font-medium flex items-center justify-center"> |
| <i class="fas fa-play-circle mr-3"></i> 데모 보기 |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="mt-16 neon-border rounded-xl bg-gray-900 bg-opacity-70 p-6 glow-box"> |
| <div class="flex flex-col lg:flex-row gap-8"> |
| |
| <div class="lg:w-1/2"> |
| <h2 class="tech-font text-2xl font-bold mb-6 gradient-text">콘텐츠 생성기</h2> |
| |
| <div class="mb-6"> |
| <label class="block text-sm font-medium text-gray-300 mb-2">프롬프트</label> |
| <div class="relative"> |
| <textarea |
| id="prompt-input" |
| class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-3 text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent" |
| rows="4" |
| placeholder="생성하고자 하는 이미지를 설명하세요..."></textarea> |
| <div class="absolute bottom-3 right-3 flex gap-2"> |
| <button class="text-gray-400 hover:text-purple-400"> |
| <i class="fas fa-microphone"></i> |
| </button> |
| <button id="prompt-suggestions" class="text-gray-400 hover:text-purple-400"> |
| <i class="fas fa-lightbulb"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| <div class="grid grid-cols-2 gap-4 mb-6"> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">스타일</label> |
| <select id="style-select" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"> |
| <option value="photorealistic">사실적</option> |
| <option value="digital_art">디지털 아트</option> |
| <option value="3d_render">3D 렌더</option> |
| <option value="anime">애니메</option> |
| <option value="watercolor">수채화</option> |
| <option value="cyberpunk">사이버펑크</option> |
| </select> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">종횡비</label> |
| <select id="aspect-r번호 -select" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent"> |
| <option value="1:1">정사각형 (1:1)</option> |
| <option value="4:5">인물 (4:5)</option> |
| <option value="16:9">풍경 (16:9)</option> |
| <option value="9:16">스토리 (9:16)</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="mb-6"> |
| <label class="block text-sm font-medium text-gray-300 mb-2">참조 이미지 업로드 (ControlNet)</label> |
| <div id="upload-area" class="border-2 border-dashed border-gray-700 rounded-lg p-8 text-center cursor-pointer hover:border-purple-500 transition"> |
| <i class="fas fa-cloud-upload-alt text-4xl text-gray-500 mb-3"></i> |
| <p class="text-gray-400">이미지를 드래그 앤 드롭하거나 클릭하여 탐색</p> |
| <p class="text-xs text-gray-500 mt-2">JPG, PNG 지원 (최대 10MB)</p> |
| <input type="file" id="file-input" class="hidden" accept="image/jpeg,image/png"> |
| </div> |
| <div id="preview-container" class="hidden mt-4"> |
| <div class="relative"> |
| <img id="preview-image" src="#" alt="미리보기" class="max-h-40 rounded-lg"> |
| <button id="remove-image" class="absolute top-2 right-2 bg-gray-900 bg-opacity-70 text-white p-1 rounded-full hover:bg-gray-800"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| <div class="flex justify-between items-center"> |
| <div class="flex items-center"> |
| <input id="advanced-toggle" type="checkbox" class="h-4 w-4 text-purple-600 focus:ring-purple-500 border-gray-700 rounded"> |
| <label for="advanced-toggle" class="ml-2 block text-sm text-gray-300">고급 설정</label> |
| </div> |
| <button id="generate-now-btn" class="bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white px-6 py-3 rounded-lg font-medium flex items-center"> |
| <i class="fas fa-bolt mr-2"></i> 지금 생성 |
| </button> |
| </div> |
| |
| |
| <div id="advanced-controls" class="hidden mt-6 space-y-4"> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">가이던스 스케일</label> |
| <input id="guidance-scale" type="range" min="1" max="20" value="7.5" step="0.5" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"> |
| <div class="flex justify-between text-xs text-gray-400"> |
| <span>더 창의적</span> |
| <span>더 정확</span> |
| </div> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">스텝</label> |
| <input id="steps-input" type="range" min="10" max="50" value="25" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"> |
| </div> |
| <div> |
| <label class="block text-sm font-medium text-gray-300 mb-2">시드</label> |
| <input id="seed-input" type="number" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent" placeholder="랜덤"> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="lg:w-1/2"> |
| <div class="flex justify-between items-center mb-6"> |
| <h2 class="tech-font text-2xl font-bold gradient-text">미리보기</h2> |
| <div class="flex gap-2"> |
| <button id="undo-btn" class="bg-gray-800 hover:bg-gray-700 text-gray-300 p-2 rounded-lg"> |
| <i class="fas fa-undo"></i> |
| </button> |
| <button id="randomize-btn" class="bg-gray-800 hover:bg-gray-700 text-gray-300 p-2 rounded-lg"> |
| <i class="fas fa-random"></i> |
| </button> |
| <button id="download-btn" class="bg-gray-800 hover:bg-gray-700 text-gray-300 p-2 rounded-lg"> |
| <i class="fas fa-download"></i> |
| </button> |
| </div> |
| </div> |
| |
| <div id="output-container" class="bg-gray-800 rounded-xl p-4 h-full min-h-[400px] flex items-center justify-center"> |
| <div id="placeholder-content" class="text-center"> |
| <i class="fas fa-image text-6xl text-gray-600 mb-4"></i> |
| <p class="text-gray-400">생성된 콘텐츠가 여기에 표시됩니다</p> |
| <p id="status-text" class="terminal-text text-xs mt-2">SDXL 1.0 모델 로드됨 | ControlNet 준비 완료</p> |
| </div> |
| <div id="loading-indicator" class="hidden flex-col items-center"> |
| <div class="loading-spinner mb-4"></div> |
| <p class="text-purple-300">이미지 생성 중...</p> |
| <p id="generation-status" class="terminal-text text-xs mt-2">모델 초기화 중</p> |
| </div> |
| <img id="generated-image" src="#" alt="생성된 이미지" class="hidden generated-image"> |
| </div> |
| |
| <div id="variations-container" class="mt-4 grid grid-cols-4 gap-2"> |
| <div class="bg-gray-800 rounded-lg h-20 flex items-center justify-center cursor-pointer hover:border-2 hover:border-purple-500"> |
| <i class="fas fa-plus text-gray-500"></i> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="mt-20"> |
| <h2 class="tech-font text-3xl font-bold text-center mb-12 gradient-text">강력한 기능</h2> |
| |
| <div class="grid md:grid-cols-3 gap-8"> |
| <div class="neon-border bg-gray-900 rounded-xl p-6 hover:bg-opacity-100 transition"> |
| <div class="text-purple-500 text-4xl mb-4"> |
| <i class="fas fa-brain"></i> |
| </div> |
| <h3 class="tech-font text-xl font-bold mb-3">AI 기반 생성</h3> |
| <p class="text-gray-300">최신 SDXL 및 ControlNet 모델을 활용하여 간단한 텍스트 프롬프트로 놀라운 시각 자료를 만듭니다.</p> |
| </div> |
| |
| <div class="neon-border bg-gray-900 rounded-xl p-6 hover:bg-opacity-100 transition"> |
| <div class="text-green-500 text-4xl mb-4"> |
| <i class="fas fa-sliders-h"></i> |
| </div> |
| <h3 class="tech-font text-xl font-bold mb-3">정밀 제어</h3> |
| <p class="text-gray-300">ControlNet과 참조 이미지를 사용하여 스타일을 변경하면서 구성, 깊이 또는 가장자리를 유지합니다.</p> |
| </div> |
| |
| <div class="neon-border bg-gray-900 rounded-xl p-6 hover:bg-opacity-100 transition"> |
| <div class="text-yellow-500 text-4xl mb-4"> |
| <i class="fas fa-templates"></i> |
| </div> |
| <h3 class="tech-font text-xl font-bold mb-3">스마트 템플릿</h3> |
| <p class="text-gray-300">각 소셜 플랫폼에 최적화된 수백 개의 전문적으로 설계된 템플릿 중에서 선택하세요.</p> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="mt-20"> |
| <div class="flex justify-between items-center mb-8"> |
| <h2 class="tech-font text-3xl font-bold gradient-text">최근 생성물</h2> |
| <button class="text-purple-400 hover:text-white flex items-center"> |
| 모두 보기 <i class="fas fa-arrow-right ml-2"></i> |
| </button> |
| </div> |
| |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4"> |
| <div class="relative group overflow-hidden rounded-lg h-48"> |
| <img src="https://source.unsplash.com/random/300x300/?cyberpunk" alt="" class="w-full h-full object-cover"> |
| <div class="absolute inset-0 bg-gradient-to-t from-black to-transparent opacity-0 group-hover:opacity-100 transition flex items-end p-4"> |
| <div> |
| <p class="text-white font-medium">"Cyberpunk cityscape"</p> |
| <p class="text-purple-300 text-sm">SDXL + ControlNet</p> |
| </div> |
| </div> |
| </div> |
| |
| <div class="relative group overflow-hidden rounded-lg h-48"> |
| <img src="https://source.unsplash.com/random/300x300/?portrait" alt="" class="w-full h-full object-cover"> |
| <div class="absolute inset-0 bg-gradient-to-t from-black to-transparent opacity-0 group-hover:opacity-100 transition flex items-end p-4"> |
| <div> |
| <p class="text-white font-medium">"Professional headshot"</p> |
| <p class="text-purple-300 text-sm">SDXL Photoreal</p> |
| </div> |
| </div> |
| </div> |
| |
| <div class="relative group overflow-hidden rounded-lg h-48"> |
| <img src="https://source.unsplash.com/random/300x300/?product" alt="" class="w-full h-full object-cover"> |
| <div class="absolute inset-0 bg-gradient-to-t from-black to-transparent opacity-0 group-hover:opacity-100 transition flex items-end p-4"> |
| <div> |
| <p class="text-white font-medium">"Minimal product display"</p> |
| <p class="text-purple-300 text-sm">ControlNet Edges</p> |
| </div> |
| </div> |
| </div> |
| |
| <div class="relative group overflow-hidden rounded-lg h-48"> |
| <img src="https://source.unsplash.com/random/300x300/?abstract" alt="" class="w-full h-full object-cover"> |
| <div class="absolute inset-0 bg-gradient-to-t from-black to-transparent opacity-0 group-hover:opacity-100 transition flex items-end p-4"> |
| <div> |
| <p class="text-white font-medium">"Colorful abstract art"</p> |
| <p class="text-purple-300 text-sm">SDXL Digital Art</p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </main> |
|
|
| |
| <footer class="bg-black bg-opacity-80 border-t border-purple-900 mt-20"> |
| <div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8"> |
| <div class="grid grid-cols-1 md:grid-cols-4 gap-8"> |
| <div> |
| <h3 class="tech-font text-lg font-bold gradient-text mb-4">soc.io</h3> |
| <p class="text-gray-400">AI-powered social media content creation for the modern creator.</p> |
| <div class="flex gap-4 mt-4"> |
| <a href="#" class="text-gray-400 hover:text-purple-400"><i class="fab fa-twitter"></i></a> |
| <a href="#" class="text-gray-400 hover:text-purple-400"><i class="fab fa-instagram"></i></a> |
| <a href="#" class="text-gray-400 hover:text-purple-400"><i class="fab fa-discord"></i></a> |
| </div> |
| </div> |
| |
| <div> |
| <h3 class="tech-font text-sm font-bold text-gray-300 mb-4">PRODUCT</h3> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white">Features</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Pricing</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Templates</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">API</a></li> |
| </ul> |
| </div> |
| |
| <div> |
| <h3 class="tech-font text-sm font-bold text-gray-300 mb-4">RESOURCES</h3> |
| <ul class="space-y-2"> |
| <li><a href="#" class="text-gray-400 hover:text-white">Documentation</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Tutorials</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Blog</a></li> |
| <li><a href="#" class="text-gray-400 hover:text-white">Community</a></li> |
| </ul> |
| </div> |
| |
| <div> |
| <h3 class="tech-font text-sm font-bold text-gray-300 mb-4">NEWSLETTER</h3> |
| <p class="text-gray-400 mb-4">Subscribe for updates and AI tips.</p> |
| <div class="flex"> |
| <input type="email" placeholder="Your email" class="bg-gray-800 text-white px-4 py-2 rounded-l-lg focus:outline-none focus:ring-1 focus:ring-purple-500 w-full"> |
| <button class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-r-lg"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
| </div> |
| </div> |
| |
| <div class="border-t border-gray-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center"> |
| <p class="text-gray-500 text-sm">© 2023 soc.io. All rights reserved.</p> |
| <div class="flex gap-6 mt-4 md:mt-0"> |
| <a href="#" class="text-gray-500 hover:text-gray-300 text-sm">Privacy</a> |
| <a href="#" class="text-gray-500 hover:text-gray-300 text-sm">Terms</a> |
| <a href="#" class="text-gray-500 hover:text-gray-300 text-sm">Cookies</a> |
| </div> |
| </div> |
| </div> |
| </footer> |
|
|
| <script> |
| |
| let apiKey = localStorage.getItem('hf_api_key'); |
| let generatedImages = []; |
| |
| |
| const apiKeyModal = document.getElementById('api-key-modal'); |
| const apiKeyInput = document.getElementById('api-key-input'); |
| const saveApiKeyBtn = document.getElementById('save-api-key'); |
| const cancelApiKeyBtn = document.getElementById('cancel-api-key'); |
| const closeModalBtn = document.getElementById('close-modal'); |
| const generateNowBtn = document.getElementById('generate-now-btn'); |
| const startCreatingBtn = document.getElementById('start-creating'); |
| const generateBtn = document.getElementById('generate-btn'); |
| const promptInput = document.getElementById('prompt-input'); |
| const styleSelect = document.getElementById('style-select'); |
| const aspectRatioSelect = document.getElementById('aspect-ratio-select'); |
| const outputContainer = document.getElementById('output-container'); |
| const placeholderContent = document.getElementById('placeholder-content'); |
| const loadingIndicator = document.getElementById('loading-indicator'); |
| const generatedImage = document.getElementById('generated-image'); |
| const statusText = document.getElementById('status-text'); |
| const generationStatus = document.getElementById('generation-status'); |
| const uploadArea = document.getElementById('upload-area'); |
| const fileInput = document.getElementById('file-input'); |
| const previewContainer = document.getElementById('preview-container'); |
| const previewImage = document.getElementById('preview-image'); |
| const removeImageBtn = document.getElementById('remove-image'); |
| const advancedToggle = document.getElementById('advanced-toggle'); |
| const advancedControls = document.getElementById('advanced-controls'); |
| const downloadBtn = document.getElementById('download-btn'); |
| const undoBtn = document.getElementById('undo-btn'); |
| const randomizeBtn = document.getElementById('randomize-btn'); |
| const promptSuggestionsBtn = document.getElementById('prompt-suggestions'); |
| const variationsContainer = document.getElementById('variations-container'); |
| const guidanceScale = document.getElementById('guidance-scale'); |
| const stepsInput = document.getElementById('steps-input'); |
| const seedInput = document.getElementById('seed-input'); |
| |
| |
| function showApiKeyModal() { |
| apiKeyModal.classList.add('active'); |
| if (apiKey) { |
| apiKeyInput.value = apiKey; |
| } |
| } |
| |
| function hideApiKeyModal() { |
| apiKeyModal.classList.remove('active'); |
| } |
| |
| |
| [saveApiKeyBtn, cancelApiKeyBtn, closeModalBtn].forEach(btn => { |
| btn.addEventListener('click', (e) => { |
| e.preventDefault(); |
| if (btn === saveApiKeyBtn && apiKeyInput.value) { |
| apiKey = apiKeyInput.value; |
| localStorage.setItem('hf_api_key', apiKey); |
| updateStatus('API key saved successfully'); |
| } |
| hideApiKeyModal(); |
| }); |
| }); |
| |
| |
| function checkApiKey() { |
| if (!apiKey) { |
| showApiKeyModal(); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| async function generateImage() { |
| if (!checkApiKey()) return; |
| |
| const prompt = promptInput.value.trim(); |
| if (!prompt) { |
| updateStatus('Please enter a prompt', 'error'); |
| return; |
| } |
| |
| |
| placeholderContent.classList.add('hidden'); |
| loadingIndicator.classList.remove('hidden'); |
| generatedImage.classList.add('hidden'); |
| outputContainer.classList.add('neon-border'); |
| |
| |
| const style = styleSelect.value; |
| const aspectRatio = aspectRatioSelect.value; |
| const guidance = parseFloat(guidanceScale.value); |
| const steps = parseInt(stepsInput.value); |
| const seed = seedInput.value ? parseInt(seedInput.value) : Math.floor(Math.random() * 1000000); |
| |
| |
| updateGenerationStatus('Validating API key...'); |
| await delay(1000); |
| |
| updateGenerationStatus('Initializing SDXL model...'); |
| await delay(1500); |
| |
| updateGenerationStatus('Processing prompt...'); |
| await delay(2000); |
| |
| updateGenerationStatus('Generating image...'); |
| await delay(2500); |
| |
| try { |
| |
| |
| const imageUrl = await mockHuggingFaceAPICall(prompt, style, aspectRatio, guidance, steps, seed); |
| |
| |
| generatedImages.push(imageUrl); |
| |
| |
| displayGeneratedImage(imageUrl); |
| updateStatus('Image generated successfully!'); |
| |
| |
| generateVariations(); |
| |
| } catch (error) { |
| console.error('Error generating image:', error); |
| updateStatus(`Error: ${error.message}`, 'error'); |
| placeholderContent.classList.remove('hidden'); |
| loadingIndicator.classList.add('hidden'); |
| } |
| } |
| |
| |
| async function mockHuggingFaceAPICall(prompt, style, aspectRatio, guidance, steps, seed) { |
| |
| console.log('Mock API call with:', { |
| prompt, |
| style, |
| aspectRatio, |
| guidance, |
| steps, |
| seed |
| }); |
| |
| |
| const styleMap = { |
| 'photorealistic': 'portrait', |
| 'digital_art': 'abstract,art', |
| '3d_render': '3d,render', |
| 'anime': 'anime', |
| 'watercolor': 'watercolor,painting', |
| 'cyberpunk': 'cyberpunk' |
| }; |
| |
| |
| const dimensions = getDimensionsFromAspectRatio(aspectRatio); |
| |
| |
| return `https://source.unsplash.com/random/${dimensions.width}x${dimensions.height}/?${styleMap[style] || 'portrait'}&${seed}`; |
| } |
| |
| function getDimensionsFromAspectRatio(ratio) { |
| switch(ratio) { |
| case '1:1': return { width: 768, height: 768 }; |
| case '4:5': return { width: 640, height: 800 }; |
| case '16:9': return { width: 1024, height: 576 }; |
| case '9:16': return { width: 432, height: 768 }; |
| default: return { width: 768, height: 768 }; |
| } |
| } |
| |
| function displayGeneratedImage(imageUrl) { |
| generatedImage.src = imageUrl; |
| loadingIndicator.classList.add('hidden'); |
| generatedImage.classList.remove('hidden'); |
| |
| |
| downloadBtn.onclick = () => { |
| downloadImage(imageUrl); |
| }; |
| } |
| |
| function downloadImage(url) { |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = `soc.io-${Date.now()}.jpg`; |
| document.body.appendChild(a); |
| a.click(); |
| document.body.removeChild(a); |
| updateStatus('Image downloaded'); |
| } |
| |
| function generateVariations() { |
| |
| variationsContainer.innerHTML = ''; |
| |
| |
| for (let i = 0; i < 3; i++) { |
| const variation = document.createElement('div'); |
| variation.className = 'bg-gray-800 rounded-lg h-20 overflow-hidden'; |
| variation.innerHTML = `<img src="https://source.unsplash.com/random/150x150/?${styleSelect.value}&${Date.now() + i}" class="w-full h-full object-cover" alt="Variation ${i+1}">`; |
| |
| variation.addEventListener('click', () => { |
| |
| const imgUrl = variation.querySelector('img').src; |
| generatedImages.push(imgUrl); |
| displayGeneratedImage(imgUrl); |
| updateStatus('Variation loaded'); |
| }); |
| |
| variationsContainer.appendChild(variation); |
| } |
| } |
| |
| function updateStatus(message, type = 'success') { |
| statusText.textContent = message; |
| statusText.className = 'terminal-text text-xs mt-2'; |
| if (type === 'error') { |
| statusText.classList.add('text-red-400'); |
| } else { |
| statusText.classList.add('text-green-400'); |
| } |
| } |
| |
| function updateGenerationStatus(message) { |
| generationStatus.textContent = message; |
| } |
| |
| function delay(ms) { |
| return new Promise(resolve => setTimeout(resolve, ms)); |
| } |
| |
| |
| uploadArea.addEventListener('click', () => fileInput.click()); |
| |
| fileInput.addEventListener('change', (e) => { |
| const file = e.target.files[0]; |
| if (file) { |
| const reader = new FileReader(); |
| reader.onload = (event) => { |
| previewImage.src = event.target.result; |
| previewContainer.classList.remove('hidden'); |
| uploadArea.classList.add('hidden'); |
| }; |
| reader.readAsDataURL(file); |
| } |
| }); |
| |
| removeImageBtn.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| fileInput.value = ''; |
| previewContainer.classList.add('hidden'); |
| uploadArea.classList.remove('hidden'); |
| }); |
| |
| |
| uploadArea.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.add('border-purple-500', 'bg-gray-800'); |
| }); |
| |
| uploadArea.addEventListener('dragleave', () => { |
| uploadArea.classList.remove('border-purple-500', 'bg-gray-800'); |
| }); |
| |
| uploadArea.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| uploadArea.classList.remove('border-purple-500', 'bg-gray-800'); |
| |
| const file = e.dataTransfer.files[0]; |
| if (file && (file.type === 'image/jpeg' || file.type === 'image/png')) { |
| fileInput.files = e.dataTransfer.files; |
| const event = new Event('change'); |
| fileInput.dispatchEvent(event); |
| } |
| }); |
| |
| |
| advancedToggle.addEventListener('change', () => { |
| if (advancedToggle.checked) { |
| advancedControls.classList.remove('hidden'); |
| } else { |
| advancedControls.classList.add('hidden'); |
| } |
| }); |
| |
| |
| [generateNowBtn, startCreatingBtn, generateBtn].forEach(btn => { |
| btn.addEventListener('click', (e) => { |
| e.preventDefault(); |
| if (btn === generateNowBtn) { |
| generateImage(); |
| } else { |
| |
| document.querySelector('.neon-border.rounded-xl').scrollIntoView({ |
| behavior: 'smooth' |
| }); |
| } |
| }); |
| }); |
| |
| |
| randomizeBtn.addEventListener('click', () => { |
| const randomPrompts = [ |
| "A futuristic cityscape at night with neon lights and flying cars", |
| "A majestic lion in the savanna at golden hour", |
| "An astronaut floating in space with Earth in the background", |
| "A cyberpunk hacker in a dark room with multiple monitors", |
| "A magical forest with glowing plants and fairies", |
| "A steampunk airship flying over Victorian London", |
| "A cute anime character with pink hair and big eyes", |
| "A watercolor painting of a sunset over mountains" |
| ]; |
| |
| promptInput.value = randomPrompts[Math.floor(Math.random() * randomPrompts.length)]; |
| updateStatus('Random prompt generated'); |
| }); |
| |
| |
| promptSuggestionsBtn.addEventListener('click', () => { |
| const suggestions = [ |
| "Try adding more details like lighting, style, or composition", |
| "Example: 'A photorealistic portrait of a woman with freckles, soft lighting'", |
| "Example: 'Cyberpunk city at night, neon lights, rain on streets'", |
| "Example: 'Cute anime girl with blue hair, wearing school uniform'" |
| ]; |
| |
| alert("Prompt Tips:\n\n" + suggestions.join("\n\n")); |
| }); |
| |
| |
| undoBtn.addEventListener('click', () => { |
| if (generatedImages.length > 1) { |
| generatedImages.pop(); |
| const prevImage = generatedImages[generatedImages.length - 1]; |
| displayGeneratedImage(prevImage); |
| updateStatus('Reverted to previous image'); |
| } else if (generatedImages.length === 1) { |
| generatedImages = []; |
| generatedImage.classList.add('hidden'); |
| placeholderContent.classList.remove('hidden'); |
| updateStatus('Generation cleared'); |
| } else { |
| updateStatus('Nothing to undo', 'error'); |
| } |
| }); |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| const elements = document.querySelectorAll('.slide-in'); |
| |
| const observer = new IntersectionObserver((entries) => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| entry.target.style.opacity = 1; |
| entry.target.style.transform = 'translateY(0)'; |
| } |
| }); |
| }, { threshold: 0.1 }); |
| |
| elements.forEach(el => { |
| el.style.opacity = 0; |
| el.style.transform = 'translateY(20px)'; |
| el.style.transition = 'all 0.5s ease-out'; |
| observer.observe(el); |
| }); |
| |
| |
| const originalText = statusText.textContent; |
| statusText.textContent = ''; |
| |
| let i = 0; |
| const typingEffect = setInterval(() => { |
| if (i < originalText.length) { |
| statusText.textContent += originalText.charAt(i); |
| i++; |
| } else { |
| clearInterval(typingEffect); |
| } |
| }, 50); |
| |
| |
| seedInput.value = Math.floor(Math.random() * 1000000); |
| }); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=pauloyatowo/soc-io" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p> |
| <section class="image-generation-section"> |
| <textarea id="promptInput" placeholder="Enter your ad prompt here...">A minimal, stylish Instagram ad for Brilla.ng, a professional cleaning service targeting Lagos homeowners and businesses. The visual features a spotless, modern Lagos living room or office with clean lines and natural light. A bold yet elegant text overlay says: “We don’t cut corners, we clean them.” The Brilla.ng logo is subtly placed in the corner. Use soft whites, warm neutrals, and a hint of the brand’s color for accen... |
| <button onclick="generateImage()">Generate Ad</button> |
| <div id="imageContainer"></div> |
| </section> |
|
|
| <script> |
| async function generateImage() { |
| const prompt = document.getElementById('promptInput').value; |
| const response = await fetch('/generate', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify({ prompt }) |
| }); |
| if (!response.ok) { |
| alert("Failed to generate image."); |
| return; |
| } |
| const blob = await response.blob(); |
| const imageUrl = URL.createObjectURL(blob); |
| document.getElementById('imageContainer').innerHTML = `<img src="${imageUrl}" alt="Generated Ad" style="max-width:100%;margin-top:1em;" />`; |
| } |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nkjoy/soc-io" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| |
| </html> |