diff --git a/browser_tests/fixtures/utils/litegraphUtils.ts b/browser_tests/fixtures/utils/litegraphUtils.ts index 1f102bbb5..94bfd059d 100644 --- a/browser_tests/fixtures/utils/litegraphUtils.ts +++ b/browser_tests/fixtures/utils/litegraphUtils.ts @@ -81,7 +81,7 @@ export class NodeWidgetReference { if (!widget) throw new Error(`Widget ${index} not found.`) const [x, y, w, h] = node.getBounding() - return window['app'].canvas.ds.convertOffsetToCanvas([ + return window['app'].canvasPosToClientPos([ x + w / 2, y + window['LiteGraph']['NODE_TITLE_HEIGHT'] + widget.last_y + 1 ]) @@ -94,6 +94,36 @@ export class NodeWidgetReference { } } + /** + * @returns The position of the widget's associated socket + */ + async getSocketPosition(): Promise { + const pos: [number, number] = await this.node.comfyPage.page.evaluate( + ([id, index]) => { + const node = window['app'].graph.getNodeById(id) + if (!node) throw new Error(`Node ${id} not found.`) + const widget = node.widgets[index] + if (!widget) throw new Error(`Widget ${index} not found.`) + + const slot = node.inputs.find( + (slot) => slot.widget?.name === widget.name + ) + if (!slot) throw new Error(`Socket ${widget.name} not found.`) + + const [x, y] = node.getBounding() + return window['app'].canvasPosToClientPos([ + x + slot.pos[0], + y + slot.pos[1] + window['LiteGraph']['NODE_TITLE_HEIGHT'] + ]) + }, + [this.node.id, this.index] as const + ) + return { + x: pos[0], + y: pos[1] + } + } + async click() { await this.node.comfyPage.canvas.click({ position: await this.getPosition() @@ -250,7 +280,7 @@ export class NodeReference { const targetWidget = await targetNode.getWidget(targetWidgetIndex) await this.comfyPage.dragAndDrop( await originSlot.getPosition(), - await targetWidget.getPosition() + await targetWidget.getSocketPosition() ) return originSlot } diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/dragged-node1-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/dragged-node1-chromium-linux.png index f7b799c0a..1d1175c04 100644 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/dragged-node1-chromium-linux.png and b/browser_tests/tests/interaction.spec.ts-snapshots/dragged-node1-chromium-linux.png differ diff --git a/browser_tests/tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png b/browser_tests/tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png index 8a9502f3a..9fc28462e 100644 Binary files a/browser_tests/tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png and b/browser_tests/tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png differ diff --git a/browser_tests/tests/nodeDisplay.spec.ts-snapshots/missing-nodes-converted-widget-chromium-linux.png b/browser_tests/tests/nodeDisplay.spec.ts-snapshots/missing-nodes-converted-widget-chromium-linux.png index f262a6ddf..58d2d8abb 100644 Binary files a/browser_tests/tests/nodeDisplay.spec.ts-snapshots/missing-nodes-converted-widget-chromium-linux.png and b/browser_tests/tests/nodeDisplay.spec.ts-snapshots/missing-nodes-converted-widget-chromium-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-chromium-linux.png index 39c3f156c..73af05faf 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-chromium-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png index 0e809fae2..4f48b1905 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-chromium-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-dom-widget-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-dom-widget-chromium-linux.png index 3cbad1249..d64f859aa 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-dom-widget-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/primitive-node-connected-dom-widget-chromium-linux.png differ diff --git a/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png b/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png index 82c299138..e24ad6d0f 100644 Binary files a/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png and b/browser_tests/tests/primitiveNode.spec.ts-snapshots/static-primitive-connected-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts b/browser_tests/tests/rightClickMenu.spec.ts index ba7713a4f..67b8dbd54 100644 --- a/browser_tests/tests/rightClickMenu.spec.ts +++ b/browser_tests/tests/rightClickMenu.spec.ts @@ -88,63 +88,6 @@ test.describe('Node Right Click Menu', () => { ) }) - test.describe('Widget conversion', () => { - const convertibleWidgetTypes = ['text', 'string', 'number', 'toggle'] - - test('Can convert widget to input', async ({ comfyPage }) => { - await comfyPage.rightClickEmptyLatentNode() - await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png') - await comfyPage.page.getByText('Convert Widget to Input').click() - await comfyPage.nextFrame() - // The submenu has an identical entry as the base menu - use last - await comfyPage.page.getByText('Convert width to input').last().click() - await comfyPage.nextFrame() - await expect(comfyPage.canvas).toHaveScreenshot( - 'right-click-node-widget-converted.png' - ) - }) - - test('Can convert widget without submenu', async ({ comfyPage }) => { - // Right-click the width widget - await comfyPage.rightClickEmptyLatentNode() - await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png') - await comfyPage.page.getByText('Convert width to input').click() - await comfyPage.nextFrame() - await expect(comfyPage.canvas).toHaveScreenshot( - 'right-click-node-widget-converted.png' - ) - }) - - convertibleWidgetTypes.forEach((widgetType) => { - test(`Can convert ${widgetType} widget to input`, async ({ - comfyPage - }) => { - const nodeType = 'KSampler' - - // To avoid needing multiple clicks, disable nesting of conversion options - await comfyPage.setSetting('Comfy.NodeInputConversionSubmenus', false) - - // Add the widget using the node's `addWidget` method - await comfyPage.page.evaluate( - ([nodeType, widgetType]) => { - const node = window['app'].graph.nodes.find( - (n) => n.type === nodeType - ) - node.addWidget(widgetType, widgetType, 'defaultValue', () => {}, {}) - }, - [nodeType, widgetType] - ) - - // Verify the context menu includes the conversion option - const node = (await comfyPage.getNodeRefsByType(nodeType))[0] - const menuOptions = await node.getContextMenuOptionNames() - expect(menuOptions.includes(`Convert ${widgetType} to input`)).toBe( - true - ) - }) - }) - }) - test('Can pin and unpin', async ({ comfyPage }) => { await comfyPage.rightClickEmptyLatentNode() await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png') diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png index 3395013ae..977389e4e 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-widget-converted-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-widget-converted-chromium-linux.png deleted file mode 100644 index 257c67a98..000000000 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-widget-converted-chromium-linux.png and /dev/null differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png index 44311e6b2..ba521babb 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-pinned-node-chromium-linux.png differ diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png index 3395013ae..977389e4e 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-unpinned-node-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/boolean-widget-toggled-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/boolean-widget-toggled-chromium-linux.png index fc729d9c0..e32c9a6b3 100644 Binary files a/browser_tests/tests/widget.spec.ts-snapshots/boolean-widget-toggled-chromium-linux.png and b/browser_tests/tests/widget.spec.ts-snapshots/boolean-widget-toggled-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/seed-widget-dragged-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/seed-widget-dragged-chromium-linux.png index a3f74acfa..4596f0f02 100644 Binary files a/browser_tests/tests/widget.spec.ts-snapshots/seed-widget-dragged-chromium-linux.png and b/browser_tests/tests/widget.spec.ts-snapshots/seed-widget-dragged-chromium-linux.png differ diff --git a/browser_tests/tests/widget.spec.ts-snapshots/slider-widget-dragged-chromium-linux.png b/browser_tests/tests/widget.spec.ts-snapshots/slider-widget-dragged-chromium-linux.png index ea1c55ed3..eea983b11 100644 Binary files a/browser_tests/tests/widget.spec.ts-snapshots/slider-widget-dragged-chromium-linux.png and b/browser_tests/tests/widget.spec.ts-snapshots/slider-widget-dragged-chromium-linux.png differ diff --git a/package-lock.json b/package-lock.json index 7a29e8d42..8b2972d3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.31", - "@comfyorg/litegraph": "^0.12.0", + "@comfyorg/litegraph": "^0.13.0-0", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", @@ -478,9 +478,9 @@ "license": "GPL-3.0-only" }, "node_modules/@comfyorg/litegraph": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.12.0.tgz", - "integrity": "sha512-2LK1tNHIPAGmalloJxVtWXndG4vWNAEBX2RuQE7Fvtj2UuMFFpV/tWq+ofFJjj8sk2K/S5CfVsx+RFnREpV3RQ==", + "version": "0.13.0-0", + "resolved": "https://registry.npmjs.org/@comfyorg/litegraph/-/litegraph-0.13.0-0.tgz", + "integrity": "sha512-jkrk3d+riU7LpiV2CUXcVyropIa8W+FvTK48cSVqzJh+/BB7kHiYtYRfzwp3fgln5/LHsfUf1TWd6OqZWFTXrA==", "license": "MIT" }, "node_modules/@cspotcode/source-map-support": { diff --git a/package.json b/package.json index 8e21ea156..5656fc174 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "@alloc/quick-lru": "^5.2.0", "@atlaskit/pragmatic-drag-and-drop": "^1.3.1", "@comfyorg/comfyui-electron-types": "^0.4.31", - "@comfyorg/litegraph": "^0.12.0", + "@comfyorg/litegraph": "^0.13.0-0", "@primevue/forms": "^4.2.5", "@primevue/themes": "^4.2.5", "@sentry/vue": "^8.48.0", diff --git a/src/components/graph/widgets/DomWidget.vue b/src/components/graph/widgets/DomWidget.vue index 245030fdd..d59e0dc2c 100644 --- a/src/components/graph/widgets/DomWidget.vue +++ b/src/components/graph/widgets/DomWidget.vue @@ -49,7 +49,9 @@ const style = computed(() => ({ ...positionStyle.value, ...(enableDomClipping.value ? clippingStyle.value : {}), zIndex: widgetState.zIndex, - pointerEvents: widgetState.readonly ? 'none' : 'auto' + pointerEvents: + widgetState.readonly || widget.computedDisabled ? 'none' : 'auto', + opacity: widget.computedDisabled ? 0.5 : 1 })) const canvasStore = useCanvasStore() diff --git a/src/extensions/core/widgetInputs.ts b/src/extensions/core/widgetInputs.ts index c2202760c..aecfae731 100644 --- a/src/extensions/core/widgetInputs.ts +++ b/src/extensions/core/widgetInputs.ts @@ -1,6 +1,5 @@ -import { LGraphNode, LiteGraph, RenderShape } from '@comfyorg/litegraph' +import { LGraphNode, LiteGraph } from '@comfyorg/litegraph' import type { - IFoundSlot, INodeInputSlot, INodeOutputSlot, ISlotType, @@ -14,13 +13,11 @@ import { useChainCallback } from '@/composables/functional/useChainCallback' import type { InputSpec } from '@/schemas/nodeDefSchema' import { app } from '@/scripts/app' import { ComfyWidgets, addValueControlWidgets } from '@/scripts/widgets' -import { useNodeDefStore } from '@/stores/nodeDefStore' -import { useSettingStore } from '@/stores/settingStore' +import { CONFIG, GET_CONFIG } from '@/services/litegraphService' import { mergeInputSpec } from '@/utils/nodeDefUtil' import { applyTextReplacements } from '@/utils/searchAndReplace' import { isPrimitiveNode } from '@/utils/typeGuardUtil' -const CONVERTED_TYPE = 'converted-widget' const VALID_TYPES = [ 'STRING', 'combo', @@ -30,8 +27,6 @@ const VALID_TYPES = [ 'text', 'string' ] -const CONFIG = Symbol() -const GET_CONFIG = Symbol() const replacePropertyName = 'Run widget replace on values' export class PrimitiveNode extends LGraphNode { @@ -107,18 +102,14 @@ export class PrimitiveNode extends LGraphNode { onAfterGraphConfigured() { if (this.outputs[0].links?.length && !this.widgets?.length) { - // TODO: Review this check - // @ts-expect-error - if (!this.#onFirstConnection()) return + this.#onFirstConnection() // Populate widget values from config data - if (this.widgets) { - // @ts-expect-error fixme ts strict error + if (this.widgets && this.widgets_values) { for (let i = 0; i < this.widgets_values.length; i++) { const w = this.widgets[i] if (w) { - // @ts-expect-error change widget type from string to unknown - w.value = this.widgets_values[i] + w.value = this.widgets_values[i] as any } } } @@ -447,109 +438,21 @@ function isConvertibleWidget(widget: IWidget, config: InputSpec): boolean { ) } -function hideWidget( - node: LGraphNode, - widget: IWidget, - options: { suffix?: string; holdSpace?: boolean } = {} -) { - const { suffix = '', holdSpace = true } = options - - if (widget.type?.startsWith(CONVERTED_TYPE)) return - widget.origType = widget.type - widget.origComputeSize = widget.computeSize - widget.origSerializeValue = widget.serializeValue - // @ts-expect-error custom widget type - widget.type = CONVERTED_TYPE + suffix - if (holdSpace) { - widget.computeSize = () => [0, LiteGraph.NODE_WIDGET_HEIGHT] - } else { - // -4 is due to the gap litegraph adds between widgets automatically - widget.computeSize = () => [0, -4] - } - widget.serializeValue = (node: LGraphNode, index: number) => { - // Prevent serializing the widget if we have no input linked - if (!node.inputs) { - return undefined - } - let node_input = node.inputs.find((i) => i.widget?.name === widget.name) - - if (!node_input || !node_input.link) { - return undefined - } - return widget.origSerializeValue - ? widget.origSerializeValue(node, index) - : widget.value - } - - // Hide any linked widgets, e.g. seed+seedControl - if (widget.linkedWidgets) { - for (const w of widget.linkedWidgets) { - hideWidget(node, w, { suffix: ':' + widget.name, holdSpace: false }) - } - } -} - -function showWidget(widget: IWidget) { - // @ts-expect-error custom widget type - widget.type = widget.origType - widget.computeSize = widget.origComputeSize - widget.serializeValue = widget.origSerializeValue - - delete widget.origType - delete widget.origComputeSize - delete widget.origSerializeValue - - // Hide any linked widgets, e.g. seed+seedControl - if (widget.linkedWidgets) { - for (const w of widget.linkedWidgets) { - showWidget(w) - } - } -} - +/** + * Convert a widget to an input slot. + * @deprecated Widget to socket conversion is no longer necessary, as they co-exist now. + * @param node The node to convert the widget to an input slot for. + * @param widget The widget to convert to an input slot. + * @returns The input slot that was converted from the widget or undefined if the widget is not found. + */ export function convertToInput( node: LGraphNode, - widget: IWidget, - config: InputSpec -): INodeInputSlot { - hideWidget(node, widget) - - const { type } = getWidgetType(config) - - // Add input and store widget config for creating on primitive node - const [oldWidth, oldHeight] = node.size - const inputIsOptional = !!widget.options?.inputIsOptional - const input = node.addInput(widget.name, type, { - widget: { name: widget.name, [GET_CONFIG]: () => config }, - ...(inputIsOptional ? { shape: RenderShape.HollowCircle } : {}) - }) - - for (const widget of node.widgets ?? []) { - widget.last_y = (widget.last_y ?? 0) + LiteGraph.NODE_SLOT_HEIGHT - } - - // Restore original size but grow if needed - node.setSize([ - Math.max(oldWidth, node.size[0]), - Math.max(oldHeight, node.size[1]) - ]) - return input -} - -function convertToWidget(node: LGraphNode, widget: IWidget) { - showWidget(widget) - const [oldWidth, oldHeight] = node.size - node.removeInput(node.inputs.findIndex((i) => i.widget?.name === widget.name)) - - for (const widget of node.widgets ?? []) { - widget.last_y = (widget.last_y ?? 0) - LiteGraph.NODE_SLOT_HEIGHT - } - - // Restore original size but grow if needed - node.setSize([ - Math.max(oldWidth, node.size[0]), - Math.max(oldHeight, node.size[1]) - ]) + widget: IWidget +): INodeInputSlot | undefined { + console.warn( + 'Please remove call to convertToInput. Widget to socket conversion is no longer necessary, as they co-exist now.' + ) + return node.inputs.find((slot) => slot.widget?.name === widget.name) } function getWidgetType(config: InputSpec) { @@ -631,167 +534,13 @@ export function mergeIfValid( app.registerExtension({ name: 'Comfy.WidgetInputs', - settings: [ - { - id: 'Comfy.NodeInputConversionSubmenus', - name: 'In the node context menu, place the entries that convert between input/widget in sub-menus.', - type: 'boolean', - defaultValue: true - } - ], - setup() { - app.canvas.getWidgetLinkType = function (widget, node) { - const nodeDefStore = useNodeDefStore() - const nodeDef = nodeDefStore.nodeDefsByName[node.type] - const input = nodeDef.inputs[widget.name] - return input?.type - } - - app.canvas.linkConnector.events.addEventListener( - 'dropped-on-widget', - (e) => { - const { node, link, widget } = e.detail - if (!node || !link || !widget) return - - const nodeData = node.constructor.nodeData - if (!nodeData) return - const all = { - ...nodeData?.input?.required, - ...nodeData?.input?.optional - } - const inputSpec = all[widget.name] - if (!inputSpec) return - - const input = convertToInput(node, widget, inputSpec) - link.node.connectSlots(link.fromSlot, node, input, link.fromReroute?.id) - } - ) - }, async beforeRegisterNodeDef(nodeType, _nodeData, app) { - // Add menu options to convert to/from widgets - const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions // @ts-expect-error adding extra property - nodeType.prototype.convertWidgetToInput = function ( - this: LGraphNode, - widget: IWidget - ) { - const config = getConfig.call(this, widget.name) ?? [ - widget.type, - widget.options || {} - ] - if (!isConvertibleWidget(widget, config)) return false - if (widget.type?.startsWith(CONVERTED_TYPE)) return false - convertToInput(this, widget, config) - return true - } - - nodeType.prototype.getExtraSlotMenuOptions = function ( - this: LGraphNode, - slot: IFoundSlot - ) { - if (!slot.input || !slot.input.widget) return [] - - const widget = this.widgets?.find( - (w) => w.name === slot.input?.widget?.name + nodeType.prototype.convertWidgetToInput = function (this: LGraphNode) { + console.warn( + 'Please remove call to convertWidgetToInput. Widget to socket conversion is no longer necessary, as they co-exist now.' ) - if (!widget) return [] - return [ - { - content: `Convert to widget`, - callback: () => convertToWidget(this, widget) - } - ] - } - - // @ts-expect-error fixme ts strict error - nodeType.prototype.getExtraMenuOptions = function ( - this: LGraphNode, - _, - options - ) { - const r = origGetExtraMenuOptions - ? // @ts-expect-error fixme ts strict error - origGetExtraMenuOptions.apply(this, arguments) - : undefined - - const getPointerCanvasPos = () => { - const pos = this.graph?.list_of_graphcanvas?.at(0)?.graph_mouse - return pos ? { canvasX: pos[0], canvasY: pos[1] } : undefined - } - - if (this.widgets) { - const { canvasX = 0, canvasY = 0 } = getPointerCanvasPos() ?? {} - const widget = this.getWidgetOnPos(canvasX, canvasY) - // @ts-expect-error custom widget type - if (widget && widget.type !== CONVERTED_TYPE) { - const config = getConfig.call(this, widget.name) ?? [ - widget.type, - widget.options || {} - ] - if (isConvertibleWidget(widget, config)) { - options.push({ - content: `Convert ${widget.name} to input`, - callback: () => convertToInput(this, widget, config) && false - }) - } - } - let toInput = [] - let toWidget = [] - for (const w of this.widgets) { - if (w.options?.forceInput) { - continue - } - // @ts-expect-error custom widget type - if (w.type === CONVERTED_TYPE) { - toWidget.push({ - // @ts-expect-error never - content: `Convert ${w.name} to widget`, - callback: () => convertToWidget(this, w) - }) - } else { - const config = getConfig.call(this, w.name) ?? [ - w.type, - w.options || {} - ] - if (isConvertibleWidget(w, config)) { - toInput.push({ - content: `Convert ${w.name} to input`, - callback: () => convertToInput(this, w, config) - }) - } - } - } - - //Convert.. main menu - if (toInput.length) { - if (useSettingStore().get('Comfy.NodeInputConversionSubmenus')) { - options.push({ - content: 'Convert Widget to Input', - submenu: { - // @ts-expect-error fixme ts strict error - options: toInput - } - }) - } else { - // @ts-expect-error fixme ts strict error - options.push(...toInput, null) - } - } - if (toWidget.length) { - if (useSettingStore().get('Comfy.NodeInputConversionSubmenus')) { - options.push({ - content: 'Convert Input to Widget', - submenu: { - options: toWidget - } - }) - } else { - options.push(...toWidget, null) - } - } - } - - return r + return false } nodeType.prototype.onGraphConfigured = useChainCallback( @@ -808,9 +557,7 @@ app.registerExtension({ } const w = this.widgets?.find((w) => w.name === name) - if (w) { - hideWidget(this, w) - } else { + if (!w) { this.removeInput(this.inputs.findIndex((i) => i === input)) } } @@ -818,24 +565,6 @@ app.registerExtension({ } ) - nodeType.prototype.onNodeCreated = useChainCallback( - nodeType.prototype.onNodeCreated, - function (this: LGraphNode) { - // When node is created, convert any force/default inputs - if (!app.configuringGraph && this.widgets) { - for (const w of this.widgets) { - if (w?.options?.forceInput || w?.options?.defaultInput) { - const config = getConfig.call(this, w.name) ?? [ - w.type, - w.options || {} - ] - convertToInput(this, w, config) - } - } - } - } - ) - nodeType.prototype.onConfigure = useChainCallback( nodeType.prototype.onConfigure, function (this: LGraphNode) { @@ -845,10 +574,6 @@ app.registerExtension({ if (input.widget && !input.widget[GET_CONFIG]) { const name = input.widget.name input.widget[GET_CONFIG] = () => getConfig.call(this, name) - const w = this.widgets?.find((w) => w.name === name) - if (w) { - hideWidget(this, w) - } } } } diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 0543c2344..1c0b53ab5 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -209,8 +209,6 @@ "Shapes": "Shapes", "Bypass": "Bypass", "Copy (Clipspace)": "Copy (Clipspace)", - "Convert Widget to Input": "Convert Widget to Input", - "Convert Input to Widget": "Convert Input to Widget", "Add Node": "Add Node", "Add Group": "Add Group", "Convert to Group Node": "Convert to Group Node", @@ -219,9 +217,6 @@ "Save Selected as Template": "Save Selected as Template", "Node Templates": "Node Templates", "Manage": "Manage", - "Convert ": "Convert ", - " to input": " to input", - " to widget": " to widget", "Search": "Search" }, "icon": { @@ -695,7 +690,6 @@ "BrushAdjustment": "Brush Adjustment", "NewEditor": "New Editor", "ModelLibrary": "Model Library", - "NodeInputConversionSubmenus": "Node Input Conversion Submenus", "NodeLibrary": "Node Library", "Node Search Box": "Node Search Box", "Pointer": "Pointer", diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index ea49213e1..267f18477 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -189,9 +189,6 @@ "Hide built-in": "Hide built-in" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "In the node context menu, place the entries that convert between input/widget in sub-menus." - }, "Comfy_NodeSearchBoxImpl": { "name": "Node search box implementation", "options": { diff --git a/src/locales/es/main.json b/src/locales/es/main.json index 470989530..86a57be5e 100644 --- a/src/locales/es/main.json +++ b/src/locales/es/main.json @@ -20,8 +20,6 @@ "yellow": "Amarillo" }, "contextMenu": { - " to input": " a entrada", - " to widget": " a widget", "Add Group": "Agregar Grupo", "Add Group For Selected Nodes": "Agregar Grupo para Nodos Seleccionados", "Add Node": "Agregar Nodo", @@ -29,9 +27,6 @@ "Clone": "Clonar", "Collapse": "Colapsar", "Colors": "Colores", - "Convert ": "Convertir ", - "Convert Input to Widget": "Convertir Entrada a Widget", - "Convert Widget to Input": "Convertir Widget a Entrada", "Convert to Group Node": "Convertir en Nodo de Grupo", "Copy (Clipspace)": "Copiar (Espacio de Clip)", "Expand": "Expandir", @@ -836,7 +831,6 @@ "Node": "Nodo", "Node Search Box": "Caja de Búsqueda de Nodo", "Node Widget": "Widget de Nodo", - "NodeInputConversionSubmenus": "Submenús de Conversión de Entrada de Nodo", "NodeLibrary": "Biblioteca de Nodos", "Pointer": "Puntero", "Queue": "Cola", diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 66e21f856..50a585df4 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -157,9 +157,6 @@ "Show all": "Mostrar todo" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "En el menú contextual del nodo, coloque las entradas que convierten entre entrada/widget en submenús." - }, "Comfy_NodeSearchBoxImpl": { "name": "Implementación de la caja de búsqueda de nodos", "options": { diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index a1cfa14c1..13e4ad3b4 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -20,8 +20,6 @@ "yellow": "Jaune" }, "contextMenu": { - " to input": " en entrée", - " to widget": " en widget", "Add Group": "Ajouter un Groupe", "Add Group For Selected Nodes": "Ajouter un Groupe pour les Nœuds Sélectionnés", "Add Node": "Ajouter un Nœud", @@ -29,9 +27,6 @@ "Clone": "Cloner", "Collapse": "Réduire", "Colors": "Couleurs", - "Convert ": "Convertir ", - "Convert Input to Widget": "Convertir l'Entrée en Widget", - "Convert Widget to Input": "Convertir le Widget en Entrée", "Convert to Group Node": "Convertir en Nœud de Groupe", "Copy (Clipspace)": "Copier (Clipspace)", "Expand": "Développer", @@ -836,7 +831,6 @@ "Node": "Nœud", "Node Search Box": "Boîte de Recherche de Nœud", "Node Widget": "Widget de Nœud", - "NodeInputConversionSubmenus": "Sous-menus de Conversion d'Entrée de Nœud", "NodeLibrary": "Bibliothèque de Nœuds", "Pointer": "Pointeur", "Queue": "File d'Attente", diff --git a/src/locales/fr/settings.json b/src/locales/fr/settings.json index b0f27bb19..e7261f0cf 100644 --- a/src/locales/fr/settings.json +++ b/src/locales/fr/settings.json @@ -157,9 +157,6 @@ "Show all": "Afficher tout" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "Dans le menu contextuel du nœud, placez les entrées qui convertissent entre l'entrée/widget dans des sous-menus." - }, "Comfy_NodeSearchBoxImpl": { "name": "Implémentation de la boîte de recherche de nœud", "options": { diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 53f780e47..f65a713ad 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -20,8 +20,6 @@ "yellow": "黄色" }, "contextMenu": { - " to input": " 入力へ", - " to widget": " ウィジェットへ", "Add Group": "グループを追加", "Add Group For Selected Nodes": "選択したノードのグループを追加", "Add Node": "ノードを追加", @@ -29,9 +27,6 @@ "Clone": "クローン", "Collapse": "折りたたむ", "Colors": "色", - "Convert ": "変換 ", - "Convert Input to Widget": "入力をウィジェットに変換", - "Convert Widget to Input": "ウィジェットを入力に変換", "Convert to Group Node": "グループノードに変換", "Copy (Clipspace)": "コピー (Clipspace)", "Expand": "展開", @@ -836,7 +831,6 @@ "Node": "ノード", "Node Search Box": "ノード検索ボックス", "Node Widget": "ノードウィジェット", - "NodeInputConversionSubmenus": "ノード入力変換サブメニュー", "NodeLibrary": "ノードライブラリ", "Pointer": "ポインタ", "Queue": "キュー", diff --git a/src/locales/ja/settings.json b/src/locales/ja/settings.json index 3bc0a7a6b..71b84949d 100644 --- a/src/locales/ja/settings.json +++ b/src/locales/ja/settings.json @@ -157,9 +157,6 @@ "Show all": "すべて表示" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "ノードのコンテキストメニューに、入力/ウィジェット間の変換を行うエントリをサブメニューに配置します。" - }, "Comfy_NodeSearchBoxImpl": { "name": "ノード検索ボックスの実装", "options": { diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index 8f916322a..654245ab2 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -20,8 +20,6 @@ "yellow": "노란색" }, "contextMenu": { - " to input": " 위젯을 입력으로", - " to widget": " 입력을 위젯으로", "Add Group": "그룹 추가", "Add Group For Selected Nodes": "선택한 노드 그룹 추가", "Add Node": "노드 추가", @@ -29,9 +27,6 @@ "Clone": "복제", "Collapse": "접기", "Colors": "색상", - "Convert ": "[변환] ", - "Convert Input to Widget": "입력을 위젯으로 변환", - "Convert Widget to Input": "위젯을 입력으로 변환", "Convert to Group Node": "그룹 노드로 변환", "Copy (Clipspace)": "복사 (Clipspace)", "Expand": "확장", @@ -836,7 +831,6 @@ "Node": "노드", "Node Search Box": "노드 검색 상자", "Node Widget": "노드 위젯", - "NodeInputConversionSubmenus": "노드 입력 변환 하위 메뉴", "NodeLibrary": "노드 라이브러리", "Pointer": "포인터", "Queue": "실행 큐", diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index d8e8c45a0..1a4ee263b 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -157,9 +157,6 @@ "Show all": "모두 표시" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "노드 컨텍스트 메뉴에서 입력/위젯 간 변환 항목을 하위 메뉴에 배치합니다." - }, "Comfy_NodeSearchBoxImpl": { "name": "노드 검색 상자 구현", "options": { diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index 7b8c4ddea..7cc8f2ff5 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -20,8 +20,6 @@ "yellow": "Жёлтый" }, "contextMenu": { - " to input": " во вход", - " to widget": " в виджет", "Add Group": "Добавить группу", "Add Group For Selected Nodes": "Добавить группу для выбранных узлов", "Add Node": "Добавить узел", @@ -29,9 +27,6 @@ "Clone": "Клонировать", "Collapse": "Свернуть", "Colors": "Цвета", - "Convert ": "Преобразовать ", - "Convert Input to Widget": "Преобразовать вход в виджет", - "Convert Widget to Input": "Преобразовать виджет во вход", "Convert to Group Node": "Преобразовать в групповой узел", "Copy (Clipspace)": "Копировать (Clipspace)", "Expand": "Развернуть", @@ -836,7 +831,6 @@ "Node": "Нода", "Node Search Box": "Поисковая строка нод", "Node Widget": "Виджет ноды", - "NodeInputConversionSubmenus": "Подменю преобразования ввода ноды", "NodeLibrary": "Библиотека нод", "Pointer": "Указатель", "Queue": "Очередь", diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index 75f7bf08e..11c270f82 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -157,9 +157,6 @@ "Show all": "Показать все" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "В контекстном меню ноды разместите элементы, которые конвертируют между вводом/виджетом в подменю." - }, "Comfy_NodeSearchBoxImpl": { "name": "Реализация поискового поля нод", "options": { diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index b4e80f37d..a353c7751 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -20,8 +20,6 @@ "yellow": "黄色" }, "contextMenu": { - " to input": " 为输入", - " to widget": " 为控件", "Add Group": "添加组", "Add Group For Selected Nodes": "为选定节点添加组", "Add Node": "添加节点", @@ -29,9 +27,6 @@ "Clone": "克隆", "Collapse": "折叠", "Colors": "颜色", - "Convert ": "转换 ", - "Convert Input to Widget": "将输入转换为控件", - "Convert Widget to Input": "将控件转换为输入", "Convert to Group Node": "转换为组节点", "Copy (Clipspace)": "复制 (Clipspace)", "Expand": "展开", @@ -836,7 +831,6 @@ "Node": "节点", "Node Search Box": "节点搜索框", "Node Widget": "节点组件", - "NodeInputConversionSubmenus": "节点输入转换子菜单", "NodeLibrary": "节点库", "Pointer": "指针", "Queue": "队列", diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index 92688e4e4..9c230364a 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -157,9 +157,6 @@ "Show all": "显示全部" } }, - "Comfy_NodeInputConversionSubmenus": { - "name": "在节点上下文菜单中,将输入/组件之间转换的条目放置在子菜单中。" - }, "Comfy_NodeSearchBoxImpl": { "name": "节点搜索框", "options": { diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 906f2d367..873d54e3c 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -364,7 +364,6 @@ const zSettings = z.record(z.any()).and( z.string(), zBookmarkCustomization ), - 'Comfy.NodeInputConversionSubmenus': z.boolean(), 'Comfy.LinkRelease.Action': zLinkReleaseTriggerAction, 'Comfy.LinkRelease.ActionShift': zLinkReleaseTriggerAction, 'Comfy.ModelLibrary.AutoLoadAll': z.boolean(), diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 23c5a2b85..a74262b05 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -66,7 +66,7 @@ import { import { $el, ComfyUI } from './ui' import { ComfyAppMenu } from './ui/menu/index' import { clone } from './utils' -import { type ComfyWidgetConstructor, ComfyWidgets } from './widgets' +import { type ComfyWidgetConstructor } from './widgets' export const ANIM_PREVIEW_WIDGET = '$$comfy_animation_preview' @@ -180,10 +180,7 @@ export class ComfyApp { * @deprecated Use useWidgetStore().widgets instead */ get widgets(): Record { - if (this.vueAppReady) { - return useWidgetStore().widgets - } - return ComfyWidgets + return Object.fromEntries(useWidgetStore().widgets.entries()) } /** diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index e2429e062..779764c03 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -132,12 +132,7 @@ abstract class BaseDOMWidgetImpl } isVisible(): boolean { - return ( - !_.isNil(this.computedHeight) && - this.computedHeight > 0 && - !['converted-widget', 'hidden'].includes(this.type) && - !this.node.collapsed - ) + return !['hidden'].includes(this.type) && this.node.isWidgetVisible(this) } draw( diff --git a/src/services/litegraphService.ts b/src/services/litegraphService.ts index 1124c9363..cee8ae3f0 100644 --- a/src/services/litegraphService.ts +++ b/src/services/litegraphService.ts @@ -4,9 +4,14 @@ import { LGraphEventMode, LGraphNode, LiteGraph, - RenderShape + RenderShape, + type Vector2 } from '@comfyorg/litegraph' -import { Vector2 } from '@comfyorg/litegraph' +import type { + ISerialisableNodeOutput, + ISerialisedNode +} from '@comfyorg/litegraph/dist/types/serialisation' +import _ from 'lodash' import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage' import { useNodeCanvasImagePreview } from '@/composables/node/useNodeCanvasImagePreview' @@ -32,6 +37,10 @@ import { isImageNode, isVideoNode } from '@/utils/litegraphUtil' import { useExtensionService } from './extensionService' +const PRIMITIVE_TYPES = new Set(['INT', 'FLOAT', 'BOOLEAN', 'STRING', 'COMBO']) +export const CONFIG = Symbol() +export const GET_CONFIG = Symbol() + /** * Service that augments litegraph with ComfyUI specific functionality. */ @@ -95,59 +104,79 @@ export const useLitegraphService = () => { } } + /** + * @internal Add input sockets to the node. (No widget) + */ + #addInputSocket(inputSpec: InputSpec) { + const inputName = inputSpec.name + const nameKey = `${this.#nodeKey}.inputs.${normalizeI18nKey(inputName)}.name` + const widgetConstructor = widgetStore.widgets.get(inputSpec.type) + if (widgetConstructor && !inputSpec.forceInput) return + + this.addInput(inputName, inputSpec.type, { + shape: inputSpec.isOptional ? RenderShape.HollowCircle : undefined, + localized_name: st(nameKey, inputName) + }) + } + + /** + * @internal Add a widget to the node. For primitive types, an input socket is also added. + */ + #addInputWidget(inputSpec: InputSpec) { + const inputName = inputSpec.name + const nameKey = `${this.#nodeKey}.inputs.${normalizeI18nKey(inputName)}.name` + const widgetConstructor = widgetStore.widgets.get(inputSpec.type) + if (!widgetConstructor || inputSpec.forceInput) return + + const { + widget, + minWidth = 1, + minHeight = 1 + } = widgetConstructor( + this, + inputName, + transformInputSpecV2ToV1(inputSpec), + app + ) ?? {} + + if (widget) { + widget.label = st(nameKey, widget.label ?? inputName) + widget.options ??= {} + Object.assign(widget.options, { + inputIsOptional: inputSpec.isOptional, + forceInput: inputSpec.forceInput, + advanced: inputSpec.advanced, + hidden: inputSpec.hidden + }) + } + + if (PRIMITIVE_TYPES.has(inputSpec.type)) { + const inputSpecV1 = transformInputSpecV2ToV1(inputSpec) + this.addInput(inputName, inputSpec.type, { + shape: inputSpec.isOptional ? RenderShape.HollowCircle : undefined, + localized_name: st(nameKey, inputName), + widget: { name: inputName, [GET_CONFIG]: () => inputSpecV1 } + }) + } + + this.#initialMinSize.width = Math.max( + this.#initialMinSize.width, + minWidth + ) + this.#initialMinSize.height = Math.max( + this.#initialMinSize.height, + minHeight + ) + } + /** * @internal Add inputs to the node. */ #addInputs(inputs: Record) { - for (const [inputName, inputSpec] of Object.entries(inputs)) { - const inputType = inputSpec.type - const nameKey = `${this.#nodeKey}.inputs.${normalizeI18nKey(inputName)}.name` - - const widgetConstructor = widgetStore.widgets[inputType] - if (widgetConstructor) { - const { - widget, - minWidth = 1, - minHeight = 1 - } = widgetConstructor( - this, - inputName, - transformInputSpecV2ToV1(inputSpec), - app - ) ?? {} - - if (widget) { - widget.label = st(nameKey, widget.label ?? inputName) - widget.options ??= {} - Object.assign(widget.options, { - inputIsOptional: inputSpec.isOptional, - forceInput: inputSpec.forceInput, - defaultInput: inputSpec.defaultInput, - advanced: inputSpec.advanced, - hidden: inputSpec.hidden - }) - } - - this.#initialMinSize.width = Math.max( - this.#initialMinSize.width, - minWidth - ) - this.#initialMinSize.height = Math.max( - this.#initialMinSize.height, - minHeight - ) - } else { - // Node connection inputs - const shapeOptions = inputSpec.isOptional - ? { shape: RenderShape.HollowCircle } - : {} - - this.addInput(inputName, inputType, { - ...shapeOptions, - localized_name: st(nameKey, inputName) - }) - } - } + for (const inputSpec of Object.values(inputs)) + this.#addInputSocket(inputSpec) + for (const inputSpec of Object.values(inputs)) + this.#addInputWidget(inputSpec) } /** @@ -182,33 +211,57 @@ export const useLitegraphService = () => { this.setSize(s) } - configure(data: any) { - // Keep 'name', 'type', 'shape', and 'localized_name' information from the original node definition. - const merge = ( - current: Record, - incoming: Record - ) => { - const result = { ...incoming } - if (current.widget === undefined && incoming.widget !== undefined) { - // Field must be input as only inputs can be converted - this.inputs.push(current as INodeInputSlot) - return incoming + /** + * Configure the node from a serialised node. Keep 'name', 'type', 'shape', + * and 'localized_name' information from the original node definition. + */ + override configure(data: ISerialisedNode): void { + const RESERVED_KEYS = ['name', 'type', 'shape', 'localized_name'] + + // Note: input name is unique in a node definition, so we can lookup + // input by name. + const inputByName = new Map( + data.inputs?.map((input) => [input.name, input]) ?? [] + ) + // Inputs defined by the node definition. + const definedInputNames = new Set( + this.inputs.map((input) => input.name) + ) + const definedInputs = this.inputs.map((input) => { + const inputData = inputByName.get(input.name) + return inputData + ? { + ...inputData, + // Whether the input has associated widget follows the + // original node definition. + ..._.pick(input, RESERVED_KEYS.concat('widget')) + } + : input + }) + // Extra inputs that potentially dynamically added by custom js logic. + const extraInputs = data.inputs?.filter( + (input) => !definedInputNames.has(input.name) + ) + data.inputs = [...definedInputs, ...(extraInputs ?? [])] + + // Note: output name is not unique, so we cannot lookup output by name. + // Use index instead. + data.outputs = _.zip(this.outputs, data.outputs).map( + ([output, outputData]) => { + // If there are extra outputs in the serialised node, use them directly. + // There are currently custom nodes that dynamically add outputs via + // js logic. + if (!output) return outputData as ISerialisableNodeOutput + + return outputData + ? { + ...outputData, + ..._.pick(output, RESERVED_KEYS) + } + : output } - for (const key of ['name', 'type', 'shape', 'localized_name']) { - if (current[key] !== undefined) { - result[key] = current[key] - } - } - return result - } - for (const field of ['inputs', 'outputs']) { - const slots = data[field] ?? [] - // @ts-expect-error fixme ts strict error - data[field] = slots.map((slot, i) => - // @ts-expect-error fixme ts strict error - merge(this[field][i] ?? {}, slot) - ) - } + ) + super.configure(data) } } diff --git a/src/stores/widgetStore.ts b/src/stores/widgetStore.ts index 6ad0fd548..b22ac17b7 100644 --- a/src/stores/widgetStore.ts +++ b/src/stores/widgetStore.ts @@ -10,23 +10,21 @@ import { ComfyWidgetConstructor, ComfyWidgets } from '@/scripts/widgets' export const useWidgetStore = defineStore('widget', () => { const coreWidgets = ComfyWidgets - const customWidgets = ref>({}) - const widgets = computed(() => ({ - ...customWidgets.value, - ...coreWidgets - })) + const customWidgets = ref>(new Map()) + const widgets = computed>( + () => new Map([...customWidgets.value, ...Object.entries(coreWidgets)]) + ) function inputIsWidget(spec: InputSpecV2 | InputSpecV1) { const type = Array.isArray(spec) ? getInputSpecType(spec) : spec.type - return type in widgets.value + return widgets.value.has(type) } function registerCustomWidgets( newWidgets: Record ) { - customWidgets.value = { - ...customWidgets.value, - ...newWidgets + for (const [type, widget] of Object.entries(newWidgets)) { + customWidgets.value.set(type, widget) } } diff --git a/src/types/litegraph-augmentation.d.ts b/src/types/litegraph-augmentation.d.ts index 8a6d18a4d..ed96d42d2 100644 --- a/src/types/litegraph-augmentation.d.ts +++ b/src/types/litegraph-augmentation.d.ts @@ -37,6 +37,8 @@ declare module '@comfyorg/litegraph/dist/types/widgets' { /** * Whether the widget defaults to input state. Can still be converted back * to widget state. + * @deprecated Widget to input conversion is no longer necessary, as they co-exist now. + * This option no longer has any effect. */ defaultInput?: boolean } @@ -60,16 +62,6 @@ declare module '@comfyorg/litegraph/dist/types/widgets' { * See extensions/core/dynamicPrompts.ts */ dynamicPrompts?: boolean - - /** - * Widget conversion fields - */ - origType?: string - origComputeSize?: (width: number) => Size - origSerializeValue?: ( - node: LGraphNode, - index: number - ) => Promise | unknown } } diff --git a/src/utils/executionUtil.ts b/src/utils/executionUtil.ts index 976894a76..39be3a543 100644 --- a/src/utils/executionUtil.ts +++ b/src/utils/executionUtil.ts @@ -38,6 +38,13 @@ export const graphToPrompt = async ( } } + // Remove all unconnected widget input slots + for (const node of workflow.nodes) { + node.inputs = node.inputs?.filter( + (input) => !(input.widget && input.link === null) + ) + } + const output: ComfyApiWorkflow = {} // Process nodes in order of execution for (const outerNode of graph.computeExecutionOrder(false)) {