mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
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:
@@ -34,6 +34,7 @@ import type {
|
|||||||
} from '@/lib/litegraph/src/litegraph'
|
} from '@/lib/litegraph/src/litegraph'
|
||||||
import type { TitleMode } from '@/lib/litegraph/src/types/globalEnums'
|
import type { TitleMode } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
|
||||||
|
import { app } from '@/scripts/app'
|
||||||
|
|
||||||
export interface WidgetSlotMetadata {
|
export interface WidgetSlotMetadata {
|
||||||
index: number
|
index: number
|
||||||
@@ -47,6 +48,7 @@ export interface SafeWidgetData {
|
|||||||
borderStyle?: string
|
borderStyle?: string
|
||||||
callback?: ((value: unknown) => void) | undefined
|
callback?: ((value: unknown) => void) | undefined
|
||||||
controlWidget?: SafeControlWidget
|
controlWidget?: SafeControlWidget
|
||||||
|
hasLayoutSize?: boolean
|
||||||
isDOMWidget?: boolean
|
isDOMWidget?: boolean
|
||||||
label?: string
|
label?: string
|
||||||
nodeType?: string
|
nodeType?: string
|
||||||
@@ -171,7 +173,12 @@ export function safeWidgetMapper(
|
|||||||
const callback = (v: unknown) => {
|
const callback = (v: unknown) => {
|
||||||
const value = normalizeWidgetValue(v)
|
const value = normalizeWidgetValue(v)
|
||||||
widget.value = value ?? undefined
|
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 {
|
return {
|
||||||
@@ -181,6 +188,7 @@ export function safeWidgetMapper(
|
|||||||
borderStyle,
|
borderStyle,
|
||||||
callback,
|
callback,
|
||||||
controlWidget: getControlWidget(widget),
|
controlWidget: getControlWidget(widget),
|
||||||
|
hasLayoutSize: typeof widget.computeLayoutSize === 'function',
|
||||||
isDOMWidget: isDOMWidget(widget),
|
isDOMWidget: isDOMWidget(widget),
|
||||||
label: widget.label,
|
label: widget.label,
|
||||||
nodeType: getNodeType(node, widget),
|
nodeType: getNodeType(node, widget),
|
||||||
|
|||||||
@@ -203,10 +203,13 @@ const processedWidgets = computed((): ProcessedWidget[] => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const gridTemplateRows = computed((): string => {
|
const gridTemplateRows = computed((): string => {
|
||||||
const widgets = toValue(processedWidgets)
|
if (!nodeData?.widgets) return ''
|
||||||
return widgets
|
const processedNames = new Set(toValue(processedWidgets).map((w) => w.name))
|
||||||
.filter((w) => !w.simplified.options?.hidden)
|
return nodeData.widgets
|
||||||
.map((w) => (shouldExpand(w.type) ? 'auto' : 'min-content'))
|
.filter((w) => processedNames.has(w.name) && !w.options?.hidden)
|
||||||
|
.map((w) =>
|
||||||
|
shouldExpand(w.type) || w.hasLayoutSize ? 'auto' : 'min-content'
|
||||||
|
)
|
||||||
.join(' ')
|
.join(' ')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -31,10 +31,17 @@ const precision = computed(() => {
|
|||||||
|
|
||||||
// Calculate the step value based on precision or widget options
|
// Calculate the step value based on precision or widget options
|
||||||
const stepValue = computed(() => {
|
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) {
|
if (props.widget.options?.step2 !== undefined) {
|
||||||
return Number(props.widget.options.step2)
|
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
|
// Otherwise, derive from precision
|
||||||
if (precision.value !== undefined) {
|
if (precision.value !== undefined) {
|
||||||
if (precision.value === 0) {
|
if (precision.value === 0) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const props = defineProps<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const canvasEl = ref()
|
const canvasEl = ref()
|
||||||
|
const containerHeight = ref(20)
|
||||||
|
|
||||||
const canvas: LGraphCanvas = useCanvasStore().canvas as LGraphCanvas
|
const canvas: LGraphCanvas = useCanvasStore().canvas as LGraphCanvas
|
||||||
let node: LGraphNode | undefined
|
let node: LGraphNode | undefined
|
||||||
@@ -52,9 +53,19 @@ onBeforeUnmount(() => {
|
|||||||
function draw() {
|
function draw() {
|
||||||
if (!widgetInstance || !node) return
|
if (!widgetInstance || !node) return
|
||||||
const width = canvasEl.value.parentElement.clientWidth
|
const width = canvasEl.value.parentElement.clientWidth
|
||||||
const height = widgetInstance.computeSize
|
// Priority: computedHeight (from litegraph) > computeLayoutSize > computeSize
|
||||||
? widgetInstance.computeSize(width)[1]
|
let height = 20
|
||||||
: 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
|
widgetInstance.y = 0
|
||||||
canvasEl.value.height = (height + 2) * scaleFactor
|
canvasEl.value.height = (height + 2) * scaleFactor
|
||||||
canvasEl.value.width = width * scaleFactor
|
canvasEl.value.width = width * scaleFactor
|
||||||
@@ -87,7 +98,10 @@ function handleMove(e: PointerEvent) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<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
|
<canvas
|
||||||
ref="canvasEl"
|
ref="canvasEl"
|
||||||
class="absolute mt-[-13px] w-full cursor-crosshair"
|
class="absolute mt-[-13px] w-full cursor-crosshair"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { computed, toValue } from 'vue'
|
|||||||
import type { MaybeRefOrGetter } from 'vue'
|
import type { MaybeRefOrGetter } from 'vue'
|
||||||
|
|
||||||
interface NumberWidgetOptions {
|
interface NumberWidgetOptions {
|
||||||
|
step?: number
|
||||||
step2?: number
|
step2?: number
|
||||||
precision?: number
|
precision?: number
|
||||||
}
|
}
|
||||||
@@ -17,10 +18,17 @@ export function useNumberStepCalculation(
|
|||||||
) {
|
) {
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
const precision = toValue(precisionArg)
|
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) {
|
if (options?.step2 !== undefined) {
|
||||||
return Number(options.step2)
|
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) {
|
if (precision === undefined) {
|
||||||
return returnUndefinedForDefault ? undefined : 0
|
return returnUndefinedForDefault ? undefined : 0
|
||||||
@@ -29,7 +37,9 @@ export function useNumberStepCalculation(
|
|||||||
if (precision === 0) return 1
|
if (precision === 0) return 1
|
||||||
|
|
||||||
// For precision > 0, step = 1 / (10^precision)
|
// For precision > 0, step = 1 / (10^precision)
|
||||||
const step = 1 / Math.pow(10, precision)
|
const calculatedStep = 1 / Math.pow(10, precision)
|
||||||
return returnUndefinedForDefault ? step : Number(step.toFixed(precision))
|
return returnUndefinedForDefault
|
||||||
|
? calculatedStep
|
||||||
|
: Number(calculatedStep.toFixed(precision))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user