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'
|
||||
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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user