From 9c95856b2fa882e8ecb9ee6f03ca298ee6894e14 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Thu, 25 Jun 2026 14:58:20 +0800 Subject: [PATCH] fix(trigger-wheel): prevent Vue render mode from intercepting strength wheel events In Vue render mode, ComfyUI's TransformPane uses a capture-phase wheel handler (@wheel.capture) that fires before the tag element's bubble-phase strength-adjustment listener. It checks wheelCapturedByFocusedElement(), which requires data-capture-wheel on a focused element. The tag divs had data-capture-wheel but were not focusable, so the check failed, causing the capture handler to forward the event to the canvas (triggering zoom) and stopPropagation() which prevented the strength handler from running. Fix: move data-capture-wheel from individual tags to the container, make it focusable (tabIndex=-1), and add a window-level capture-phase wheel listener that focuses the container before TransformPane checks it. --- web/comfyui/tags_widget.js | 55 +++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/web/comfyui/tags_widget.js b/web/comfyui/tags_widget.js index f685eda8..4b170837 100644 --- a/web/comfyui/tags_widget.js +++ b/web/comfyui/tags_widget.js @@ -260,7 +260,6 @@ function createTagElement({ }) { const tagEl = document.createElement("div"); tagEl.className = "comfy-tag"; - tagEl.dataset.captureWheel = "true"; const baseStyles = { padding: `${roundScaled(group ? 5 : 3, styleScale)}px ${roundScaled(group ? 8 : 10, styleScale)}px`, @@ -619,6 +618,36 @@ function showTagContextMenu(event, tagData, index, widget, anchorEl) { setTimeout(() => document.addEventListener('click', closeMenu), 0); } +// Singleton window capture-phase wheel hook: focuses the tags container when a +// wheel event occurs inside it, so that ComfyUI's wheelCapturedByFocusedElement +// recognises this zone and does NOT forward the event to canvas (which would +// trigger zoom and stopPropagation, preventing the strength-adjustment handler). +/** @type {boolean} */ +let tagWheelCaptureHookInstalled = false; +function installTagWheelCaptureHook() { + if (tagWheelCaptureHookInstalled) return; + tagWheelCaptureHookInstalled = true; + + window.addEventListener( + "wheel", + (event) => { + // Only handle vertical mouse wheel (not pinch-zoom or horizontal swipe) + if (event.ctrlKey || event.metaKey) return; + if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) return; + + const target = /** @type {Element} */ (event.target); + if (!target?.closest) return; + const targetContainer = target.closest( + '.comfy-tags-container[data-capture-wheel="true"]' + ); + if (!targetContainer) return; + + targetContainer.focus({ preventScroll: true }); + }, + { capture: true, passive: true } + ); +} + export function addTagsWidget(node, name, opts, callback, wheelSensitivity = 0.02, options = {}) { const container = document.createElement("div"); container.className = "comfy-tags-container"; @@ -628,6 +657,29 @@ export function addTagsWidget(node, name, opts, callback, wheelSensitivity = 0.0 forwardMiddleMouseToCanvas(container); forwardWheelToCanvas(container); + // Vue render mode: ComfyUI's TransformPane uses a capture-phase wheel handler + // (TransformPane @wheel.capture) that checks wheelCapturedByFocusedElement. + // For that check to return true (preventing canvas zoom and allowing our + // strength-adjustment wheel handler to fire), the container needs both + // data-capture-wheel AND document.activeElement inside it. + // We make the container focusable and auto-focus it on wheel events via a + // window capture-phase hook. + container.dataset.captureWheel = "true"; + container.tabIndex = -1; + + // Blur on mouseleave to avoid lingering focus side effects. + container.addEventListener("mouseleave", () => { + if (document.activeElement === container) { + container.blur(); + } + }); + + // Singleton window capture-phase wheel handler: focuses our container when + // a wheel event occurs inside it, so that wheelCapturedByFocusedElement + // recognises this zone and does NOT forward the event to canvas (which would + // trigger zoom and stopPropagation, preventing our strength handler). + installTagWheelCaptureHook(); + Object.assign(container.style, { display: "flex", flexWrap: "wrap", @@ -641,6 +693,7 @@ export function addTagsWidget(node, name, opts, callback, wheelSensitivity = 0.0 overflow: "auto", alignItems: "flex-start", alignContent: "flex-start", + outline: "none", }); const initialTagsData = opts?.defaultVal || [];