fix: Improve legacy widget compatibility in vueNodes mode (#7766)

## Summary
- Fix widget callback signature to pass node as 3rd parameter for
extensions like Impact Pack
- Add triggerDraw call to update all legacy widgets when any widget
value changes
- Support computeLayoutSize for dynamic widget height calculation
- Set node.canvasHeight for extensions that rely on this property
- Use step/10 for number input buttons to match litegraph behavior

Fixes display and interaction issues with Impact Pack's Mask Rect Area
nodes.

fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7615 and
https://github.com/Comfy-Org/ComfyUI_frontend/issues/7616

it also requires Impact pack PR
https://github.com/ltdrdata/ComfyUI-Impact-Pack/pull/1167

## Screenshots
Before


https://github.com/user-attachments/assets/eb890f7c-c1a0-4c7b-a8d7-dde304de83e4


After


https://github.com/user-attachments/assets/dad65b52-d71e-4c19-92c0-367b7dcafed0

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7766-fix-Improve-legacy-widget-compatibility-in-vueNodes-mode-2d56d73d365081b18d83f4a41ff0f377)
by [Unito](https://www.unito.io)
This commit is contained in:
Terry Jia
2026-01-01 21:35:08 -05:00
committed by GitHub
parent 5e932bb1e8
commit 4c955f6725
5 changed files with 55 additions and 13 deletions

View File

@@ -34,6 +34,7 @@ import type {
} from '@/lib/litegraph/src/litegraph'
import type { TitleMode } from '@/lib/litegraph/src/types/globalEnums'
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
import { app } from '@/scripts/app'
export interface WidgetSlotMetadata {
index: number
@@ -47,6 +48,7 @@ export interface SafeWidgetData {
borderStyle?: string
callback?: ((value: unknown) => void) | undefined
controlWidget?: SafeControlWidget
hasLayoutSize?: boolean
isDOMWidget?: boolean
label?: string
nodeType?: string
@@ -171,7 +173,12 @@ export function safeWidgetMapper(
const callback = (v: unknown) => {
const value = normalizeWidgetValue(v)
widget.value = value ?? undefined
widget.callback?.(value)
// Match litegraph callback signature: (value, canvas, node, pos, event)
// Some extensions (e.g., Impact Pack) expect node as the 3rd parameter
widget.callback?.(value, app.canvas, node)
// Trigger redraw for all legacy widgets on this node (e.g., mask preview)
// This ensures widgets that depend on other widget values get updated
node.widgets?.forEach((w) => w.triggerDraw?.())
}
return {
@@ -181,6 +188,7 @@ export function safeWidgetMapper(
borderStyle,
callback,
controlWidget: getControlWidget(widget),
hasLayoutSize: typeof widget.computeLayoutSize === 'function',
isDOMWidget: isDOMWidget(widget),
label: widget.label,
nodeType: getNodeType(node, widget),

View File

@@ -203,10 +203,13 @@ const processedWidgets = computed((): ProcessedWidget[] => {
})
const gridTemplateRows = computed((): string => {
const widgets = toValue(processedWidgets)
return widgets
.filter((w) => !w.simplified.options?.hidden)
.map((w) => (shouldExpand(w.type) ? 'auto' : 'min-content'))
if (!nodeData?.widgets) return ''
const processedNames = new Set(toValue(processedWidgets).map((w) => w.name))
return nodeData.widgets
.filter((w) => processedNames.has(w.name) && !w.options?.hidden)
.map((w) =>
shouldExpand(w.type) || w.hasLayoutSize ? 'auto' : 'min-content'
)
.join(' ')
})
</script>

View File

@@ -31,10 +31,17 @@ const precision = computed(() => {
// Calculate the step value based on precision or widget options
const stepValue = computed(() => {
// Use step2 (correct input spec value) instead of step (legacy 10x value)
// Use step2 (correct input spec value) if available
if (props.widget.options?.step2 !== undefined) {
return Number(props.widget.options.step2)
}
// Use step / 10 for custom large step values (> 10) to match litegraph behavior
// This is important for extensions like Impact Pack that use custom step values (e.g., 640)
// We skip default step values (1, 10) to avoid affecting normal widgets
const step = props.widget.options?.step
if (step !== undefined && step > 10) {
return Number(step) / 10
}
// Otherwise, derive from precision
if (precision.value !== undefined) {
if (precision.value === 0) {

View File

@@ -17,6 +17,7 @@ const props = defineProps<{
}>()
const canvasEl = ref()
const containerHeight = ref(20)
const canvas: LGraphCanvas = useCanvasStore().canvas as LGraphCanvas
let node: LGraphNode | undefined
@@ -52,9 +53,19 @@ onBeforeUnmount(() => {
function draw() {
if (!widgetInstance || !node) return
const width = canvasEl.value.parentElement.clientWidth
const height = widgetInstance.computeSize
? widgetInstance.computeSize(width)[1]
: 20
// Priority: computedHeight (from litegraph) > computeLayoutSize > computeSize
let height = 20
if (widgetInstance.computedHeight) {
height = widgetInstance.computedHeight
} else if (widgetInstance.computeLayoutSize) {
height = widgetInstance.computeLayoutSize(node).minHeight
} else if (widgetInstance.computeSize) {
height = widgetInstance.computeSize(width)[1]
}
containerHeight.value = height
// Set node.canvasHeight for legacy widgets that use it (e.g., Impact Pack)
// @ts-expect-error canvasHeight is a custom property used by some extensions
node.canvasHeight = height
widgetInstance.y = 0
canvasEl.value.height = (height + 2) * scaleFactor
canvasEl.value.width = width * scaleFactor
@@ -87,7 +98,10 @@ function handleMove(e: PointerEvent) {
}
</script>
<template>
<div class="relative mx-[-12px] min-w-0 basis-0">
<div
class="relative mx-[-12px] min-w-0 basis-0"
:style="{ minHeight: `${containerHeight}px` }"
>
<canvas
ref="canvasEl"
class="absolute mt-[-13px] w-full cursor-crosshair"

View File

@@ -2,6 +2,7 @@ import { computed, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
interface NumberWidgetOptions {
step?: number
step2?: number
precision?: number
}
@@ -17,10 +18,17 @@ export function useNumberStepCalculation(
) {
return computed(() => {
const precision = toValue(precisionArg)
// Use step2 (correct input spec value) instead of step (legacy 10x value)
// Use step2 (correct input spec value) if available
if (options?.step2 !== undefined) {
return Number(options.step2)
}
// Use step / 10 for custom large step values (> 10) to match litegraph behavior
// This is important for extensions like Impact Pack that use custom step values (e.g., 640)
// We skip default step values (1, 10) to avoid affecting normal widgets
const step = options?.step
if (step !== undefined && step > 10) {
return Number(step) / 10
}
if (precision === undefined) {
return returnUndefinedForDefault ? undefined : 0
@@ -29,7 +37,9 @@ export function useNumberStepCalculation(
if (precision === 0) return 1
// For precision > 0, step = 1 / (10^precision)
const step = 1 / Math.pow(10, precision)
return returnUndefinedForDefault ? step : Number(step.toFixed(precision))
const calculatedStep = 1 / Math.pow(10, precision)
return returnUndefinedForDefault
? calculatedStep
: Number(calculatedStep.toFixed(precision))
})
}