()
| 24 | } |
| 25 | |
| 26 | export function ImageStudio() { |
| 27 | const container = document.createElement('div'); |
| 28 | container.className = 'w-full h-full flex flex-col items-center justify-center bg-app-bg relative p-4 md:p-6 overflow-y-auto custom-scrollbar overflow-x-hidden'; |
| 29 | |
| 30 | // --- State --- |
| 31 | const defaultModel = t2iModels[0]; |
| 32 | let selectedModel = defaultModel.id; |
| 33 | let selectedModelName = defaultModel.name; |
| 34 | let selectedAr = defaultModel.inputs?.aspect_ratio?.default || '1:1'; |
| 35 | let dropdownOpen = null; |
| 36 | let uploadedImageUrls = []; // array of uploaded image URLs (multi-image support) |
| 37 | let imageMode = false; // false = t2i models, true = i2i models |
| 38 | |
| 39 | // Local inference state — only image-capable models surface here. |
| 40 | // sd.cpp uses type='sd1'|'sdxl'|'z-image'; Wan2GP image models use type='image'. |
| 41 | // Wan2GP video models (type='video') are hidden from ImageStudio. |
| 42 | const LOCAL_IMAGE_MODELS = LOCAL_MODEL_CATALOG.filter(m => m.type !== 'video'); |
| 43 | let useLocalModel = false; |
| 44 | let selectedLocalModel = LOCAL_IMAGE_MODELS[0]?.id || null; |
| 45 | let localGenProgress = 0; // 0–1 |
| 46 | |
| 47 | // Advanced parameters state |
| 48 | let negativePrompt = ''; |
| 49 | let guidanceScale = 7.5; |
| 50 | let steps = 25; |
| 51 | let seed = -1; |
| 52 | let showAdvanced = false; |
| 53 | let selectedStyle = 'None'; |
| 54 | let batchCount = 1; |
| 55 | |
| 56 | // New advanced controls |
| 57 | let customWidth = 0; // 0 means use default (aspect ratio based) |
| 58 | let customHeight = 0; |
| 59 | let referenceStrength = 50; // 0-100, for style reference models |
| 60 | let selectedLora = ''; // LoRA model ID from Civitai |
| 61 | let loraWeight = 1.0; |
| 62 | |
| 63 | // Quick tools panel state |
| 64 | let showToolsPanel = false; |
| 65 | |
| 66 | const getCurrentModels = () => imageMode ? i2iModels : t2iModels; |
| 67 | const getCurrentAspectRatios = (id) => imageMode ? getAspectRatiosForI2IModel(id) : getAspectRatiosForModel(id); |
| 68 | const getCurrentResolutions = (id) => imageMode ? getResolutionsForI2IModel(id) : getResolutionsForModel(id); |
| 69 | const getCurrentQualityField = (id) => imageMode ? getQualityFieldForI2IModel(id) : getQualityFieldForModel(id); |
| 70 | |
| 71 | // ========================================== |
| 72 | // 1. HERO SECTION |
| 73 | // ========================================== |
| 74 | const hero = document.createElement('div'); |
| 75 | hero.className = 'flex flex-col items-center mb-10 md:mb-20 animate-fade-in-up transition-all duration-700'; |
| 76 | hero.innerHTML = ` |
| 77 | <div class="mb-10 relative group"> |
| 78 | <div class="absolute inset-0 bg-primary/20 blur-[100px] rounded-full opacity-40 group-hover:opacity-70 transition-opacity duration-1000"></div> |
| 79 | <div class="relative w-24 h-24 md:w-32 md:h-32 bg-teal-900/40 rounded-3xl flex items-center justify-center border border-white/5 overflow-hidden"> |
| 80 | <svg width="80" height="80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="text-primary opacity-20 absolute -right-4 -bottom-4"> |
| 81 | <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/> |
| 82 | </svg> |
| 83 | <div class="w-16 h-16 bg-primary/10 rounded-2xl flex items-center justify-center border border-primary/20 shadow-glow relative z-10"> |
no test coverage detected