Inline numeric widget configurations (#2792)

This commit is contained in:
Chenlei Hu
2025-03-01 18:09:23 -05:00
committed by GitHub
parent e58fab92d1
commit 503341b966
4 changed files with 49 additions and 74 deletions

View File

@@ -2,10 +2,9 @@ import type { LGraphNode } from '@comfyorg/litegraph'
import type { INumericWidget } from '@comfyorg/litegraph/dist/types/widgets' import type { INumericWidget } from '@comfyorg/litegraph/dist/types/widgets'
import _ from 'lodash' import _ from 'lodash'
import type { FloatInputOptions, InputSpec } from '@/schemas/nodeDefSchema' import { type InputSpec, isFloatInputSpec } from '@/schemas/nodeDefSchema'
import type { ComfyWidgetConstructor } from '@/scripts/widgets' import type { ComfyWidgetConstructor } from '@/scripts/widgets'
import { useSettingStore } from '@/stores/settingStore' import { useSettingStore } from '@/stores/settingStore'
import { getNumberDefaults } from '@/utils/mathUtil'
function onFloatValueChange(this: INumericWidget, v: number) { function onFloatValueChange(this: INumericWidget, v: number) {
this.value = this.options.round this.value = this.options.round
@@ -28,40 +27,48 @@ export const useFloatWidget = () => {
inputName: string, inputName: string,
inputData: InputSpec inputData: InputSpec
) => { ) => {
if (!isFloatInputSpec(inputData)) {
throw new Error(`Invalid input data: ${inputData}`)
}
// TODO: Move to outer scope to avoid re-initializing on every call // TODO: Move to outer scope to avoid re-initializing on every call
// Blocked on ComfyWidgets lazy initialization. // Blocked on ComfyWidgets lazy initialization.
const settingStore = useSettingStore() const settingStore = useSettingStore()
const sliderEnabled = !settingStore.get('Comfy.DisableSliders') const sliderEnabled = !settingStore.get('Comfy.DisableSliders')
const inputOptions = inputData[1] const inputOptions = inputData[1] ?? {}
const widgetType = sliderEnabled const widgetType = sliderEnabled
? inputOptions?.display === 'slider' ? inputOptions.display === 'slider'
? 'slider' ? 'slider'
: 'number' : 'number'
: 'number' : 'number'
const step = inputOptions.step ?? 0.5
const precision = const precision =
settingStore.get('Comfy.FloatRoundingPrecision') || undefined settingStore.get('Comfy.FloatRoundingPrecision') ||
Math.max(0, -Math.floor(Math.log10(step)))
const enableRounding = !settingStore.get('Comfy.DisableFloatRounding') const enableRounding = !settingStore.get('Comfy.DisableFloatRounding')
const { val, config } = getNumberDefaults( const defaultValue = inputOptions.default ?? 0
inputOptions as FloatInputOptions,
{
defaultStep: 0.5,
precision,
enableRounding
}
)
return { return {
widget: node.addWidget( widget: node.addWidget(
widgetType, widgetType,
inputName, inputName,
val, defaultValue,
onFloatValueChange, onFloatValueChange,
// @ts-expect-error InputSpec is not typed correctly {
config min: inputOptions.min ?? 0,
max: inputOptions.max ?? 2048,
round:
enableRounding && precision && !inputOptions.round
? (1_000_000 * Math.pow(0.1, precision)) / 1_000_000
: (inputOptions.round as number),
/** @deprecated Use step2 instead. The 10x value is a legacy implementation. */
step: step * 10.0,
step2: step,
precision
}
) )
} }
} }

View File

@@ -1,14 +1,13 @@
import type { LGraphNode } from '@comfyorg/litegraph' import type { LGraphNode } from '@comfyorg/litegraph'
import type { INumericWidget } from '@comfyorg/litegraph/dist/types/widgets' import type { INumericWidget } from '@comfyorg/litegraph/dist/types/widgets'
import type { InputSpec, IntInputOptions } from '@/schemas/nodeDefSchema' import { type InputSpec, isIntInputSpec } from '@/schemas/nodeDefSchema'
import type { ComfyApp } from '@/scripts/app' import type { ComfyApp } from '@/scripts/app'
import { import {
type ComfyWidgetConstructor, type ComfyWidgetConstructor,
addValueControlWidget addValueControlWidget
} from '@/scripts/widgets' } from '@/scripts/widgets'
import { useSettingStore } from '@/stores/settingStore' import { useSettingStore } from '@/stores/settingStore'
import { getNumberDefaults } from '@/utils/mathUtil'
function onValueChange(this: INumericWidget, v: number) { function onValueChange(this: INumericWidget, v: number) {
// For integers, always round to the nearest step // For integers, always round to the nearest step
@@ -41,28 +40,39 @@ export const useIntWidget = () => {
app: ComfyApp, app: ComfyApp,
widgetName?: string widgetName?: string
) => { ) => {
if (!isIntInputSpec(inputData)) {
throw new Error(`Invalid input data: ${inputData}`)
}
const settingStore = useSettingStore() const settingStore = useSettingStore()
const sliderEnabled = !settingStore.get('Comfy.DisableSliders') const sliderEnabled = !settingStore.get('Comfy.DisableSliders')
const inputOptions = inputData[1] const inputOptions = inputData[1] ?? {}
const widgetType = sliderEnabled const widgetType = sliderEnabled
? inputOptions?.display === 'slider' ? inputOptions?.display === 'slider'
? 'slider' ? 'slider'
: 'number' : 'number'
: 'number' : 'number'
const { val, config } = getNumberDefaults(inputOptions as IntInputOptions, { const step = inputOptions.step ?? 1
defaultStep: 1, const defaultValue = inputOptions.default ?? 0
precision: 0,
enableRounding: true
})
config.precision = 0
const result = { const result = {
// @ts-expect-error InputSpec is not typed correctly widget: node.addWidget(
widget: node.addWidget(widgetType, inputName, val, onValueChange, config) widgetType,
inputName,
defaultValue,
onValueChange,
{
min: inputOptions.min ?? 0,
max: inputOptions.max ?? 2048,
/** @deprecated Use step2 instead. The 10x value is a legacy implementation. */
step: step * 10,
step2: step,
precision: 0
}
)
} }
if (inputData[1]?.control_after_generate) { if (inputOptions.control_after_generate) {
const seedControl = addValueControlWidget( const seedControl = addValueControlWidget(
node, node,
result.widget, result.widget,

View File

@@ -31,7 +31,8 @@ const zNumericInputOptions = zBaseInputOptions.extend({
max: z.number().optional(), max: z.number().optional(),
step: z.number().optional(), step: z.number().optional(),
// Note: Many node authors are using INT/FLOAT to pass list of INT/FLOAT. // Note: Many node authors are using INT/FLOAT to pass list of INT/FLOAT.
default: z.union([z.number(), z.array(z.number())]).optional() default: z.union([z.number(), z.array(z.number())]).optional(),
display: z.enum(['slider', 'number']).optional()
}) })
const zIntInputOptions = zNumericInputOptions.extend({ const zIntInputOptions = zNumericInputOptions.extend({

View File

@@ -1,43 +0,0 @@
import type {
FloatInputOptions,
IntInputOptions
} from '@/schemas/nodeDefSchema'
export function getNumberDefaults(
inputOptions: IntInputOptions | FloatInputOptions,
options: {
defaultStep: number
precision?: number
enableRounding: boolean
}
) {
const { defaultStep } = options
const {
default: defaultVal = 0,
min = 0,
max = 2048,
step = defaultStep
} = inputOptions
// precision is the number of decimal places to show.
// by default, display the the smallest number of decimal places such that changes of size step are visible.
const { precision = Math.max(-Math.floor(Math.log10(step)), 0) } = options
let round = inputOptions.round
if (options.enableRounding && (round == undefined || round === true)) {
// by default, round the value to those decimal places shown.
round = Math.round(1000000 * Math.pow(0.1, precision)) / 1000000
}
return {
val: defaultVal,
config: {
min,
max,
/** @deprecated Use step2 instead. The 10x value is a legacy implementation. */
step: step * 10.0,
step2: step,
round,
precision
}
}
}