Skip to content

Commit 7144ec5

Browse files
authored
Fix UI crash when selecting broken node + TS fixes (#3859)
1 parent b2f144c commit 7144ec5

File tree

14 files changed

+65
-59
lines changed

14 files changed

+65
-59
lines changed

src/components/graph/DomWidgets.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import { computed, watch } from 'vue'
1616
1717
import DomWidget from '@/components/graph/widgets/DomWidget.vue'
1818
import { useChainCallback } from '@/composables/functional/useChainCallback'
19-
import { type DomWidgetState, useDomWidgetStore } from '@/stores/domWidgetStore'
19+
import { useDomWidgetStore } from '@/stores/domWidgetStore'
2020
import { useCanvasStore } from '@/stores/graphStore'
2121
2222
const domWidgetStore = useDomWidgetStore()
23-
const widgetStates = computed(
24-
() => Array.from(domWidgetStore.widgetStates.values()) as DomWidgetState[]
23+
const widgetStates = computed(() =>
24+
Array.from(domWidgetStore.widgetStates.values())
2525
)
2626
2727
const updateWidgets = () => {

src/components/graph/GraphCanvas.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ onMounted(async () => {
293293
workspaceStore.spinner = true
294294
// ChangeTracker needs to be initialized before setup, as it will overwrite
295295
// some listeners of litegraph canvas.
296-
ChangeTracker.init(comfyApp)
296+
ChangeTracker.init()
297297
await loadCustomNodesI18n()
298298
try {
299299
await settingStore.loadSettingValues()

src/components/graph/selectionToolbox/ExecuteButton.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const buttonHovered = ref(false)
3636
const selectedOutputNodes = computed(
3737
() =>
3838
canvasStore.selectedItems.filter(
39-
(item) => isLGraphNode(item) && item.constructor.nodeData.output_node
39+
(item) => isLGraphNode(item) && item.constructor.nodeData?.output_node
4040
) as LGraphNode[]
4141
)
4242
@@ -45,7 +45,7 @@ const isDisabled = computed(() => selectedOutputNodes.value.length === 0)
4545
function outputNodeStokeStyle(this: LGraphNode) {
4646
if (
4747
this.selected &&
48-
this.constructor.nodeData.output_node &&
48+
this.constructor.nodeData?.output_node &&
4949
buttonHovered.value
5050
) {
5151
return { color: 'orange', lineWidth: 2, padding: 10 }

src/composables/useCoreCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export function useCoreCommands(): ComfyCommand[] {
320320
function: async () => {
321321
const batchCount = useQueueSettingsStore().batchCount
322322
const queueNodeIds = getSelectedNodes()
323-
.filter((node) => node.constructor.nodeData.output_node)
323+
.filter((node) => node.constructor.nodeData?.output_node)
324324
.map((node) => node.id)
325325
if (queueNodeIds.length === 0) {
326326
toastStore.add({

src/extensions/core/groupNode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ export class GroupNodeConfig {
797797

798798
export class GroupNodeHandler {
799799
node: LGraphNode
800-
groupData
800+
groupData: any
801801
innerNodes: any
802802

803803
constructor(node: LGraphNode) {

src/extensions/core/nodeTemplates.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ app.registerExtension({
401401
// @ts-expect-error
402402
data.groupNodes = {}
403403
}
404+
if (nodeData == null) throw new TypeError('nodeData is not set')
404405
// @ts-expect-error
405406
data.groupNodes[nodeData.name] = groupData
406407
// @ts-expect-error

src/extensions/core/uploadAudio.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ app.registerExtension({
117117
node.addDOMWidget(inputName, /* name=*/ 'audioUI', audio)
118118
audioUIWidget.serialize = false
119119

120-
const isOutputNode = node.constructor.nodeData.output_node
120+
const { nodeData } = node.constructor
121+
if (nodeData == null) throw new TypeError('nodeData is null')
122+
123+
const isOutputNode = nodeData.output_node
121124
if (isOutputNode) {
122125
// Hide the audio widget when there is no audio initially.
123126
audioUIWidget.element.classList.add('empty-audio-widget')

src/schemas/comfyWorkflowSchema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ const zComfyNode = z
216216

217217
const zGroup = z
218218
.object({
219+
id: z.number().optional(),
219220
title: z.string(),
220221
bounding: z.tuple([z.number(), z.number(), z.number(), z.number()]),
221222
color: z.string().optional(),

src/scripts/changeTracker.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ComfyWorkflow, useWorkflowStore } from '@/stores/workflowStore'
1010

1111
import { api } from './api'
1212
import type { ComfyApp } from './app'
13+
import { app } from './app'
1314

1415
function clone<T>(obj: T): T {
1516
return JSON.parse(JSON.stringify(obj))
@@ -36,11 +37,6 @@ export class ChangeTracker {
3637
ds?: { scale: number; offset: [number, number] }
3738
nodeOutputs?: Record<string, any>
3839

39-
static app?: ComfyApp
40-
get app(): ComfyApp {
41-
return ChangeTracker.app!
42-
}
43-
4440
constructor(
4541
/**
4642
* The workflow that this change tracker is tracking
@@ -68,18 +64,18 @@ export class ChangeTracker {
6864

6965
store() {
7066
this.ds = {
71-
scale: this.app.canvas.ds.scale,
72-
offset: [this.app.canvas.ds.offset[0], this.app.canvas.ds.offset[1]]
67+
scale: app.canvas.ds.scale,
68+
offset: [app.canvas.ds.offset[0], app.canvas.ds.offset[1]]
7369
}
7470
}
7571

7672
restore() {
7773
if (this.ds) {
78-
this.app.canvas.ds.scale = this.ds.scale
79-
this.app.canvas.ds.offset = this.ds.offset
74+
app.canvas.ds.scale = this.ds.scale
75+
app.canvas.ds.offset = this.ds.offset
8076
}
8177
if (this.nodeOutputs) {
82-
this.app.nodeOutputs = this.nodeOutputs
78+
app.nodeOutputs = this.nodeOutputs
8379
}
8480
}
8581

@@ -105,10 +101,8 @@ export class ChangeTracker {
105101
}
106102

107103
checkState() {
108-
if (!this.app.graph || this.changeCount) return
109-
// @ts-expect-error zod type issue on ComfyWorkflowJSON. ComfyWorkflowJSON
110-
// is stricter than LiteGraph's serialisation schema.
111-
const currentState = clone(this.app.graph.serialize()) as ComfyWorkflowJSON
104+
if (!app.graph || this.changeCount) return
105+
const currentState = clone(app.graph.serialize()) as ComfyWorkflowJSON
112106
if (!this.activeState) {
113107
this.activeState = currentState
114108
return
@@ -132,7 +126,7 @@ export class ChangeTracker {
132126
target.push(this.activeState)
133127
this.restoringState = true
134128
try {
135-
await this.app.loadGraphData(prevState, false, false, this.workflow, {
129+
await app.loadGraphData(prevState, false, false, this.workflow, {
136130
showMissingModelsDialog: false,
137131
showMissingNodesDialog: false,
138132
checkForRerouteMigration: false
@@ -189,13 +183,11 @@ export class ChangeTracker {
189183
}
190184
}
191185

192-
static init(app: ComfyApp) {
186+
static init() {
193187
const getCurrentChangeTracker = () =>
194188
useWorkflowStore().activeWorkflow?.changeTracker
195189
const checkState = () => getCurrentChangeTracker()?.checkState()
196190

197-
ChangeTracker.app = app
198-
199191
let keyIgnored = false
200192
window.addEventListener(
201193
'keydown',
@@ -237,7 +229,7 @@ export class ChangeTracker {
237229
if (await changeTracker.undoRedo(e)) return
238230

239231
// If our active element is some type of input then handle changes after they're done
240-
if (ChangeTracker.bindInput(app, bindInputEl)) return
232+
if (ChangeTracker.bindInput(bindInputEl)) return
241233
logger.debug('checkState on keydown')
242234
changeTracker.checkState()
243235
})
@@ -339,7 +331,7 @@ export class ChangeTracker {
339331
})
340332
}
341333

342-
static bindInput(_app: ComfyApp, activeEl: Element | null): boolean {
334+
static bindInput(activeEl: Element | null): boolean {
343335
if (
344336
!activeEl ||
345337
activeEl.tagName === 'CANVAS' ||

src/stores/domWidgetStore.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
* Stores all DOM widgets that are used in the canvas.
33
*/
44
import { defineStore } from 'pinia'
5-
import { markRaw, ref } from 'vue'
5+
import { type Raw, markRaw, ref } from 'vue'
66

77
import type { PositionConfig } from '@/composables/element/useAbsolutePosition'
88
import type { BaseDOMWidget } from '@/scripts/domWidget'
99

1010
export interface DomWidgetState extends PositionConfig {
1111
// Raw widget instance
12-
widget: BaseDOMWidget<object | string>
12+
widget: Raw<BaseDOMWidget<object | string>>
1313
visible: boolean
1414
readonly: boolean
1515
zIndex: number
@@ -23,7 +23,7 @@ export const useDomWidgetStore = defineStore('domWidget', () => {
2323
widget: BaseDOMWidget<V>
2424
) => {
2525
widgetStates.value.set(widget.id, {
26-
widget: markRaw(widget) as unknown as BaseDOMWidget<object | string>,
26+
widget: markRaw(widget) as unknown as Raw<BaseDOMWidget<object | string>>,
2727
visible: true,
2828
readonly: false,
2929
zIndex: 0,

0 commit comments

Comments
 (0)