mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
[fix] properly show error states (#5758)
## Summary I want to take a more general look at `comfyApp.graph.onTrigger` but this is the cleanest fix I could come up with for #5694. I will explore simplifying onTrigger in a separate PR. ## Changes 1. Create a `node:slot-errors:changed` trigger. 2. Trigger it if we find any of the node slots have errors. 3. Check each node to see if there is any error present. 4. Add an error class if there are. ## Screenshots (if applicable) Working error states! <img width="1049" height="987" alt="Screenshot 2025-09-23 at 8 40 04 PM" src="https://github.com/user-attachments/assets/30e13283-129c-4d9c-b342-e7037582998a" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5758-fix-properly-show-error-states-2786d73d365081cbbf62c314c7f5f380) by [Unito](https://www.unito.io) --------- Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
@@ -307,13 +307,23 @@ watch(
|
||||
removeSlotError(node)
|
||||
const nodeErrors = lastNodeErrors?.[node.id]
|
||||
if (!nodeErrors) continue
|
||||
|
||||
let slotErrorsChanged = false
|
||||
for (const error of nodeErrors.errors) {
|
||||
if (error.extra_info && error.extra_info.input_name) {
|
||||
const inputIndex = node.findInputSlot(error.extra_info.input_name)
|
||||
if (inputIndex !== -1) {
|
||||
node.inputs[inputIndex].hasErrors = true
|
||||
}
|
||||
}
|
||||
if (!error.extra_info?.input_name) continue
|
||||
|
||||
const inputIndex = node.findInputSlot(error.extra_info.input_name)
|
||||
if (inputIndex === -1) continue
|
||||
|
||||
node.inputs[inputIndex].hasErrors = true
|
||||
slotErrorsChanged = true
|
||||
}
|
||||
|
||||
// Trigger Vue node data update if slot errors changed
|
||||
if (slotErrorsChanged && comfyApp.graph.onTrigger) {
|
||||
comfyApp.graph.onTrigger('node:slot-errors:changed', {
|
||||
nodeId: node.id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +435,28 @@ export function useGraphNodeManager(graph: LGraph): GraphNodeManager {
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
action === 'node:slot-errors:changed' &&
|
||||
param &&
|
||||
typeof param === 'object'
|
||||
) {
|
||||
const event = param as { nodeId: string | number }
|
||||
const nodeId = String(event.nodeId)
|
||||
const litegraphNode = nodeRefs.get(nodeId)
|
||||
const currentData = vueNodeData.get(nodeId)
|
||||
|
||||
if (litegraphNode && currentData) {
|
||||
// Re-extract slot data with updated hasErrors properties
|
||||
vueNodeData.set(nodeId, {
|
||||
...currentData,
|
||||
inputs: litegraphNode.inputs
|
||||
? [...litegraphNode.inputs]
|
||||
: undefined,
|
||||
outputs: litegraphNode.outputs
|
||||
? [...litegraphNode.outputs]
|
||||
: undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Call original trigger handler if it exists
|
||||
|
||||
@@ -136,7 +136,11 @@ import { computed, inject, onErrorCaptured, onMounted, provide, ref } from 'vue'
|
||||
|
||||
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import {
|
||||
type INodeInputSlot,
|
||||
type INodeOutputSlot,
|
||||
LiteGraph
|
||||
} from '@/lib/litegraph/src/litegraph'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { TransformStateKey } from '@/renderer/core/layout/injectionKeys'
|
||||
@@ -200,9 +204,21 @@ const hasExecutionError = computed(
|
||||
)
|
||||
|
||||
// Computed error states for styling
|
||||
const hasAnyError = computed(
|
||||
(): boolean => !!(hasExecutionError.value || nodeData.hasErrors || error)
|
||||
)
|
||||
const hasAnyError = computed((): boolean => {
|
||||
return (
|
||||
!!hasExecutionError.value ||
|
||||
!!nodeData.hasErrors ||
|
||||
!!error ||
|
||||
// Type assertions needed because VueNodeData.inputs/outputs are typed as unknown[]
|
||||
// but at runtime they contain INodeInputSlot/INodeOutputSlot objects
|
||||
!!nodeData.inputs?.some(
|
||||
(slot) => (slot as INodeInputSlot)?.hasErrors ?? false
|
||||
) ||
|
||||
!!nodeData.outputs?.some(
|
||||
(slot) => (slot as INodeOutputSlot)?.hasErrors ?? false
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
const bypassed = computed((): boolean => nodeData.mode === 4)
|
||||
|
||||
@@ -263,7 +279,7 @@ const { latestPreviewUrl, shouldShowPreviewImg } = useNodePreviewState(
|
||||
|
||||
const borderClass = computed(() => {
|
||||
if (hasAnyError.value) {
|
||||
return 'border-error'
|
||||
return 'border-red-500 dark-theme:border-red-500'
|
||||
}
|
||||
if (executing.value) {
|
||||
return 'border-blue-500'
|
||||
@@ -276,7 +292,7 @@ const outlineClass = computed(() => {
|
||||
return undefined
|
||||
}
|
||||
if (hasAnyError.value) {
|
||||
return 'outline-error'
|
||||
return 'outline-red-500'
|
||||
}
|
||||
if (executing.value) {
|
||||
return 'outline-blue-500'
|
||||
|
||||
Reference in New Issue
Block a user