import { app } from "../../scripts/app.js"; import { ComfyWidgets } from "../../scripts/widgets.js"; // Parameter definitions const PARAM_INFO = { none: { description: "No parameter selected", hasOptions: false }, model: { description: "Checkpoint/Model files", hasOptions: true, inputNames: ['ckpt_name', 'checkpoint', 'model_name', 'unet_name'] }, vae: { description: "VAE models", hasOptions: true, inputNames: ['vae_name', 'vae'], addDefault: "Automatic" }, lora: { description: "LoRA models", hasOptions: true, inputNames: ['lora_name', 'lora'], addDefault: "None" }, sampler: { description: "Sampling algorithms", hasOptions: true, inputNames: ['sampler_name', 'sampler'] }, scheduler: { description: "Noise schedulers", hasOptions: true, inputNames: ['scheduler', 'scheduler_name'] }, cfg_scale: { description: "CFG Scale values", example: "Examples: 5, 7.5, 10\nRange: 5:15:2.5" }, steps: { description: "Sampling steps", example: "Examples: 20, 30, 40\nRange: 10:50:10" }, seed: { description: "Random seeds", example: "Examples: 42, 123, 456\nRange: 0:1000:100" }, denoise: { description: "Denoising strength", example: "Examples: 0.3, 0.5, 0.7\nRange: 0.2:1.0:0.2" }, flux_guidance: { description: "Flux guidance strength", example: "Examples: 1.0, 3.5, 5.0\nRange: 1:5:0.5" }, prompt: { description: "Text prompts", example: "Enter one prompt per line:\n\na beautiful sunset\na mystical forest" } }; // Get options for parameter types async function getParameterOptions(paramType) { if (!PARAM_INFO[paramType]?.hasOptions) return []; try { const resp = await fetch('/object_info'); const data = await resp.json(); const inputNames = PARAM_INFO[paramType].inputNames || [paramType]; let allOptions = []; // Search through all nodes for (const [nodeName, nodeData] of Object.entries(data)) { if (nodeData.input?.required) { for (const [inputName, inputDef] of Object.entries(nodeData.input.required)) { if (inputNames.includes(inputName) && Array.isArray(inputDef[0])) { allOptions = [...new Set([...allOptions, ...inputDef[0]])]; } } } } // Add default option if specified if (PARAM_INFO[paramType].addDefault) { allOptions.unshift(PARAM_INFO[paramType].addDefault); } return allOptions; } catch (e) { console.error(`Error fetching ${paramType} options:`, e); return []; } } // Create multi-select dialog function showMultiSelectDialog(options, currentValues, paramType, onApply) { // Create overlay const overlay = document.createElement("div"); overlay.style.cssText = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); z-index: 10000; display: flex; align-items: center; justify-content: center; `; // Create dialog const dialog = document.createElement("div"); dialog.style.cssText = ` background: #1e1e1e; border: 2px solid #444; border-radius: 8px; padding: 20px; max-width: 500px; width: 90%; max-height: 70vh; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 10px 30px rgba(0,0,0,0.5); `; // Title const title = document.createElement("h3"); title.textContent = `Select ${paramType.charAt(0).toUpperCase() + paramType.slice(1)}s`; title.style.cssText = "margin: 0 0 15px 0; color: #4ecdc4; font-size: 16px;"; dialog.appendChild(title); // Search box const searchBox = document.createElement("input"); searchBox.type = "text"; searchBox.placeholder = "Search..."; searchBox.style.cssText = ` padding: 8px; margin-bottom: 10px; background: #2a2a2a; border: 1px solid #444; border-radius: 4px; color: #fff; font-size: 14px; `; dialog.appendChild(searchBox); // Options container const optionsContainer = document.createElement("div"); optionsContainer.style.cssText = ` flex: 1; overflow-y: auto; border: 1px solid #444; border-radius: 4px; padding: 10px; background: #2a2a2a; max-height: 400px; `; const checkboxes = []; const labels = []; // Create checkboxes options.forEach(option => { const label = document.createElement("label"); label.style.cssText = ` display: block; padding: 8px; cursor: pointer; color: #ddd; transition: background 0.2s; border-radius: 4px; margin-bottom: 2px; `; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.value = option; checkbox.checked = currentValues.includes(option); checkbox.style.cssText = ` margin-right: 10px; width: 16px; height: 16px; cursor: pointer; `; label.appendChild(checkbox); label.appendChild(document.createTextNode(option)); label.onmouseenter = () => label.style.background = "rgba(78, 205, 196, 0.1)"; label.onmouseleave = () => label.style.background = "transparent"; checkboxes.push(checkbox); labels.push(label); optionsContainer.appendChild(label); }); dialog.appendChild(optionsContainer); // Search functionality searchBox.oninput = () => { const searchTerm = searchBox.value.toLowerCase(); labels.forEach((label, i) => { const text = options[i].toLowerCase(); label.style.display = text.includes(searchTerm) ? "block" : "none"; }); }; // Buttons const buttonContainer = document.createElement("div"); buttonContainer.style.cssText = ` margin-top: 15px; display: flex; gap: 10px; justify-content: flex-end; `; const selectAllBtn = document.createElement("button"); selectAllBtn.textContent = "Select All"; selectAllBtn.style.cssText = ` padding: 8px 15px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer; `; selectAllBtn.onclick = () => { checkboxes.forEach(cb => { if (cb.parentElement.style.display !== "none") { cb.checked = true; } }); }; const clearBtn = document.createElement("button"); clearBtn.textContent = "Clear All"; clearBtn.style.cssText = ` padding: 8px 15px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer; `; clearBtn.onclick = () => { checkboxes.forEach(cb => cb.checked = false); }; const cancelBtn = document.createElement("button"); cancelBtn.textContent = "Cancel"; cancelBtn.style.cssText = ` padding: 8px 15px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer; `; cancelBtn.onclick = () => document.body.removeChild(overlay); const applyBtn = document.createElement("button"); applyBtn.textContent = "Apply"; applyBtn.style.cssText = ` padding: 8px 15px; background: #4a90e2; color: white; border: none; border-radius: 4px; cursor: pointer; `; applyBtn.onclick = () => { const selected = checkboxes .filter(cb => cb.checked) .map(cb => cb.value); onApply(selected); document.body.removeChild(overlay); }; buttonContainer.appendChild(selectAllBtn); buttonContainer.appendChild(clearBtn); buttonContainer.appendChild(cancelBtn); buttonContainer.appendChild(applyBtn); dialog.appendChild(buttonContainer); overlay.appendChild(dialog); document.body.appendChild(overlay); // Focus search box searchBox.focus(); } // Create the enhanced UI function createEnhancedUI(node) { const container = document.createElement("div"); container.className = "xyz-controller-ui"; container.innerHTML = `