mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 14:54:12 +00:00
Migrate legacy reroute to litegraph native reroute (#3151)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 104 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
@@ -524,17 +524,6 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
defaultValue: true,
|
||||
versionAdded: '1.3.42'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.RerouteBeta',
|
||||
category: ['LiteGraph', 'RerouteBeta'],
|
||||
name: 'Opt-in to the reroute beta test',
|
||||
tooltip: 'No longer has any effect; reroutes are always enabled.',
|
||||
deprecated: true,
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
versionAdded: '1.3.42',
|
||||
versionModified: '1.13.3'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Graph.LinkMarkers',
|
||||
category: ['LiteGraph', 'Link', 'LinkMarkers'],
|
||||
|
||||
@@ -357,78 +357,6 @@ export class GroupNodeConfig {
|
||||
output_is_list: []
|
||||
})
|
||||
return def
|
||||
} else if (node.type === 'Reroute') {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const linksTo = this.linksTo[node.index]
|
||||
// @ts-expect-error fixme ts strict error
|
||||
if (linksTo && linksFrom && !this.externalFrom[node.index]?.[0]) {
|
||||
// Being used internally
|
||||
return null
|
||||
}
|
||||
|
||||
let config = {}
|
||||
let rerouteType = '*'
|
||||
if (linksFrom) {
|
||||
for (const [, , id, slot] of linksFrom['0']) {
|
||||
const node = this.nodeData.nodes[id]
|
||||
const input = node.inputs[slot]
|
||||
if (rerouteType === '*') {
|
||||
rerouteType = input.type
|
||||
}
|
||||
if (input.widget) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const targetDef = globalDefs[node.type]
|
||||
const targetWidget =
|
||||
targetDef.input.required[input.widget.name] ??
|
||||
targetDef.input.optional[input.widget.name]
|
||||
|
||||
const widget = [targetWidget[0], config]
|
||||
const res = mergeIfValid(
|
||||
// @ts-expect-error invalid slot type
|
||||
{
|
||||
widget
|
||||
},
|
||||
targetWidget,
|
||||
false,
|
||||
null,
|
||||
widget
|
||||
)
|
||||
config = res?.customConfig ?? config
|
||||
}
|
||||
}
|
||||
} else if (linksTo) {
|
||||
const [id, slot] = linksTo['0']
|
||||
rerouteType = this.nodeData.nodes[id].outputs[slot].type
|
||||
} else {
|
||||
// Reroute used as a pipe
|
||||
for (const l of this.nodeData.links) {
|
||||
if (l[2] === node.index) {
|
||||
rerouteType = l[5]
|
||||
break
|
||||
}
|
||||
}
|
||||
if (rerouteType === '*') {
|
||||
// Check for an external link
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const t = this.externalFrom[node.index]?.[0]
|
||||
if (t) {
|
||||
rerouteType = t
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
config.forceInput = true
|
||||
return {
|
||||
input: {
|
||||
required: {
|
||||
[rerouteType]: [rerouteType, config]
|
||||
}
|
||||
},
|
||||
output: [rerouteType],
|
||||
output_name: [],
|
||||
output_is_list: []
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(
|
||||
@@ -880,22 +808,13 @@ export class GroupNodeHandler {
|
||||
link = { ...link }
|
||||
const output = this.groupData.newToOldOutputMap[link.origin_slot]
|
||||
let innerNode = this.innerNodes[output.node.index]
|
||||
let l
|
||||
while (innerNode?.type === 'Reroute') {
|
||||
l = innerNode.getInputLink(0)
|
||||
innerNode = innerNode.getInputNode(0)
|
||||
}
|
||||
|
||||
if (!innerNode) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (l && GroupNodeHandler.isGroupNode(innerNode)) {
|
||||
return innerNode.updateLink(l)
|
||||
}
|
||||
|
||||
link.origin_id = innerNode.id
|
||||
link.origin_slot = l?.origin_slot ?? output.slot
|
||||
link.origin_slot = output.slot
|
||||
return link
|
||||
}
|
||||
|
||||
@@ -1352,23 +1271,6 @@ export class GroupNodeHandler {
|
||||
}
|
||||
}
|
||||
continue
|
||||
} else if (innerNode.type === 'Reroute') {
|
||||
const rerouteLinks = this.groupData.linksFrom[old.node.index]
|
||||
if (rerouteLinks) {
|
||||
for (const [_, , targetNodeId, targetSlot] of rerouteLinks['0']) {
|
||||
const node = this.innerNodes[targetNodeId]
|
||||
const input = node.inputs[targetSlot]
|
||||
if (input.widget) {
|
||||
const widget = node.widgets?.find(
|
||||
// @ts-expect-error fixme ts strict error
|
||||
(w) => w.name === input.widget.name
|
||||
)
|
||||
if (widget) {
|
||||
widget.value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
@@ -1411,30 +1313,6 @@ export class GroupNodeHandler {
|
||||
return true
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
populateReroute(node, nodeId, map) {
|
||||
if (node.type !== 'Reroute') return
|
||||
|
||||
const link = this.groupData.linksFrom[nodeId]?.[0]?.[0]
|
||||
if (!link) return
|
||||
const [, , targetNodeId, targetNodeSlot] = link
|
||||
const targetNode = this.groupData.nodeData.nodes[targetNodeId]
|
||||
const inputs = targetNode.inputs
|
||||
const targetWidget = inputs?.[targetNodeSlot]?.widget
|
||||
if (!targetWidget) return
|
||||
|
||||
const offset = inputs.length - (targetNode.widgets_values?.length ?? 0)
|
||||
const v = targetNode.widgets_values?.[targetNodeSlot - offset]
|
||||
if (v == null) return
|
||||
|
||||
const widgetName = Object.values(map)[0]
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const widget = this.node.widgets.find((w) => w.name === widgetName)
|
||||
if (widget) {
|
||||
widget.value = v
|
||||
}
|
||||
}
|
||||
|
||||
populateWidgets() {
|
||||
if (!this.node.widgets) return
|
||||
|
||||
@@ -1448,9 +1326,6 @@ export class GroupNodeHandler {
|
||||
const widgets = Object.keys(map)
|
||||
|
||||
if (!node.widgets_values?.length) {
|
||||
// special handling for populating values into reroutes
|
||||
// this allows primitives connect to them to pick up the correct value
|
||||
this.populateReroute(node, nodeId, map)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import './load3d'
|
||||
import './maskeditor'
|
||||
import './nodeTemplates'
|
||||
import './noteNode'
|
||||
import './rerouteNode'
|
||||
import './saveImageExtraOutput'
|
||||
import './saveMesh'
|
||||
import './simpleTouchSupport'
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
import type { IContextMenuValue } from '@comfyorg/litegraph'
|
||||
import { LGraphCanvas, LGraphNode, LiteGraph } from '@comfyorg/litegraph'
|
||||
|
||||
import { app } from '../../scripts/app'
|
||||
import { getWidgetConfig, mergeIfValid, setWidgetConfig } from './widgetInputs'
|
||||
|
||||
// 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 extends LGraphNode {
|
||||
static category: string | undefined
|
||||
static defaultVisibility = false
|
||||
|
||||
constructor(title?: string) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
super(title)
|
||||
if (!this.properties) {
|
||||
this.properties = {}
|
||||
}
|
||||
this.properties.showOutputText = RerouteNode.defaultVisibility
|
||||
this.properties.horizontal = false
|
||||
|
||||
this.addInput('', '*')
|
||||
this.addOutput(this.properties.showOutputText ? '*' : '', '*')
|
||||
|
||||
this.onAfterGraphConfigured = function () {
|
||||
requestAnimationFrame(() => {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.onConnectionsChange(LiteGraph.INPUT, null, true, null)
|
||||
})
|
||||
}
|
||||
|
||||
this.onConnectionsChange = (type, _index, connected) => {
|
||||
if (app.configuringGraph) return
|
||||
|
||||
// Prevent multiple connections to different types when we have no input
|
||||
if (connected && type === LiteGraph.OUTPUT) {
|
||||
// Ignore wildcard nodes as these will be updated to real types
|
||||
const types = new Set(
|
||||
// @ts-expect-error fixme ts strict error
|
||||
this.outputs[0].links
|
||||
.map((l) => app.graph.links[l].type)
|
||||
.filter((t) => t !== '*')
|
||||
)
|
||||
if (types.size > 1) {
|
||||
const linksToDisconnect = []
|
||||
// @ts-expect-error fixme ts strict error
|
||||
for (let i = 0; i < this.outputs[0].links.length - 1; i++) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const linkId = this.outputs[0].links[i]
|
||||
const link = app.graph.links[linkId]
|
||||
linksToDisconnect.push(link)
|
||||
}
|
||||
for (const link of linksToDisconnect) {
|
||||
const node = app.graph.getNodeById(link.target_id)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.disconnectInput(link.target_slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find root input
|
||||
let currentNode: LGraphNode | null = this
|
||||
let updateNodes = []
|
||||
let inputType = null
|
||||
let inputNode = null
|
||||
while (currentNode) {
|
||||
updateNodes.unshift(currentNode)
|
||||
const linkId = currentNode.inputs[0].link
|
||||
if (linkId !== null) {
|
||||
const link = app.graph.links[linkId]
|
||||
if (!link) return
|
||||
const node = app.graph.getNodeById(link.origin_id)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = node.constructor.type
|
||||
if (type === 'Reroute') {
|
||||
if (node === this) {
|
||||
// We've found a circle
|
||||
currentNode.disconnectInput(link.target_slot)
|
||||
currentNode = null
|
||||
} else {
|
||||
// Move the previous node
|
||||
currentNode = node
|
||||
}
|
||||
} else {
|
||||
// We've found the end
|
||||
inputNode = currentNode
|
||||
// @ts-expect-error fixme ts strict error
|
||||
inputType = node.outputs[link.origin_slot]?.type ?? null
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// This path has no input node
|
||||
currentNode = null
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find all outputs
|
||||
const nodes: LGraphNode[] = [this]
|
||||
let outputType = null
|
||||
while (nodes.length) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
currentNode = nodes.pop()
|
||||
const outputs =
|
||||
// @ts-expect-error fixme ts strict error
|
||||
(currentNode.outputs ? currentNode.outputs[0].links : []) || []
|
||||
if (outputs.length) {
|
||||
for (const linkId of outputs) {
|
||||
const link = app.graph.links[linkId]
|
||||
|
||||
// When disconnecting sometimes the link is still registered
|
||||
if (!link) continue
|
||||
|
||||
const node = app.graph.getNodeById(link.target_id)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const type = node.constructor.type
|
||||
|
||||
if (type === 'Reroute') {
|
||||
// Follow reroute nodes
|
||||
// @ts-expect-error fixme ts strict error
|
||||
nodes.push(node)
|
||||
updateNodes.push(node)
|
||||
} else {
|
||||
// We've found an output
|
||||
const nodeOutType =
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.inputs &&
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.inputs[link?.target_slot] &&
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.inputs[link.target_slot].type
|
||||
? // @ts-expect-error fixme ts strict error
|
||||
node.inputs[link.target_slot].type
|
||||
: null
|
||||
if (
|
||||
inputType &&
|
||||
// @ts-expect-error fixme ts strict error
|
||||
!LiteGraph.isValidConnection(inputType, nodeOutType)
|
||||
) {
|
||||
// The output doesnt match our input so disconnect it
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.disconnectInput(link.target_slot)
|
||||
} else {
|
||||
outputType = nodeOutType
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No more outputs for this path
|
||||
}
|
||||
}
|
||||
|
||||
const displayType = inputType || outputType || '*'
|
||||
const color = LGraphCanvas.link_type_colors[displayType]
|
||||
|
||||
let widgetConfig
|
||||
let widgetType
|
||||
// Update the types of each node
|
||||
for (const node of updateNodes) {
|
||||
// If we dont have an input type we are always wildcard but we'll show the output type
|
||||
// This lets you change the output link to a different type and all nodes will update
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.outputs[0].type = inputType || '*'
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.__outputType = displayType
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.outputs[0].name = node.properties.showOutputText
|
||||
? displayType
|
||||
: ''
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.setSize(node.computeSize())
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
for (const l of node.outputs[0].links || []) {
|
||||
const link = app.graph.links[l]
|
||||
if (link) {
|
||||
link.color = color
|
||||
|
||||
if (app.configuringGraph) continue
|
||||
const targetNode = app.graph.getNodeById(link.target_id)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const targetInput = targetNode.inputs?.[link.target_slot]
|
||||
if (targetInput?.widget) {
|
||||
const config = getWidgetConfig(targetInput)
|
||||
if (!widgetConfig) {
|
||||
widgetConfig = config[1] ?? {}
|
||||
widgetType = config[0]
|
||||
}
|
||||
|
||||
const merged = mergeIfValid(targetInput, [
|
||||
config[0],
|
||||
widgetConfig
|
||||
])
|
||||
if (merged.customConfig) {
|
||||
widgetConfig = merged.customConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const node of updateNodes) {
|
||||
if (widgetConfig && outputType) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
node.inputs[0].widget = { name: 'value' }
|
||||
// @ts-expect-error fixme ts strict error
|
||||
setWidgetConfig(node.inputs[0], [
|
||||
widgetType ?? displayType,
|
||||
widgetConfig
|
||||
])
|
||||
} else {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
setWidgetConfig(node.inputs[0], null)
|
||||
}
|
||||
}
|
||||
|
||||
if (inputNode) {
|
||||
// @ts-expect-error fixme ts strict error
|
||||
const link = app.graph.links[inputNode.inputs[0].link]
|
||||
if (link) {
|
||||
link.color = color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.clone = function () {
|
||||
const cloned = RerouteNode.prototype.clone.apply(this)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
cloned.removeOutput(0)
|
||||
// @ts-expect-error fixme ts strict error
|
||||
cloned.addOutput(this.properties.showOutputText ? '*' : '', '*')
|
||||
// @ts-expect-error fixme ts strict error
|
||||
cloned.setSize(cloned.computeSize())
|
||||
return cloned
|
||||
}
|
||||
|
||||
// This node is purely frontend and does not impact the resulting prompt so should not be serialized
|
||||
this.isVirtualNode = true
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
getExtraMenuOptions(_, options): IContextMenuValue[] {
|
||||
options.unshift(
|
||||
{
|
||||
content:
|
||||
(this.properties.showOutputText ? 'Hide' : 'Show') + ' Type',
|
||||
callback: () => {
|
||||
this.properties.showOutputText = !this.properties.showOutputText
|
||||
if (this.properties.showOutputText) {
|
||||
this.outputs[0].name =
|
||||
this.__outputType || (this.outputs[0].type as string)
|
||||
} else {
|
||||
this.outputs[0].name = ''
|
||||
}
|
||||
this.setSize(this.computeSize())
|
||||
app.graph.setDirtyCanvas(true, true)
|
||||
}
|
||||
},
|
||||
{
|
||||
content:
|
||||
(RerouteNode.defaultVisibility ? 'Hide' : 'Show') +
|
||||
' Type By Default',
|
||||
callback: () => {
|
||||
RerouteNode.setDefaultTextVisibility(
|
||||
!RerouteNode.defaultVisibility
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
return []
|
||||
}
|
||||
computeSize(): [number, number] {
|
||||
return [
|
||||
this.properties.showOutputText && this.outputs && this.outputs.length
|
||||
? Math.max(
|
||||
75,
|
||||
LiteGraph.NODE_TEXT_SIZE * this.outputs[0].name.length * 0.6 +
|
||||
40
|
||||
)
|
||||
: 75,
|
||||
26
|
||||
]
|
||||
}
|
||||
|
||||
// @ts-expect-error fixme ts strict error
|
||||
static setDefaultTextVisibility(visible) {
|
||||
RerouteNode.defaultVisibility = visible
|
||||
if (visible) {
|
||||
localStorage['Comfy.RerouteNode.DefaultVisibility'] = 'true'
|
||||
} else {
|
||||
delete localStorage['Comfy.RerouteNode.DefaultVisibility']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load default visibility
|
||||
RerouteNode.setDefaultTextVisibility(
|
||||
!!localStorage['Comfy.RerouteNode.DefaultVisibility']
|
||||
)
|
||||
|
||||
LiteGraph.registerNodeType(
|
||||
'Reroute',
|
||||
Object.assign(RerouteNode, {
|
||||
title_mode: LiteGraph.NO_TITLE,
|
||||
title: 'Reroute',
|
||||
collapsable: false
|
||||
})
|
||||
)
|
||||
|
||||
RerouteNode.category = 'utils'
|
||||
}
|
||||
})
|
||||
@@ -44,7 +44,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
if (!(type in this.slot_types_default_out)) {
|
||||
this.slot_types_default_out[type] = ['Reroute']
|
||||
this.slot_types_default_out[type] = []
|
||||
}
|
||||
if (this.slot_types_default_out[type].includes(nodeId)) continue
|
||||
this.slot_types_default_out[type].push(nodeId)
|
||||
@@ -65,7 +65,7 @@ app.registerExtension({
|
||||
for (const el of outputs) {
|
||||
const type = el as string
|
||||
if (!(type in this.slot_types_default_in)) {
|
||||
this.slot_types_default_in[type] = ['Reroute'] // ["Reroute", "Primitive"]; primitive doesn't always work :'()
|
||||
this.slot_types_default_in[type] = []
|
||||
}
|
||||
|
||||
if (this.slot_types_default_in[type].includes(nodeId)) continue
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "Batch count limit",
|
||||
"tooltip": "The maximum number of tasks added to the queue at one button click"
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "Opt-in to the reroute beta test",
|
||||
"tooltip": "No longer has any effect; reroutes are always enabled."
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "Sidebar location",
|
||||
"options": {
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "Taille de l'historique de la file d'attente",
|
||||
"tooltip": "Le nombre maximum de tâches qui s'affichent dans l'historique de la file d'attente."
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "Participer au test bêta de reroute",
|
||||
"tooltip": "Active les nouveaux reroutes natifs.\n\nLes reroutes peuvent être ajoutés en maintenant alt et en glissant à partir d'une ligne de lien, ou sur le menu de lien.\n\nLa désactivation de cette option est non destructive - les reroutes sont cachés."
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "Emplacement de la barre latérale",
|
||||
"options": {
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "キュー履歴サイズ",
|
||||
"tooltip": "キュー履歴に表示されるタスクの最大数。"
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "リルートベータテストに参加する",
|
||||
"tooltip": "新しいネイティブリルートを有効にします。\n\nリルートは、リンクラインからドラッグしながらaltを押すか、リンクメニューで追加できます。\n\nこのオプションを無効にしても破壊的ではなく、リルートは隠されます。"
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "サイドバーの位置",
|
||||
"options": {
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "실행 큐 기록 갯수",
|
||||
"tooltip": "실행 큐 기록에 표시되는 최대 작업 수입니다."
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "경로재설정 베타 테스트 참여",
|
||||
"tooltip": "새로운 기본 경로재설정을 활성화합니다.\n\n링크 라인에서 Alt를 누른 채 드래그하거나 링크 메뉴에서 경로재설정을 추가할 수 있습니다.\n\n이 옵션을 비활성화해도 경로재설정은 삭제되지 않고 숨겨집니다."
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "사이드바 위치",
|
||||
"options": {
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "Размер истории очереди",
|
||||
"tooltip": "Максимальное количество задач, отображаемых в истории очереди."
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "Участвовать в бета-тестировании перенаправления",
|
||||
"tooltip": "Включает новые нативные перенаправления.\n\nПеренаправления можно добавлять, удерживая alt и перетаскивая от линии ссылки или в меню ссылки.\n\nОтключение этой опции не разрушительно — перенаправления скрыты."
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "Расположение боковой панели",
|
||||
"options": {
|
||||
|
||||
@@ -243,10 +243,6 @@
|
||||
"name": "队列历史大小",
|
||||
"tooltip": "队列历史中显示的最大任务数量。"
|
||||
},
|
||||
"Comfy_RerouteBeta": {
|
||||
"name": "选择加入转接点Beta",
|
||||
"tooltip": "启用新的转接点。\n\n通过按住alt并划过连线来添加转接点,或在连线中点菜单中添加。\n\n禁用此选项不会造成破坏 - 转接点将被隐藏。"
|
||||
},
|
||||
"Comfy_Sidebar_Location": {
|
||||
"name": "侧边栏位置",
|
||||
"options": {
|
||||
|
||||
@@ -278,6 +278,9 @@ export type NodeInput = z.infer<typeof zNodeInput>
|
||||
export type NodeOutput = z.infer<typeof zNodeOutput>
|
||||
export type ComfyLink = z.infer<typeof zComfyLink>
|
||||
export type ComfyNode = z.infer<typeof zComfyNode>
|
||||
export type Reroute = z.infer<typeof zReroute>
|
||||
export type WorkflowJSON04 = z.infer<typeof zComfyWorkflow>
|
||||
export type WorkflowJSON10 = z.infer<typeof zComfyWorkflow1>
|
||||
export type ComfyWorkflowJSON = z.infer<
|
||||
typeof zComfyWorkflow | typeof zComfyWorkflow1
|
||||
>
|
||||
|
||||
@@ -44,6 +44,7 @@ import { ExtensionManager } from '@/types/extensionTypes'
|
||||
import { ColorAdjustOptions, adjustColor } from '@/utils/colorUtil'
|
||||
import { graphToPrompt } from '@/utils/executionUtil'
|
||||
import { executeWidgetsCallback, isImageNode } from '@/utils/litegraphUtil'
|
||||
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
|
||||
import { deserialiseAndCreate } from '@/utils/vintageClipboard'
|
||||
|
||||
import { type ComfyApi, api } from './api'
|
||||
@@ -1061,6 +1062,11 @@ export class ComfyApp {
|
||||
graphData = validatedGraphData ?? graphData
|
||||
}
|
||||
|
||||
// Migrate legacy reroute nodes to the new format
|
||||
if (graphData.version === 0.4) {
|
||||
graphData = migrateLegacyRerouteNodes(graphData)
|
||||
}
|
||||
|
||||
useWorkflowService().beforeLoadNewGraph()
|
||||
|
||||
const missingNodeTypes: MissingNodeType[] = []
|
||||
@@ -1069,7 +1075,6 @@ export class ComfyApp {
|
||||
'beforeConfigureGraph',
|
||||
graphData,
|
||||
missingNodeTypes
|
||||
// TODO: missingModels
|
||||
)
|
||||
|
||||
const embeddedModels: ModelFile[] = []
|
||||
@@ -1239,7 +1244,6 @@ export class ComfyApp {
|
||||
useExtensionService().invokeExtensions('loadedGraphNode', node)
|
||||
}
|
||||
|
||||
// TODO: Properly handle if both nodes and models are missing (sequential dialogs?)
|
||||
if (missingNodeTypes.length && showMissingNodesDialog) {
|
||||
this.#showMissingNodesError(missingNodeTypes)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { LGraphCanvas } from '@comfyorg/litegraph'
|
||||
import type { Vector2 } from '@comfyorg/litegraph'
|
||||
import { LGraph, LGraphCanvas } from '@comfyorg/litegraph'
|
||||
import type { SerialisableGraph, Vector2 } from '@comfyorg/litegraph'
|
||||
import { toRaw } from 'vue'
|
||||
|
||||
import { t } from '@/i18n'
|
||||
import { ComfyWorkflowJSON } from '@/schemas/comfyWorkflowSchema'
|
||||
import {
|
||||
ComfyWorkflowJSON,
|
||||
WorkflowJSON04
|
||||
} from '@/schemas/comfyWorkflowSchema'
|
||||
import { app } from '@/scripts/app'
|
||||
import { blankGraph, defaultGraph } from '@/scripts/defaultGraph'
|
||||
import { downloadBlob } from '@/scripts/utils'
|
||||
@@ -12,6 +15,7 @@ import { useToastStore } from '@/stores/toastStore'
|
||||
import { ComfyWorkflow, useWorkflowStore } from '@/stores/workflowStore'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
import { appendJsonExt } from '@/utils/formatUtil'
|
||||
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
|
||||
|
||||
import { useDialogService } from './dialogService'
|
||||
|
||||
@@ -324,17 +328,19 @@ export const useWorkflowService = () => {
|
||||
) => {
|
||||
const loadedWorkflow = await workflow.load()
|
||||
const data = loadedWorkflow.initialState
|
||||
const workflowJSON =
|
||||
data.version === 0.4
|
||||
? migrateLegacyRerouteNodes(data as WorkflowJSON04)
|
||||
: data
|
||||
const old = localStorage.getItem('litegrapheditor_clipboard')
|
||||
// @ts-expect-error: zod issue. Should be fixed after enable ts-strict globally
|
||||
const graph = new LGraph(data)
|
||||
// unknown conversion: ComfyWorkflowJSON is stricter than LiteGraph's
|
||||
// serialisation schema.
|
||||
const graph = new LGraph(workflowJSON as unknown as SerialisableGraph)
|
||||
const canvasElement = document.createElement('canvas')
|
||||
const canvas = new LGraphCanvas(canvasElement, graph, {
|
||||
skip_events: true,
|
||||
skip_render: true
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore Temporary fix for Litegraph issue.
|
||||
canvas.reroutesEnabled = true
|
||||
canvas.selectItems()
|
||||
canvas.copyToClipboard()
|
||||
app.canvas.pasteFromClipboard(options)
|
||||
|
||||
@@ -140,18 +140,6 @@ export const SYSTEM_NODE_DEFS: Record<string, ComfyNodeDefV1> = {
|
||||
python_module: 'nodes',
|
||||
description: 'Primitive values like numbers, strings, and booleans.'
|
||||
},
|
||||
Reroute: {
|
||||
name: 'Reroute',
|
||||
display_name: 'Reroute',
|
||||
category: 'utils',
|
||||
input: { required: { '': ['*', {}] }, optional: {} },
|
||||
output: ['*'],
|
||||
output_name: [''],
|
||||
output_is_list: [false],
|
||||
output_node: false,
|
||||
python_module: 'nodes',
|
||||
description: 'Reroute the connection to another node.'
|
||||
},
|
||||
Note: {
|
||||
name: 'Note',
|
||||
display_name: 'Note',
|
||||
|
||||
193
src/utils/migration/migrateReroute.ts
Normal file
193
src/utils/migration/migrateReroute.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import type {
|
||||
ComfyLink,
|
||||
ComfyNode,
|
||||
NodeId,
|
||||
Reroute,
|
||||
WorkflowJSON04
|
||||
} from '@/schemas/comfyWorkflowSchema'
|
||||
|
||||
type RerouteNode = ComfyNode & {
|
||||
type: 'Reroute'
|
||||
}
|
||||
|
||||
type LinkExtension = {
|
||||
id: number
|
||||
parentId: number
|
||||
}
|
||||
|
||||
type RerouteEntry = {
|
||||
reroute: Reroute
|
||||
rerouteNode: RerouteNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies all legacy Reroute nodes in a workflow
|
||||
*/
|
||||
function findLegacyRerouteNodes(workflow: WorkflowJSON04): RerouteNode[] {
|
||||
return workflow.nodes.filter(
|
||||
(node) => node.type === 'Reroute'
|
||||
) as RerouteNode[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the center position of a node
|
||||
*/
|
||||
function getNodeCenter(node: ComfyNode): [number, number] {
|
||||
return [node.pos[0] + node.size[0] / 2, node.pos[1] + node.size[1] / 2]
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates native reroute points from legacy Reroute nodes
|
||||
*/
|
||||
export function createReroutePoints(
|
||||
rerouteNodes: RerouteNode[]
|
||||
): Map<NodeId, RerouteEntry> {
|
||||
const rerouteMap = new Map<NodeId, RerouteEntry>()
|
||||
|
||||
let rerouteIdCounter = 1
|
||||
rerouteNodes.forEach((node) => {
|
||||
const rerouteId = rerouteIdCounter++
|
||||
rerouteMap.set(node.id, {
|
||||
reroute: {
|
||||
id: rerouteId,
|
||||
pos: getNodeCenter(node),
|
||||
linkIds: []
|
||||
},
|
||||
rerouteNode: node
|
||||
})
|
||||
})
|
||||
|
||||
return rerouteMap
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new links and link extensions for the migrated workflow
|
||||
*/
|
||||
export function createNewLinks(
|
||||
workflow: WorkflowJSON04,
|
||||
rerouteMap: Map<NodeId, RerouteEntry>
|
||||
): {
|
||||
links: ComfyLink[]
|
||||
linkExtensions: LinkExtension[]
|
||||
} {
|
||||
const links: ComfyLink[] = []
|
||||
const linkExtensions: LinkExtension[] = []
|
||||
|
||||
const rerouteMapByRerouteId = new Map<number, RerouteEntry>(
|
||||
Array.from(rerouteMap.values()).map((entry) => [entry.reroute.id, entry])
|
||||
)
|
||||
const linksMap = new Map<number, ComfyLink>(
|
||||
Array.from(workflow.links).map((link) => [link[0], link])
|
||||
)
|
||||
|
||||
// Process each link in the workflow
|
||||
for (const link of workflow.links) {
|
||||
const [
|
||||
linkId,
|
||||
sourceNodeId,
|
||||
_sourceSlot,
|
||||
targetNodeId,
|
||||
_targetSlot,
|
||||
_dataType
|
||||
] = link
|
||||
|
||||
// Check if this link connects to or from a reroute node
|
||||
const sourceEntry = rerouteMap.get(sourceNodeId)
|
||||
const targetEntry = rerouteMap.get(targetNodeId)
|
||||
const sourceIsReroute = !!sourceEntry
|
||||
const targetIsReroute = !!targetEntry
|
||||
|
||||
if (!sourceIsReroute && !targetIsReroute) {
|
||||
// If neither end is a reroute, keep the link as is
|
||||
links.push(link)
|
||||
} else if (sourceIsReroute && !targetIsReroute) {
|
||||
// This is a link from a reroute node to a regular node
|
||||
linkExtensions.push({
|
||||
id: linkId,
|
||||
parentId: sourceEntry.reroute.id
|
||||
})
|
||||
} else if (sourceIsReroute && targetIsReroute) {
|
||||
targetEntry.reroute.parentId = sourceEntry.reroute.id
|
||||
}
|
||||
}
|
||||
|
||||
// Populate linkIds on reroute nodes
|
||||
for (const linkExtension of linkExtensions) {
|
||||
let entry = rerouteMapByRerouteId.get(linkExtension.parentId)
|
||||
|
||||
while (entry) {
|
||||
const reroute = entry.reroute
|
||||
reroute.linkIds ??= []
|
||||
reroute.linkIds.push(linkExtension.id)
|
||||
|
||||
if (reroute.parentId) {
|
||||
entry = rerouteMapByRerouteId.get(reroute.parentId)
|
||||
} else {
|
||||
const rerouteNode = entry.rerouteNode
|
||||
const rerouteInputLink = linksMap.get(
|
||||
rerouteNode?.inputs?.[0]?.link ?? -1
|
||||
)
|
||||
const rerouteOutputLink = linksMap.get(linkExtension.id)
|
||||
|
||||
if (rerouteInputLink && rerouteOutputLink) {
|
||||
const [_, sourceNodeId, sourceSlot] = rerouteInputLink
|
||||
const [linkId, __, ___, targetNodeId, targetSlot, dataType] =
|
||||
rerouteOutputLink
|
||||
|
||||
links.push([
|
||||
linkId,
|
||||
sourceNodeId,
|
||||
sourceSlot,
|
||||
targetNodeId,
|
||||
targetSlot,
|
||||
dataType
|
||||
])
|
||||
}
|
||||
entry = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { links, linkExtensions }
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to migrate legacy reroute nodes to native reroute points
|
||||
*/
|
||||
export const migrateLegacyRerouteNodes = (
|
||||
workflow: WorkflowJSON04
|
||||
): WorkflowJSON04 => {
|
||||
// Find all legacy Reroute nodes
|
||||
const legacyRerouteNodes = findLegacyRerouteNodes(workflow)
|
||||
|
||||
// If no reroute nodes, return the workflow unchanged
|
||||
if (legacyRerouteNodes.length === 0) {
|
||||
return workflow
|
||||
}
|
||||
|
||||
// Create a deep copy of the workflow to avoid mutating the original
|
||||
const newWorkflow = JSON.parse(JSON.stringify(workflow)) as WorkflowJSON04
|
||||
|
||||
// Initialize extra structure if needed
|
||||
if (!newWorkflow.extra) {
|
||||
newWorkflow.extra = {}
|
||||
}
|
||||
|
||||
// Create native reroute points
|
||||
const rerouteMap = createReroutePoints(legacyRerouteNodes)
|
||||
|
||||
// Create new links and link extensions
|
||||
const { links, linkExtensions } = createNewLinks(workflow, rerouteMap)
|
||||
|
||||
// Update the workflow
|
||||
newWorkflow.links = links
|
||||
newWorkflow.nodes = newWorkflow.nodes.filter(
|
||||
(node) => node.type !== 'Reroute'
|
||||
)
|
||||
newWorkflow.extra.reroutes = Array.from(rerouteMap.values()).map(
|
||||
(entry) => entry.reroute
|
||||
)
|
||||
newWorkflow.extra.linkExtensions = linkExtensions
|
||||
|
||||
return newWorkflow
|
||||
}
|
||||
35
tests-ui/tests/utils/migration/migrateReroute.test.ts
Normal file
35
tests-ui/tests/utils/migration/migrateReroute.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { WorkflowJSON04 } from '@/schemas/comfyWorkflowSchema'
|
||||
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
|
||||
|
||||
describe('migrateReroute', () => {
|
||||
describe('migrateReroute snapshots', () => {
|
||||
// Helper function to load workflow JSON files
|
||||
const loadWorkflow = (filePath: string): WorkflowJSON04 => {
|
||||
const fullPath = path.resolve(__dirname, filePath)
|
||||
const fileContent = fs.readFileSync(fullPath, 'utf-8')
|
||||
return JSON.parse(fileContent) as WorkflowJSON04
|
||||
}
|
||||
|
||||
it.each(['branching.json', 'single_connected.json'])(
|
||||
'should correctly migrate %s',
|
||||
(fileName) => {
|
||||
// Load the legacy workflow
|
||||
const legacyWorkflow = loadWorkflow(
|
||||
`workflows/reroute/legacy/${fileName}`
|
||||
)
|
||||
|
||||
// Migrate the workflow
|
||||
const migratedWorkflow = migrateLegacyRerouteNodes(legacyWorkflow)
|
||||
|
||||
// Compare with snapshot
|
||||
expect(JSON.stringify(migratedWorkflow, null, 2)).toMatchFileSnapshot(
|
||||
`workflows/reroute/native/${fileName}`
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,288 @@
|
||||
{
|
||||
"last_node_id": 27,
|
||||
"last_link_id": 34,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
620,
|
||||
260
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 21
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
47.948699951171875,
|
||||
239.2628173828125
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
13,
|
||||
31
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"v1-5-pruned-emaonly.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
510,
|
||||
280
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 32
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "VAE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
21
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
404.7915344238281,
|
||||
280.9454650878906
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 31
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
32,
|
||||
33
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
514,
|
||||
386
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
34
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
625,
|
||||
373
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
21,
|
||||
13,
|
||||
0,
|
||||
12,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
31,
|
||||
4,
|
||||
2,
|
||||
25,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
32,
|
||||
25,
|
||||
0,
|
||||
13,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
33,
|
||||
25,
|
||||
0,
|
||||
27,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
34,
|
||||
27,
|
||||
0,
|
||||
26,
|
||||
1,
|
||||
"VAE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 2.3195551508147507,
|
||||
"offset": [
|
||||
96.55985005696607,
|
||||
-41.449812921703376
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"last_node_id": 24,
|
||||
"last_link_id": 30,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
620,
|
||||
260
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 21
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
510,
|
||||
280
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 13
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "VAE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
21
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
160,
|
||||
240
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
13
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"v1-5-pruned-emaonly.safetensors"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
13,
|
||||
4,
|
||||
2,
|
||||
13,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
21,
|
||||
13,
|
||||
0,
|
||||
12,
|
||||
1,
|
||||
"VAE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 2.7130434782608694,
|
||||
"offset": [
|
||||
-35,
|
||||
-40.86698717948718
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
{
|
||||
"last_node_id": 27,
|
||||
"last_link_id": 34,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
620,
|
||||
260
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 21
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
47.948699951171875,
|
||||
239.2628173828125
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
13,
|
||||
31
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"v1-5-pruned-emaonly.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
625,
|
||||
373
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
21,
|
||||
4,
|
||||
2,
|
||||
12,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
34,
|
||||
4,
|
||||
2,
|
||||
26,
|
||||
1,
|
||||
"VAE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 2.3195551508147507,
|
||||
"offset": [
|
||||
96.55985005696607,
|
||||
-41.449812921703376
|
||||
]
|
||||
},
|
||||
"reroutes": [
|
||||
{
|
||||
"id": 1,
|
||||
"pos": [
|
||||
547.5,
|
||||
293
|
||||
],
|
||||
"linkIds": [
|
||||
21
|
||||
],
|
||||
"parentId": 2
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"pos": [
|
||||
442.2915344238281,
|
||||
293.9454650878906
|
||||
],
|
||||
"linkIds": [
|
||||
21,
|
||||
34
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"pos": [
|
||||
551.5,
|
||||
399
|
||||
],
|
||||
"linkIds": [
|
||||
34
|
||||
],
|
||||
"parentId": 2
|
||||
}
|
||||
],
|
||||
"linkExtensions": [
|
||||
{
|
||||
"id": 21,
|
||||
"parentId": 1
|
||||
},
|
||||
{
|
||||
"id": 34,
|
||||
"parentId": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"last_node_id": 24,
|
||||
"last_link_id": 30,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 12,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
620,
|
||||
260
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 21
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
160,
|
||||
240
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"slot_index": 0,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"slot_index": 1,
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"slot_index": 2,
|
||||
"links": [
|
||||
13
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"v1-5-pruned-emaonly.safetensors"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
21,
|
||||
4,
|
||||
2,
|
||||
12,
|
||||
1,
|
||||
"VAE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 2.7130434782608694,
|
||||
"offset": [
|
||||
-35,
|
||||
-40.86698717948718
|
||||
]
|
||||
},
|
||||
"reroutes": [
|
||||
{
|
||||
"id": 1,
|
||||
"pos": [
|
||||
547.5,
|
||||
293
|
||||
],
|
||||
"linkIds": [
|
||||
21
|
||||
]
|
||||
}
|
||||
],
|
||||
"linkExtensions": [
|
||||
{
|
||||
"id": 21,
|
||||
"parentId": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user