Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>AI Image Editor - Text Prompt Magic</title> | |
| <meta name="theme-color" content="#6366f1"> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiQUkgSW1hZ2UgRWRpdG9yIiwic2hvcnRfbmFtZSI6IkltYWdlRWRpdCIsInN0YXJ0X3VybCI6Ii8iLCJkaXNwbGF5Ijoic3RhbmRhbG9uZSIsImJhY2tncm91bmRfY29sb3IiOiIjMWUxYjI0IiwidGhlbWVfY29sb3IiOiIjNjM2NmYxIn0="> | |
| <link rel="icon" type="image/png" sizes="192x192" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHKSURBVHic7cExAQAAAMKg9U9tCF+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOL/AP8gBMEBVEiBQwABmWWL6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDFPgAAFfEJdQAAAAASUVORK5CYII="> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| --primary: #6366f1; | |
| --primary-dark: #4f46e5; | |
| --secondary: #ec4899; | |
| --surface: #1e1b2e; | |
| --surface-light: #2d2a3e; | |
| --surface-lighter: #3a3652; | |
| --text-primary: #ffffff; | |
| --text-secondary: #a8a3b8; | |
| --accent: #22d3ee; | |
| --success: #10b981; | |
| --warning: #f59e0b; | |
| --error: #ef4444; | |
| --gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| --gradient-2: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; | |
| background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); | |
| color: var(--text-primary); | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| position: relative; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: | |
| radial-gradient(circle at 20% 80%, rgba(99, 102, 241, 0.2) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(236, 72, 153, 0.2) 0%, transparent 50%), | |
| radial-gradient(circle at 40% 40%, rgba(34, 211, 238, 0.1) 0%, transparent 50%); | |
| pointer-events: none; | |
| z-index: 1; | |
| } | |
| .app-container { | |
| position: relative; | |
| z-index: 2; | |
| max-width: 100vw; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .status-bar { | |
| background: rgba(30, 27, 46, 0.95); | |
| backdrop-filter: blur(20px); | |
| padding: 0.5rem 1rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-size: 0.85rem; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .status-bar-left { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .status-bar-right { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .main-header { | |
| background: rgba(30, 27, 46, 0.9); | |
| backdrop-filter: blur(20px); | |
| padding: 1rem; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .header-content { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .logo-icon { | |
| width: 40px; | |
| height: 40px; | |
| background: var(--gradient); | |
| border-radius: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.5rem; | |
| } | |
| .logo-text { | |
| font-size: 1.25rem; | |
| font-weight: 700; | |
| background: var(--gradient); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .main-content { | |
| flex: 1; | |
| padding: 1rem; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| .editor-workspace { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 1rem; | |
| margin-bottom: 1rem; | |
| } | |
| @media (min-width: 768px) { | |
| .editor-workspace { | |
| grid-template-columns: 2fr 1fr; | |
| } | |
| } | |
| .canvas-section { | |
| background: var(--surface-light); | |
| border-radius: 20px; | |
| padding: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| min-height: 400px; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .canvas-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| } | |
| .canvas-title { | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .canvas-actions { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .icon-btn { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 10px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| background: rgba(255, 255, 255, 0.05); | |
| color: var(--text-secondary); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .icon-btn:hover { | |
| background: rgba(255, 255, 255, 0.1); | |
| color: var(--text-primary); | |
| transform: translateY(-2px); | |
| } | |
| .canvas-container { | |
| flex: 1; | |
| background: rgba(0, 0, 0, 0.3); | |
| border-radius: 16px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| overflow: hidden; | |
| min-height: 300px; | |
| } | |
| #imageCanvas { | |
| max-width: 100%; | |
| max-height: 100%; | |
| border-radius: 12px; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); | |
| } | |
| .upload-prompt { | |
| text-align: center; | |
| color: var(--text-secondary); | |
| } | |
| .upload-icon { | |
| font-size: 4rem; | |
| margin-bottom: 1rem; | |
| opacity: 0.5; | |
| } | |
| .prompt-section { | |
| background: var(--surface-light); | |
| border-radius: 20px; | |
| padding: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .prompt-header { | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| margin-bottom: 1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .prompt-input-group { | |
| margin-bottom: 1rem; | |
| } | |
| .prompt-label { | |
| display: block; | |
| margin-bottom: 0.5rem; | |
| color: var(--text-secondary); | |
| font-size: 0.9rem; | |
| } | |
| .prompt-input { | |
| width: 100%; | |
| padding: 0.75rem; | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 2px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 12px; | |
| color: var(--text-primary); | |
| font-size: 1rem; | |
| transition: all 0.3s; | |
| } | |
| .prompt-input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| background: rgba(255, 255, 255, 0.08); | |
| box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); | |
| } | |
| .prompt-suggestions { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 0.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .suggestion-chip { | |
| padding: 0.5rem 1rem; | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| font-size: 0.85rem; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .suggestion-chip:hover { | |
| background: var(--primary); | |
| border-color: var(--primary); | |
| transform: translateY(-2px); | |
| } | |
| .action-btn { | |
| width: 100%; | |
| padding: 1rem; | |
| background: var(--gradient); | |
| border: none; | |
| border-radius: 12px; | |
| color: white; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .action-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; | |
| } | |
| .action-btn:hover::before { | |
| left: 100%; | |
| } | |
| .action-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3); | |
| } | |
| .action-btn:active { | |
| transform: translateY(0); | |
| } | |
| .action-btn:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .adjustments-section { | |
| background: var(--surface-light); | |
| border-radius: 20px; | |
| padding: 1.5rem; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| margin-top: 1rem; | |
| } | |
| .adjustment-slider { | |
| margin-bottom: 1.5rem; | |
| } | |
| .slider-header { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 0.5rem; | |
| } | |
| .slider-label { | |
| color: var(--text-secondary); | |
| font-size: 0.9rem; | |
| } | |
| .slider-value { | |
| color: var(--primary); | |
| font-weight: 600; | |
| } | |
| .slider { | |
| width: 100%; | |
| height: 6px; | |
| border-radius: 3px; | |
| background: rgba(255, 255, 255, 0.1); | |
| outline: none; | |
| -webkit-appearance: none; | |
| } | |
| .slider::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: var(--primary); | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .slider::-webkit-slider-thumb:hover { | |
| transform: scale(1.2); | |
| box-shadow: 0 0 10px rgba(99, 102, 241, 0.5); | |
| } | |
| .slider::-moz-range-thumb { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: var(--primary); | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .bottom-nav { | |
| background: rgba(30, 27, 46, 0.95); | |
| backdrop-filter: blur(20px); | |
| border-top: 1px solid rgba(255, 255, 255, 0.1); | |
| display: flex; | |
| justify-content: space-around; | |
| padding: 0.5rem 0; | |
| } | |
| .nav-item { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 0.5rem 1rem; | |
| color: var(--text-secondary); | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| border-radius: 12px; | |
| } | |
| .nav-item:hover { | |
| color: var(--text-primary); | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .nav-item.active { | |
| color: var(--primary); | |
| } | |
| .nav-icon { | |
| font-size: 1.5rem; | |
| margin-bottom: 0.25rem; | |
| } | |
| .nav-label { | |
| font-size: 0.75rem; | |
| } | |
| .hidden { | |
| display: none ; | |
| } | |
| .file-input { | |
| display: none; | |
| } | |
| .loading-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.8); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 1000; | |
| backdrop-filter: blur(5px); | |
| } | |
| .loading-content { | |
| text-align: center; | |
| } | |
| .loading-spinner { | |
| width: 60px; | |
| height: 60px; | |
| border: 4px solid rgba(255, 255, 255, 0.1); | |
| border-top: 4px solid var(--primary); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin: 0 auto 1rem; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .loading-text { | |
| color: var(--text-secondary); | |
| } | |
| .toast { | |
| position: fixed; | |
| bottom: 5rem; | |
| left: 50%; | |
| transform: translateX(-50%) translateY(100px); | |
| background: var(--surface-light); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 12px; | |
| padding: 1rem 1.5rem; | |
| backdrop-filter: blur(10px); | |
| transition: transform 0.3s; | |
| z-index: 2000; | |
| } | |
| .toast.show { | |
| transform: translateX(-50%) translateY(0); | |
| } | |
| .toast.success { | |
| border-color: var(--success); | |
| background: rgba(16, 185, 129, 0.1); | |
| } | |
| .toast.error { | |
| border-color: var(--error); | |
| background: rgba(239, 68, 68, 0.1); | |
| } | |
| .gallery-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); | |
| gap: 1rem; | |
| padding: 1rem; | |
| } | |
| .gallery-item { | |
| aspect-ratio: 1; | |
| background: var(--surface-light); | |
| border-radius: 12px; | |
| overflow: hidden; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| position: relative; | |
| } | |
| .gallery-item:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .gallery-item img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .gallery-item-overlay { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); | |
| padding: 0.5rem; | |
| color: white; | |
| font-size: 0.75rem; | |
| } | |
| .credit { | |
| position: fixed; | |
| bottom: 1rem; | |
| left: 1rem; | |
| font-size: 0.75rem; | |
| color: var(--text-secondary); | |
| z-index: 10; | |
| } | |
| .credit a { | |
| color: var(--primary); | |
| text-decoration: none; | |
| } | |
| .credit a:hover { | |
| text-decoration: underline; | |
| } | |
| @media (max-width: 768px) { | |
| .logo-text { | |
| display: none; | |
| } | |
| .canvas-section { | |
| padding: 1rem; | |
| } | |
| .prompt-section { | |
| padding: 1rem; | |
| } | |
| .adjustments-section { | |
| padding: 1rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Status Bar --> | |
| <div class="status-bar"> | |
| <div class="status-bar-left"> | |
| <span id="time">9:41 AM</span> | |
| </div> | |
| <div class="status-bar-right"> | |
| <span>📶</span> | |
| <span>📶</span> | |
| <span>🔋 100%</span> | |
| </div> | |
| </div> | |
| <!-- Main Header --> | |
| <header class="main-header"> | |
| <div class="header-content"> | |
| <div class="logo"> | |
| <div class="logo-icon">🎨</div> | |
| <div class="logo-text">AI Image Editor</div> | |
| </div> | |
| <button class="icon-btn" onclick="showInfo()"> | |
| <span>ℹ️</span> | |
| </button> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| <!-- Editor Tab --> | |
| <div id="editorTab" class="tab-content"> | |
| <div class="editor-workspace"> | |
| <!-- Canvas Section --> | |
| <div class="canvas-section"> | |
| <div class="canvas-header"> | |
| <div class="canvas-title"> | |
| <span>📸</span> | |
| <span>Canvas</span> | |
| </div> | |
| <div class="canvas-actions"> | |
| <button class="icon-btn" onclick="resetImage()"> | |
| <span>🔄</span> | |
| </button> | |
| <button class="icon-btn" onclick="saveImage()"> | |
| <span>💾</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="canvas-container"> | |
| <div id="uploadPrompt" class="upload-prompt"> | |
| <div class="upload-icon">📷</div> | |
| <p>Upload an image to start editing</p> | |
| <button class="action-btn" style="margin-top: 1rem; max-width: 200px;" onclick="document.getElementById('fileInput').click()"> | |
| Choose Image | |
| </button> | |
| </div> | |
| <canvas id="imageCanvas" class="hidden"></canvas> | |
| </div> | |
| </div> | |
| <!-- Prompt Section --> | |
| <div class="prompt-section"> | |
| <div class="prompt-header"> | |
| <span>✨</span> | |
| <span>AI Magic Prompt</span> | |
| </div> | |
| <div class="prompt-input-group"> | |
| <label class="prompt-label">Describe your edit:</label> | |
| <input type="text" id="promptInput" class="prompt-input" placeholder="e.g., make it look like a painting..."> | |
| </div> | |
| <div class="prompt-suggestions"> | |
| <div class="suggestion-chip" onclick="setPrompt('make it vintage')">🎞️ Vintage</div> | |
| <div class="suggestion-chip" onclick="setPrompt('add neon lights')">💫 Neon</div> | |
| <div class="suggestion-chip" onclick="setPrompt('cartoon style')">🎨 Cartoon</div> | |
| <div class="suggestion-chip" onclick="setPrompt('cyberpunk')">🤖 Cyberpunk</div> | |
| <div class="suggestion-chip" onclick="setPrompt('black and white')">⚫ B&W</div> | |
| </div> | |
| <button class="action-btn" id="applyBtn" onclick="applyPrompt()" disabled> | |
| Apply Magic ✨ | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Adjustments Section --> | |
| <div class="adjustments-section"> | |
| <div class="prompt-header"> | |
| <span>🎛️</span> | |
| <span>Quick Adjustments</span> | |
| </div> | |
| <div class="adjustment-slider"> | |
| <div class="slider-header"> | |
| <span class="slider-label">Brightness</span> | |
| <span class="slider-value" id="brightnessValue">100%</span> | |
| </div> | |
| <input type="range" class="slider" id="brightnessSlider" min="0" max="200" value="100" oninput="adjustImage()"> | |
| </div> | |
| <div class="adjustment-slider"> | |
| <div class="slider-header"> | |
| <span class="slider-label">Contrast</span> | |
| <span class="slider-value" id="contrastValue">100%</span> | |
| </div> | |
| <input type="range" class="slider" id="contrastSlider" min="0" max="200" value="100" oninput="adjustImage()"> | |
| </div> | |
| <div class="adjustment-slider"> | |
| <div class="slider-header"> | |
| <span class="slider-label">Saturation</span> | |
| <span class="slider-value" id="saturationValue">100%</span> | |
| </div> | |
| <input type="range" class="slider" id="saturationSlider" min="0" max="200" value="100" oninput="adjustImage()"> | |
| </div> | |
| <div class="adjustment-slider"> | |
| <div class="slider-header"> | |
| <span class="slider-label">Blur</span> | |
| <span class="slider-value" id="blurValue">0px</span> | |
| </div> | |
| <input type="range" class="slider" id="blurSlider" min="0" max="10" value="0" oninput="adjustImage()"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Gallery Tab --> | |
| <div id="galleryTab" class="tab-content hidden"> | |
| <div class="gallery-grid" id="galleryGrid"> | |
| <!-- Gallery items will be added dynamically --> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Bottom Navigation --> | |
| <nav class="bottom-nav"> | |
| <div class="nav-item active" onclick="switchTab('editor')"> | |
| <span class="nav-icon">🎨</span> | |
| <span class="nav-label">Edit</span> | |
| </div> | |
| <div class="nav-item" onclick="switchTab('gallery')"> | |
| <span class="nav-icon">🖼️</span> | |
| <span class="nav-label">Gallery</span> | |
| </div> | |
| <div class="nav-item" onclick="switchTab('templates')"> | |
| <span class="nav-icon">📋</span> | |
| <span class="nav-label">Templates</span> | |
| </div> | |
| <div class="nav-item" onclick="switchTab('settings')"> | |
| <span class="nav-icon">⚙️</span> | |
| <span class="nav-label">Settings</span> | |
| </div> | |
| </nav> | |
| </div> | |
| <!-- Hidden File Input --> | |
| <input type="file" id="fileInput" class="file-input" accept="image/*" onchange="handleImageUpload(event)"> | |
| <!-- Loading Overlay --> | |
| <div id="loadingOverlay" class="loading-overlay hidden"> | |
| <div class="loading-content"> | |
| <div class="loading-spinner"></div> | |
| <div class="loading-text">Applying magic...</div> | |
| </div> | |
| </div> | |
| <!-- Toast Notification --> | |
| <div id="toast" class="toast"></div> | |
| <!-- Credit --> | |
| <div class="credit"> | |
| Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder">anycoder</a> | |
| </div> | |
| <script> | |
| // Global variables | |
| let currentImage = null; | |
| let originalImageData = null; | |
| let galleryImages = []; | |
| // Initialize app | |
| document.addEventListener('DOMContentLoaded', function() { | |
| updateTime(); | |
| setInterval(updateTime, 60000); | |
| loadGallery(); | |
| initializeCanvas(); | |
| }); | |
| // Update time in status bar | |
| function updateTime() { | |
| const now = new Date(); | |
| const time = now.toLocaleTimeString('en-US', { | |
| hour: 'numeric', | |
| minute: '2-digit', | |
| hour12: true | |
| }); | |
| document.getElementById('time').textContent = time; | |
| } | |
| // Initialize canvas | |
| function initializeCanvas() { | |
| const canvas = document.getElementById('imageCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| ctx.imageSmoothingEnabled = true; | |
| ctx.imageSmoothingQuality = 'high'; | |
| } | |
| // Handle image upload | |
| function handleImageUpload(event) { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const img = new Image(); | |
| img.onload = function() { | |
| currentImage = img; | |
| displayImage(img); | |
| document.getElementById('uploadPrompt').classList.add('hidden'); | |
| document.getElementById('imageCanvas').classList.remove('hidden'); | |
| document.getElementById('applyBtn').disabled = false; | |
| showToast('Image loaded successfully!', 'success'); | |
| }; | |
| img.src = e.target.result; | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| // Display image on canvas | |
| function displayImage(img) { | |
| const canvas = document.getElementById('imageCanvas'); | |
| const container = canvas.parentElement; | |
| const ctx = canvas.getContext('2d'); | |
| // Calculate size to fit container | |
| const maxWidth = container.clientWidth - 40; | |
| const maxHeight = container.clientHeight - 40; | |
| let width = img.width; | |
| let height = img.height; | |
| if (width > maxWidth || height > maxHeight) { | |
| const ratio = Math.min(maxWidth / width, maxHeight / height); | |
| width *= ratio; | |
| height *= ratio; | |
| } | |
| canvas.width = width; | |
| canvas.height = height; | |
| ctx.drawImage(img, 0, 0, width, height); | |
| // Store original image data | |
| originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| } | |
| // Apply text prompt (simulated) | |
| function applyPrompt() { | |
| if (!currentImage) { | |
| showToast('Please upload an image first', 'error'); | |
| return; | |
| } | |
| const prompt = document.getElementById('promptInput').value.trim(); | |
| if (!prompt) { | |
| showToast('Please enter a prompt', 'error'); | |
| return; | |
| } | |
| showLoading(true); | |
| // Simulate AI processing | |
| setTimeout(() => { | |
| applyFilter(prompt); | |
| showLoading(false); | |
| showToast('Magic applied successfully!', 'success'); | |
| }, 2000); | |
| } | |
| // Apply filter based on prompt | |
| function applyFilter(prompt) { | |
| const canvas = document.getElementById('imageCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Reset to original | |
| ctx.putImageData(originalImageData, 0, 0); | |
| const lowerPrompt = prompt.toLowerCase(); | |
| if (lowerPrompt.includes('vintage') || lowerPrompt.includes('old')) { | |
| applyVintageFilter(ctx, canvas); | |
| } else if (lowerPrompt.includes('neon') || lowerPrompt.includes('glow')) { | |
| applyNeonFilter(ctx, canvas); | |
| } else if (lowerPrompt.includes('cartoon') || lowerPrompt.includes('anime')) { | |
| applyCartoonFilter(ctx, canvas); | |
| } else if (lowerPrompt.includes('cyberpunk')) { | |
| applyCyberpunkFilter(ctx, canvas); | |
| } else if (lowerPrompt.includes('black and white') || lowerPrompt.includes('bw')) { | |
| applyBlackWhiteFilter(ctx, canvas); | |
| } else { | |
| // Apply random filter | |
| const filters = [applyVintageFilter, applyNeonFilter, applyCartoonFilter, applyCyberpunkFilter]; | |
| filters[Math.floor(Math.random() * filters.length)](ctx, canvas); | |
| } | |
| } | |
| // Filter implementations | |
| function applyVintageFilter(ctx, canvas) { | |
| const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| const data = imageData.data; | |
| for (let i = 0; i < data.length; i += 4) { | |
| data[i] = Math.min(255, data[i] * 1.2); // Red | |
| data[i + 1] = Math.min(255, data[i + 1] * 0.8); // Green | |
| data[i + 2] = Math.min(255, data[i + 2] * 0.5); // Blue | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| ctx.globalCompositeOperation = 'overlay'; | |
| ctx.fillStyle = 'rgba(139, 69, 19, 0.1)'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| ctx.globalCompositeOperation = 'source-over'; | |
| } | |
| function applyNeonFilter(ctx, canvas) { | |
| ctx.filter = 'contrast(2) saturate(2) brightness(1.2)'; | |
| ctx.drawImage(canvas, 0, 0); | |
| ctx.filter = 'none'; | |
| // Add glow effect | |
| ctx.shadowBlur = 20; | |
| ctx.shadowColor = '#00ffff'; | |
| ctx.globalCompositeOperation = 'screen'; | |
| ctx.drawImage(canvas, 0, 0); | |
| ctx.globalCompositeOperation = 'source-over'; | |
| ctx.shadowBlur = 0; | |
| } | |
| function applyCartoonFilter(ctx, canvas) { | |
| ctx.filter = 'contrast(1.5) saturate(1.5)'; | |
| ctx.drawImage(canvas, 0, 0); | |
| // Simplify colors | |
| const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| const data = imageData.data; | |
| for (let i = 0; i < data.length; i += 4) { | |
| data[i] = Math.round(data[i] / 50) * 50; | |
| data[i + 1] = Math.round(data[i + 1] / 50) * 50; | |
| data[i + 2] = Math.round(data[i + 2] / 50) * 50; | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| ctx.filter = 'none'; | |
| } | |
| function applyCyberpunkFilter(ctx, canvas) { | |
| const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| const data = imageData.data; | |
| for (let i = 0; i < data.length; i += 4) { | |
| data[i] = data[i] * 0.5; // Reduce red | |
| data[i + 1] = data[i + 1] * 0.3; // Reduce green | |
| data[i + 2] = data[i + 2] * 1.5; // Enhance blue | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| // Add scan lines | |
| ctx.strokeStyle = 'rgba(0, 255, 255, 0.1)'; | |
| ctx.lineWidth = 1; | |
| for (let y = 0; y < canvas.height; y += 4) { | |
| ctx.beginPath(); | |
| ctx.moveTo(0, y); | |
| ctx.lineTo(canvas.width, y); | |
| ctx.stroke(); | |
| } | |
| } | |
| function applyBlackWhiteFilter(ctx, canvas) { | |
| const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| const data = imageData.data; | |
| for (let i = 0; i < data.length; i += 4) { | |
| const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114; | |
| data[i] = gray; | |
| data[i + 1] = gray; | |
| data[i + 2] = gray; | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| } | |
| // Adjust image with sliders | |
| function adjustImage() { | |
| if (!currentImage) return; | |
| const brightness = document.getElementById('brightnessSlider').value; | |
| const contrast = document.getElementById('contrastSlider').value; | |
| const saturation = document.getElementById('saturationSlider').value; | |
| const blur = document.getElementById('blurSlider').value; | |
| document.getElementById('brightnessValue').textContent = brightness + '%'; | |
| document.getElementById('contrastValue').textContent = contrast + '%'; | |
| document.getElementById('saturationValue').textContent = saturation + '%'; | |
| document.getElementById('blurValue').textContent = blur + 'px'; | |
| const canvas = document.getElementById('imageCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Reset to original | |
| ctx.putImageData(originalImageData, 0, 0); | |
| // Apply filters | |
| ctx.filter = `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%) blur(${blur}px)`; | |
| ctx.drawImage(canvas, 0, 0); | |
| ctx.filter = 'none'; | |
| } | |
| // Reset image | |
| function resetImage() { | |
| if (!currentImage) return; | |
| const canvas = document.getElementById('imageCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| ctx.putImageData(originalImageData, 0, 0); | |
| // Reset sliders | |
| document.getElementById('brightnessSlider').value = 100; | |
| document.getElementById('contrastSlider').value = 100; |