Compare commits

...

2 Commits

Author SHA1 Message Date
Austin Mroz
26c139739f cleanup 2026-03-25 15:23:10 -07:00
Austin Mroz
862de1b78c WIP promotion linkage 2026-03-25 15:23:06 -07:00
3 changed files with 71 additions and 4 deletions

View File

@@ -1,3 +1,7 @@
import { intersectionWith, differenceWith } from 'es-toolkit'
import { watch } from 'vue'
import type { PromotedWidgetSource } from '@/core/graph/subgraph/promotedWidgetTypes'
import type { LGraphNode, NodeId } from '@/lib/litegraph/src/LGraphNode'
import type { LGraphCanvas } from '@/lib/litegraph/src/LGraphCanvas'
import type { CanvasPointer } from '@/lib/litegraph/src/CanvasPointer'
@@ -9,6 +13,7 @@ import type { BaseWidget } from '@/lib/litegraph/src/widgets/BaseWidget'
import { toConcreteWidget } from '@/lib/litegraph/src/widgets/widgetMap'
import { t } from '@/i18n'
import { useDomWidgetStore } from '@/stores/domWidgetStore'
import { usePromotionStore } from '@/stores/promotionStore'
import {
stripGraphPrefix,
useWidgetValueStore
@@ -106,6 +111,14 @@ class PromotedWidgetView implements IPromotedWidgetView {
this.sourceNodeId = nodeId
this.sourceWidgetName = widgetName
this.graphId = subgraphNode.rootGraph.id
if (this.resolveDeepest()?.widget?.hasDynamicChildren) {
watch(
() => this.value,
() => this.updateDependents()
)
this.updateDependents(true)
}
}
get node(): SubgraphNode {
@@ -540,6 +553,49 @@ class PromotedWidgetView implements IPromotedWidgetView {
widget: this
})
}
private updateDependents(skipExisting?: boolean) {
const parent = this.node
const { demote, promote } = usePromotionStore()
const child = this.node.subgraph.getNodeById(this.sourceNodeId)
if (!child) return
const key = this.sourceWidgetName + '.'
const existing = skipExisting
? []
: parent.widgets
.filter((w) => w.name.startsWith(key))
.map((w) => getSource(parent, w))
const candidate =
child.widgets
?.filter((w) => w.name.startsWith(key))
.map((w) => getSource(child, w)) ?? []
const overlap = intersectionWith(existing, candidate, sourcesEqual)
const toPrune = differenceWith(existing, overlap, sourcesEqual)
const toAdd = differenceWith(candidate, overlap, sourcesEqual)
for (const source of toPrune) demote(parent.rootGraph.id, parent.id, source)
for (const source of toAdd) promote(parent.rootGraph.id, parent.id, source)
}
}
function getSource(node: LGraphNode, wid: IBaseWidget): PromotedWidgetSource {
return isPromotedWidgetView(wid)
? {
sourceNodeId: wid.sourceNodeId,
sourceWidgetName: wid.sourceWidgetName,
disambiguatingSourceNodeId: wid.disambiguatingSourceNodeId
}
: { sourceNodeId: String(node.id), sourceWidgetName: wid.name }
}
function sourcesEqual(a: PromotedWidgetSource, b: PromotedWidgetSource) {
return (
a.sourceWidgetName === b.sourceWidgetName &&
(a.disambiguatingSourceNodeId ?? a.sourceNodeId) ===
(b.disambiguatingSourceNodeId ?? b.sourceNodeId)
)
}
/** Checks if a widget is a BaseDOMWidget (DOMWidget or ComponentWidget). */

View File

@@ -22,6 +22,7 @@ import {
import { useLitegraphService } from '@/services/litegraphService'
import { app } from '@/scripts/app'
import type { ComfyApp } from '@/scripts/app'
import { useWidgetValueStore } from '@/stores/widgetValueStore'
const INLINE_INPUTS = false
@@ -88,6 +89,7 @@ function dynamicComboWidget(
appArg,
widgetName
)
widget.hasDynamicChildren = true
function isInGroup(e: { name: string }): boolean {
return e.name.startsWith(inputName + '.')
}
@@ -184,17 +186,25 @@ function dynamicComboWidget(
}
//A little hacky, but onConfigure won't work.
//It fires too late and is overly disruptive
let widgetValue = widget.value
Object.defineProperty(widget, 'value', {
get() {
return widgetValue
return useWidgetValueStore().getWidget(
app.rootGraph.id,
node.id,
widget.name
)?.value
},
set(value) {
widgetValue = value
const state = useWidgetValueStore().getWidget(
app.rootGraph.id,
node.id,
widget.name
)
if (state) state.value = value
updateWidgets(value)
}
})
widget.value = widgetValue
widget.value = widget.value
return { widget, minWidth, minHeight }
}

View File

@@ -424,6 +424,7 @@ export interface IBaseWidget<
hidden?: boolean
advanced?: boolean
tooltip?: string
hasDynamicChildren?: boolean
// TODO: Confirm this format
callback?(