mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-05 13:41:59 +00:00
[feat] Surface missing models in Errors tab (Cloud) (#9743)
## Summary When a workflow is loaded with missing models, users currently have no way to identify or resolve them from within the UI. This PR adds a full missing-model detection and resolution pipeline that surfaces missing models in the Errors tab, allowing users to install or import them without leaving the editor. ## Changes ### Missing Model Detection - Scan all COMBO widgets across root graph and subgraphs for model-like filenames during workflow load - Enrich candidates with embedded workflow metadata (url, hash, directory) when available - Verify asset-supported candidates against the asset store asynchronously to confirm installation status - Propagate missing model state to `executionErrorStore` alongside existing node/prompt errors ### Errors Tab UI — Model Resolution - Group missing models by directory (e.g. `checkpoints`, `loras`, `vae`) with collapsible category cards - Each model row displays: - Model name with copy-to-clipboard button - Expandable list of referencing nodes with locate-on-canvas button - **Library selector**: Pick an alternative from the user's existing models to substitute the missing model with one click - **URL import**: Paste a Civitai or HuggingFace URL to import a model directly; debounced metadata fetch shows filename and file size before confirming; type-mismatch warnings (e.g. importing a LoRA into checkpoints directory) are surfaced with an "Import Anyway" option - **Upgrade prompt**: In cloud environment, free-tier subscribers are shown an upgrade modal when attempting URL import - Separate "Import Not Supported" section for custom-node models that cannot be auto-resolved - Status card with live download progress, completion, failure, and category-mismatch states ### Canvas Integration - Highlight nodes and widgets that reference missing models with error indicators - Propagate missing-model badges through subgraph containers so issues are visible at every graph level ### Code Cleanup - Simplify `surfacePendingWarnings` in workflowService, remove stale widget-detected model merging logic - Add `flattenWorkflowNodes` utility to workflowSchema for traversing nested subgraph structures - Extract `MissingModelUrlInput`, `MissingModelLibrarySelect`, `MissingModelStatusCard` as focused single-responsibility components ## Testing - Unit tests for scan pipeline (`missingModelScan.test.ts`): enrichment, skip-installed, subgraph flattening - Unit tests for store (`missingModelStore.test.ts`): state management, removal helpers - Unit tests for interactions (`useMissingModelInteractions.test.ts`): combo select, URL input, import flow, library confirm - Component tests for `MissingModelCard` and error grouping (`useErrorGroups.test.ts`) - Updated `workflowService.test.ts` and `workflowSchema.test.ts` for new logic ## Review Focus - Missing model scan + enrichment pipeline in `missingModelScan.ts` - Interaction composable `useMissingModelInteractions.ts` — URL metadata fetch, library install, upload fallback - Store integration and canvas-level error propagation ## Screenshots https://github.com/user-attachments/assets/339a6d5b-93a3-43cd-98dd-0fb00681b66f ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9743-feat-Surface-missing-models-in-Errors-tab-Cloud-3206d73d365081678326d3a16c2165d8) by [Unito](https://www.unito.io)
This commit is contained in:
78
src/platform/missingModel/components/MissingModelCard.vue
Normal file
78
src/platform/missingModel/components/MissingModelCard.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="px-4 pb-2">
|
||||
<!-- Category groups (by directory) -->
|
||||
<div
|
||||
v-for="group in missingModelGroups"
|
||||
:key="`${group.isAssetSupported ? 'supported' : 'unsupported'}::${group.directory ?? '__unknown__'}`"
|
||||
class="flex w-full flex-col border-t border-interface-stroke py-2 first:border-t-0 first:pt-0"
|
||||
>
|
||||
<!-- Category header -->
|
||||
<div class="flex h-8 w-full items-center">
|
||||
<p
|
||||
class="min-w-0 flex-1 truncate text-sm font-medium"
|
||||
:class="
|
||||
!group.isAssetSupported || group.directory === null
|
||||
? 'text-warning-background'
|
||||
: 'text-destructive-background-hover'
|
||||
"
|
||||
>
|
||||
<span v-if="!group.isAssetSupported" class="text-warning-background">
|
||||
{{ t('rightSidePanel.missingModels.importNotSupported') }}
|
||||
({{ group.models.length }})
|
||||
</span>
|
||||
<span v-else>
|
||||
{{
|
||||
group.directory ??
|
||||
t('rightSidePanel.missingModels.unknownCategory')
|
||||
}}
|
||||
({{ group.models.length }})
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Asset unsupported group notice -->
|
||||
<div
|
||||
v-if="!group.isAssetSupported"
|
||||
class="flex items-start gap-1.5 px-0.5 py-1 pl-2"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="mt-0.5 icon-[lucide--info] size-3.5 shrink-0 text-muted-foreground"
|
||||
/>
|
||||
<span class="text-xs/tight text-muted-foreground">
|
||||
{{ t('rightSidePanel.missingModels.customNodeDownloadDisabled') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Model rows -->
|
||||
<div class="flex flex-col gap-1 overflow-hidden pl-2">
|
||||
<MissingModelRow
|
||||
v-for="model in group.models"
|
||||
:key="model.name"
|
||||
:model="model"
|
||||
:directory="group.directory"
|
||||
:show-node-id-badge="showNodeIdBadge"
|
||||
:is-asset-supported="group.isAssetSupported"
|
||||
@locate-model="emit('locateModel', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { MissingModelGroup } from '@/platform/missingModel/types'
|
||||
import MissingModelRow from '@/platform/missingModel/components/MissingModelRow.vue'
|
||||
|
||||
const { missingModelGroups, showNodeIdBadge } = defineProps<{
|
||||
missingModelGroups: MissingModelGroup[]
|
||||
showNodeIdBadge: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
locateModel: [nodeId: string]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
Reference in New Issue
Block a user