diff --git a/src/core/graph/subgraph/proxyWidgetUtils.ts b/src/core/graph/subgraph/proxyWidgetUtils.ts index 97c6bf23b..8274182b5 100644 --- a/src/core/graph/subgraph/proxyWidgetUtils.ts +++ b/src/core/graph/subgraph/proxyWidgetUtils.ts @@ -28,4 +28,5 @@ export function promoteWidget(widget: IBaseWidget, node: LGraphNode) { pushWidgets(onode, [`${node.id}`, widget.name]) } } + widget.promoted = true } diff --git a/src/lib/litegraph/src/LGraphCanvas.ts b/src/lib/litegraph/src/LGraphCanvas.ts index 9e7ef226f..b4eb9e99b 100644 --- a/src/lib/litegraph/src/LGraphCanvas.ts +++ b/src/lib/litegraph/src/LGraphCanvas.ts @@ -1865,13 +1865,13 @@ export class LGraphCanvas this.#dirty() } - openSubgraph(subgraph: Subgraph): void { + openSubgraph(subgraph: Subgraph, fromNode: SubgraphNode): void { const { graph } = this if (!graph) throw new NullGraphError() const options = { bubbles: true, - detail: { subgraph, closingGraph: graph }, + detail: { subgraph, closingGraph: graph, fromNode }, cancelable: true } const mayContinue = this.canvas.dispatchEvent( @@ -2792,7 +2792,7 @@ export class LGraphCanvas if (pos[1] < 0 && !inCollapse) { node.onNodeTitleDblClick?.(e, pos, this) } else if (node instanceof SubgraphNode) { - this.openSubgraph(node.subgraph) + this.openSubgraph(node.subgraph, node) } node.onDblClick?.(e, pos, this) diff --git a/src/lib/litegraph/src/infrastructure/LGraphCanvasEventMap.ts b/src/lib/litegraph/src/infrastructure/LGraphCanvasEventMap.ts index 6b51765f4..1c25e862c 100644 --- a/src/lib/litegraph/src/infrastructure/LGraphCanvasEventMap.ts +++ b/src/lib/litegraph/src/infrastructure/LGraphCanvasEventMap.ts @@ -4,6 +4,7 @@ import type { LGraphGroup } from '@/lib/litegraph/src/LGraphGroup' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { ConnectingLink } from '@/lib/litegraph/src/interfaces' import type { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph' +import type { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode' import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events' export interface LGraphCanvasEventMap { @@ -14,6 +15,11 @@ export interface LGraphCanvasEventMap { /** The old active graph, or `null` if there was no active graph. */ oldGraph: LGraph | Subgraph | null | undefined } + 'subgraph-opened': { + subgraph: Subgraph + closingGraph: LGraph + fromNode: SubgraphNode + } 'litegraph:canvas': | { subType: 'before-change' | 'after-change' } diff --git a/src/lib/litegraph/src/subgraph/SubgraphNode.ts b/src/lib/litegraph/src/subgraph/SubgraphNode.ts index d87e49658..58ea65cad 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphNode.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphNode.ts @@ -164,7 +164,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph { canvas: LGraphCanvas ): void { if (button.name === 'enter_subgraph') { - canvas.openSubgraph(this.subgraph) + canvas.openSubgraph(this.subgraph, this) } else { super.onTitleButtonClick(button, canvas) } diff --git a/src/lib/litegraph/src/types/widgets.ts b/src/lib/litegraph/src/types/widgets.ts index fa47c6a93..40b3ab5ac 100644 --- a/src/lib/litegraph/src/types/widgets.ts +++ b/src/lib/litegraph/src/types/widgets.ts @@ -297,6 +297,12 @@ export interface IBaseWidget< hidden?: boolean advanced?: boolean + /** + * Set if the node is displayed on the parent subgraphNode + * Promoted widgets have a green border + * @readonly [Computed] This property is computed on graph change + */ + promoted?: boolean tooltip?: string diff --git a/src/scripts/app.ts b/src/scripts/app.ts index b22fe2d7b..3c9b6bf7e 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -38,6 +38,7 @@ import { getFromIsobmffFile } from '@/scripts/metadata/isobmff' import { getMp3Metadata } from '@/scripts/metadata/mp3' import { getOggMetadata } from '@/scripts/metadata/ogg' import { getSvgMetadata } from '@/scripts/metadata/svg' +import { registerProxyWidgets } from '@/scripts/proxyWidget' import { useDialogService } from '@/services/dialogService' import { useExtensionService } from '@/services/extensionService' import { useLitegraphService } from '@/services/litegraphService' @@ -862,6 +863,7 @@ export class ComfyApp { } } ) + registerProxyWidgets(this.canvas) this.graph.start() diff --git a/src/scripts/proxyWidget.ts b/src/scripts/proxyWidget.ts index 7927a3723..ff5ec258c 100644 --- a/src/scripts/proxyWidget.ts +++ b/src/scripts/proxyWidget.ts @@ -1,5 +1,9 @@ import { useNodeImage } from '@/composables/node/useNodeImage' -import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph' +import type { + LGraph, + LGraphCanvas, + LGraphNode +} from '@/lib/litegraph/src/litegraph' import { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode' import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets.ts' import { disconnectedWidget } from '@/lib/litegraph/src/widgets/DisconnectedWidget' @@ -10,54 +14,67 @@ import { useCanvasStore } from '@/stores/graphStore' import { useNodeOutputStore } from '@/stores/imagePreviewStore' import { getNodeByExecutionId } from '@/utils/graphTraversalUtil' -const originalOnConfigure = SubgraphNode.prototype.onConfigure -SubgraphNode.prototype.onConfigure = function (serialisedNode) { - if (!this.isSubgraphNode()) - throw new Error("Can't add proxyWidgets to non-subgraphNode") - - const canvasStore = useCanvasStore() - const subgraphNode = this - //Must give value to proxyWidgets prior to defining or it won't serialize - subgraphNode.properties.proxyWidgets ??= '[]' - let proxyWidgets = subgraphNode.properties.proxyWidgets - - originalOnConfigure?.bind(this)?.(serialisedNode) - - Object.defineProperty(subgraphNode.properties, 'proxyWidgets', { - get: () => { - return proxyWidgets - }, - set: (property: string) => { - const parsed = parseProxyWidgets(property) - const { widgetStates } = useDomWidgetStore() - for (const w of subgraphNode.widgets.filter((w) => isProxyWidget(w))) { - if (w instanceof DOMWidgetImpl && widgetStates.has(w.id)) { - const widgetState = widgetStates.get(w.id) - if (!widgetState) continue - widgetState.active = false - } +export function registerProxyWidgets(canvas: LGraphCanvas) { + //NOTE: canvasStore hasn't been initialized yet + canvas.canvas.addEventListener<'subgraph-opened'>('subgraph-opened', (e) => { + const { subgraph, fromNode } = e.detail + const pw = parseProxyWidgets(fromNode.properties.proxyWidgets) + for (const node of subgraph.nodes) { + for (const widget of node.widgets ?? []) { + widget.promoted = pw.some(([n, w]) => node.id == n && widget.name == w) } - //NOTE: This does not apply to pushed entries, only initial load - subgraphNode.widgets = subgraphNode.widgets.filter( - (w) => !isProxyWidget(w) - ) - for (const [nodeId, widgetName] of parsed) { - const w = addProxyWidget(subgraphNode, `${nodeId}`, widgetName) - if (w instanceof DOMWidgetImpl) { - const widgetState = widgetStates.get(w.id) - if (!widgetState) continue - widgetState.active = true - widgetState.widget = w - } - } - proxyWidgets = property - canvasStore.canvas?.setDirty(true, true) - subgraphNode._setConcreteSlots() - subgraphNode.arrange() } }) - subgraphNode.properties.proxyWidgets = proxyWidgets + const originalOnConfigure = SubgraphNode.prototype.onConfigure + SubgraphNode.prototype.onConfigure = function onConfigure(serialisedNode) { + if (!this.isSubgraphNode()) + throw new Error("Can't add proxyWidgets to non-subgraphNode") + + const canvasStore = useCanvasStore() + const subgraphNode = this + //Must give value to proxyWidgets prior to defining or it won't serialize + subgraphNode.properties.proxyWidgets ??= '[]' + let proxyWidgets = subgraphNode.properties.proxyWidgets + + originalOnConfigure?.bind(this)?.(serialisedNode) + + Object.defineProperty(subgraphNode.properties, 'proxyWidgets', { + get: () => { + return proxyWidgets + }, + set: (property: string) => { + const parsed = parseProxyWidgets(property) + const { widgetStates } = useDomWidgetStore() + for (const w of subgraphNode.widgets.filter((w) => isProxyWidget(w))) { + if (w instanceof DOMWidgetImpl && widgetStates.has(w.id)) { + const widgetState = widgetStates.get(w.id) + if (!widgetState) continue + widgetState.active = false + } + } + //NOTE: This does not apply to pushed entries, only initial load + subgraphNode.widgets = subgraphNode.widgets.filter( + (w) => !isProxyWidget(w) + ) + for (const [nodeId, widgetName] of parsed) { + const w = addProxyWidget(subgraphNode, `${nodeId}`, widgetName) + if (w instanceof DOMWidgetImpl) { + const widgetState = widgetStates.get(w.id) + if (!widgetState) continue + widgetState.active = true + widgetState.widget = w + } + } + proxyWidgets = property + canvasStore.canvas?.setDirty(true, true) + subgraphNode._setConcreteSlots() + subgraphNode.arrange() + } + }) + subgraphNode.properties.proxyWidgets = proxyWidgets + } } + type Overlay = Partial & { graph: LGraph nodeId: string @@ -89,7 +106,8 @@ function addProxyWidget( computedHeight: undefined, afterQueued: undefined, onRemove: undefined, - node: subgraphNode + node: subgraphNode, + promoted: undefined } return addProxyFromOverlay(subgraphNode, overlay) } diff --git a/src/scripts/widgets.ts b/src/scripts/widgets.ts index 50944381a..357dc2bf1 100644 --- a/src/scripts/widgets.ts +++ b/src/scripts/widgets.ts @@ -29,7 +29,6 @@ import { useSettingStore } from '@/stores/settingStore' import type { ComfyApp } from './app' import './domWidget' import './errorNodeWidgets' -import './proxyWidget' export type ComfyWidgetConstructorV2 = ( node: LGraphNode,