Skip to content

Commit 779732b

Browse files
committed
Add model dropdown retry logic and engine UI selector sync
1 parent 7e9b301 commit 779732b

1 file changed

Lines changed: 71 additions & 34 deletions

File tree

web_studio/static/app.js

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -197,17 +197,24 @@ document.addEventListener('DOMContentLoaded', () => {
197197
document.getElementById('seed-input').value = Math.floor(Math.random() * 1000000000);
198198
});
199199

200-
// Fetch Available Models
201-
async function loadModels() {
200+
// Fetch Available Models with Automatic Retry
201+
async function loadModels(retries = 5) {
202202
try {
203203
const res = await fetch('/api/models');
204204
const data = await res.json();
205205

206-
// Checkpoint Select
207206
const ckptSelect = document.getElementById('ckpt-select');
208-
ckptSelect.innerHTML = '';
207+
if (!ckptSelect) return;
208+
209+
const availableCkpts = (data.comfy_checkpoints && data.comfy_checkpoints.length > 0) ? data.comfy_checkpoints : (data.checkpoints || []);
209210

210-
const availableCkpts = data.comfy_checkpoints.length > 0 ? data.comfy_checkpoints : data.checkpoints;
211+
if (availableCkpts.length === 0 && retries > 0) {
212+
console.log("[Studio UI] Models API returned empty, retrying in 1.5s...");
213+
setTimeout(() => loadModels(retries - 1), 1500);
214+
return;
215+
}
216+
217+
ckptSelect.innerHTML = '';
211218
availableCkpts.forEach(ckpt => {
212219
const opt = document.createElement('option');
213220
opt.value = ckpt;
@@ -217,7 +224,7 @@ document.addEventListener('DOMContentLoaded', () => {
217224
}
218225
ckptSelect.appendChild(opt);
219226
});
220-
// Fallback if not selected yet
227+
221228
if (!ckptSelect.value && ckptSelect.options.length > 0) {
222229
ckptSelect.selectedIndex = 0;
223230
}
@@ -253,44 +260,74 @@ document.addEventListener('DOMContentLoaded', () => {
253260
const triggerBadges = document.getElementById('trigger-badges');
254261
const promptInput = document.getElementById('prompt-input');
255262

256-
loraSelect.innerHTML = '<option value="None">None (Disabled)</option>';
257-
data.loras.forEach(lora => {
258-
const opt = document.createElement('option');
259-
opt.value = lora;
260-
opt.textContent = lora;
261-
loraSelect.appendChild(opt);
262-
});
263+
if (loraSelect) {
264+
loraSelect.innerHTML = '<option value="None">None (Disabled)</option>';
265+
(data.loras || []).forEach(lora => {
266+
const opt = document.createElement('option');
267+
opt.value = lora;
268+
opt.textContent = lora;
269+
loraSelect.appendChild(opt);
270+
});
263271

264-
function updateTriggerBadges() {
265-
const selectedLora = loraSelect.value;
266-
triggerBadges.innerHTML = '';
267-
if (selectedLora && selectedLora !== 'None' && loraTriggersMap[selectedLora]) {
268-
triggersContainer.classList.remove('hidden');
269-
const tags = loraTriggersMap[selectedLora];
270-
tags.forEach(tag => {
271-
const chip = document.createElement('span');
272-
chip.className = 'trigger-chip';
273-
chip.innerHTML = `<i class="fa-solid fa-plus"></i> ${tag}`;
274-
chip.title = "Click to add to prompt";
275-
chip.addEventListener('click', () => {
276-
if (!promptInput.value.includes(tag)) {
277-
promptInput.value += (promptInput.value.trim() ? ', ' : '') + tag;
278-
}
272+
function updateTriggerBadges() {
273+
if (!triggersContainer || !triggerBadges) return;
274+
const selectedLora = loraSelect.value;
275+
const tags = loraTriggersMap[selectedLora] || [];
276+
triggerBadges.innerHTML = '';
277+
if (tags.length > 0) {
278+
triggersContainer.classList.remove('hidden');
279+
tags.forEach(tag => {
280+
const chip = document.createElement('span');
281+
chip.className = 'trigger-chip';
282+
chip.innerHTML = `<i class="fa-solid fa-plus"></i> ${tag}`;
283+
chip.title = "Click to add to prompt";
284+
chip.addEventListener('click', () => {
285+
if (!promptInput.value.includes(tag)) {
286+
promptInput.value += (promptInput.value.trim() ? ', ' : '') + tag;
287+
}
288+
});
289+
triggerBadges.appendChild(chip);
279290
});
280-
triggerBadges.appendChild(chip);
281-
});
282-
} else {
283-
triggersContainer.classList.add('hidden');
291+
} else {
292+
triggersContainer.classList.add('hidden');
293+
}
284294
}
285-
}
286295

287-
loraSelect.addEventListener('change', updateTriggerBadges);
296+
loraSelect.addEventListener('change', updateTriggerBadges);
297+
}
288298
} catch (e) {
289299
console.error("Failed to load models:", e);
300+
if (retries > 0) {
301+
setTimeout(() => loadModels(retries - 1), 2000);
302+
}
290303
}
291304
}
292305
loadModels();
293306

307+
// Inference Engine Radio UI Sync
308+
const engineRadios = document.querySelectorAll('input[name="inference-engine"]');
309+
const modelSelectContainer = document.getElementById('model-select-container');
310+
const ckptSelect = document.getElementById('ckpt-select');
311+
312+
function syncEngineUI() {
313+
const selected = document.querySelector('input[name="inference-engine"]:checked')?.value || 'local';
314+
if (selected === 'zerogpu') {
315+
if (ckptSelect) ckptSelect.disabled = true;
316+
if (modelSelectContainer) {
317+
modelSelectContainer.style.opacity = '0.5';
318+
modelSelectContainer.title = "HF ZeroGPU Cloud executes remotely via serverless FLUX.1-dev H100";
319+
}
320+
} else {
321+
if (ckptSelect) ckptSelect.disabled = false;
322+
if (modelSelectContainer) {
323+
modelSelectContainer.style.opacity = '1.0';
324+
modelSelectContainer.title = "";
325+
}
326+
}
327+
}
328+
engineRadios.forEach(r => r.addEventListener('change', syncEngineUI));
329+
syncEngineUI();
330+
294331
// WebSocket Connection to ComfyUI
295332
const clientId = 'studio-' + Math.random().toString(36).substring(2, 9);
296333
let ws;

0 commit comments

Comments
 (0)