mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-08 09:00:05 +00:00
refactor(groupNode): remove all @ts-expect-error suppressions
Amp-Thread-ID: https://ampcode.com/threads/T-019ba9df-6ee8-7541-9105-c657cd9ec692 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
import { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'
|
||||
import { t } from '@/i18n'
|
||||
import type { GroupNodeWorkflowData } from '@/lib/litegraph/src/LGraph'
|
||||
import type { SerialisedLLinkArray } from '@/lib/litegraph/src/LLink'
|
||||
import type {
|
||||
GroupNodeInputConfig,
|
||||
GroupNodeInputsSpec,
|
||||
GroupNodeOutputType,
|
||||
PartialLinkInfo
|
||||
} from './groupNodeTypes'
|
||||
import { LLink, type SerialisedLLinkArray } from '@/lib/litegraph/src/LLink'
|
||||
import type { NodeId } from '@/lib/litegraph/src/LGraphNode'
|
||||
import type { IContextMenuValue } from '@/lib/litegraph/src/interfaces'
|
||||
import {
|
||||
type ExecutableLGraphNode,
|
||||
type ExecutionId,
|
||||
type ISerialisedNode,
|
||||
LGraphNode,
|
||||
type LGraphNodeConstructor,
|
||||
LiteGraph,
|
||||
@@ -54,9 +61,8 @@ interface GroupNodeOutput {
|
||||
|
||||
interface GroupNodeData extends Omit<
|
||||
GroupNodeWorkflowData['nodes'][number],
|
||||
'inputs' | 'outputs'
|
||||
'inputs' | 'outputs' | 'widgets_values'
|
||||
> {
|
||||
title?: string
|
||||
widgets_values?: unknown[]
|
||||
inputs?: GroupNodeInput[]
|
||||
outputs?: GroupNodeOutput[]
|
||||
@@ -241,7 +247,13 @@ export class GroupNodeConfig {
|
||||
>
|
||||
nodeInputs: Record<number, Record<string, string>>
|
||||
outputVisibility: boolean[]
|
||||
nodeDef: (ComfyNodeDef & { [GROUP]: GroupNodeConfig }) | undefined
|
||||
nodeDef:
|
||||
| (Omit<ComfyNodeDef, 'input' | 'output'> & {
|
||||
input: GroupNodeInputsSpec
|
||||
output: GroupNodeOutputType[]
|
||||
[GROUP]: GroupNodeConfig
|
||||
})
|
||||
| undefined
|
||||
inputs!: unknown[]
|
||||
linksFrom!: LinksFromMap
|
||||
linksTo!: LinksToMap
|
||||
@@ -297,8 +309,11 @@ export class GroupNodeConfig {
|
||||
}
|
||||
this.#convertedToProcess = []
|
||||
if (!this.nodeDef) return
|
||||
await app.registerNodeDef(`${PREFIX}${SEPARATOR}` + this.name, this.nodeDef)
|
||||
useNodeDefStore().addNodeDef(this.nodeDef)
|
||||
const finalizedDef = this.nodeDef as ComfyNodeDef & {
|
||||
[GROUP]: GroupNodeConfig
|
||||
}
|
||||
await app.registerNodeDef(`${PREFIX}${SEPARATOR}` + this.name, finalizedDef)
|
||||
useNodeDefStore().addNodeDef(finalizedDef)
|
||||
}
|
||||
|
||||
getLinks() {
|
||||
@@ -520,9 +535,13 @@ export class GroupNodeConfig {
|
||||
node: GroupNodeData,
|
||||
inputName: string,
|
||||
seenInputs: Record<string, number>,
|
||||
config: unknown[],
|
||||
inputConfig: unknown[],
|
||||
extra?: Record<string, unknown>
|
||||
) {
|
||||
): {
|
||||
name: string
|
||||
config: GroupNodeInputConfig
|
||||
customConfig: { name?: string; visible?: boolean } | undefined
|
||||
} {
|
||||
const nodeConfig = this.nodeData.config?.[node.index ?? -1] as
|
||||
| NodeConfigEntry
|
||||
| undefined
|
||||
@@ -543,28 +562,34 @@ export class GroupNodeConfig {
|
||||
}
|
||||
seenInputs[key] = (seenInputs[key] ?? 1) + 1
|
||||
|
||||
const typeName = String(inputConfig[0])
|
||||
let options =
|
||||
typeof inputConfig[1] === 'object' && inputConfig[1] !== null
|
||||
? (inputConfig[1] as Record<string, unknown>)
|
||||
: undefined
|
||||
|
||||
if (inputName === 'seed' || inputName === 'noise_seed') {
|
||||
if (!extra) extra = {}
|
||||
extra.control_after_generate = `${prefix}control_after_generate`
|
||||
}
|
||||
if (config[0] === 'IMAGEUPLOAD') {
|
||||
if (typeName === 'IMAGEUPLOAD') {
|
||||
if (!extra) extra = {}
|
||||
const nodeIndex = node.index ?? -1
|
||||
const configOptions =
|
||||
typeof config[1] === 'object' && config[1] !== null ? config[1] : {}
|
||||
const widgetKey =
|
||||
'widget' in configOptions && typeof configOptions.widget === 'string'
|
||||
? configOptions.widget
|
||||
options && 'widget' in options && typeof options.widget === 'string'
|
||||
? options.widget
|
||||
: 'image'
|
||||
extra.widget = this.oldToNewWidgetMap[nodeIndex]?.[widgetKey] ?? 'image'
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
const configObj =
|
||||
typeof config[1] === 'object' && config[1] ? config[1] : {}
|
||||
config = [config[0], { ...configObj, ...extra }]
|
||||
options = { ...(options ?? {}), ...extra }
|
||||
}
|
||||
|
||||
const config: GroupNodeInputConfig = options
|
||||
? [typeName, options]
|
||||
: [typeName]
|
||||
|
||||
return { name, config, customConfig }
|
||||
}
|
||||
|
||||
@@ -608,7 +633,6 @@ export class GroupNodeConfig {
|
||||
inputs[inputName] as unknown[]
|
||||
)
|
||||
if (this.nodeDef?.input?.required) {
|
||||
// @ts-expect-error legacy dynamic input assignment
|
||||
this.nodeDef.input.required[name] = config
|
||||
}
|
||||
widgetMap[inputName] = name
|
||||
@@ -641,14 +665,15 @@ export class GroupNodeConfig {
|
||||
unknown,
|
||||
Record<string, unknown>
|
||||
]
|
||||
const output = { widget: primitiveConfig }
|
||||
const output = { widget: primitiveConfig } as unknown as Parameters<
|
||||
typeof mergeIfValid
|
||||
>[0]
|
||||
const config = mergeIfValid(
|
||||
// @ts-expect-error slot type mismatch - legacy API
|
||||
output,
|
||||
targetWidget,
|
||||
targetWidget as Parameters<typeof mergeIfValid>[1],
|
||||
false,
|
||||
undefined,
|
||||
primitiveConfig
|
||||
primitiveConfig as Parameters<typeof mergeIfValid>[4]
|
||||
)
|
||||
const inputConfig = inputs[inputName]?.[1]
|
||||
primitiveConfig[1] =
|
||||
@@ -713,7 +738,6 @@ export class GroupNodeConfig {
|
||||
if (customConfig?.visible === false) continue
|
||||
|
||||
if (this.nodeDef?.input?.required) {
|
||||
// @ts-expect-error legacy dynamic input assignment
|
||||
this.nodeDef.input.required[name] = config
|
||||
}
|
||||
inputMap[i] = this.inputCount++
|
||||
@@ -757,7 +781,6 @@ export class GroupNodeConfig {
|
||||
)
|
||||
|
||||
if (this.nodeDef?.input?.required) {
|
||||
// @ts-expect-error legacy dynamic input assignment
|
||||
this.nodeDef.input.required[name] = config
|
||||
}
|
||||
this.newToOldWidgetMap[name] = { node, inputName }
|
||||
@@ -851,8 +874,7 @@ export class GroupNodeConfig {
|
||||
node,
|
||||
slot: outputId
|
||||
}
|
||||
// @ts-expect-error legacy dynamic output type assignment
|
||||
this.nodeDef.output.push(defOutput[outputId])
|
||||
this.nodeDef.output.push(defOutput[outputId] as GroupNodeOutputType)
|
||||
this.nodeDef.output_is_list?.push(
|
||||
def.output_is_list?.[outputId] ?? false
|
||||
)
|
||||
@@ -951,8 +973,13 @@ export class GroupNodeHandler {
|
||||
|
||||
for (const w of innerNode.widgets ?? []) {
|
||||
if (w.type === 'converted-widget') {
|
||||
// @ts-expect-error legacy widget property for converted widgets
|
||||
w.serializeValue = w.origSerializeValue
|
||||
type SerializeValueFn = (node: LGraphNode, index: number) => unknown
|
||||
const convertedWidget = w as typeof w & {
|
||||
origSerializeValue?: SerializeValueFn
|
||||
}
|
||||
if (convertedWidget.origSerializeValue) {
|
||||
w.serializeValue = convertedWidget.origSerializeValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,20 +1005,18 @@ export class GroupNodeHandler {
|
||||
return inputNode
|
||||
}
|
||||
|
||||
// @ts-expect-error returns partial link object, not full LLink
|
||||
innerNode.getInputLink = (slot: number) => {
|
||||
innerNode.getInputLink = ((slot: number): PartialLinkInfo | null => {
|
||||
const nodeIdx = innerNode.index ?? 0
|
||||
const externalSlot = this.groupData.oldToNewInputMap[nodeIdx]?.[slot]
|
||||
if (externalSlot != null) {
|
||||
// The inner node is connected via the group node inputs
|
||||
const linkId = this.node.inputs[externalSlot].link
|
||||
if (linkId == null) return null
|
||||
const existingLink = app.rootGraph.links[linkId]
|
||||
if (!existingLink) return null
|
||||
|
||||
// Use the outer link, but update the target to the inner node
|
||||
return {
|
||||
...existingLink,
|
||||
origin_id: existingLink.origin_id,
|
||||
origin_slot: existingLink.origin_slot,
|
||||
target_id: innerNode.id,
|
||||
target_slot: +slot
|
||||
}
|
||||
@@ -1001,21 +1026,18 @@ export class GroupNodeHandler {
|
||||
if (!innerLink) return null
|
||||
const linkSrcIdx = innerLink[0]
|
||||
if (linkSrcIdx == null) return null
|
||||
// Use the inner link, but update the origin node to be inner node id
|
||||
return {
|
||||
origin_id: innerNodes[Number(linkSrcIdx)].id,
|
||||
origin_slot: innerLink[1],
|
||||
target_id: innerNode.id,
|
||||
target_slot: +slot
|
||||
}
|
||||
}
|
||||
}) as typeof innerNode.getInputLink
|
||||
}
|
||||
}
|
||||
|
||||
this.node.updateLink = (link) => {
|
||||
// Replace the group node reference with the internal node
|
||||
// @ts-expect-error Can this be removed? Or replaced with: LLink.create(link.asSerialisable())
|
||||
link = { ...link }
|
||||
this.node.updateLink = (inputLink) => {
|
||||
const link = LLink.create(inputLink.asSerialisable())
|
||||
const output = this.groupData.newToOldOutputMap[link.origin_slot]
|
||||
if (!output || !this.innerNodes) return null
|
||||
const nodeIdx = output.node.index ?? 0
|
||||
@@ -1063,8 +1085,7 @@ export class GroupNodeHandler {
|
||||
if (!n.type) return null
|
||||
const innerNode = LiteGraph.createNode(n.type)
|
||||
if (!innerNode) return null
|
||||
// @ts-expect-error legacy node data format used for configure
|
||||
innerNode.configure(n)
|
||||
innerNode.configure(n as ISerialisedNode)
|
||||
innerNode.id = `${this.node.id}:${i}`
|
||||
innerNode.graph = this.node.graph
|
||||
return innerNode
|
||||
@@ -1085,10 +1106,9 @@ export class GroupNodeHandler {
|
||||
for (const node of this.innerNodes ?? []) {
|
||||
node.graph ??= this.node.graph
|
||||
|
||||
// Create minimal DTOs rather than cloning the node
|
||||
const currentId = String(node.id)
|
||||
// @ts-expect-error temporary id reassignment for DTO creation
|
||||
node.id = currentId.split(':').at(-1)
|
||||
const shortId = currentId.split(':').at(-1) ?? currentId
|
||||
node.id = shortId
|
||||
const aVeryRealNode = new ExecutableGroupNodeChildDTO(
|
||||
node,
|
||||
subgraphInstanceIdPath,
|
||||
@@ -1103,7 +1123,6 @@ export class GroupNodeHandler {
|
||||
return nodes
|
||||
}
|
||||
|
||||
// @ts-expect-error recreate returns null if creation fails
|
||||
this.node.recreate = async () => {
|
||||
const id = this.node.id
|
||||
const sz = this.node.size
|
||||
@@ -1139,11 +1158,9 @@ export class GroupNodeHandler {
|
||||
this.node as LGraphNode & { convertToNodes: () => LGraphNode[] }
|
||||
).convertToNodes = () => {
|
||||
const addInnerNodes = () => {
|
||||
// Clone the node data so we dont mutate it for other nodes
|
||||
const c = { ...this.groupData.nodeData }
|
||||
c.nodes = [...c.nodes]
|
||||
// @ts-expect-error getInnerNodes called without args in legacy conversion code
|
||||
const innerNodes = this.node.getInnerNodes?.()
|
||||
const innerNodes = this.innerNodes
|
||||
const ids: (string | number)[] = []
|
||||
for (let i = 0; i < c.nodes.length; i++) {
|
||||
let id: string | number | undefined = innerNodes?.[i]?.id
|
||||
@@ -1153,7 +1170,6 @@ export class GroupNodeHandler {
|
||||
} else {
|
||||
ids.push(id)
|
||||
}
|
||||
// @ts-expect-error adding id to node copy for serialization
|
||||
c.nodes[i] = { ...c.nodes[i], id }
|
||||
}
|
||||
deserialiseAndCreate(JSON.stringify(c), app.canvas)
|
||||
@@ -1182,7 +1198,6 @@ export class GroupNodeHandler {
|
||||
|
||||
if (!newNode.widgets || !innerNode) continue
|
||||
|
||||
// @ts-expect-error index property access on ExecutableLGraphNode
|
||||
const map = this.groupData.oldToNewWidgetMap[innerNode.index ?? 0]
|
||||
if (map) {
|
||||
const widgets = Object.keys(map)
|
||||
@@ -1305,14 +1320,13 @@ export class GroupNodeHandler {
|
||||
null,
|
||||
{
|
||||
content: 'Convert to nodes',
|
||||
// @ts-expect-error async callback not expected by legacy menu API
|
||||
callback: async () => {
|
||||
const convertFn = (
|
||||
handlerNode as LGraphNode & {
|
||||
convertToNodes?: () => LGraphNode[]
|
||||
}
|
||||
).convertToNodes
|
||||
return convertFn?.()
|
||||
convertFn?.()
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1519,7 +1533,6 @@ export class GroupNodeHandler {
|
||||
if (!innerNode) continue
|
||||
|
||||
if (innerNode.type === 'PrimitiveNode') {
|
||||
// @ts-expect-error primitiveValue is a custom property on PrimitiveNode
|
||||
innerNode.primitiveValue = newValue
|
||||
const primitiveLinked = this.groupData.primitiveToWidget[nodeIdx]
|
||||
for (const linked of primitiveLinked ?? []) {
|
||||
@@ -1748,12 +1761,7 @@ export class GroupNodeHandler {
|
||||
this.groupData.oldToNewInputMap[Number(targetId)]?.[Number(targetSlot)]
|
||||
if (mappedSlot == null) continue
|
||||
if (typeof originSlot === 'number' || typeof originSlot === 'string') {
|
||||
originNode.connect(
|
||||
originSlot,
|
||||
// @ts-expect-error Valid - uses deprecated interface (node ID instead of node reference)
|
||||
this.node.id,
|
||||
mappedSlot
|
||||
)
|
||||
originNode.connect(originSlot, this.node, mappedSlot)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1783,13 +1791,13 @@ export class GroupNodeHandler {
|
||||
}
|
||||
|
||||
static getHandler(node: LGraphNode): GroupNodeHandler | undefined {
|
||||
// @ts-expect-error GROUP symbol indexing on LGraphNode
|
||||
let handler = node[GROUP] as GroupNodeHandler | undefined
|
||||
// Handler may not be set yet if nodeCreated async hook hasn't run
|
||||
// Create it synchronously if needed
|
||||
type GroupNodeWithHandler = LGraphNode & {
|
||||
[GROUP]?: GroupNodeHandler
|
||||
}
|
||||
let handler = (node as GroupNodeWithHandler)[GROUP]
|
||||
if (!handler && GroupNodeHandler.isGroupNode(node)) {
|
||||
handler = new GroupNodeHandler(node)
|
||||
;(node as LGraphNode & { [GROUP]: GroupNodeHandler })[GROUP] = handler
|
||||
;(node as GroupNodeWithHandler)[GROUP] = handler
|
||||
}
|
||||
return handler
|
||||
}
|
||||
@@ -1948,8 +1956,9 @@ const ext: ComfyExtension = {
|
||||
items.push({
|
||||
content: `Convert to Group Node (Deprecated)`,
|
||||
disabled: !convertEnabled,
|
||||
// @ts-expect-error async callback - legacy menu API doesn't expect Promise
|
||||
callback: async () => convertSelectedNodesToGroupNode()
|
||||
callback: async () => {
|
||||
await convertSelectedNodesToGroupNode()
|
||||
}
|
||||
})
|
||||
|
||||
const groups = canvas.graph?.extra?.groupNodes
|
||||
@@ -1975,8 +1984,9 @@ const ext: ComfyExtension = {
|
||||
{
|
||||
content: `Convert to Group Node (Deprecated)`,
|
||||
disabled: !convertEnabled,
|
||||
// @ts-expect-error async callback - legacy menu API doesn't expect Promise
|
||||
callback: async () => convertSelectedNodesToGroupNode()
|
||||
callback: async () => {
|
||||
await convertSelectedNodesToGroupNode()
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { merge } from 'es-toolkit'
|
||||
|
||||
import { PREFIX, SEPARATOR } from '@/constants/groupNodeConstants'
|
||||
import {
|
||||
type GroupNodeWorkflowData,
|
||||
type LGraphNode,
|
||||
type LGraphNodeConstructor,
|
||||
LiteGraph
|
||||
@@ -13,70 +16,56 @@ import { DraggableList } from '../../scripts/ui/draggableList'
|
||||
import { GroupNodeConfig, GroupNodeHandler } from './groupNode'
|
||||
import './groupNodeManage.css'
|
||||
|
||||
const ORDER: symbol = Symbol()
|
||||
const ORDER: unique symbol = Symbol('ORDER')
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
function merge(target, source) {
|
||||
if (typeof target === 'object' && typeof source === 'object') {
|
||||
for (const key in source) {
|
||||
const sv = source[key]
|
||||
if (typeof sv === 'object') {
|
||||
let tv = target[key]
|
||||
if (!tv) tv = target[key] = {}
|
||||
merge(tv, source[key])
|
||||
} else {
|
||||
target[key] = sv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
interface NodeModification {
|
||||
name?: string
|
||||
visible?: boolean
|
||||
}
|
||||
|
||||
interface OrderModification {
|
||||
order: number
|
||||
}
|
||||
|
||||
type NodeModifications = Record<string, NodeModification> & {
|
||||
[ORDER]?: OrderModification
|
||||
}
|
||||
|
||||
type DragEndEvent = CustomEvent<{
|
||||
element: Element
|
||||
oldPosition: number
|
||||
newPosition: number
|
||||
}>
|
||||
|
||||
export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
tabs: Record<
|
||||
tabs!: Record<
|
||||
'Inputs' | 'Outputs' | 'Widgets',
|
||||
{ tab: HTMLAnchorElement; page: HTMLElement }
|
||||
>
|
||||
selectedNodeIndex: number | null | undefined
|
||||
selectedTab: keyof ManageGroupDialog['tabs'] = 'Inputs'
|
||||
selectedGroup: string | undefined
|
||||
modifications: Record<
|
||||
string,
|
||||
Record<
|
||||
string,
|
||||
Record<
|
||||
string,
|
||||
{ name?: string | undefined; visible?: boolean | undefined }
|
||||
>
|
||||
>
|
||||
> = {}
|
||||
// @ts-expect-error fixme ts strict error
|
||||
nodeItems: any[]
|
||||
modifications: Record<string, { nodes?: Record<string, NodeModifications> }> =
|
||||
{}
|
||||
nodeItems: HTMLLIElement[] = []
|
||||
app: ComfyApp
|
||||
// @ts-expect-error fixme ts strict error
|
||||
groupNodeType: LGraphNodeConstructor<LGraphNode>
|
||||
groupNodeDef: any
|
||||
groupData: any
|
||||
groupNodeType!: LGraphNodeConstructor<LGraphNode>
|
||||
groupNodeDef: unknown
|
||||
groupData: ReturnType<typeof GroupNodeHandler.getGroupData> | null = null
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
innerNodesList: HTMLUListElement
|
||||
// @ts-expect-error fixme ts strict error
|
||||
widgetsPage: HTMLElement
|
||||
// @ts-expect-error fixme ts strict error
|
||||
inputsPage: HTMLElement
|
||||
// @ts-expect-error fixme ts strict error
|
||||
outputsPage: HTMLElement
|
||||
draggable: any
|
||||
innerNodesList!: HTMLUListElement
|
||||
widgetsPage!: HTMLElement
|
||||
inputsPage!: HTMLElement
|
||||
outputsPage!: HTMLElement
|
||||
draggable: DraggableList | null = null
|
||||
|
||||
get selectedNodeInnerIndex() {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
return +this.nodeItems[this.selectedNodeIndex].dataset.nodeindex
|
||||
get selectedNodeInnerIndex(): number {
|
||||
if (this.selectedNodeIndex == null) return 0
|
||||
const item = this.nodeItems[this.selectedNodeIndex]
|
||||
return +(item?.dataset?.nodeindex ?? 0)
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
constructor(app) {
|
||||
constructor(app: ComfyApp) {
|
||||
super()
|
||||
this.app = app
|
||||
this.element = $el('dialog.comfy-group-manage', {
|
||||
@@ -84,19 +73,15 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
}) as HTMLDialogElement
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
changeTab(tab) {
|
||||
changeTab(tab: keyof ManageGroupDialog['tabs']) {
|
||||
this.tabs[this.selectedTab].tab.classList.remove('active')
|
||||
this.tabs[this.selectedTab].page.classList.remove('active')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.tabs[tab].tab.classList.add('active')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.tabs[tab].page.classList.add('active')
|
||||
this.selectedTab = tab
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
changeNode(index, force?) {
|
||||
changeNode(index: number, force?: boolean) {
|
||||
if (!force && this.selectedNodeIndex === index) return
|
||||
|
||||
if (this.selectedNodeIndex != null) {
|
||||
@@ -126,19 +111,17 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType)
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
changeGroup(group, reset = true) {
|
||||
changeGroup(group: string, reset = true) {
|
||||
this.selectedGroup = group
|
||||
this.getGroupData()
|
||||
|
||||
const nodes = this.groupData.nodeData.nodes
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const nodes = this.groupData?.nodeData.nodes ?? []
|
||||
this.nodeItems = nodes.map((n, i) =>
|
||||
$el(
|
||||
'li.draggable-item',
|
||||
{
|
||||
dataset: {
|
||||
nodeindex: n.index + ''
|
||||
nodeindex: String(n.index ?? i)
|
||||
},
|
||||
onclick: () => {
|
||||
this.changeNode(i)
|
||||
@@ -159,7 +142,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
) as HTMLLIElement[]
|
||||
|
||||
this.innerNodesList.replaceChildren(...this.nodeItems)
|
||||
|
||||
@@ -167,63 +150,76 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
this.selectedNodeIndex = null
|
||||
this.changeNode(0)
|
||||
} else {
|
||||
const items = this.draggable.getAllItems()
|
||||
// @ts-expect-error fixme ts strict error
|
||||
let index = items.findIndex((item) => item.classList.contains('selected'))
|
||||
if (index === -1) index = this.selectedNodeIndex
|
||||
const items = this.draggable?.getAllItems() ?? []
|
||||
let index = items.findIndex((item: Element) =>
|
||||
item.classList.contains('selected')
|
||||
)
|
||||
if (index === -1) index = this.selectedNodeIndex ?? 0
|
||||
this.changeNode(index, true)
|
||||
}
|
||||
|
||||
const ordered = [...nodes]
|
||||
this.draggable?.dispose()
|
||||
this.draggable = new DraggableList(this.innerNodesList, 'li')
|
||||
this.draggable.addEventListener(
|
||||
'dragend',
|
||||
// @ts-expect-error fixme ts strict error
|
||||
({ detail: { oldPosition, newPosition } }) => {
|
||||
if (oldPosition === newPosition) return
|
||||
ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0])
|
||||
for (let i = 0; i < ordered.length; i++) {
|
||||
this.storeModification({
|
||||
nodeIndex: ordered[i].index,
|
||||
section: ORDER,
|
||||
prop: 'order',
|
||||
value: i
|
||||
})
|
||||
}
|
||||
this.draggable.addEventListener('dragend', (e: Event) => {
|
||||
const detail = (e as DragEndEvent).detail
|
||||
const { oldPosition, newPosition } = detail
|
||||
if (oldPosition === newPosition) return
|
||||
ordered.splice(newPosition, 0, ordered.splice(oldPosition, 1)[0])
|
||||
for (let i = 0; i < ordered.length; i++) {
|
||||
const nodeIndex = ordered[i].index
|
||||
if (nodeIndex == null) continue
|
||||
this.storeModification({
|
||||
nodeIndex,
|
||||
section: ORDER,
|
||||
prop: 'order',
|
||||
value: i
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
storeModification(props: {
|
||||
nodeIndex?: number
|
||||
section: symbol
|
||||
section: string | typeof ORDER
|
||||
prop: string
|
||||
value: any
|
||||
value: unknown
|
||||
}) {
|
||||
const { nodeIndex, section, prop, value } = props
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (!this.selectedGroup) return
|
||||
|
||||
const groupMod = (this.modifications[this.selectedGroup] ??= {})
|
||||
const nodesMod = (groupMod.nodes ??= {})
|
||||
const nodeMod = (nodesMod[nodeIndex ?? this.selectedNodeInnerIndex] ??= {})
|
||||
const typeMod = (nodeMod[section] ??= {})
|
||||
if (typeof value === 'object') {
|
||||
const objMod = (typeMod[prop] ??= {})
|
||||
Object.assign(objMod, value)
|
||||
const nodeKey = String(nodeIndex ?? this.selectedNodeInnerIndex)
|
||||
const nodeMod = (nodesMod[nodeKey] ??= {} as NodeModifications)
|
||||
|
||||
if (section === ORDER) {
|
||||
nodeMod[ORDER] = { order: value as number }
|
||||
} else {
|
||||
typeMod[prop] = value
|
||||
const sectionMod = (nodeMod[section] ??= {})
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
Object.assign(sectionMod, value)
|
||||
} else {
|
||||
Object.assign(sectionMod, { [prop]: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
getEditElement(section, prop, value, placeholder, checked, checkable = true) {
|
||||
getEditElement(
|
||||
section: string,
|
||||
prop: string | number,
|
||||
value: string,
|
||||
placeholder: string,
|
||||
checked: boolean,
|
||||
checkable = true
|
||||
) {
|
||||
if (value === placeholder) value = ''
|
||||
|
||||
const mods =
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.modifications[this.selectedGroup]?.nodes?.[
|
||||
this.selectedNodeInnerIndex
|
||||
]?.[section]?.[prop]
|
||||
const mods = this.selectedGroup
|
||||
? this.modifications[this.selectedGroup]?.nodes?.[
|
||||
this.selectedNodeInnerIndex
|
||||
]?.[section]
|
||||
: undefined
|
||||
if (mods) {
|
||||
if (mods.name != null) {
|
||||
value = mods.name
|
||||
@@ -238,12 +234,11 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
value,
|
||||
placeholder,
|
||||
type: 'text',
|
||||
// @ts-expect-error fixme ts strict error
|
||||
onchange: (e) => {
|
||||
onchange: (e: Event) => {
|
||||
this.storeModification({
|
||||
section,
|
||||
prop,
|
||||
value: { name: e.target.value }
|
||||
prop: String(prop),
|
||||
value: { name: (e.target as HTMLInputElement).value }
|
||||
})
|
||||
}
|
||||
}),
|
||||
@@ -252,12 +247,11 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
type: 'checkbox',
|
||||
checked,
|
||||
disabled: !checkable,
|
||||
// @ts-expect-error fixme ts strict error
|
||||
onchange: (e) => {
|
||||
onchange: (e: Event) => {
|
||||
this.storeModification({
|
||||
section,
|
||||
prop,
|
||||
value: { visible: !!e.target.checked }
|
||||
prop: String(prop),
|
||||
value: { visible: !!(e.target as HTMLInputElement).checked }
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -267,17 +261,22 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
|
||||
buildWidgetsPage() {
|
||||
const widgets =
|
||||
this.groupData.oldToNewWidgetMap[this.selectedNodeInnerIndex]
|
||||
this.groupData?.oldToNewWidgetMap[this.selectedNodeInnerIndex]
|
||||
const items = Object.keys(widgets ?? {})
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
|
||||
const config = type.config?.[this.selectedNodeInnerIndex]?.input
|
||||
const type = this.selectedGroup
|
||||
? app.rootGraph.extra?.groupNodes?.[this.selectedGroup]
|
||||
: undefined
|
||||
const config = (
|
||||
type?.config as
|
||||
| Record<number, { input?: Record<string, { visible?: boolean }> }>
|
||||
| undefined
|
||||
)?.[this.selectedNodeInnerIndex]?.input
|
||||
this.widgetsPage.replaceChildren(
|
||||
...items.map((oldName) => {
|
||||
return this.getEditElement(
|
||||
'input',
|
||||
oldName,
|
||||
widgets[oldName],
|
||||
widgets?.[oldName] ?? '',
|
||||
oldName,
|
||||
config?.[oldName]?.visible !== false
|
||||
)
|
||||
@@ -287,54 +286,68 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
}
|
||||
|
||||
buildInputsPage() {
|
||||
const inputs = this.groupData.nodeInputs[this.selectedNodeInnerIndex]
|
||||
const items = Object.keys(inputs ?? {})
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
|
||||
const config = type.config?.[this.selectedNodeInnerIndex]?.input
|
||||
this.inputsPage.replaceChildren(
|
||||
// @ts-expect-error fixme ts strict error
|
||||
...items
|
||||
.map((oldName) => {
|
||||
let value = inputs[oldName]
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
const inputs = this.groupData?.nodeInputs[this.selectedNodeInnerIndex] ?? {}
|
||||
const items = Object.keys(inputs)
|
||||
const type = this.selectedGroup
|
||||
? app.rootGraph.extra?.groupNodes?.[this.selectedGroup]
|
||||
: undefined
|
||||
const config = (
|
||||
type?.config as
|
||||
| Record<number, { input?: Record<string, { visible?: boolean }> }>
|
||||
| undefined
|
||||
)?.[this.selectedNodeInnerIndex]?.input
|
||||
const filteredElements = items
|
||||
.map((oldName) => {
|
||||
const value = inputs[oldName]
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.getEditElement(
|
||||
'input',
|
||||
oldName,
|
||||
value,
|
||||
oldName,
|
||||
config?.[oldName]?.visible !== false
|
||||
)
|
||||
})
|
||||
.filter(Boolean)
|
||||
)
|
||||
return this.getEditElement(
|
||||
'input',
|
||||
oldName,
|
||||
value as string,
|
||||
oldName,
|
||||
config?.[oldName]?.visible !== false
|
||||
)
|
||||
})
|
||||
.filter((el): el is HTMLDivElement => el !== null)
|
||||
this.inputsPage.replaceChildren(...filteredElements)
|
||||
return !!items.length
|
||||
}
|
||||
|
||||
buildOutputsPage() {
|
||||
const nodes = this.groupData.nodeData.nodes
|
||||
const innerNodeDef = this.groupData.getNodeDef(
|
||||
nodes[this.selectedNodeInnerIndex]
|
||||
)
|
||||
const outputs = innerNodeDef?.output ?? []
|
||||
const nodes = this.groupData?.nodeData.nodes ?? []
|
||||
const nodeData = nodes[this.selectedNodeInnerIndex]
|
||||
const innerNodeDef = nodeData
|
||||
? this.groupData?.getNodeDef(
|
||||
nodeData as Parameters<typeof this.groupData.getNodeDef>[0]
|
||||
)
|
||||
: undefined
|
||||
const outputs = (innerNodeDef?.output ?? []) as string[]
|
||||
const groupOutputs =
|
||||
this.groupData.oldToNewOutputMap[this.selectedNodeInnerIndex]
|
||||
this.groupData?.oldToNewOutputMap[this.selectedNodeInnerIndex]
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = app.rootGraph.extra.groupNodes[this.selectedGroup]
|
||||
const config = type.config?.[this.selectedNodeInnerIndex]?.output
|
||||
const node = this.groupData.nodeData.nodes[this.selectedNodeInnerIndex]
|
||||
const checkable = node.type !== 'PrimitiveNode'
|
||||
const workflowType = this.selectedGroup
|
||||
? app.rootGraph.extra?.groupNodes?.[this.selectedGroup]
|
||||
: undefined
|
||||
const config = (
|
||||
workflowType?.config as
|
||||
| Record<
|
||||
number,
|
||||
{ output?: Record<number, { name?: string; visible?: boolean }> }
|
||||
>
|
||||
| undefined
|
||||
)?.[this.selectedNodeInnerIndex]?.output
|
||||
const node = nodes[this.selectedNodeInnerIndex]
|
||||
const checkable = node?.type !== 'PrimitiveNode'
|
||||
this.outputsPage.replaceChildren(
|
||||
...outputs
|
||||
// @ts-expect-error fixme ts strict error
|
||||
.map((type, slot) => {
|
||||
.map((outputType: string, slot: number) => {
|
||||
const groupOutputIndex = groupOutputs?.[slot]
|
||||
const oldName = innerNodeDef.output_name?.[slot] ?? type
|
||||
let value = config?.[slot]?.name
|
||||
const oldName = (innerNodeDef?.output_name?.[slot] ??
|
||||
outputType) as string
|
||||
let value = config?.[slot]?.name ?? ''
|
||||
const visible = config?.[slot]?.visible || groupOutputIndex != null
|
||||
if (!value || value === oldName) {
|
||||
value = ''
|
||||
@@ -353,8 +366,7 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
return !!outputs.length
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
show(type?) {
|
||||
override show(type?: string) {
|
||||
const groupNodes = Object.keys(app.rootGraph.extra?.groupNodes ?? {}).sort(
|
||||
(a, b) => a.localeCompare(b)
|
||||
)
|
||||
@@ -371,24 +383,28 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
this.outputsPage
|
||||
])
|
||||
|
||||
this.tabs = [
|
||||
['Inputs', this.inputsPage],
|
||||
['Widgets', this.widgetsPage],
|
||||
['Outputs', this.outputsPage]
|
||||
// @ts-expect-error fixme ts strict error
|
||||
].reduce((p, [name, page]: [string, HTMLElement]) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
p[name] = {
|
||||
tab: $el('a', {
|
||||
onclick: () => {
|
||||
this.changeTab(name)
|
||||
},
|
||||
textContent: name
|
||||
}),
|
||||
page
|
||||
}
|
||||
return p
|
||||
}, {}) as any
|
||||
type TabName = keyof ManageGroupDialog['tabs']
|
||||
this.tabs = (
|
||||
[
|
||||
['Inputs', this.inputsPage],
|
||||
['Widgets', this.widgetsPage],
|
||||
['Outputs', this.outputsPage]
|
||||
] as [TabName, HTMLElement][]
|
||||
).reduce(
|
||||
(p, [name, page]) => {
|
||||
p[name] = {
|
||||
tab: $el('a', {
|
||||
onclick: () => {
|
||||
this.changeTab(name)
|
||||
},
|
||||
textContent: name
|
||||
}) as HTMLAnchorElement,
|
||||
page
|
||||
}
|
||||
return p
|
||||
},
|
||||
{} as ManageGroupDialog['tabs']
|
||||
)
|
||||
|
||||
const outer = $el('div.comfy-group-manage-outer', [
|
||||
$el('header', [
|
||||
@@ -396,9 +412,8 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
$el(
|
||||
'select',
|
||||
{
|
||||
// @ts-expect-error fixme ts strict error
|
||||
onchange: (e) => {
|
||||
this.changeGroup(e.target.value)
|
||||
onchange: (e: Event) => {
|
||||
this.changeGroup((e.target as HTMLSelectElement).value)
|
||||
}
|
||||
},
|
||||
groupNodes.map((g) =>
|
||||
@@ -439,8 +454,9 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
`Are you sure you want to remove the node: "${this.selectedGroup}"`
|
||||
)
|
||||
) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
delete app.rootGraph.extra.groupNodes[this.selectedGroup]
|
||||
if (this.selectedGroup && app.rootGraph.extra?.groupNodes) {
|
||||
delete app.rootGraph.extra.groupNodes[this.selectedGroup]
|
||||
}
|
||||
LiteGraph.unregisterNodeType(
|
||||
`${PREFIX}${SEPARATOR}` + this.selectedGroup
|
||||
)
|
||||
@@ -454,97 +470,105 @@ export class ManageGroupDialog extends ComfyDialog<HTMLDialogElement> {
|
||||
'button.comfy-btn',
|
||||
{
|
||||
onclick: async () => {
|
||||
let nodesByType
|
||||
let recreateNodes = []
|
||||
const types = {}
|
||||
let nodesByType: Record<string, LGraphNode[]> | null = null
|
||||
const recreateNodes: LGraphNode[] = []
|
||||
const types: Record<string, GroupNodeWorkflowData> = {}
|
||||
for (const g in this.modifications) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = app.rootGraph.extra.groupNodes[g]
|
||||
let config = (type.config ??= {})
|
||||
const groupNodeData = app.rootGraph.extra?.groupNodes?.[g]
|
||||
if (!groupNodeData) continue
|
||||
|
||||
let config = (groupNodeData.config ??= {}) as Record<
|
||||
number,
|
||||
unknown
|
||||
>
|
||||
|
||||
let nodeMods = this.modifications[g]?.nodes
|
||||
if (nodeMods) {
|
||||
const keys = Object.keys(nodeMods)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (nodeMods[keys[0]][ORDER]) {
|
||||
const firstMod = nodeMods[keys[0]]
|
||||
if (firstMod?.[ORDER]) {
|
||||
// If any node is reordered, they will all need sequencing
|
||||
const orderedNodes = []
|
||||
const orderedMods = {}
|
||||
const orderedConfig = {}
|
||||
const orderedNodes: typeof groupNodeData.nodes = []
|
||||
const orderedMods: Record<string, NodeModifications> = {}
|
||||
const orderedConfig: Record<number, unknown> = {}
|
||||
|
||||
for (const n of keys) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const order = nodeMods[n][ORDER].order
|
||||
orderedNodes[order] = type.nodes[+n]
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const order = nodeMods[n]?.[ORDER]?.order ?? 0
|
||||
orderedNodes[order] = groupNodeData.nodes[+n]
|
||||
orderedMods[order] = nodeMods[n]
|
||||
orderedNodes[order].index = order
|
||||
}
|
||||
|
||||
// Rewrite links
|
||||
for (const l of type.links) {
|
||||
// @ts-expect-error l[0]/l[2] used as node index
|
||||
if (l[0] != null) l[0] = type.nodes[l[0]].index
|
||||
// @ts-expect-error l[0]/l[2] used as node index
|
||||
if (l[2] != null) l[2] = type.nodes[l[2]].index
|
||||
for (const l of groupNodeData.links) {
|
||||
const srcIdx = l[1]
|
||||
const tgtIdx = l[3]
|
||||
if (srcIdx != null)
|
||||
l[1] =
|
||||
groupNodeData.nodes[srcIdx as number]?.index ?? srcIdx
|
||||
if (tgtIdx != null)
|
||||
l[3] =
|
||||
groupNodeData.nodes[tgtIdx as number]?.index ?? tgtIdx
|
||||
}
|
||||
|
||||
// Rewrite externals
|
||||
if (type.external) {
|
||||
for (const ext of type.external) {
|
||||
if (groupNodeData.external) {
|
||||
for (const ext of groupNodeData.external) {
|
||||
if (ext[0] != null) {
|
||||
// @ts-expect-error ext[0] used as node index
|
||||
ext[0] = type.nodes[ext[0]].index
|
||||
ext[0] =
|
||||
groupNodeData.nodes[ext[0] as number]?.index ??
|
||||
ext[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite modifications
|
||||
for (const id of keys) {
|
||||
// @ts-expect-error id used as node index
|
||||
if (config[id]) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
orderedConfig[type.nodes[id].index] = config[id]
|
||||
const nodeIdx = +id
|
||||
if (config[nodeIdx]) {
|
||||
const newIdx =
|
||||
groupNodeData.nodes[nodeIdx]?.index ?? nodeIdx
|
||||
orderedConfig[newIdx] = config[nodeIdx]
|
||||
}
|
||||
// @ts-expect-error id used as config key
|
||||
delete config[id]
|
||||
delete config[nodeIdx]
|
||||
}
|
||||
|
||||
type.nodes = orderedNodes
|
||||
groupNodeData.nodes = orderedNodes
|
||||
nodeMods = orderedMods
|
||||
type.config = config = orderedConfig
|
||||
groupNodeData.config = config = orderedConfig
|
||||
}
|
||||
|
||||
merge(config, nodeMods)
|
||||
merge(config, nodeMods as Record<string, unknown>)
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
types[g] = type
|
||||
types[g] = groupNodeData
|
||||
|
||||
if (!nodesByType) {
|
||||
nodesByType = app.rootGraph.nodes.reduce((p, n) => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
p[n.type] ??= []
|
||||
// @ts-expect-error fixme ts strict error
|
||||
p[n.type].push(n)
|
||||
return p
|
||||
}, {})
|
||||
nodesByType = app.rootGraph.nodes.reduce(
|
||||
(p, n) => {
|
||||
const nodeType = n.type ?? ''
|
||||
;(p[nodeType] ??= []).push(n)
|
||||
return p
|
||||
},
|
||||
{} as Record<string, LGraphNode[]>
|
||||
)
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const nodes = nodesByType[`${PREFIX}${SEPARATOR}` + g]
|
||||
if (nodes) recreateNodes.push(...nodes)
|
||||
const groupTypeNodes = nodesByType[`${PREFIX}${SEPARATOR}` + g]
|
||||
if (groupTypeNodes) recreateNodes.push(...groupTypeNodes)
|
||||
}
|
||||
|
||||
await GroupNodeConfig.registerFromWorkflow(types, [])
|
||||
|
||||
for (const node of recreateNodes) {
|
||||
node.recreate()
|
||||
;(node as LGraphNode & { recreate?: () => void }).recreate?.()
|
||||
}
|
||||
|
||||
this.modifications = {}
|
||||
this.app.canvas.setDirty(true, true)
|
||||
this.changeGroup(this.selectedGroup, false)
|
||||
if (this.selectedGroup) {
|
||||
this.changeGroup(this.selectedGroup, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
'Save'
|
||||
|
||||
47
src/extensions/core/groupNodeTypes.ts
Normal file
47
src/extensions/core/groupNodeTypes.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { SerialisedLLinkArray } from '@/lib/litegraph/src/LLink'
|
||||
import type { ISerialisedNode } from '@/lib/litegraph/src/types/serialisation'
|
||||
|
||||
/** Serialized node data within a group node workflow, with group-specific index */
|
||||
export interface GroupNodeSerializedNode extends Partial<ISerialisedNode> {
|
||||
/** Position of this node within the group */
|
||||
index?: number
|
||||
}
|
||||
|
||||
export interface GroupNodeWorkflowData {
|
||||
external: (number | string)[][]
|
||||
links: SerialisedLLinkArray[]
|
||||
nodes: GroupNodeSerializedNode[]
|
||||
config?: Record<number, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Input config tuple type for group nodes.
|
||||
* First element is the input type name (e.g. 'INT', 'FLOAT', 'MODEL', etc.)
|
||||
* Second element (optional) is the input options object.
|
||||
*/
|
||||
export type GroupNodeInputConfig = [string, Record<string, unknown>?]
|
||||
|
||||
/**
|
||||
* Mutable inputs specification for group nodes that are built dynamically.
|
||||
* Uses a more permissive type than ComfyInputsSpec to allow dynamic assignment.
|
||||
*/
|
||||
export interface GroupNodeInputsSpec {
|
||||
required: Record<string, GroupNodeInputConfig>
|
||||
optional?: Record<string, GroupNodeInputConfig>
|
||||
}
|
||||
|
||||
/**
|
||||
* Output type for group nodes - can be a type string or an array of combo options.
|
||||
*/
|
||||
export type GroupNodeOutputType = string | (string | number)[]
|
||||
|
||||
/**
|
||||
* Partial link info used internally by group node getInputLink override.
|
||||
* Contains only the properties needed for group node execution context.
|
||||
*/
|
||||
export interface PartialLinkInfo {
|
||||
origin_id: string | number
|
||||
origin_slot: number | string
|
||||
target_id: string | number
|
||||
target_slot: number
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import { LGraphGroup } from './LGraphGroup'
|
||||
import { LGraphNode } from './LGraphNode'
|
||||
import type { NodeId } from './LGraphNode'
|
||||
import { LLink } from './LLink'
|
||||
import type { LinkId, SerialisedLLinkArray } from './LLink'
|
||||
import type { LinkId } from './LLink'
|
||||
import { MapProxyHandler } from './MapProxyHandler'
|
||||
import { Reroute } from './Reroute'
|
||||
import type { RerouteId } from './Reroute'
|
||||
@@ -63,6 +63,7 @@ import type {
|
||||
LGraphTriggerHandler,
|
||||
LGraphTriggerParam
|
||||
} from './types/graphTriggers'
|
||||
import type { GroupNodeWorkflowData } from '@/extensions/core/groupNodeTypes'
|
||||
import type {
|
||||
ExportedSubgraph,
|
||||
ExposedWidget,
|
||||
@@ -74,6 +75,8 @@ import type {
|
||||
} from './types/serialisation'
|
||||
import { getAllNestedItems } from './utils/collections'
|
||||
|
||||
export type { GroupNodeWorkflowData } from '@/extensions/core/groupNodeTypes'
|
||||
|
||||
export type {
|
||||
LGraphTriggerAction,
|
||||
LGraphTriggerParam
|
||||
@@ -102,18 +105,6 @@ export interface LGraphConfig {
|
||||
links_ontop?: any
|
||||
}
|
||||
|
||||
export interface GroupNodeWorkflowData {
|
||||
external: (number | string)[][]
|
||||
links: SerialisedLLinkArray[]
|
||||
nodes: {
|
||||
index?: number
|
||||
type?: string
|
||||
inputs?: unknown[]
|
||||
outputs?: unknown[]
|
||||
}[]
|
||||
config?: Record<number, unknown>
|
||||
}
|
||||
|
||||
export interface LGraphExtra extends Dictionary<unknown> {
|
||||
reroutes?: SerialisableReroute[]
|
||||
linkExtensions?: { id: number; parentId: number | undefined }[]
|
||||
|
||||
@@ -103,10 +103,12 @@ export type {
|
||||
Size
|
||||
} from './interfaces'
|
||||
export {
|
||||
type GroupNodeWorkflowData,
|
||||
LGraph,
|
||||
type LGraphTriggerAction,
|
||||
type LGraphTriggerParam
|
||||
} from './LGraph'
|
||||
|
||||
export type { LGraphTriggerEvent } from './types/graphTriggers'
|
||||
export { BadgePosition, LGraphBadge } from './LGraphBadge'
|
||||
export { LGraphCanvas } from './LGraphCanvas'
|
||||
|
||||
4
src/types/litegraph-augmentation.d.ts
vendored
4
src/types/litegraph-augmentation.d.ts
vendored
@@ -113,7 +113,7 @@ declare module '@/lib/litegraph/src/litegraph' {
|
||||
): ExecutableLGraphNode[]
|
||||
/** @deprecated groupNode */
|
||||
convertToNodes?(): LGraphNode[]
|
||||
recreate?(): Promise<LGraphNode>
|
||||
recreate?(): Promise<LGraphNode | null>
|
||||
refreshComboInNode?(defs: Record<string, ComfyNodeDef>)
|
||||
/** @deprecated groupNode */
|
||||
updateLink?(link: LLink): LLink | null
|
||||
@@ -143,6 +143,8 @@ declare module '@/lib/litegraph/src/litegraph' {
|
||||
|
||||
index?: number
|
||||
runningInternalNodeId?: NodeId
|
||||
/** @deprecated Used by PrimitiveNode for group node value propagation */
|
||||
primitiveValue?: unknown
|
||||
|
||||
comfyClass?: string
|
||||
|
||||
|
||||
Reference in New Issue
Block a user