From 52b39a4f6bdde62eeac7a9addf74e20a0f7af1ab Mon Sep 17 00:00:00 2001 From: Adam Ladachowski Date: Sat, 14 Feb 2026 03:41:07 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=AC=20Commit=20message:=20Update=20202?= =?UTF-8?q?6-02-14=2003:41:07,=201=20files,=20278=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📁 Files changed: 1 📝 Lines changed: 278 • index.html --- tensors/server/static/index.html | 278 +++++++++++++++++++++++++++---- 1 file changed, 250 insertions(+), 28 deletions(-) diff --git a/tensors/server/static/index.html b/tensors/server/static/index.html index d0262a8..f4fe379 100644 --- a/tensors/server/static/index.html +++ b/tensors/server/static/index.html @@ -226,6 +226,76 @@ .empty { text-align: center; padding: 4rem 1rem; color: #666; } .error { color: #dc2626; } + /* Generate controls */ + .generate-controls { + padding: 1rem 1.5rem; + background: #0f0f0f; + border-top: 1px solid #333; + display: flex; + flex-wrap: wrap; + gap: 1.25rem; + justify-content: center; + align-items: center; + } + .control-group { + display: flex; + align-items: center; + gap: 0.6rem; + } + .control-group label { + font-size: 0.8rem; + color: #666; + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 500; + } + .control-group select, + .control-group input[type="number"] { + padding: 0.75rem 1rem; + border: 1px solid #333; + border-radius: 8px; + background: #1a1a1a; + color: #e5e5e5; + font-size: 0.9rem; + height: 46px; + box-sizing: border-box; + } + .control-group select:focus, + .control-group input[type="number"]:focus { + outline: none; + border-color: #4ade80; + } + .control-group select { min-width: 120px; cursor: pointer; } + .control-group input[type="number"] { width: 65px; text-align: center; } + + /* Large styled selects (like prompt input) */ + .control-group select.styled-select { + min-width: 180px; + } + + /* Resolution buttons */ + .res-buttons { + display: flex; + gap: 6px; + } + .res-btn { + padding: 0 0.9rem; + border: 1px solid #333; + border-radius: 8px; + background: #1a1a1a; + color: #888; + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + height: 46px; + display: flex; + align-items: center; + justify-content: center; + } + .res-btn:hover { border-color: #4ade80; color: #e5e5e5; } + .res-btn.active { background: #4ade80; color: #0f0f0f; border-color: #4ade80; } + /* Search view */ #search-view { display: flex; @@ -540,10 +610,46 @@
+
+
+ + +
+
+ + + +
+
+ +
+
+ + + +
+
+
+
+ +
+ + + +
+
+
+ + +
+
+ + +
+
-
@@ -652,7 +758,95 @@ const chatArea = document.getElementById('chat-area'); const promptInput = document.getElementById('prompt-input'); const stepsInput = document.getElementById('steps-input'); + const batchInput = document.getElementById('batch-input'); const generateBtn = document.getElementById('generate-btn'); + const checkpointSelect = document.getElementById('checkpoint-select'); + const loraSelect = document.getElementById('lora-select'); + const loraWeight = document.getElementById('lora-weight'); + + // Resolution state + let currentBase = 768; + let currentRatio = '1:1'; + + // Resolution presets: base -> ratio -> [width, height] + const resolutions = { + 512: { '1:1': [512, 512], '4:3': [512, 384], '3:4': [384, 512] }, + 768: { '1:1': [768, 768], '4:3': [768, 576], '3:4': [576, 768] }, + 1024: { '1:1': [1024, 1024], '4:3': [1024, 768], '3:4': [768, 1024] } + }; + + function getResolution() { + return resolutions[currentBase][currentRatio]; + } + + // Resolution button handlers + document.querySelectorAll('#res-base-btns .res-btn').forEach(btn => { + btn.onclick = () => { + document.querySelectorAll('#res-base-btns .res-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + currentBase = parseInt(btn.dataset.base); + }; + }); + + document.querySelectorAll('#res-ratio-btns .res-btn').forEach(btn => { + btn.onclick = () => { + document.querySelectorAll('#res-ratio-btns .res-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + currentRatio = btn.dataset.ratio; + }; + }); + + // Load models on init + async function loadModels() { + try { + const [modelsRes, lorasRes, activeRes] = await Promise.all([ + fetch('/api/models'), + fetch('/api/models/loras'), + fetch('/api/models/active') + ]); + const models = await modelsRes.json(); + const loras = await lorasRes.json(); + const active = await activeRes.json(); + + // Populate checkpoints + checkpointSelect.innerHTML = models.models.map(m => + `` + ).join(''); + + // Populate LoRAs + loraSelect.innerHTML = '' + + loras.loras.map(l => ``).join(''); + } catch (e) { + console.error('Failed to load models:', e); + checkpointSelect.innerHTML = ''; + } + } + + // Switch checkpoint + checkpointSelect.onchange = async () => { + const model = checkpointSelect.value; + if (!model) return; + + checkpointSelect.disabled = true; + generateBtn.disabled = true; + + try { + const res = await fetch('/api/models/switch', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ model }) + }); + const data = await res.json(); + if (data.error) { + alert('Failed to switch model: ' + data.error); + } + } catch (e) { + alert('Failed to switch model: ' + e.message); + } + + checkpointSelect.disabled = false; + generateBtn.disabled = false; + }; promptInput.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { @@ -667,56 +861,84 @@ if (!prompt) return; const steps = parseInt(stepsInput.value) || 20; + const batch = parseInt(batchInput.value) || 1; + const [width, height] = getResolution(); + const lora = loraSelect.value; + const weight = parseFloat(loraWeight.value) || 0.8; + + // Build prompt with LoRA if selected + let finalPrompt = prompt; + if (lora) { + const loraName = loraSelect.options[loraSelect.selectedIndex].text; + finalPrompt = ` ${prompt}`; + } + promptInput.value = ''; generateBtn.disabled = true; // Add message const msgDiv = document.createElement('div'); msgDiv.className = 'chat-message'; + const resLabel = `${width}×${height}`; msgDiv.innerHTML = ` -
${escapeHtml(prompt)}
+
${escapeHtml(prompt)} [${resLabel}, ${steps} steps${batch > 1 ? ', batch ' + batch : ''}${lora ? ', +LoRA' : ''}]
-
Generating
+ ${Array(batch).fill('
Generating
').join('')}
`; chatArea.appendChild(msgDiv); chatArea.scrollTop = chatArea.scrollHeight; - try { - const res = await fetch('/api/generate', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - prompt: prompt, - steps: steps, - width: 512, - height: 512, - save_to_gallery: true - }) - }); - const data = await res.json(); - const imagesDiv = msgDiv.querySelector('.chat-images'); + const imagesDiv = msgDiv.querySelector('.chat-images'); + let allImages = []; - if (data.images && data.images.length > 0) { - imagesDiv.innerHTML = data.images.map(img => ` - - ${img.id} - - `).join(''); - initLightbox(); - } else if (data.error) { - imagesDiv.innerHTML = `
${escapeHtml(data.error)}
`; - } else { + try { + // Generate batch sequentially + for (let i = 0; i < batch; i++) { + const res = await fetch('/api/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + prompt: finalPrompt, + steps: steps, + width: width, + height: height, + seed: -1, + save_to_gallery: true + }) + }); + const data = await res.json(); + + if (data.images && data.images.length > 0) { + allImages.push(...data.images); + // Update display progressively + imagesDiv.innerHTML = allImages.map(img => ` + + ${img.id} + + `).join('') + (i < batch - 1 ? '
Generating
'.repeat(batch - i - 1) : ''); + initLightbox(); + } else if (data.error) { + imagesDiv.innerHTML = `
${escapeHtml(data.error)}
`; + break; + } + chatArea.scrollTop = chatArea.scrollHeight; + } + + if (allImages.length === 0) { imagesDiv.innerHTML = '
Generation failed
'; } } catch (e) { - msgDiv.querySelector('.chat-images').innerHTML = '
Error: ' + escapeHtml(e.message) + '
'; + imagesDiv.innerHTML = '
Error: ' + escapeHtml(e.message) + '
'; } generateBtn.disabled = false; chatArea.scrollTop = chatArea.scrollHeight; } + // Load models on page load + loadModels(); + // Search const searchInput = document.getElementById('search-input'); const searchType = document.getElementById('search-type');