mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-11 02:20:08 +00:00
Manual backport of #5981 to `core/1.28` Requires minor styling change since theme code will not be backported. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6009-Backport-1-28-Allow-reordering-of-linked-subgraph-widgets-2886d73d36508125a125e8acc8ae08a7) by [Unito](https://www.unito.io)
141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import {
|
|
type ProxyWidgetsProperty,
|
|
parseProxyWidgets
|
|
} from '@/core/schemas/proxyWidget'
|
|
import type {
|
|
IContextMenuValue,
|
|
LGraphNode
|
|
} from '@/lib/litegraph/src/litegraph'
|
|
import type { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode'
|
|
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets.ts'
|
|
import { useLitegraphService } from '@/services/litegraphService'
|
|
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
|
|
|
type PartialNode = Pick<LGraphNode, 'title' | 'id' | 'type'>
|
|
|
|
export type WidgetItem = [PartialNode, IBaseWidget]
|
|
|
|
function getProxyWidgets(node: SubgraphNode) {
|
|
return parseProxyWidgets(node.properties.proxyWidgets)
|
|
}
|
|
export function promoteWidget(
|
|
node: PartialNode,
|
|
widget: IBaseWidget,
|
|
parents: SubgraphNode[]
|
|
) {
|
|
for (const parent of parents) {
|
|
const proxyWidgets = [
|
|
...getProxyWidgets(parent),
|
|
widgetItemToProperty([node, widget])
|
|
]
|
|
parent.properties.proxyWidgets = proxyWidgets
|
|
}
|
|
widget.promoted = true
|
|
}
|
|
|
|
export function demoteWidget(
|
|
node: PartialNode,
|
|
widget: IBaseWidget,
|
|
parents: SubgraphNode[]
|
|
) {
|
|
for (const parent of parents) {
|
|
const proxyWidgets = getProxyWidgets(parent).filter(
|
|
(widgetItem) => !matchesPropertyItem([node, widget])(widgetItem)
|
|
)
|
|
parent.properties.proxyWidgets = proxyWidgets
|
|
}
|
|
widget.promoted = false
|
|
}
|
|
|
|
export function matchesWidgetItem([nodeId, widgetName]: [string, string]) {
|
|
return ([n, w]: WidgetItem) => n.id == nodeId && w.name === widgetName
|
|
}
|
|
export function matchesPropertyItem([n, w]: WidgetItem) {
|
|
return ([nodeId, widgetName]: [string, string]) =>
|
|
n.id == nodeId && w.name === widgetName
|
|
}
|
|
export function widgetItemToProperty([n, w]: WidgetItem): [string, string] {
|
|
return [`${n.id}`, w.name]
|
|
}
|
|
|
|
function getParentNodes(): SubgraphNode[] {
|
|
//NOTE: support for determining parents of a subgraph is limited
|
|
//This function will require rework to properly support linked subgraphs
|
|
//Either by including actual parents in the navigation stack,
|
|
//or by adding a new event for parent listeners to collect from
|
|
const { navigationStack } = useSubgraphNavigationStore()
|
|
const subgraph = navigationStack.at(-1)
|
|
if (!subgraph) throw new Error("Can't promote widget when not in subgraph")
|
|
const parentGraph = navigationStack.at(-2) ?? subgraph.rootGraph
|
|
return parentGraph.nodes.filter(
|
|
(node): node is SubgraphNode =>
|
|
node.type === subgraph.id && node.isSubgraphNode()
|
|
)
|
|
}
|
|
|
|
export function addWidgetPromotionOptions(
|
|
options: (IContextMenuValue<unknown> | null)[],
|
|
widget: IBaseWidget,
|
|
node: LGraphNode
|
|
) {
|
|
const parents = getParentNodes()
|
|
const promotableParents = parents.filter(
|
|
(s) => !getProxyWidgets(s).some(matchesPropertyItem([node, widget]))
|
|
)
|
|
if (promotableParents.length > 0)
|
|
options.unshift({
|
|
content: `Promote Widget: ${widget.label ?? widget.name}`,
|
|
callback: () => {
|
|
promoteWidget(node, widget, promotableParents)
|
|
}
|
|
})
|
|
else {
|
|
options.unshift({
|
|
content: `Un-Promote Widget: ${widget.label ?? widget.name}`,
|
|
callback: () => {
|
|
demoteWidget(node, widget, parents)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
const recommendedNodes = [
|
|
'CLIPTextEncode',
|
|
'LoadImage',
|
|
'SaveImage',
|
|
'PreviewImage'
|
|
]
|
|
const recommendedWidgetNames = ['seed']
|
|
export function isRecommendedWidget([node, widget]: WidgetItem) {
|
|
return (
|
|
!widget.computedDisabled &&
|
|
(recommendedNodes.includes(node.type) ||
|
|
recommendedWidgetNames.includes(widget.name))
|
|
)
|
|
}
|
|
|
|
function nodeWidgets(n: LGraphNode): WidgetItem[] {
|
|
return n.widgets?.map((w: IBaseWidget) => [n, w]) ?? []
|
|
}
|
|
export function promoteRecommendedWidgets(subgraphNode: SubgraphNode) {
|
|
const { updatePreviews } = useLitegraphService()
|
|
const interiorNodes = subgraphNode.subgraph.nodes
|
|
for (const node of interiorNodes) {
|
|
node.updateComputedDisabled()
|
|
function checkWidgets() {
|
|
updatePreviews(node)
|
|
const widget = node.widgets?.find((w) => w.name.startsWith('$$'))
|
|
if (!widget) return
|
|
const pw = getProxyWidgets(subgraphNode)
|
|
if (pw.some(matchesPropertyItem([node, widget]))) return
|
|
promoteWidget(node, widget, [subgraphNode])
|
|
}
|
|
requestAnimationFrame(() => updatePreviews(node, checkWidgets))
|
|
}
|
|
const filteredWidgets: WidgetItem[] = interiorNodes
|
|
.flatMap(nodeWidgets)
|
|
.filter(isRecommendedWidget)
|
|
const proxyWidgets: ProxyWidgetsProperty =
|
|
filteredWidgets.map(widgetItemToProperty)
|
|
subgraphNode.properties.proxyWidgets = proxyWidgets
|
|
}
|