From 3d41d555ff63ae481f3f9a2ec8f848587c257a6a Mon Sep 17 00:00:00 2001 From: AustinMroz Date: Mon, 26 Jan 2026 10:24:53 -0800 Subject: [PATCH] Frontend code for custom number nodes (#7768) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows creation of Int and Float widgets with configurable, min, max, step, and precision. This PR has been fairly heavily reworked. Options are no longer exposed as widgets, but set as properties on the node. Since the changes no longer modify the sizing or serialization of the node, backend changes are no longer required and the extended functionality has been added directly onto the existing PrimitiveFloat and PrimitiveInt nodes. There's intent to expose these configuration parameters on the new properties panel, but this PR can be merged as is. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7768-Frontend-code-for-custom-number-nodes-2d66d73d365081879541cbda7db3c24f) by [Unito](https://www.unito.io) --- .../core/{customCombo.ts => customWidgets.ts} | 100 ++++++++++++++++-- src/extensions/core/index.ts | 2 +- 2 files changed, 93 insertions(+), 9 deletions(-) rename src/extensions/core/{customCombo.ts => customWidgets.ts} (53%) diff --git a/src/extensions/core/customCombo.ts b/src/extensions/core/customWidgets.ts similarity index 53% rename from src/extensions/core/customCombo.ts rename to src/extensions/core/customWidgets.ts index 58d41eb62..df796c03e 100644 --- a/src/extensions/core/customCombo.ts +++ b/src/extensions/core/customWidgets.ts @@ -46,8 +46,8 @@ function applyToGraph(this: LGraphNode, extraLinks: LLink[] = []) { } } -function onNodeCreated(this: LGraphNode) { - this.applyToGraph = useChainCallback(this.applyToGraph, applyToGraph) +function onCustomComboCreated(this: LGraphNode) { + this.applyToGraph = applyToGraph const comboWidget = this.widgets![0] const values = shallowReactive([]) @@ -114,13 +114,97 @@ function onNodeCreated(this: LGraphNode) { addOption(this) } +function onCustomIntCreated(this: LGraphNode) { + const valueWidget = this.widgets?.[0] + if (!valueWidget) return + + Object.defineProperty(valueWidget.options, 'min', { + get: () => this.properties.min ?? -(2 ** 63), + set: (v) => { + this.properties.min = v + valueWidget.callback?.(valueWidget.value) + } + }) + Object.defineProperty(valueWidget.options, 'max', { + get: () => this.properties.max ?? 2 ** 63, + set: (v) => { + this.properties.max = v + valueWidget.callback?.(valueWidget.value) + } + }) + Object.defineProperty(valueWidget.options, 'step2', { + get: () => this.properties.step ?? 1, + set: (v) => { + this.properties.step = v + valueWidget.callback?.(valueWidget.value) // for vue reactivity + } + }) +} +function onCustomFloatCreated(this: LGraphNode) { + const valueWidget = this.widgets?.[0] + if (!valueWidget) return + + Object.defineProperty(valueWidget.options, 'min', { + get: () => this.properties.min ?? -Infinity, + set: (v) => { + this.properties.min = v + valueWidget.callback?.(valueWidget.value) + } + }) + Object.defineProperty(valueWidget.options, 'max', { + get: () => this.properties.max ?? Infinity, + set: (v) => { + this.properties.max = v + valueWidget.callback?.(valueWidget.value) + } + }) + Object.defineProperty(valueWidget.options, 'precision', { + get: () => this.properties.precision ?? 1, + set: (v) => { + this.properties.precision = v + valueWidget.callback?.(valueWidget.value) + } + }) + Object.defineProperty(valueWidget.options, 'step2', { + get: () => { + if (this.properties.step) return this.properties.step + + const { precision } = this.properties + return typeof precision === 'number' ? 5 * 10 ** -precision : 1 + }, + set: (v) => (this.properties.step = v) + }) + Object.defineProperty(valueWidget.options, 'round', { + get: () => { + if (this.properties.round) return this.properties.round + + const { precision } = this.properties + return typeof precision === 'number' ? 10 ** -precision : 0.1 + }, + set: (v) => { + this.properties.round = v + valueWidget.callback?.(valueWidget.value) + } + }) +} + app.registerExtension({ - name: 'Comfy.CustomCombo', + name: 'Comfy.CustomWidgets', beforeRegisterNodeDef(nodeType, nodeData) { - if (nodeData?.name !== 'CustomCombo') return - nodeType.prototype.onNodeCreated = useChainCallback( - nodeType.prototype.onNodeCreated, - onNodeCreated - ) + if (nodeData?.name === 'CustomCombo') + nodeType.prototype.onNodeCreated = useChainCallback( + nodeType.prototype.onNodeCreated, + onCustomComboCreated + ) + else if (nodeData?.name === 'PrimitiveInt') + nodeType.prototype.onNodeCreated = useChainCallback( + nodeType.prototype.onNodeCreated, + onCustomIntCreated + ) + else if (nodeData?.name === 'PrimitiveFloat') + nodeType.prototype.onNodeCreated = useChainCallback( + nodeType.prototype.onNodeCreated, + onCustomFloatCreated + ) } }) diff --git a/src/extensions/core/index.ts b/src/extensions/core/index.ts index 367f7eb92..fe8cbf2c7 100644 --- a/src/extensions/core/index.ts +++ b/src/extensions/core/index.ts @@ -2,7 +2,7 @@ import { isCloud, isNightly } from '@/platform/distribution/types' import './clipspace' import './contextMenuFilter' -import './customCombo' +import './customWidgets' import './dynamicPrompts' import './editAttention' import './electronAdapter'