Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Aegis Engine | WebGPU AI</title> | |
| <style> | |
| :root { | |
| --bg: #030712; | |
| --surface: rgba(17, 24, 39, 0.7); | |
| --accent: #00f6ff; | |
| --text: #f3f4f6; | |
| --text-dim: #9ca3af; | |
| } | |
| * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Courier New', Courier, monospace; } | |
| body { background-color: var(--bg); color: var(--text); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 20px; overflow: hidden;} | |
| .grid-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-image: linear-gradient(rgba(0, 246, 255, 0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 246, 255, 0.05) 1px, transparent 1px); background-size: 30px 30px; z-index: -1; } | |
| .terminal { background: var(--surface); backdrop-filter: blur(12px); border: 1px solid rgba(0, 246, 255, 0.2); border-radius: 12px; width: 100%; max-width: 800px; padding: 30px; box-shadow: 0 0 40px rgba(0, 246, 255, 0.1); } | |
| .header { border-bottom: 1px solid rgba(0, 246, 255, 0.3); padding-bottom: 15px; margin-bottom: 25px; display: flex; justify-content: space-between; align-items: flex-end; } | |
| .header h1 { font-size: 24px; text-transform: uppercase; letter-spacing: 3px; color: var(--accent); text-shadow: 0 0 10px rgba(0, 246, 255, 0.5); } | |
| .badge { background: rgba(0, 246, 255, 0.1); color: var(--accent); padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; border: 1px solid var(--accent); } | |
| /* --- PHASE 1: LOADING UI --- */ | |
| #bootSequence { display: flex; flex-direction: column; gap: 15px; } | |
| .log-line { color: var(--text-dim); font-size: 14px; } | |
| .progress-box { width: 100%; height: 20px; border: 1px solid var(--accent); border-radius: 4px; padding: 2px; position: relative; } | |
| .progress-bar { height: 100%; background: var(--accent); width: 0%; box-shadow: 0 0 10px var(--accent); transition: width 0.2s; } | |
| .progress-text { position: absolute; top: 0; left: 50%; transform: translateX(-50%); font-size: 12px; font-weight: bold; color: #000; line-height: 18px; mix-blend-mode: overlay;} | |
| /* --- PHASE 2: GENERATION UI --- */ | |
| #inferenceUI { display: none; flex-direction: column; gap: 20px; } | |
| .canvas-container { width: 100%; aspect-ratio: 1/1; background: #000; border-radius: 8px; border: 1px dashed var(--text-dim); display: flex; justify-content: center; align-items: center; overflow: hidden; position: relative;} | |
| .canvas-container img { width: 100%; height: 100%; object-fit: cover; display: none; } | |
| .status-pulse { color: var(--accent); font-size: 14px; letter-spacing: 2px; animation: pulse 1.5s infinite; } | |
| @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } } | |
| .input-group { display: flex; gap: 10px; } | |
| input[type="text"] { flex: 1; background: rgba(0,0,0,0.5); border: 1px solid rgba(0, 246, 255, 0.3); color: var(--text); padding: 15px; border-radius: 6px; font-size: 16px; outline: none; transition: 0.3s; } | |
| input[type="text"]:focus { border-color: var(--accent); box-shadow: 0 0 15px rgba(0, 246, 255, 0.2); } | |
| button { background: var(--accent); color: #000; border: none; padding: 0 25px; font-weight: bold; font-size: 16px; letter-spacing: 1px; border-radius: 6px; cursor: pointer; transition: 0.2s; text-transform: uppercase; } | |
| button:hover { background: #fff; box-shadow: 0 0 20px var(--accent); } | |
| button:disabled { background: #333; color: #666; cursor: not-allowed; box-shadow: none; } | |
| .error-state { color: #ff3366; background: rgba(255, 51, 102, 0.1); padding: 15px; border: 1px solid #ff3366; border-radius: 6px; display: none; margin-top: 15px;} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="grid-overlay"></div> | |
| <div class="terminal"> | |
| <div class="header"> | |
| <h1>Aegis Engine</h1> | |
| <span class="badge" id="hardwareBadge">CHECKING WEBGPU...</span> | |
| </div> | |
| <div id="errorPanel" class="error-state"></div> | |
| <div id="bootSequence"> | |
| <div class="log-line" id="logMsg">> Initializing Local Neural Runtime...</div> | |
| <div class="log-line">> Target: SD-Turbo (WebGPU)</div> | |
| <div class="log-line" style="color: #ffcc00; font-size: 12px;">⚠ Note: First-time boot requires ~1.5GB local cache allocation.</div> | |
| <div class="progress-box"> | |
| <div class="progress-bar" id="progressBar"></div> | |
| <div class="progress-text" id="progressText">0%</div> | |
| </div> | |
| <button id="btnBoot" style="padding: 12px; margin-top: 10px;">MOUNT NEURAL WEIGHTS</button> | |
| </div> | |
| <div id="inferenceUI"> | |
| <div class="canvas-container"> | |
| <div class="status-pulse" id="statusPulse" style="display: none;">COMPUTING LATENT MATRICES...</div> | |
| <img id="outputImage" alt="Generated Output"> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" id="promptInput" placeholder="Enter prompt (e.g., A cyberpunk samurai reading a neon book)..." autocomplete="off"> | |
| <button id="btnGenerate">RENDER</button> | |
| </div> | |
| <div class="log-line" id="renderStats" style="text-align: right; font-size: 11px;">Awaiting Input</div> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| import { client } from 'https://esm.sh/web-txt2img@latest'; | |
| const dom = { | |
| bootSeq: document.getElementById('bootSequence'), | |
| infUI: document.getElementById('inferenceUI'), | |
| btnBoot: document.getElementById('btnBoot'), | |
| btnGen: document.getElementById('btnGenerate'), | |
| prompt: document.getElementById('promptInput'), | |
| img: document.getElementById('outputImage'), | |
| pulse: document.getElementById('statusPulse'), | |
| progBar: document.getElementById('progressBar'), | |
| progTxt: document.getElementById('progressText'), | |
| log: document.getElementById('logMsg'), | |
| badge: document.getElementById('hardwareBadge'), | |
| err: document.getElementById('errorPanel'), | |
| stats: document.getElementById('renderStats') | |
| }; | |
| // 1. Hardware Check | |
| if (!navigator.gpu) { | |
| dom.badge.innerText = "WEBGPU NOT SUPPORTED"; | |
| dom.badge.style.color = "#ff3366"; | |
| dom.badge.style.borderColor = "#ff3366"; | |
| showError("Your browser does not support WebGPU. Please use a recent version of Chrome, Edge, or Brave on a desktop computer."); | |
| dom.btnBoot.disabled = true; | |
| } else { | |
| dom.badge.innerText = "WEBGPU ACTIVE"; | |
| } | |
| function showError(msg) { | |
| dom.err.style.display = "block"; | |
| dom.err.innerText = msg; | |
| } | |
| // 2. Mount Model to VRAM | |
| dom.btnBoot.addEventListener('click', async () => { | |
| dom.btnBoot.disabled = true; | |
| dom.btnBoot.innerText = "DOWNLOADING & COMPILING..."; | |
| dom.err.style.display = "none"; | |
| try { | |
| // Initialize the SD-Turbo pipeline | |
| await client.load('sd-turbo', { backendPreference: ['webgpu'] }, (msg) => { | |
| // Update Progress UI | |
| if(msg.progress !== undefined) { | |
| const pct = Math.round(msg.progress * 100); | |
| dom.progBar.style.width = pct + '%'; | |
| dom.progTxt.innerText = pct + '%'; | |
| } | |
| dom.log.innerText = `> ${msg.text || msg.phase || 'Loading...'}`; | |
| }); | |
| // Transition to Inference UI | |
| dom.bootSeq.style.display = "none"; | |
| dom.infUI.style.display = "flex"; | |
| } catch (error) { | |
| showError("Failed to mount model: " + error.message); | |
| dom.btnBoot.disabled = false; | |
| dom.btnBoot.innerText = "RETRY MOUNT"; | |
| } | |
| }); | |
| // 3. Local Offline Generation | |
| dom.btnGen.addEventListener('click', async () => { | |
| const promptText = dom.prompt.value.trim(); | |
| if(!promptText) return; | |
| // UI State Update | |
| dom.btnGen.disabled = true; | |
| dom.prompt.disabled = true; | |
| dom.img.style.display = "none"; | |
| dom.pulse.style.display = "block"; | |
| dom.stats.innerText = "Processing through local GPU..."; | |
| dom.err.style.display = "none"; | |
| const startTime = performance.now(); | |
| try { | |
| // Execute WebGPU Inference | |
| const { promise } = client.generate({ | |
| prompt: promptText | |
| }); | |
| const response = await promise; | |
| // The library returns a Blob/Image data depending on internal handling | |
| // We convert it to a renderable URL | |
| const imageUrl = (response instanceof Blob) ? URL.createObjectURL(response) : response; | |
| const endTime = performance.now(); | |
| const renderTime = ((endTime - startTime) / 1000).toFixed(2); | |
| dom.img.src = imageUrl; | |
| dom.img.style.display = "block"; | |
| dom.stats.innerText = `Rendered locally in ${renderTime} seconds.`; | |
| } catch (error) { | |
| showError("Generation Error: " + error.message); | |
| dom.stats.innerText = "Render Failed."; | |
| } finally { | |
| dom.pulse.style.display = "none"; | |
| dom.btnGen.disabled = false; | |
| dom.prompt.disabled = false; | |
| dom.prompt.focus(); | |
| } | |
| }); | |
| // Allow Enter key to trigger generation | |
| dom.prompt.addEventListener('keypress', (e) => { | |
| if(e.key === 'Enter') dom.btnGen.click(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |