mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-27 18:24:11 +00:00
## Summary Surfaces missing node pack information in the Errors Tab, grouped by registry pack, with one-click install support via ComfyUI Manager. ## Changes - **What**: Errors Tab now groups missing nodes by their registry pack and shows a `MissingPackGroupRow` with pack name, node/pack counts, and an Install button that triggers Manager installation. A `MissingNodeCard` shows individual unresolvable nodes that have no associated pack. `useErrorGroups` was extended to resolve missing node types to their registry packs using the `/api/workflow/missing_nodes` endpoint. `executionErrorStore` was refactored to track missing node types separately from execution errors and expose them reactively. - **Breaking**: None ## Review Focus - `useErrorGroups.ts` — the new `resolveMissingNodePacks` logic fetches pack metadata and maps node types to pack IDs; edge cases around partial resolution (some nodes have a pack, some don't) produce both `MissingPackGroupRow` and `MissingNodeCard` entries - `executionErrorStore.ts` — the store now separates `missingNodeTypes` state from `errors`; the deferred-warnings path in `app.ts` now calls `setMissingNodeTypes` so the Errors Tab is populated even when a workflow loads without executing ## Screenshots (if applicable) https://github.com/user-attachments/assets/97f8d009-0cac-4739-8740-fd3333b5a85b ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9213-feat-show-missing-node-packs-in-Errors-Tab-with-install-support-3126d73d36508197bc4bf8ebfd2125c8) by [Unito](https://www.unito.io)
135 lines
3.5 KiB
Vue
135 lines
3.5 KiB
Vue
<template>
|
|
<div class="overflow-hidden">
|
|
<!-- Card Header -->
|
|
<div
|
|
v-if="card.nodeId && !compact"
|
|
class="flex flex-wrap items-center gap-2 py-2"
|
|
>
|
|
<span
|
|
v-if="showNodeIdBadge"
|
|
class="shrink-0 rounded-md bg-secondary-background-selected px-2 py-0.5 text-xs font-mono text-muted-foreground font-bold"
|
|
>
|
|
#{{ card.nodeId }}
|
|
</span>
|
|
<span
|
|
v-if="card.nodeTitle"
|
|
class="flex-1 text-sm text-muted-foreground truncate font-medium"
|
|
>
|
|
{{ card.nodeTitle }}
|
|
</span>
|
|
<div class="flex items-center shrink-0">
|
|
<Button
|
|
v-if="card.isSubgraphNode"
|
|
variant="secondary"
|
|
size="sm"
|
|
class="rounded-lg text-sm shrink-0 h-8"
|
|
@click.stop="handleEnterSubgraph"
|
|
>
|
|
{{ t('rightSidePanel.enterSubgraph') }}
|
|
</Button>
|
|
<Button
|
|
variant="textonly"
|
|
size="icon-sm"
|
|
class="size-8 text-muted-foreground hover:text-base-foreground shrink-0"
|
|
:aria-label="t('rightSidePanel.locateNode')"
|
|
@click.stop="handleLocateNode"
|
|
>
|
|
<i class="icon-[lucide--locate] size-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Multiple Errors within one Card -->
|
|
<div class="divide-y divide-interface-stroke/20 space-y-4">
|
|
<!-- Card Content -->
|
|
<div
|
|
v-for="(error, idx) in card.errors"
|
|
:key="idx"
|
|
class="flex flex-col gap-3"
|
|
>
|
|
<!-- Error Message -->
|
|
<p
|
|
v-if="error.message && !compact"
|
|
class="m-0 text-sm break-words whitespace-pre-wrap leading-relaxed px-0.5 max-h-[4lh] overflow-y-auto"
|
|
>
|
|
{{ error.message }}
|
|
</p>
|
|
|
|
<!-- Traceback / Details -->
|
|
<div
|
|
v-if="error.details"
|
|
:class="
|
|
cn(
|
|
'rounded-lg bg-secondary-background-hover p-2.5 overflow-y-auto border border-interface-stroke/30',
|
|
error.isRuntimeError ? 'max-h-[10lh]' : 'max-h-[6lh]'
|
|
)
|
|
"
|
|
>
|
|
<p
|
|
class="m-0 text-xs text-muted-foreground break-words whitespace-pre-wrap font-mono leading-relaxed"
|
|
>
|
|
{{ error.details }}
|
|
</p>
|
|
</div>
|
|
|
|
<Button
|
|
variant="secondary"
|
|
size="sm"
|
|
class="w-full justify-center gap-2 h-8 text-xs"
|
|
@click="handleCopyError(error)"
|
|
>
|
|
<i class="icon-[lucide--copy] size-3.5" />
|
|
{{ t('g.copy') }}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import { cn } from '@/utils/tailwindUtil'
|
|
|
|
import type { ErrorCardData, ErrorItem } from './types'
|
|
|
|
const {
|
|
card,
|
|
showNodeIdBadge = false,
|
|
compact = false
|
|
} = defineProps<{
|
|
card: ErrorCardData
|
|
showNodeIdBadge?: boolean
|
|
/** Hide card header and error message (used in single-node selection mode) */
|
|
compact?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
locateNode: [nodeId: string]
|
|
enterSubgraph: [nodeId: string]
|
|
copyToClipboard: [text: string]
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
function handleLocateNode() {
|
|
if (card.nodeId) {
|
|
emit('locateNode', card.nodeId)
|
|
}
|
|
}
|
|
|
|
function handleEnterSubgraph() {
|
|
if (card.nodeId) {
|
|
emit('enterSubgraph', card.nodeId)
|
|
}
|
|
}
|
|
|
|
function handleCopyError(error: ErrorItem) {
|
|
emit(
|
|
'copyToClipboard',
|
|
[error.message, error.details].filter(Boolean).join('\n\n')
|
|
)
|
|
}
|
|
</script>
|