mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-27 10:14:06 +00:00
## Summary Adds a node replacement UI to the Errors Tab so users can swap missing nodes with compatible alternatives directly from the error panel, without opening a separate dialog. ## Changes - **What**: New `SwapNodesCard` and `SwapNodeGroupRow` components render swap groups in the Errors Tab; each group shows the missing node type, its instances (with locate buttons), and a Replace button. Added `useMissingNodeScan` composable to scan the graph for missing nodes and populate `executionErrorStore`. Added `removeMissingNodesByType()` to `executionErrorStore` so replaced nodes are pruned from the error list reactively. ## Bug Fixes Found During Implementation ### Bug 1: Replaced nodes render as empty shells until page refresh `replaceWithMapping()` directly mutates `_nodes[idx]`, bypassing the Vue rendering pipeline entirely. Because the replacement node reuses the same ID, `vueNodeData` retains the stale entry from the old placeholder (`hasErrors: true`, empty widgets/inputs). `graph.setDirtyCanvas()` only repaints the LiteGraph canvas and has no effect on Vue. **Fix**: After `replaceWithMapping()`, manually call `nodeGraph.onNodeAdded?.(newNode)` to trigger `handleNodeAdded` in `useGraphNodeManager`, which runs `extractVueNodeData(newNode)` and updates `vueNodeData` correctly. Also added a guard in `handleNodeAdded` to skip `layoutStore.createNode()` when a layout for the same ID already exists, preventing a duplicate `spatialIndex.insert()`. ### Bug 2: Missing node error list overwritten by incomplete server response Two compounding issues: (A) the server's `missing_node_type` error only reports the *first* missing node — the old handler parsed this and called `surfaceMissingNodes([singleNode])`, overwriting the full list collected at load time. (B) `queuePrompt()` calls `clearAllErrors()` before the API request; if the subsequent rescan used the stale `has_errors` flag and found nothing, the missing nodes were permanently lost. **Fix**: Created `useMissingNodeScan.ts` which scans `LiteGraph.registered_node_types` directly (not `has_errors`). The `missing_node_type` catch block in `app.ts` now calls `rescanAndSurfaceMissingNodes(this.rootGraph)` instead of parsing the server's partial response. ## Review Focus - `handleReplaceNode` removes the group from the store only when `replaceNodesInPlace` returns at least one replaced node — should we always clear, or only on full success? - `useMissingNodeScan` re-scans on every execution-error change; confirm no performance concerns for large graphs with many subgraphs. ## Screenshots https://github.com/user-attachments/assets/78310fc4-0424-4920-b369-cef60a123d50 https://github.com/user-attachments/assets/3d2fd5e1-5e85-4c20-86aa-8bf920e86987 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9253-feat-add-node-replacement-UI-to-Errors-Tab-3136d73d365081718d4ddfd628cb4449) by [Unito](https://www.unito.io)
39 lines
1012 B
Vue
39 lines
1012 B
Vue
<template>
|
|
<div class="px-4 pb-2 mt-2">
|
|
<!-- Sub-label: guidance message shown above all swap groups -->
|
|
<p class="m-0 pb-5 text-sm text-muted-foreground leading-relaxed">
|
|
{{
|
|
t(
|
|
'nodeReplacement.swapNodesGuide',
|
|
'The following nodes can be automatically replaced with compatible alternatives.'
|
|
)
|
|
}}
|
|
</p>
|
|
<!-- Group Rows -->
|
|
<SwapNodeGroupRow
|
|
v-for="group in swapNodeGroups"
|
|
:key="group.type"
|
|
:group="group"
|
|
:show-node-id-badge="showNodeIdBadge"
|
|
@locate-node="emit('locate-node', $event)"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n'
|
|
import type { SwapNodeGroup } from './useErrorGroups'
|
|
import SwapNodeGroupRow from './SwapNodeGroupRow.vue'
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { swapNodeGroups, showNodeIdBadge } = defineProps<{
|
|
swapNodeGroups: SwapNodeGroup[]
|
|
showNodeIdBadge: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'locate-node': [nodeId: string]
|
|
}>()
|
|
</script>
|