mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-24 22:58:08 +00:00
## Summary - Remove the legacy missing nodes modal dialog and migrate all functionality to the existing Error Overlay / TabErrors system - Migrate core node version warning from `MissingCoreNodesMessage.vue` to `MissingNodeCard` in the errors tab - Remove `Comfy.Workflow.ShowMissingNodesWarning` setting (errors tab always surfaces missing nodes) - Delete 6 legacy files: `useMissingNodesDialog.ts`, `MissingNodesContent.vue`, `MissingNodesFooter.vue`, `MissingNodesHeader.vue`, `MissingCoreNodesMessage.vue` and its test - Rename `showMissingNodesDialog`/`showMissingModelsDialog` params to `showMissingNodes`/`showMissingModels` - Add `errorOverlay` and `missingNodeCard` to centralized `TestIds` - Migrate all E2E tests from legacy dialog selectors to error overlay testIds - Add new E2E test: MissingNodeCard visible via "See Errors" button flow - Add new E2E test: subgraph missing node type verified by expanding pack row - Add `surfaceMissingNodes` unit tests to `executionErrorStore` - Guard `semver.compare` against invalid version strings - Add `role="alert"`, `aria-hidden` for accessibility - Use reactive props destructuring in `MissingNodeCard` and `MissingPackGroupRow` **Net change: -669 lines** (19 files, +323 / -992) <img width="733" height="579" alt="image" src="https://github.com/user-attachments/assets/c497809d-b176-43bf-9872-34bd74c6ea0d" /> ## Test plan - [x] Unit tests: MissingNodeCard core node warning (7 tests) - [x] Unit tests: surfaceMissingNodes (4 tests) - [x] Unit tests: workflowService showPendingWarnings (updated) - [x] E2E: Error overlay visible on missing nodes workflow - [x] E2E: Error overlay visible on subgraph missing nodes - [x] E2E: MissingNodeCard visible via See Errors button - [x] E2E: Subgraph node type visible after expanding pack row - [x] E2E: Error overlay does not resurface on undo/redo - [x] E2E: Error overlay does not reappear on workflow tab switch - [x] Typecheck, lint, knip all passing ## Related issues - Closes #9923 (partially — `errorOverlay` and `missingNodeCard` added to TestIds) - References #10027 (mock hoisting inconsistency) - References #10033 (i18n-based test selectors) - References #10085 (DDD layer violation + focusedErrorNodeId) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10102-refactor-remove-legacy-missing-nodes-dialog-3256d73d365081c194d2e90bc6401846) by [Unito](https://www.unito.io)
184 lines
6.1 KiB
Vue
184 lines
6.1 KiB
Vue
<template>
|
|
<div data-testid="missing-node-card" class="px-4 pb-2">
|
|
<!-- Core node version warning (OSS only) -->
|
|
<div
|
|
v-if="!isCloud && hasMissingCoreNodes"
|
|
role="alert"
|
|
class="mb-3 flex gap-2.5 rounded-lg border border-warning-background/30 bg-warning-background/10 p-3"
|
|
>
|
|
<i
|
|
aria-hidden="true"
|
|
class="mt-0.5 icon-[lucide--triangle-alert] size-4 shrink-0 text-warning-background"
|
|
/>
|
|
<div class="flex flex-col gap-1.5 text-xs/relaxed text-muted-foreground">
|
|
<p class="m-0">
|
|
{{
|
|
currentComfyUIVersion
|
|
? t('loadWorkflowWarning.outdatedVersion', {
|
|
version: currentComfyUIVersion
|
|
})
|
|
: t('loadWorkflowWarning.outdatedVersionGeneric')
|
|
}}
|
|
</p>
|
|
<div
|
|
v-for="[version, nodes] in sortedMissingCoreNodes"
|
|
:key="version"
|
|
class="ml-2"
|
|
>
|
|
<span class="font-medium">
|
|
{{
|
|
t('loadWorkflowWarning.coreNodesFromVersion', {
|
|
version: version || t('loadWorkflowWarning.unknownVersion')
|
|
})
|
|
}}
|
|
</span>
|
|
<span class="ml-1">{{ getUniqueNodeNames(nodes).join(', ') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sub-label: cloud or OSS message shown above all pack groups -->
|
|
<p
|
|
class="m-0 text-sm/relaxed text-muted-foreground"
|
|
:class="showManagerHint ? 'pb-3' : 'pb-5'"
|
|
>
|
|
{{
|
|
isCloud
|
|
? t('rightSidePanel.missingNodePacks.cloudMessage')
|
|
: t('rightSidePanel.missingNodePacks.ossMessage')
|
|
}}
|
|
</p>
|
|
|
|
<!-- Manager disabled hint: shown on OSS when manager is not active -->
|
|
<i18n-t
|
|
v-if="showManagerHint"
|
|
keypath="rightSidePanel.missingNodePacks.ossManagerDisabledHint"
|
|
tag="p"
|
|
class="m-0 pb-5 text-sm/relaxed text-muted-foreground"
|
|
>
|
|
<template #pipCmd>
|
|
<code
|
|
class="rounded-sm bg-comfy-menu-bg px-1 py-0.5 font-mono text-xs text-comfy-input-foreground"
|
|
>pip install -U --pre comfyui-manager</code
|
|
>
|
|
</template>
|
|
<template #flag>
|
|
<code
|
|
class="rounded-sm bg-comfy-menu-bg px-1 py-0.5 font-mono text-xs text-comfy-input-foreground"
|
|
>--enable-manager</code
|
|
>
|
|
</template>
|
|
</i18n-t>
|
|
<MissingPackGroupRow
|
|
v-for="group in missingPackGroups"
|
|
:key="group.packId ?? '__unknown__'"
|
|
:group="group"
|
|
:show-info-button="showInfoButton"
|
|
:show-node-id-badge="showNodeIdBadge"
|
|
@locate-node="emit('locateNode', $event)"
|
|
@open-manager-info="emit('openManagerInfo', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Apply Changes: shown when manager enabled and at least one pack install succeeded -->
|
|
<div v-if="shouldShowManagerButtons" class="px-4">
|
|
<Button
|
|
v-if="hasInstalledPacksPendingRestart"
|
|
variant="primary"
|
|
:disabled="isRestarting"
|
|
class="mt-2 h-9 w-full justify-center gap-2 text-sm font-semibold"
|
|
@click="applyChanges()"
|
|
>
|
|
<DotSpinner v-if="isRestarting" duration="1s" :size="14" />
|
|
<i
|
|
v-else
|
|
aria-hidden="true"
|
|
class="icon-[lucide--refresh-cw] size-4 shrink-0"
|
|
/>
|
|
<span class="min-w-0 truncate">{{
|
|
t('rightSidePanel.missingNodePacks.applyChanges')
|
|
}}</span>
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { compare, valid } from 'semver'
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import DotSpinner from '@/components/common/DotSpinner.vue'
|
|
import { useApplyChanges } from '@/workbench/extensions/manager/composables/useApplyChanges'
|
|
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
|
import { isCloud } from '@/platform/distribution/types'
|
|
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
|
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
|
|
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
|
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
|
import type { MissingPackGroup } from '@/components/rightSidePanel/errors/useErrorGroups'
|
|
import MissingPackGroupRow from '@/components/rightSidePanel/errors/MissingPackGroupRow.vue'
|
|
|
|
const { showInfoButton, showNodeIdBadge, missingPackGroups } = defineProps<{
|
|
showInfoButton: boolean
|
|
showNodeIdBadge: boolean
|
|
missingPackGroups: MissingPackGroup[]
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
locateNode: [nodeId: string]
|
|
openManagerInfo: [packId: string]
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { missingCoreNodes } = useMissingNodes()
|
|
const systemStatsStore = useSystemStatsStore()
|
|
|
|
const hasMissingCoreNodes = computed(
|
|
() => Object.keys(missingCoreNodes.value).length > 0
|
|
)
|
|
|
|
const currentComfyUIVersion = computed<string | null>(() => {
|
|
if (!hasMissingCoreNodes.value) return null
|
|
return systemStatsStore.systemStats?.system?.comfyui_version ?? null
|
|
})
|
|
|
|
const sortedMissingCoreNodes = computed(() =>
|
|
Object.entries(missingCoreNodes.value).sort(([a], [b]) => {
|
|
const aValid = valid(a)
|
|
const bValid = valid(b)
|
|
if (!aValid && !bValid) return 0
|
|
if (!aValid) return 1
|
|
if (!bValid) return -1
|
|
return compare(b, a)
|
|
})
|
|
)
|
|
|
|
function getUniqueNodeNames(nodes: LGraphNode[]): string[] {
|
|
const types = new Set(nodes.map((node) => node.type).filter(Boolean))
|
|
return [...types].sort()
|
|
}
|
|
|
|
const comfyManagerStore = useComfyManagerStore()
|
|
const { isRestarting, applyChanges } = useApplyChanges()
|
|
const { shouldShowManagerButtons } = useManagerState()
|
|
|
|
/**
|
|
* Show the --enable-manager hint when:
|
|
* - Not on Cloud (OSS/local only)
|
|
* - Manager is disabled (showInfoButton is false)
|
|
*/
|
|
const showManagerHint = computed(() => !isCloud && !showInfoButton)
|
|
|
|
/**
|
|
* Show Apply Changes when any pack from the error group is already installed
|
|
* on disk but ComfyUI hasn't restarted yet to load it.
|
|
* This is server-state based → persists across browser refreshes.
|
|
*/
|
|
const hasInstalledPacksPendingRestart = computed(() =>
|
|
missingPackGroups.some(
|
|
(g) => g.packId !== null && comfyManagerStore.isPackInstalled(g.packId)
|
|
)
|
|
)
|
|
</script>
|