From 2fedd572ff70c737c66a85fa375325c4773ccf0d Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Thu, 15 May 2025 10:12:46 +0800 Subject: [PATCH] Add header drag functionality for proportional strength adjustment of LoRAs --- web/comfyui/loras_widget.js | 32 ++++++++- web/comfyui/loras_widget_events.js | 108 +++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/web/comfyui/loras_widget.js b/web/comfyui/loras_widget.js index cb6fa9bf..b2c6c1e4 100644 --- a/web/comfyui/loras_widget.js +++ b/web/comfyui/loras_widget.js @@ -11,7 +11,7 @@ import { CONTAINER_PADDING, EMPTY_CONTAINER_HEIGHT } from "./loras_widget_utils.js"; -import { initDrag, createContextMenu } from "./loras_widget_events.js"; +import { initDrag, createContextMenu, initHeaderDrag } from "./loras_widget_events.js"; export function addLorasWidget(node, name, opts, callback) { // Create container for loras @@ -83,7 +83,8 @@ export function addLorasWidget(node, name, opts, callback) { alignItems: "center", padding: "4px 8px", borderBottom: "1px solid rgba(226, 232, 240, 0.2)", - marginBottom: "5px" + marginBottom: "5px", + position: "relative" // Added for positioning the drag hint }); // Add toggle all control @@ -118,7 +119,7 @@ export function addLorasWidget(node, name, opts, callback) { toggleContainer.appendChild(toggleAll); toggleContainer.appendChild(toggleLabel); - // Strength label + // Strength label with drag hint const strengthLabel = document.createElement("div"); strengthLabel.textContent = "Strength"; Object.assign(strengthLabel.style, { @@ -129,11 +130,36 @@ export function addLorasWidget(node, name, opts, callback) { WebkitUserSelect: "none", MozUserSelect: "none", msUserSelect: "none", + display: "flex", + alignItems: "center" + }); + + // Add drag hint icon next to strength label + const dragHint = document.createElement("span"); + dragHint.innerHTML = "↔"; // Simple left-right arrow as drag indicator + Object.assign(dragHint.style, { + marginLeft: "5px", + fontSize: "11px", + opacity: "0.6", + transition: "opacity 0.2s ease" + }); + strengthLabel.appendChild(dragHint); + + // Add hover effect to improve discoverability + header.addEventListener("mouseenter", () => { + dragHint.style.opacity = "1"; + }); + + header.addEventListener("mouseleave", () => { + dragHint.style.opacity = "0.6"; }); header.appendChild(toggleContainer); header.appendChild(strengthLabel); container.appendChild(header); + + // Initialize the header drag functionality + initHeaderDrag(header, widget, renderLoras); // Track the total visible entries for height calculation let totalVisibleEntries = lorasData.length; diff --git a/web/comfyui/loras_widget_events.js b/web/comfyui/loras_widget_events.js index 21feca87..a876d0cd 100644 --- a/web/comfyui/loras_widget_events.js +++ b/web/comfyui/loras_widget_events.js @@ -46,6 +46,55 @@ export function handleStrengthDrag(name, initialStrength, initialX, event, widge } } +// Function to handle proportional strength adjustment for all LoRAs via header dragging +export function handleAllStrengthsDrag(initialStrengths, initialX, event, widget) { + // Define sensitivity (less sensitive than individual adjustment) + const sensitivity = 0.0005; + + // Get current mouse position + const currentX = event.clientX; + + // Calculate the distance moved + const deltaX = currentX - initialX; + + // Calculate adjustment factor (1.0 means no change, >1.0 means increase, <1.0 means decrease) + // For positive deltaX, we want to increase strengths, for negative we want to decrease + const adjustmentFactor = 1.0 + (deltaX * sensitivity); + + // Ensure adjustment factor is reasonable (prevent extreme changes) + const limitedFactor = Math.max(0.01, Math.min(3.0, adjustmentFactor)); + + // Get current loras data + const lorasData = parseLoraValue(widget.value); + + // Apply the adjustment factor to each LoRA's strengths + lorasData.forEach((loraData, index) => { + // Get initial strengths for this LoRA + const initialModelStrength = initialStrengths[index].modelStrength; + const initialClipStrength = initialStrengths[index].clipStrength; + + // Apply the adjustment factor to both strengths + let newModelStrength = (initialModelStrength * limitedFactor).toFixed(2); + let newClipStrength = (initialClipStrength * limitedFactor).toFixed(2); + + // Limit the values to reasonable bounds (-10 to 10) + newModelStrength = Math.max(-10, Math.min(10, newModelStrength)); + newClipStrength = Math.max(-10, Math.min(10, newClipStrength)); + + // Update strengths + lorasData[index].strength = Number(newModelStrength); + lorasData[index].clipStrength = Number(newClipStrength); + }); + + // Update widget value + widget.value = formatLoraValue(lorasData); + + // Force re-render via callback + if (widget.callback) { + widget.callback(widget.value); + } +} + // Function to initialize drag operation export function initDrag(dragEl, name, widget, isClipStrength = false, previewTooltip, renderFunction) { let isDragging = false; @@ -119,6 +168,65 @@ export function initDrag(dragEl, name, widget, isClipStrength = false, previewTo }); } +// Function to initialize header drag for proportional strength adjustment +export function initHeaderDrag(headerEl, widget, renderFunction) { + let isDragging = false; + let initialX = 0; + let initialStrengths = []; + + // Add cursor style to indicate draggable + headerEl.style.cursor = 'ew-resize'; + + // Create a drag handler + headerEl.addEventListener('mousedown', (e) => { + // Skip if clicking on toggle or other interactive elements + if (e.target.closest('.comfy-lora-toggle') || + e.target.closest('input')) { + return; + } + + // Store initial X position + initialX = e.clientX; + + // Store initial strengths of all LoRAs + const lorasData = parseLoraValue(widget.value); + initialStrengths = lorasData.map(lora => ({ + modelStrength: Number(lora.strength), + clipStrength: Number(lora.clipStrength) + })); + + isDragging = true; + + // Add class to body to enforce cursor style globally + document.body.classList.add('comfy-lora-dragging'); + + // Prevent text selection during drag + e.preventDefault(); + }); + + // Handle mouse move for dragging + document.addEventListener('mousemove', (e) => { + if (!isDragging) return; + + // Call the strength adjustment function + handleAllStrengthsDrag(initialStrengths, initialX, e, widget); + + // Force re-render to show updated strength values + if (renderFunction) { + renderFunction(widget.value, widget); + } + }); + + // Handle mouse up to end dragging + document.addEventListener('mouseup', () => { + if (isDragging) { + isDragging = false; + // Remove the class to restore normal cursor behavior + document.body.classList.remove('comfy-lora-dragging'); + } + }); +} + // Function to create context menu export function createContextMenu(x, y, loraName, widget, previewTooltip, renderFunction) { // Hide preview tooltip first