mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-10 04:49:24 -03:00
fix(ui): cap lora widget height and enable wheel scroll in Node 2.0 mode (#959)
- Add 'Node 2.0: Maximum visible LoRA entries' setting (default 12) - Apply max-height to loras container in Vue mode to prevent unbounded growth - Add enableListWheelScroll: window capture-phase wheel hook so scroll inside the widget scrolls the list instead of zooming the canvas
This commit is contained in:
@@ -11,10 +11,10 @@ import {
|
|||||||
EMPTY_CONTAINER_HEIGHT
|
EMPTY_CONTAINER_HEIGHT
|
||||||
} from "./loras_widget_utils.js";
|
} from "./loras_widget_utils.js";
|
||||||
import { initDrag, createContextMenu, initHeaderDrag, initReorderDrag, handleKeyboardNavigation } from "./loras_widget_events.js";
|
import { initDrag, createContextMenu, initHeaderDrag, initReorderDrag, handleKeyboardNavigation } from "./loras_widget_events.js";
|
||||||
import { forwardMiddleMouseToCanvas, forwardWheelToCanvas } from "./utils.js";
|
import { forwardMiddleMouseToCanvas, forwardWheelToCanvas, enableListWheelScroll } from "./utils.js";
|
||||||
import { PreviewTooltip } from "./preview_tooltip.js";
|
import { PreviewTooltip } from "./preview_tooltip.js";
|
||||||
import { ensureLmStyles } from "./lm_styles_loader.js";
|
import { ensureLmStyles } from "./lm_styles_loader.js";
|
||||||
import { getStrengthStepPreference } from "./settings.js";
|
import { getStrengthStepPreference, getLoraWidgetMaxVisibleLoras } from "./settings.js";
|
||||||
|
|
||||||
export function addLorasWidget(node, name, opts, callback) {
|
export function addLorasWidget(node, name, opts, callback) {
|
||||||
ensureLmStyles();
|
ensureLmStyles();
|
||||||
@@ -29,6 +29,20 @@ export function addLorasWidget(node, name, opts, callback) {
|
|||||||
// Set initial height using CSS variables approach
|
// Set initial height using CSS variables approach
|
||||||
const defaultHeight = 200;
|
const defaultHeight = 200;
|
||||||
|
|
||||||
|
// In Vue/node-2.0 mode, cap the widget height so it shows at most N entries.
|
||||||
|
// This prevents content from driving the node size beyond the cap.
|
||||||
|
// canvas/legacy mode is unaffected.
|
||||||
|
if (typeof LiteGraph !== 'undefined' && LiteGraph.vueNodesMode) {
|
||||||
|
const maxLoras = getLoraWidgetMaxVisibleLoras();
|
||||||
|
const gap = 5; // flex gap from .lm-loras-container CSS
|
||||||
|
const maxH = CONTAINER_PADDING + HEADER_HEIGHT + maxLoras * LORA_ENTRY_HEIGHT + maxLoras * gap;
|
||||||
|
container.style.maxHeight = `${maxH}px`;
|
||||||
|
container.style.setProperty('--comfy-widget-max-height', `${maxH}px`);
|
||||||
|
// Window capture-phase hook: scroll the widget instead of zooming the canvas
|
||||||
|
// when the wheel is over a scrollable loras list.
|
||||||
|
enableListWheelScroll(container);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is a randomizer node (lock button instead of drag handle)
|
// Check if this is a randomizer node (lock button instead of drag handle)
|
||||||
const isRandomizerNode = opts?.isRandomizerNode === true;
|
const isRandomizerNode = opts?.isRandomizerNode === true;
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ const NEW_TAB_ZOOM_LEVEL = 0.8;
|
|||||||
const STRENGTH_STEP_SETTING_ID = "loramanager.strength_step";
|
const STRENGTH_STEP_SETTING_ID = "loramanager.strength_step";
|
||||||
const STRENGTH_STEP_DEFAULT = 0.05;
|
const STRENGTH_STEP_DEFAULT = 0.05;
|
||||||
|
|
||||||
|
const LORA_WIDGET_MAX_VISIBLE_SETTING_ID = "loramanager.lora_widget_max_visible_loras";
|
||||||
|
const LORA_WIDGET_MAX_VISIBLE_DEFAULT = 12;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Helper Functions
|
// Helper Functions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -360,6 +363,32 @@ const getStrengthStepPreference = (() => {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
const getLoraWidgetMaxVisibleLoras = (() => {
|
||||||
|
let settingsUnavailableLogged = false;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const settingManager = app?.extensionManager?.setting;
|
||||||
|
if (!settingManager || typeof settingManager.get !== "function") {
|
||||||
|
if (!settingsUnavailableLogged) {
|
||||||
|
console.warn("LoRA Manager: settings API unavailable, using default max visible loras.");
|
||||||
|
settingsUnavailableLogged = true;
|
||||||
|
}
|
||||||
|
return LORA_WIDGET_MAX_VISIBLE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const value = settingManager.get(LORA_WIDGET_MAX_VISIBLE_SETTING_ID);
|
||||||
|
return value ?? LORA_WIDGET_MAX_VISIBLE_DEFAULT;
|
||||||
|
} catch (error) {
|
||||||
|
if (!settingsUnavailableLogged) {
|
||||||
|
console.warn("LoRA Manager: unable to read max visible loras setting, using default.", error);
|
||||||
|
settingsUnavailableLogged = true;
|
||||||
|
}
|
||||||
|
return LORA_WIDGET_MAX_VISIBLE_DEFAULT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Register Extension with All Settings
|
// Register Extension with All Settings
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -463,6 +492,19 @@ app.registerExtension({
|
|||||||
tooltip: "Step size for adjusting LoRA strength via arrow buttons or keyboard (default: 0.05)",
|
tooltip: "Step size for adjusting LoRA strength via arrow buttons or keyboard (default: 0.05)",
|
||||||
category: ["LoRA Manager", "LoRA Widget", "Strength Step"],
|
category: ["LoRA Manager", "LoRA Widget", "Strength Step"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: LORA_WIDGET_MAX_VISIBLE_SETTING_ID,
|
||||||
|
name: "Node 2.0: Maximum visible LoRA entries",
|
||||||
|
type: "slider",
|
||||||
|
attrs: {
|
||||||
|
min: 3,
|
||||||
|
max: 50,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
defaultValue: LORA_WIDGET_MAX_VISIBLE_DEFAULT,
|
||||||
|
tooltip: "When using Node 2.0 rendering, limit the loras widget height to show at most this many entries (default: 12). Excess entries are accessible via scrollbar.",
|
||||||
|
category: ["LoRA Manager", "LoRA Widget", "Max Visible"],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
async setup() {
|
async setup() {
|
||||||
await loadWorkflowOptions();
|
await loadWorkflowOptions();
|
||||||
@@ -549,4 +591,5 @@ export {
|
|||||||
getUsageStatisticsPreference,
|
getUsageStatisticsPreference,
|
||||||
getNewTabTemplatePreference,
|
getNewTabTemplatePreference,
|
||||||
getStrengthStepPreference,
|
getStrengthStepPreference,
|
||||||
|
getLoraWidgetMaxVisibleLoras,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -784,6 +784,51 @@ export function forwardWheelToCanvas(container, options = {}) {
|
|||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marks scrollable containers whose wheel scrolling must win over canvas zoom.
|
||||||
|
const LM_WHEEL_CLASS = 'lm-wheel-scrollable';
|
||||||
|
let lmWheelHookInstalled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep vertical wheel scrolling inside a scrollable widget container, even in
|
||||||
|
* Nodes 2.0 / Vue mode where ComfyUI's wheel→zoom handler runs on the document
|
||||||
|
* in the capture phase (outer than any container-level listener).
|
||||||
|
* Installs a single capture-phase hook on `window` (the outermost dispatch
|
||||||
|
* point). When the wheel is over a marked, scrollable element, we manually
|
||||||
|
* scroll it and fully consume the event so canvas zoom never sees it.
|
||||||
|
*/
|
||||||
|
export function enableListWheelScroll(container) {
|
||||||
|
if (!container) return;
|
||||||
|
container.classList.add(LM_WHEEL_CLASS);
|
||||||
|
|
||||||
|
if (lmWheelHookInstalled) return;
|
||||||
|
lmWheelHookInstalled = true;
|
||||||
|
|
||||||
|
window.addEventListener('wheel', (event) => {
|
||||||
|
// Let pinch/zoom and horizontal gestures pass through.
|
||||||
|
if (event.ctrlKey) return;
|
||||||
|
if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) return;
|
||||||
|
|
||||||
|
const target = event.target;
|
||||||
|
if (!target || typeof target.closest !== 'function') return;
|
||||||
|
const el = target.closest(`.${LM_WHEEL_CLASS}`);
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const canScrollY = el.scrollHeight > el.clientHeight + 1;
|
||||||
|
if (!canScrollY) return;
|
||||||
|
|
||||||
|
// Translate deltaMode to approximate pixels.
|
||||||
|
const unit = event.deltaMode === 1 ? 16
|
||||||
|
: event.deltaMode === 2 ? el.clientHeight
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
el.scrollTop += event.deltaY * unit;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}, { capture: true, passive: false });
|
||||||
|
}
|
||||||
|
|
||||||
// Get connected Lora Pool node from pool_config input
|
// Get connected Lora Pool node from pool_config input
|
||||||
export function getConnectedPoolConfigNode(node) {
|
export function getConnectedPoolConfigNode(node) {
|
||||||
if (!node?.inputs) {
|
if (!node?.inputs) {
|
||||||
|
|||||||
Reference in New Issue
Block a user