Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta name="theme-color" content="#10B981"> | |
| <title>Screenshot Studio</title> | |
| <meta name="description" content="Convert text and HTML into beautiful, formatted screenshots using AI"> | |
| <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}?v=5"> | |
| </head> | |
| <body> | |
| <!-- Mobile Header --> | |
| <div class="mobile-header"> | |
| <button class="hamburger-btn" onclick="toggleSidebar()" aria-label="Toggle menu"> | |
| <svg fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> | |
| </svg> | |
| </button> | |
| <div class="logo"> | |
| <h1> | |
| <div class="logo-icon">S</div> | |
| Screenshot Studio | |
| </h1> | |
| </div> | |
| </div> | |
| <!-- Sidebar Overlay (mobile) --> | |
| <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div> | |
| <div class="app-container"> | |
| <!-- Sidebar --> | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="logo"> | |
| <h1> | |
| <div class="logo-icon">S</div> | |
| Screenshot Studio | |
| </h1> | |
| </div> | |
| <nav class="nav-menu"> | |
| <div class="nav-item active" onclick="switchTool('text-to-image')"> | |
| <svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" | |
| d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> | |
| </svg> | |
| Text to Image | |
| </div> | |
| <div class="nav-item" onclick="switchTool('html-to-image')"> | |
| <svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" | |
| d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /> | |
| </svg> | |
| HTML to Image | |
| </div> | |
| <div class="nav-item" onclick="switchTool('image-to-screenshots')"> | |
| <svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
| </svg> | |
| Image to Screenshots | |
| </div> | |
| <div class="nav-item" onclick="switchTool('resources')"> | |
| <svg class="nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" | |
| d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" /> | |
| </svg> | |
| Resources | |
| </div> | |
| </nav> | |
| <!-- Cache Clear Button --> | |
| <div class="cache-button-container"> | |
| <button onclick="clearCache()" class="cache-clear-btn"> | |
| <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| Clear AI Cache | |
| </button> | |
| <div id="cacheStats" class="cache-stats"></div> | |
| </div> | |
| </aside> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| <!-- ═══ Text to Image ═══ --> | |
| <div id="text-to-image" class="tool-section active"> | |
| <div class="header"> | |
| <h2>Text to Image</h2> | |
| <p>Convert your text content into beautifully formatted screenshots using AI</p> | |
| </div> | |
| <div class="card"> | |
| <div class="form-group mb-16"> | |
| <label class="form-label">AI Model</label> | |
| <select id="textModelChoice" class="form-input"> | |
| <option value="default">Default Model (122B Qwen ~ High Quality)</option> | |
| <option value="fast">Fast Model (80B Qwen ~ High Speed)</option> | |
| <option value="kimi">Kimi K2.5 (Thinking ~ 16k Tokens)</option> | |
| <option value="deepseek">DeepSeek V3.2 (Thinking ~ 8k Tokens)</option> | |
| <option value="devstral">Mistral Devstral (Fast ~ 8k Tokens)</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label">Enter your text content</label> | |
| <textarea id="textInput" class="form-textarea" | |
| placeholder="Type or paste your content here... Example: Write notes for Class 12 Nepali subject..."></textarea> | |
| </div> | |
| <!-- Advanced Settings --> | |
| <div class="advanced-toggle" onclick="toggleAdvancedText()"> | |
| <svg id="advancedIcon" width="18" height="18" fill="none" stroke="currentColor" | |
| viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | |
| </svg> | |
| <span>Advanced Settings</span> | |
| </div> | |
| <div id="advancedSettings" class="advanced-settings hidden"> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Zoom Level</label> | |
| <input type="number" id="textZoom" class="form-input" value="2.1" min="1" max="5" | |
| step="0.1"> | |
| <p class="form-hint">Default: 2.1× (210%)</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Overlap (px)</label> | |
| <input type="number" id="textOverlap" class="form-input" value="20" min="0" max="200" | |
| step="5"> | |
| <p class="form-hint">Default: 15px</p> | |
| </div> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Width</label> | |
| <input type="number" id="textViewportWidth" class="form-input" value="1920" min="800" | |
| max="3840" step="80"> | |
| <p class="form-hint">Output width in px</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Height</label> | |
| <input type="number" id="textViewportHeight" class="form-input" value="1080" min="600" | |
| max="2160" step="60"> | |
| <p class="form-hint">Output height in px</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Max Shots</label> | |
| <input type="number" id="textMaxScreenshots" class="form-input" value="50" min="1" | |
| max="100" step="1"> | |
| <p class="form-hint">Safety limit</p> | |
| </div> | |
| </div> | |
| <div class="form-row mt-10"> | |
| <div class="form-group w-full"> | |
| <label class="checkbox-container flex items-center gap-8"> | |
| <input type="checkbox" id="textEnableVerification" checked> | |
| <span class="font-medium">Enable AI Content Verification (Recommended)</span> | |
| </label> | |
| <p class="form-hint mt-4">Secondary AI pass to fix missing data and hallucinations</p> | |
| </div> | |
| </div> | |
| <button class="btn btn-secondary mt-4" onclick="resetAdvancedText()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| Reset Defaults | |
| </button> | |
| </div> | |
| <div class="flex gap-12 mt-20"> | |
| <button class="btn btn-secondary flex-1" onclick="previewHTML()"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> | |
| </svg> | |
| Preview HTML | |
| </button> | |
| <div id="textButtonContainer" class="flex gap-12 flex-2"> | |
| <button class="btn btn-primary flex-1" onclick="generateFromText()"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M13 10V3L4 14h7v7l9-11h-7z" /> | |
| </svg> | |
| Generate Screenshots | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- HTML Preview Modal --> | |
| <div id="htmlPreviewModal" class="image-modal"> | |
| <button class="modal-close" onclick="closePreviewModal()">×</button> | |
| <div class="preview-modal-body"> | |
| <h3 class="preview-modal-title">HTML Preview</h3> | |
| <div class="preview-modal-actions"> | |
| <button class="btn btn-secondary" onclick="copyHTMLToClipboard()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> | |
| </svg> | |
| Copy HTML | |
| </button> | |
| <button class="btn btn-primary" onclick="closePreviewModal(); generateFromText();"> | |
| Continue to Generate | |
| </button> | |
| </div> | |
| <div class="preview-frame-wrapper"> | |
| <iframe id="htmlPreviewFrame" class="preview-frame"></iframe> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="textLoading" class="loading"> | |
| <div class="spinner"></div> | |
| <p class="loading-text" id="loadingMessage">Processing your content with AI...</p> | |
| <div class="progress-container mt-14"> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progressFill"></div> | |
| </div> | |
| <p class="progress-text" id="progressText">0%</p> | |
| <p class="eta-text" id="etaText"></p> | |
| </div> | |
| </div> | |
| <div id="textError" class="alert alert-error hidden"></div> | |
| <div id="textResult" class="result-section"> | |
| <div class="alert alert-success"> | |
| <svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| <div id="textResultMessage"></div> | |
| </div> | |
| <div id="textScreenshotGrid" class="screenshot-grid"></div> | |
| </div> | |
| </div> | |
| <!-- ═══ HTML to Image ═══ --> | |
| <div id="html-to-image" class="tool-section"> | |
| <div class="header"> | |
| <h2>HTML to Image</h2> | |
| <p>Convert HTML files or code directly into high-quality screenshots</p> | |
| </div> | |
| <div class="card"> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="switchHtmlTab('paste')">Paste HTML</button> | |
| <button class="tab" onclick="switchHtmlTab('upload')">Upload File</button> | |
| </div> | |
| <div id="htmlPasteTab" class="tab-content"> | |
| <div class="form-group"> | |
| <label class="form-label">Paste your HTML code</label> | |
| <textarea id="htmlInput" class="form-textarea font-mono" | |
| placeholder="<!DOCTYPE html> <html> <head> <title>My Page</title> </head> <body> <h1>Hello World</h1> </body> </html>"></textarea> | |
| </div> | |
| </div> | |
| <div id="htmlUploadTab" class="tab-content hidden"> | |
| <div class="form-group"> | |
| <label class="form-label">Upload HTML file</label> | |
| <div class="file-upload" onclick="document.getElementById('htmlFile').click()"> | |
| <input type="file" id="htmlFile" accept=".html,.htm" onchange="handleFileUpload(event)"> | |
| <svg width="40" height="40" fill="none" stroke="currentColor" viewBox="0 0 24 24" | |
| class="file-upload-icon"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /> | |
| </svg> | |
| <p class="file-upload-text">Click to upload or drag and drop</p> | |
| <p class="file-upload-hint">HTML files only</p> | |
| </div> | |
| <p id="fileName" class="file-name"></p> | |
| </div> | |
| </div> | |
| <!-- Advanced Settings --> | |
| <div class="advanced-toggle" onclick="toggleAdvancedHtml()"> | |
| <svg id="advancedIconHtml" width="18" height="18" fill="none" stroke="currentColor" | |
| viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | |
| </svg> | |
| <span>Advanced Settings</span> | |
| </div> | |
| <div id="advancedSettingsHtml" class="advanced-settings hidden"> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Zoom Level</label> | |
| <input type="number" id="htmlZoom" class="form-input" value="2.1" min="1" max="5" | |
| step="0.1"> | |
| <p class="form-hint">Default: 2.1× (210%)</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Overlap (px)</label> | |
| <input type="number" id="htmlOverlap" class="form-input" value="20" min="0" max="200" | |
| step="5"> | |
| <p class="form-hint">Default: 15px</p> | |
| </div> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Width</label> | |
| <input type="number" id="htmlViewportWidth" class="form-input" value="1920" min="800" | |
| max="3840" step="80"> | |
| <p class="form-hint">Output width in px</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Height</label> | |
| <input type="number" id="htmlViewportHeight" class="form-input" value="1080" min="600" | |
| max="2160" step="60"> | |
| <p class="form-hint">Output height in px</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Max Shots</label> | |
| <input type="number" id="htmlMaxScreenshots" class="form-input" value="50" min="1" | |
| max="100" step="1"> | |
| <p class="form-hint">Safety limit</p> | |
| </div> | |
| </div> | |
| <button class="btn btn-secondary mt-4" onclick="resetAdvancedHtml()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| Reset Defaults | |
| </button> | |
| </div> | |
| <div id="htmlButtonContainer" class="flex gap-12 mt-20"> | |
| <button class="btn btn-primary flex-1" onclick="generateFromHtml()"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
| </svg> | |
| Generate Screenshots | |
| </button> | |
| </div> | |
| </div> | |
| <div id="htmlLoading" class="loading"> | |
| <div class="spinner"></div> | |
| <p class="loading-text">Generating screenshots from HTML...</p> | |
| </div> | |
| <div id="htmlError" class="alert alert-error hidden"></div> | |
| <div id="htmlResult" class="result-section"> | |
| <div class="alert alert-success"> | |
| <svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| <div id="htmlResultMessage"></div> | |
| </div> | |
| <div id="htmlScreenshotGrid" class="screenshot-grid"></div> | |
| </div> | |
| </div> | |
| <!-- ═══ Image to Screenshots ═══ --> | |
| <div id="image-to-screenshots" class="tool-section"> | |
| <div class="header"> | |
| <h2>Image to Screenshots</h2> | |
| <p>Extract text from an image and convert it into a formatted HTML screenshot workflow</p> | |
| </div> | |
| <div class="card"> | |
| <div class="form-group"> | |
| <label class="form-label">Upload Image or PDF</label> | |
| <div class="file-upload" onclick="document.getElementById('imageFile').click()"> | |
| <input type="file" id="imageFile" accept="image/*,application/pdf" | |
| onchange="handleImageSelect(event)"> | |
| <svg width="40" height="40" fill="none" stroke="currentColor" viewBox="0 0 24 24" | |
| class="file-upload-icon"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
| </svg> | |
| <p class="file-upload-text">Click to upload a file</p> | |
| <p class="file-upload-hint">JPG, PNG, WebP or PDF supported</p> | |
| </div> | |
| <div id="imagePreview" class="image-preview"></div> | |
| </div> | |
| <div class="form-group mt-20"> | |
| <label class="form-label">Optional Instructions (Context for AI)</label> | |
| <textarea id="imageInstructions" class="form-textarea" rows="2" | |
| placeholder="Example: This is a math worksheet. Extract all equations carefully."></textarea> | |
| </div> | |
| <div class="flex gap-12 mt-20"> | |
| <button class="btn btn-secondary flex-1" onclick="extractTextFromImage()" | |
| id="btn-extract-stage-1"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | |
| </svg> | |
| Stage 1: Extract & Review Text | |
| </button> | |
| <button class="btn btn-primary flex-1" onclick="processImageComplete()" | |
| id="btn-process-complete"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M13 10V3L4 14h7v7l9-11h-7z" /> | |
| </svg> | |
| Complete Workflow (Auto Generate) | |
| </button> | |
| <button class="btn btn-secondary hidden" onclick="cancelImageWorkflow()" id="btn-cancel-image"> | |
| ❌ Cancel | |
| </button> | |
| </div> | |
| <!-- Advanced Settings Toggle --> | |
| <div class="advanced-toggle" onclick="toggleImageAdvanced()"> | |
| <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24" | |
| id="image-advanced-toggle-icon"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | |
| </svg> | |
| <span id="image-advanced-toggle-text">Advanced Settings</span> | |
| </div> | |
| <div id="image-advanced-settings" class="advanced-settings hidden"> | |
| <!-- AI Settings --> | |
| <div class="settings-group"> | |
| <h4>AI Settings</h4> | |
| <div class="form-group"> | |
| <label class="form-label">System Prompt / Custom Instructions</label> | |
| <textarea id="image-system-prompt" class="form-textarea" rows="3" | |
| placeholder="Custom instructions for AI HTML generation... Example: Use Urdu language with a dark theme and modern design Leave empty to use default prompt"></textarea> | |
| <p class="form-hint">Customize how AI generates HTML from the extracted text</p> | |
| </div> | |
| </div> | |
| <!-- Screenshot Settings --> | |
| <div class="settings-group"> | |
| <h4>Screenshot Settings</h4> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Zoom Level</label> | |
| <input type="number" id="image-zoom" class="form-input" value="2.1" min="0.5" | |
| max="5" step="0.1"> | |
| <p class="form-hint">Default: 2.1×</p> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Overlap (px)</label> | |
| <input type="number" id="image-overlap" class="form-input" value="20" min="0" | |
| max="200" step="5"> | |
| <p class="form-hint">Default: 20px</p> | |
| </div> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Viewport Width</label> | |
| <input type="number" id="image-viewport-width" class="form-input" value="1920" | |
| min="800" max="3840" step="10"> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Viewport Height</label> | |
| <input type="number" id="image-viewport-height" class="form-input" value="1080" | |
| min="600" max="2160" step="10"> | |
| </div> | |
| <div class="form-group flex-1"> | |
| <label class="form-label">Max Screenshots</label> | |
| <input type="number" id="image-max-screenshots" class="form-input" value="50" | |
| min="1" max="100"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress Section (for SSE streaming) --> | |
| <div id="image-progress-container" class="card hidden mt-20"> | |
| <h3>Progress</h3> | |
| <!-- Step Indicators --> | |
| <div class="step-indicators"> | |
| <div class="step-indicator" data-step="1"> | |
| <div class="step-icon">1</div> | |
| <div class="step-label">Extract Text</div> | |
| </div> | |
| <div class="step-indicator" data-step="2"> | |
| <div class="step-icon">2</div> | |
| <div class="step-label">Generate HTML</div> | |
| </div> | |
| <div class="step-indicator" data-step="3"> | |
| <div class="step-icon">3</div> | |
| <div class="step-label">Create Screenshots</div> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress-bar-container"> | |
| <div class="progress-bar" id="image-progress-bar" role="progressbar" aria-valuenow="0" | |
| aria-valuemin="0" aria-valuemax="100"></div> | |
| </div> | |
| <div class="progress-info"> | |
| <span id="image-progress-text">0%</span> | |
| <span id="image-status-text">Starting...</span> | |
| </div> | |
| </div> | |
| <div id="imageLoading" class="loading hidden"> | |
| <div class="spinner"></div> | |
| <p class="loading-text">Processing image with Vision AI...</p> | |
| </div> | |
| <div id="imageError" class="alert alert-error hidden mt-20"></div> | |
| <div id="imageResults" class="result-section hidden mt-20"> | |
| <!-- Result will be populated here -> Text for review or final Screenshots grid --> | |
| </div> | |
| </div> | |
| <!-- ═══ Resources ═══ --> | |
| <div id="resources" class="tool-section"> | |
| <div class="header"> | |
| <h2>Resources</h2> | |
| <p>View, download, and manage your generated files</p> | |
| </div> | |
| <div class="card"> | |
| <div class="flex justify-between items-center mb-18 flex-wrap gap-10"> | |
| <div class="tabs mb-0"> | |
| <button class="tab active" onclick="switchResourceTab('screenshots')">Screenshots</button> | |
| <button class="tab" onclick="switchResourceTab('html')">HTML Files</button> | |
| </div> | |
| <div class="flex gap-8 items-center"> | |
| <div id="bulkActions" class="hidden gap-8"> | |
| <span id="selectedCount" class="progress-text">0 selected</span> | |
| <button class="btn btn-secondary" onclick="selectAll()">Select All</button> | |
| <button class="btn btn-danger" onclick="deleteSelected()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> | |
| </svg> | |
| Delete | |
| </button> | |
| </div> | |
| <button class="btn btn-secondary" onclick="refreshResources()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| Refresh | |
| </button> | |
| </div> | |
| </div> | |
| <div id="resourcesLoading" class="loading"> | |
| <div class="spinner"></div> | |
| <p class="loading-text">Loading resources...</p> | |
| </div> | |
| <div id="resourcesError" class="alert alert-error hidden"></div> | |
| <!-- Screenshots Tab --> | |
| <div id="screenshotsTab" class="resource-tab-content"> | |
| <div id="screenshotsList" class="resources-grid"></div> | |
| <div id="noScreenshots" class="empty-state hidden"> | |
| <svg width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24" | |
| class="empty-state-icon"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
| </svg> | |
| <p class="empty-state-title">No screenshots yet</p> | |
| <p class="empty-state-hint">Generate some screenshots to see them here</p> | |
| </div> | |
| </div> | |
| <!-- HTML Files Tab --> | |
| <div id="htmlTab" class="resource-tab-content hidden"> | |
| <div id="htmlFilesList" class="resources-list"></div> | |
| <div id="noHtmlFiles" class="empty-state hidden"> | |
| <svg width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24" | |
| class="empty-state-icon"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> | |
| </svg> | |
| <p class="empty-state-title">No HTML files yet</p> | |
| <p class="empty-state-hint">Generate content to see HTML files here</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Image Preview Modal --> | |
| <div id="imageModal" class="image-modal"> | |
| <button class="modal-close" onclick="closeImageModal()">×</button> | |
| <button class="modal-download" onclick="downloadCurrentImage()"> | |
| <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /> | |
| </svg> | |
| Download | |
| </button> | |
| <button class="modal-nav modal-nav-prev" onclick="navigateImage(-1)">‹</button> | |
| <button class="modal-nav modal-nav-next" onclick="navigateImage(1)">›</button> | |
| <div class="image-modal-content"> | |
| <img id="modalImage" src="" alt="Preview"> | |
| </div> | |
| <div class="modal-info" id="modalInfo">1 / 1</div> | |
| </div> | |
| <!-- JavaScript Modules --> | |
| <script src="{{ url_for('static', filename='js/notifications.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/navigation.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/settings-manager.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/generation-manager.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/text-to-image.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/html-to-image.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/image-to-screenshots.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/resources.js') }}"></script> | |
| <script src="{{ url_for('static', filename='js/utils.js') }}"></script> | |
| <!-- Mobile sidebar control --> | |
| <script> | |
| function toggleSidebar() { | |
| document.getElementById('sidebar').classList.toggle('open'); | |
| document.getElementById('sidebarOverlay').classList.toggle('active'); | |
| } | |
| function closeSidebar() { | |
| document.getElementById('sidebar').classList.remove('open'); | |
| document.getElementById('sidebarOverlay').classList.remove('active'); | |
| } | |
| // Close sidebar when a nav item is clicked on mobile | |
| document.querySelectorAll('.nav-item').forEach(item => { | |
| item.addEventListener('click', () => { | |
| if (window.innerWidth <= 768) closeSidebar(); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |