From 9762b7884cc9bc7267f3e2115bdbe02d3bdccebc Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:20:35 +0100 Subject: [PATCH] Convert more js to ts (#101) * Add testing for ComfyUI examples * Remove examples, add test to github action * Create dir * Update readme * Convert additional files to ts --- src/extensions/core/{index.js => index.ts} | 0 .../core/{rerouteNode.js => rerouteNode.ts} | 13 +- .../core/{slotDefaults.js => slotDefaults.ts} | 2 +- .../core/{widgetInputs.js => widgetInputs.ts} | 738 +++++++++--------- src/types/litegraph-augmentation.d.ts | 46 ++ src/types/litegraph-core-augmentation.d.ts | 19 + 6 files changed, 452 insertions(+), 366 deletions(-) rename src/extensions/core/{index.js => index.ts} (100%) rename src/extensions/core/{rerouteNode.js => rerouteNode.ts} (96%) rename src/extensions/core/{slotDefaults.js => slotDefaults.ts} (98%) rename src/extensions/core/{widgetInputs.js => widgetInputs.ts} (59%) create mode 100644 src/types/litegraph-augmentation.d.ts create mode 100644 src/types/litegraph-core-augmentation.d.ts diff --git a/src/extensions/core/index.js b/src/extensions/core/index.ts similarity index 100% rename from src/extensions/core/index.js rename to src/extensions/core/index.ts diff --git a/src/extensions/core/rerouteNode.js b/src/extensions/core/rerouteNode.ts similarity index 96% rename from src/extensions/core/rerouteNode.js rename to src/extensions/core/rerouteNode.ts index 23694a8fe..79c4ed5d5 100644 --- a/src/extensions/core/rerouteNode.js +++ b/src/extensions/core/rerouteNode.ts @@ -1,13 +1,20 @@ import { app } from "../../scripts/app"; import { mergeIfValid, getWidgetConfig, setWidgetConfig } from "./widgetInputs"; -import { LiteGraph, LGraphCanvas } from "@comfyorg/litegraph"; +import { LiteGraph, LGraphCanvas, LGraphNode } from "@comfyorg/litegraph"; // Node that allows you to redirect connections for cleaner graphs app.registerExtension({ name: "Comfy.RerouteNode", registerCustomNodes(app) { + interface RerouteNode extends LGraphNode { + __outputType?: string; + } + class RerouteNode { + static category: string | undefined; + static defaultVisibility = false; + constructor() { if (!this.properties) { this.properties = {}; @@ -227,7 +234,7 @@ app.registerExtension({ this.properties.showOutputText = !this.properties.showOutputText; if (this.properties.showOutputText) { this.outputs[0].name = - this.__outputType || this.outputs[0].type; + this.__outputType || (this.outputs[0].type as string); } else { this.outputs[0].name = ""; } @@ -273,7 +280,7 @@ app.registerExtension({ app.graph.setDirtyCanvas(true, true); } - computeSize() { + computeSize(): [number, number] { return [ this.properties.showOutputText && this.outputs && this.outputs.length ? Math.max( diff --git a/src/extensions/core/slotDefaults.js b/src/extensions/core/slotDefaults.ts similarity index 98% rename from src/extensions/core/slotDefaults.js rename to src/extensions/core/slotDefaults.ts index a8fb76f30..ae033728f 100644 --- a/src/extensions/core/slotDefaults.js +++ b/src/extensions/core/slotDefaults.ts @@ -59,7 +59,7 @@ app.registerExtension({ var outputs = nodeData["output"]; for (const key in outputs) { - var type = outputs[key]; + var type = outputs[key] as string; if (!(type in this.slot_types_default_in)) { this.slot_types_default_in[type] = ["Reroute"]; // ["Reroute", "Primitive"]; primitive doesn't always work :'() } diff --git a/src/extensions/core/widgetInputs.js b/src/extensions/core/widgetInputs.ts similarity index 59% rename from src/extensions/core/widgetInputs.js rename to src/extensions/core/widgetInputs.ts index b1c08010e..94d578d74 100644 --- a/src/extensions/core/widgetInputs.js +++ b/src/extensions/core/widgetInputs.ts @@ -2,6 +2,7 @@ import { ComfyWidgets, addValueControlWidgets } from "../../scripts/widgets"; import { app } from "../../scripts/app"; import { applyTextReplacements } from "../../scripts/utils"; import { LiteGraph } from "@comfyorg/litegraph"; +import type { LGraphNode, INodeInputSlot, IWidget } from "@comfyorg/litegraph"; const CONVERTED_TYPE = "converted-widget"; const VALID_TYPES = ["STRING", "combo", "number", "BOOLEAN"]; @@ -9,6 +10,368 @@ const CONFIG = Symbol(); const GET_CONFIG = Symbol(); const TARGET = Symbol(); // Used for reroutes to specify the real target widget +interface PrimitiveNode extends LGraphNode {} + +const replacePropertyName = "Run widget replace on values"; +class PrimitiveNode { + controlValues: any[]; + lastType: string; + static category: string; + constructor() { + this.addOutput("connect to widget input", "*"); + this.serialize_widgets = true; + this.isVirtualNode = true; + + if (!this.properties || !(replacePropertyName in this.properties)) { + this.addProperty(replacePropertyName, false, "boolean"); + } + } + + applyToGraph(extraLinks = []) { + if (!this.outputs[0].links?.length) return; + + function get_links(node) { + let links = []; + for (const l of node.outputs[0].links) { + const linkInfo = app.graph.links[l]; + const n = node.graph.getNodeById(linkInfo.target_id); + if (n.type == "Reroute") { + links = links.concat(get_links(n)); + } else { + links.push(l); + } + } + return links; + } + + let links = [ + ...get_links(this).map((l) => app.graph.links[l]), + ...extraLinks, + ]; + let v = this.widgets?.[0].value; + if (v && this.properties[replacePropertyName]) { + v = applyTextReplacements(app, v); + } + + // For each output link copy our value over the original widget value + for (const linkInfo of links) { + const node = this.graph.getNodeById(linkInfo.target_id); + const input = node.inputs[linkInfo.target_slot]; + let widget; + if (input.widget[TARGET]) { + widget = input.widget[TARGET]; + } else { + const widgetName = (input.widget as { name: string }).name; + if (widgetName) { + widget = node.widgets.find((w) => w.name === widgetName); + } + } + + if (widget) { + widget.value = v; + if (widget.callback) { + widget.callback( + widget.value, + app.canvas, + node, + app.canvas.graph_mouse, + {} + ); + } + } + } + } + + refreshComboInNode() { + const widget = this.widgets?.[0]; + if (widget?.type === "combo") { + widget.options.values = this.outputs[0].widget[GET_CONFIG]()[0]; + + if (!widget.options.values.includes(widget.value)) { + widget.value = widget.options.values[0]; + (widget.callback as Function)(widget.value); + } + } + } + + onAfterGraphConfigured() { + if (this.outputs[0].links?.length && !this.widgets?.length) { + // TODO: Review this check + // @ts-ignore + if (!this.#onFirstConnection()) return; + + // Populate widget values from config data + if (this.widgets) { + for (let i = 0; i < this.widgets_values.length; i++) { + const w = this.widgets[i]; + if (w) { + w.value = this.widgets_values[i]; + } + } + } + + // Merge values if required + this.#mergeWidgetConfig(); + } + } + + onConnectionsChange(_, index, connected) { + if (app.configuringGraph) { + // Dont run while the graph is still setting up + return; + } + + const links = this.outputs[0].links; + if (connected) { + if (links?.length && !this.widgets?.length) { + this.#onFirstConnection(); + } + } else { + // We may have removed a link that caused the constraints to change + this.#mergeWidgetConfig(); + + if (!links?.length) { + this.onLastDisconnect(); + } + } + } + + onConnectOutput(slot, type, input, target_node, target_slot) { + // Fires before the link is made allowing us to reject it if it isn't valid + // No widget, we cant connect + if (!input.widget) { + if (!(input.type in ComfyWidgets)) return false; + } + + if (this.outputs[slot].links?.length) { + const valid = this.#isValidConnection(input); + if (valid) { + // On connect of additional outputs, copy our value to their widget + this.applyToGraph([{ target_id: target_node.id, target_slot }]); + } + return valid; + } + } + + #onFirstConnection(recreating?: boolean) { + // First connection can fire before the graph is ready on initial load so random things can be missing + if (!this.outputs[0].links) { + this.onLastDisconnect(); + return; + } + const linkId = this.outputs[0].links[0]; + const link = this.graph.links[linkId]; + if (!link) return; + + const theirNode = this.graph.getNodeById(link.target_id); + if (!theirNode || !theirNode.inputs) return; + + const input = theirNode.inputs[link.target_slot]; + if (!input) return; + + let widget; + if (!input.widget) { + if (!(input.type in ComfyWidgets)) return; + widget = { name: input.name, [GET_CONFIG]: () => [input.type, {}] }; //fake widget + } else { + widget = input.widget; + } + + const config = widget[GET_CONFIG]?.(); + if (!config) return; + + const { type } = getWidgetType(config); + // Update our output to restrict to the widget type + this.outputs[0].type = type; + this.outputs[0].name = type; + this.outputs[0].widget = widget; + + this.#createWidget( + widget[CONFIG] ?? config, + theirNode, + widget.name, + recreating, + widget[TARGET] + ); + } + + #createWidget(inputData, node, widgetName, recreating, targetWidget) { + let type = inputData[0]; + + if (type instanceof Array) { + type = "COMBO"; + } + + let widget; + if (type in ComfyWidgets) { + widget = (ComfyWidgets[type](this, "value", inputData, app) || {}).widget; + } else { + widget = this.addWidget(type, "value", null, () => {}, {}); + } + + if (targetWidget) { + widget.value = targetWidget.value; + } else if (node?.widgets && widget) { + const theirWidget = node.widgets.find((w) => w.name === widgetName); + if (theirWidget) { + widget.value = theirWidget.value; + } + } + + if ( + !inputData?.[1]?.control_after_generate && + (widget.type === "number" || widget.type === "combo") + ) { + let control_value = this.widgets_values?.[1]; + if (!control_value) { + control_value = "fixed"; + } + addValueControlWidgets( + this, + widget, + control_value as string, + undefined, + inputData + ); + let filter = this.widgets_values?.[2]; + if (filter && this.widgets.length === 3) { + this.widgets[2].value = filter; + } + } + + // Restore any saved control values + const controlValues = this.controlValues; + if ( + this.lastType === this.widgets[0].type && + controlValues?.length === this.widgets.length - 1 + ) { + for (let i = 0; i < controlValues.length; i++) { + this.widgets[i + 1].value = controlValues[i]; + } + } + + // When our value changes, update other widgets to reflect our changes + // e.g. so LoadImage shows correct image + const callback = widget.callback; + const self = this; + widget.callback = function () { + const r = callback ? callback.apply(this, arguments) : undefined; + self.applyToGraph(); + return r; + }; + + if (!recreating) { + // Grow our node if required + const sz = this.computeSize(); + if (this.size[0] < sz[0]) { + this.size[0] = sz[0]; + } + if (this.size[1] < sz[1]) { + this.size[1] = sz[1]; + } + + requestAnimationFrame(() => { + if (this.onResize) { + this.onResize(this.size); + } + }); + } + } + + recreateWidget() { + const values = this.widgets?.map((w) => w.value); + this.#removeWidgets(); + this.#onFirstConnection(true); + if (values?.length) { + for (let i = 0; i < this.widgets?.length; i++) + this.widgets[i].value = values[i]; + } + return this.widgets?.[0]; + } + + #mergeWidgetConfig() { + // Merge widget configs if the node has multiple outputs + const output = this.outputs[0]; + const links = output.links; + + const hasConfig = !!output.widget[CONFIG]; + if (hasConfig) { + delete output.widget[CONFIG]; + } + + if (links?.length < 2 && hasConfig) { + // Copy the widget options from the source + if (links.length) { + this.recreateWidget(); + } + + return; + } + + const config1 = output.widget[GET_CONFIG](); + const isNumber = config1[0] === "INT" || config1[0] === "FLOAT"; + if (!isNumber) return; + + for (const linkId of links) { + const link = app.graph.links[linkId]; + if (!link) continue; // Can be null when removing a node + + const theirNode = app.graph.getNodeById(link.target_id); + const theirInput = theirNode.inputs[link.target_slot]; + + // Call is valid connection so it can merge the configs when validating + this.#isValidConnection(theirInput, hasConfig); + } + } + + #isValidConnection(input: INodeInputSlot, forceUpdate?: boolean) { + // Only allow connections where the configs match + const output = this.outputs[0]; + const config2 = input.widget[GET_CONFIG](); + return !!mergeIfValid.call( + this, + output, + config2, + forceUpdate, + this.recreateWidget + ); + } + + #removeWidgets() { + if (this.widgets) { + // Allow widgets to cleanup + for (const w of this.widgets) { + if (w.onRemove) { + w.onRemove(); + } + } + + // Temporarily store the current values in case the node is being recreated + // e.g. by group node conversion + this.controlValues = []; + this.lastType = this.widgets[0]?.type; + for (let i = 1; i < this.widgets.length; i++) { + this.controlValues.push(this.widgets[i].value); + } + setTimeout(() => { + delete this.lastType; + delete this.controlValues; + }, 15); + this.widgets.length = 0; + } + } + + onLastDisconnect() { + // We cant remove + re-add the output here as if you drag a link over the same link + // it removes, then re-adds, causing it to break + this.outputs[0].type = "*"; + this.outputs[0].name = "connect to widget input"; + delete this.outputs[0].widget; + + this.#removeWidgets(); + } +} + export function getWidgetConfig(slot) { return slot.widget[CONFIG] ?? slot.widget[GET_CONFIG](); } @@ -138,7 +501,11 @@ function isValidCombo(combo, obj) { return true; } -export function setWidgetConfig(slot, config, target) { +function isPrimitiveNode(node: LGraphNode): node is PrimitiveNode { + return node.type === "PrimitiveNode"; +} + +export function setWidgetConfig(slot, config, target?: IWidget) { if (!slot.widget) return; if (config) { slot.widget[GET_CONFIG] = () => config; @@ -151,7 +518,7 @@ export function setWidgetConfig(slot, config, target) { const link = app.graph.links[slot.link]; if (link) { const originNode = app.graph.getNodeById(link.origin_id); - if (originNode.type === "PrimitiveNode") { + if (isPrimitiveNode(originNode)) { if (config) { originNode.recreateWidget(); } else if (!app.configuringGraph) { @@ -166,9 +533,9 @@ export function setWidgetConfig(slot, config, target) { export function mergeIfValid( output, config2, - forceUpdate, - recreateWidget, - config1 + forceUpdate?: boolean, + recreateWidget?: () => void, + config1?: unknown ) { if (!config1) { config1 = output.widget[CONFIG] ?? output.widget[GET_CONFIG](); @@ -489,7 +856,10 @@ app.registerExtension({ app.graph.add(node); // Calculate a position that wont directly overlap another node - const pos = [this.pos[0] - node.size[0] - 30, this.pos[1]]; + const pos: [number, number] = [ + this.pos[0] - node.size[0] - 30, + this.pos[1], + ]; while (isNodeAtPos(pos)) { pos[1] += LiteGraph.NODE_TITLE_HEIGHT; } @@ -537,362 +907,6 @@ app.registerExtension({ }; }, registerCustomNodes() { - const replacePropertyName = "Run widget replace on values"; - class PrimitiveNode { - constructor() { - this.addOutput("connect to widget input", "*"); - this.serialize_widgets = true; - this.isVirtualNode = true; - - if (!this.properties || !(replacePropertyName in this.properties)) { - this.addProperty(replacePropertyName, false, "boolean"); - } - } - - applyToGraph(extraLinks = []) { - if (!this.outputs[0].links?.length) return; - - function get_links(node) { - let links = []; - for (const l of node.outputs[0].links) { - const linkInfo = app.graph.links[l]; - const n = node.graph.getNodeById(linkInfo.target_id); - if (n.type == "Reroute") { - links = links.concat(get_links(n)); - } else { - links.push(l); - } - } - return links; - } - - let links = [ - ...get_links(this).map((l) => app.graph.links[l]), - ...extraLinks, - ]; - let v = this.widgets?.[0].value; - if (v && this.properties[replacePropertyName]) { - v = applyTextReplacements(app, v); - } - - // For each output link copy our value over the original widget value - for (const linkInfo of links) { - const node = this.graph.getNodeById(linkInfo.target_id); - const input = node.inputs[linkInfo.target_slot]; - let widget; - if (input.widget[TARGET]) { - widget = input.widget[TARGET]; - } else { - const widgetName = input.widget.name; - if (widgetName) { - widget = node.widgets.find((w) => w.name === widgetName); - } - } - - if (widget) { - widget.value = v; - if (widget.callback) { - widget.callback( - widget.value, - app.canvas, - node, - app.canvas.graph_mouse, - {} - ); - } - } - } - } - - refreshComboInNode() { - const widget = this.widgets?.[0]; - if (widget?.type === "combo") { - widget.options.values = this.outputs[0].widget[GET_CONFIG]()[0]; - - if (!widget.options.values.includes(widget.value)) { - widget.value = widget.options.values[0]; - widget.callback(widget.value); - } - } - } - - onAfterGraphConfigured() { - if (this.outputs[0].links?.length && !this.widgets?.length) { - if (!this.#onFirstConnection()) return; - - // Populate widget values from config data - if (this.widgets) { - for (let i = 0; i < this.widgets_values.length; i++) { - const w = this.widgets[i]; - if (w) { - w.value = this.widgets_values[i]; - } - } - } - - // Merge values if required - this.#mergeWidgetConfig(); - } - } - - onConnectionsChange(_, index, connected) { - if (app.configuringGraph) { - // Dont run while the graph is still setting up - return; - } - - const links = this.outputs[0].links; - if (connected) { - if (links?.length && !this.widgets?.length) { - this.#onFirstConnection(); - } - } else { - // We may have removed a link that caused the constraints to change - this.#mergeWidgetConfig(); - - if (!links?.length) { - this.onLastDisconnect(); - } - } - } - - onConnectOutput(slot, type, input, target_node, target_slot) { - // Fires before the link is made allowing us to reject it if it isn't valid - // No widget, we cant connect - if (!input.widget) { - if (!(input.type in ComfyWidgets)) return false; - } - - if (this.outputs[slot].links?.length) { - const valid = this.#isValidConnection(input); - if (valid) { - // On connect of additional outputs, copy our value to their widget - this.applyToGraph([{ target_id: target_node.id, target_slot }]); - } - return valid; - } - } - - #onFirstConnection(recreating) { - // First connection can fire before the graph is ready on initial load so random things can be missing - if (!this.outputs[0].links) { - this.onLastDisconnect(); - return; - } - const linkId = this.outputs[0].links[0]; - const link = this.graph.links[linkId]; - if (!link) return; - - const theirNode = this.graph.getNodeById(link.target_id); - if (!theirNode || !theirNode.inputs) return; - - const input = theirNode.inputs[link.target_slot]; - if (!input) return; - - let widget; - if (!input.widget) { - if (!(input.type in ComfyWidgets)) return; - widget = { name: input.name, [GET_CONFIG]: () => [input.type, {}] }; //fake widget - } else { - widget = input.widget; - } - - const config = widget[GET_CONFIG]?.(); - if (!config) return; - - const { type } = getWidgetType(config); - // Update our output to restrict to the widget type - this.outputs[0].type = type; - this.outputs[0].name = type; - this.outputs[0].widget = widget; - - this.#createWidget( - widget[CONFIG] ?? config, - theirNode, - widget.name, - recreating, - widget[TARGET] - ); - } - - #createWidget(inputData, node, widgetName, recreating, targetWidget) { - let type = inputData[0]; - - if (type instanceof Array) { - type = "COMBO"; - } - - let widget; - if (type in ComfyWidgets) { - widget = (ComfyWidgets[type](this, "value", inputData, app) || {}) - .widget; - } else { - widget = this.addWidget(type, "value", null, () => {}, {}); - } - - if (targetWidget) { - widget.value = targetWidget.value; - } else if (node?.widgets && widget) { - const theirWidget = node.widgets.find((w) => w.name === widgetName); - if (theirWidget) { - widget.value = theirWidget.value; - } - } - - if ( - !inputData?.[1]?.control_after_generate && - (widget.type === "number" || widget.type === "combo") - ) { - let control_value = this.widgets_values?.[1]; - if (!control_value) { - control_value = "fixed"; - } - addValueControlWidgets( - this, - widget, - control_value, - undefined, - inputData - ); - let filter = this.widgets_values?.[2]; - if (filter && this.widgets.length === 3) { - this.widgets[2].value = filter; - } - } - - // Restore any saved control values - const controlValues = this.controlValues; - if ( - this.lastType === this.widgets[0].type && - controlValues?.length === this.widgets.length - 1 - ) { - for (let i = 0; i < controlValues.length; i++) { - this.widgets[i + 1].value = controlValues[i]; - } - } - - // When our value changes, update other widgets to reflect our changes - // e.g. so LoadImage shows correct image - const callback = widget.callback; - const self = this; - widget.callback = function () { - const r = callback ? callback.apply(this, arguments) : undefined; - self.applyToGraph(); - return r; - }; - - if (!recreating) { - // Grow our node if required - const sz = this.computeSize(); - if (this.size[0] < sz[0]) { - this.size[0] = sz[0]; - } - if (this.size[1] < sz[1]) { - this.size[1] = sz[1]; - } - - requestAnimationFrame(() => { - if (this.onResize) { - this.onResize(this.size); - } - }); - } - } - - recreateWidget() { - const values = this.widgets?.map((w) => w.value); - this.#removeWidgets(); - this.#onFirstConnection(true); - if (values?.length) { - for (let i = 0; i < this.widgets?.length; i++) - this.widgets[i].value = values[i]; - } - return this.widgets?.[0]; - } - - #mergeWidgetConfig() { - // Merge widget configs if the node has multiple outputs - const output = this.outputs[0]; - const links = output.links; - - const hasConfig = !!output.widget[CONFIG]; - if (hasConfig) { - delete output.widget[CONFIG]; - } - - if (links?.length < 2 && hasConfig) { - // Copy the widget options from the source - if (links.length) { - this.recreateWidget(); - } - - return; - } - - const config1 = output.widget[GET_CONFIG](); - const isNumber = config1[0] === "INT" || config1[0] === "FLOAT"; - if (!isNumber) return; - - for (const linkId of links) { - const link = app.graph.links[linkId]; - if (!link) continue; // Can be null when removing a node - - const theirNode = app.graph.getNodeById(link.target_id); - const theirInput = theirNode.inputs[link.target_slot]; - - // Call is valid connection so it can merge the configs when validating - this.#isValidConnection(theirInput, hasConfig); - } - } - - #isValidConnection(input, forceUpdate) { - // Only allow connections where the configs match - const output = this.outputs[0]; - const config2 = input.widget[GET_CONFIG](); - return !!mergeIfValid.call( - this, - output, - config2, - forceUpdate, - this.recreateWidget - ); - } - - #removeWidgets() { - if (this.widgets) { - // Allow widgets to cleanup - for (const w of this.widgets) { - if (w.onRemove) { - w.onRemove(); - } - } - - // Temporarily store the current values in case the node is being recreated - // e.g. by group node conversion - this.controlValues = []; - this.lastType = this.widgets[0]?.type; - for (let i = 1; i < this.widgets.length; i++) { - this.controlValues.push(this.widgets[i].value); - } - setTimeout(() => { - delete this.lastType; - delete this.controlValues; - }, 15); - this.widgets.length = 0; - } - } - - onLastDisconnect() { - // We cant remove + re-add the output here as if you drag a link over the same link - // it removes, then re-adds, causing it to break - this.outputs[0].type = "*"; - this.outputs[0].name = "connect to widget input"; - delete this.outputs[0].widget; - - this.#removeWidgets(); - } - } - LiteGraph.registerNodeType( "PrimitiveNode", Object.assign(PrimitiveNode, { diff --git a/src/types/litegraph-augmentation.d.ts b/src/types/litegraph-augmentation.d.ts new file mode 100644 index 000000000..8a2adfe76 --- /dev/null +++ b/src/types/litegraph-augmentation.d.ts @@ -0,0 +1,46 @@ +import "@comfyorg/litegraph"; + +/** + * ComfyUI extensions of litegraph + */ +declare module "@comfyorg/litegraph" { + interface LGraphNode { + /** + * Callback fired on each node after the graph is configured + */ + onAfterGraphConfigured?(): void; + + /** + * If the node is a frontend only node and should not be serialized into the prompt. + */ + isVirtualNode?: boolean; + } + + interface IWidget { + /** + * Allows for additional cleanup when removing a widget when converting to input. + */ + onRemove?(): void; + } + + interface INodeOutputSlot { + widget?: unknown; + } + + interface INodeInputSlot { + widget?: unknown; + } +} + +/** + * Extended types for litegraph, to be merged upstream once it has stabilized. + */ +declare module "@comfyorg/litegraph" { + interface INodeInputSlot { + pos?: [number, number]; + } + + interface LGraphNode { + widgets_values?: unknown[]; + } +} diff --git a/src/types/litegraph-core-augmentation.d.ts b/src/types/litegraph-core-augmentation.d.ts new file mode 100644 index 000000000..fdfdd537e --- /dev/null +++ b/src/types/litegraph-core-augmentation.d.ts @@ -0,0 +1,19 @@ +/** + Extended types for litegraph, to be merged upstream once it has stabilized. + Augmenting the LiteGraph type really didn't want to work, however doing it like this seems to allow it. +*/ +declare module "@comfyorg/litegraph" { + interface LiteGraphExtended { + search_filter_enabled: boolean; + middle_click_slot_add_default_node: boolean; + registered_slot_out_types: Record; + registered_slot_in_types: Record; + slot_types_out: string[]; + slot_types_default_out: Record; + slot_types_default_in: Record; + } + + import type { LiteGraph as LG } from "@comfyorg/litegraph/src/litegraph"; + export const LiteGraph: LiteGraphExtended & typeof LG; + export * from "@comfyorg/litegraph/src/litegraph"; +}