Skip to content

Commit e2d0bf3

Browse files
viva-jinyiclaude
andcommitted
[refactor] Unify Cloud/OSS Missing Nodes modal
- Merged separate Cloud and OSS workflow warning modals into single unified modal - Removed legacy LoadWorkflowWarning.vue - Renamed CloudMissingNodes* components to MissingNodes* for clarity - Environment branching now handled internally via isCloud flag - Restructured i18n: removed loadWorkflowWarning, added missingNodes.cloud/oss sections - Improved OSS button styling to match Cloud consistency using TextButton Key changes: - OSS: 'Open Manager' + 'Install All' buttons - Cloud: 'Learn More' + 'Got It' buttons (unchanged) - Single unified modal displays different UI/text based on environment 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ecd87ae commit e2d0bf3

File tree

8 files changed

+94
-174
lines changed

8 files changed

+94
-174
lines changed

src/components/dialog/content/CloudMissingNodesFooter.vue

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/components/dialog/content/CloudMissingNodesContent.vue renamed to src/components/dialog/content/MissingNodesContent.vue

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,17 @@
66
<!-- Description -->
77
<div>
88
<p class="m-0 text-sm leading-4 text-muted-foreground">
9-
{{ $t('cloud.missingNodes.description') }}
10-
<br /><br />
11-
{{ $t('cloud.missingNodes.priorityMessage') }}
9+
{{
10+
isCloud
11+
? $t('missingNodes.cloud.description')
12+
: $t('missingNodes.oss.description')
13+
}}
1214
</p>
1315
</div>
16+
<MissingCoreNodesMessage
17+
v-if="!isCloud"
18+
:missing-core-nodes="missingCoreNodes"
19+
/>
1420

1521
<!-- Missing Nodes List Wrapper -->
1622
<div
@@ -28,9 +34,9 @@
2834
</div>
2935

3036
<!-- Bottom instruction -->
31-
<div>
37+
<div v-if="isCloud">
3238
<p class="m-0 text-sm leading-4 text-muted-foreground">
33-
{{ $t('cloud.missingNodes.replacementInstruction') }}
39+
{{ $t('missingNodes.cloud.replacementInstruction') }}
3440
</p>
3541
</div>
3642
</div>
@@ -40,12 +46,18 @@
4046
<script setup lang="ts">
4147
import { computed } from 'vue'
4248
49+
import MissingCoreNodesMessage from '@/components/dialog/content/MissingCoreNodesMessage.vue'
50+
import { isCloud } from '@/platform/distribution/types'
4351
import type { MissingNodeType } from '@/types/comfy'
52+
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
4453
4554
const props = defineProps<{
4655
missingNodeTypes: MissingNodeType[]
4756
}>()
4857
58+
// Get missing core nodes for OSS mode
59+
const { missingCoreNodes } = useMissingNodes()
60+
4961
const uniqueNodes = computed(() => {
5062
const seenTypes = new Set()
5163
return props.missingNodeTypes
Lines changed: 51 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
11
<template>
2-
<NoResultsPlaceholder
3-
class="pb-0"
4-
icon="pi pi-exclamation-circle"
5-
:title="$t('loadWorkflowWarning.missingNodesTitle')"
6-
:message="$t('loadWorkflowWarning.missingNodesDescription')"
7-
/>
8-
<MissingCoreNodesMessage :missing-core-nodes="missingCoreNodes" />
9-
<ListBox
10-
:options="uniqueNodes"
11-
option-label="label"
12-
scroll-height="100%"
13-
class="comfy-missing-nodes"
14-
:pt="{
15-
list: { class: 'border-none' }
16-
}"
2+
<!-- Cloud mode: Learn More + Got It buttons -->
3+
<div
4+
v-if="isCloud"
5+
class="flex w-full items-center justify-between gap-2 py-2 px-4"
176
>
18-
<template #option="slotProps">
19-
<div class="align-items-center flex">
20-
<span class="node-type">{{ slotProps.option.label }}</span>
21-
<span v-if="slotProps.option.hint" class="node-hint">{{
22-
slotProps.option.hint
23-
}}</span>
24-
<Button
25-
v-if="slotProps.option.action"
26-
:label="slotProps.option.action.text"
27-
size="small"
28-
outlined
29-
@click="slotProps.option.action.callback"
30-
/>
31-
</div>
32-
</template>
33-
</ListBox>
34-
<div v-if="showManagerButtons" class="flex justify-end py-3">
7+
<IconTextButton
8+
:label="$t('missingNodes.cloud.learnMore')"
9+
type="transparent"
10+
size="sm"
11+
icon-position="left"
12+
@click="handleLearnMoreClick"
13+
>
14+
<template #icon>
15+
<i class="icon-[lucide--info]"></i>
16+
</template>
17+
</IconTextButton>
18+
<TextButton
19+
:label="$t('missingNodes.cloud.gotIt')"
20+
type="secondary"
21+
size="md"
22+
@click="handleGotItClick"
23+
/>
24+
</div>
25+
26+
<!-- OSS mode: Open Manager + Install All buttons -->
27+
<div v-else-if="showManagerButtons" class="flex justify-end gap-1 py-2 px-4">
28+
<TextButton
29+
:label="$t('g.openManager')"
30+
type="transparent"
31+
size="sm"
32+
@click="openManager"
33+
/>
3534
<PackInstallButton
3635
v-if="showInstallAllButton"
36+
type="secondary"
3737
size="md"
3838
:disabled="
3939
isLoading || !!error || missingNodePacks.length === 0 || isInstalling
@@ -46,40 +46,38 @@
4646
: $t('manager.installAllMissingNodes')
4747
"
4848
/>
49-
<Button
50-
:label="$t('g.openManager')"
51-
size="small"
52-
outlined
53-
@click="openManager"
54-
/>
5549
</div>
5650
</template>
5751

5852
<script setup lang="ts">
59-
import Button from 'primevue/button'
60-
import ListBox from 'primevue/listbox'
6153
import { computed, nextTick, watch } from 'vue'
6254
import { useI18n } from 'vue-i18n'
6355
64-
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
65-
import MissingCoreNodesMessage from '@/components/dialog/content/MissingCoreNodesMessage.vue'
56+
import IconTextButton from '@/components/button/IconTextButton.vue'
57+
import TextButton from '@/components/button/TextButton.vue'
58+
import { isCloud } from '@/platform/distribution/types'
6659
import { useToastStore } from '@/platform/updates/common/toastStore'
6760
import { useDialogStore } from '@/stores/dialogStore'
68-
import type { MissingNodeType } from '@/types/comfy'
6961
import PackInstallButton from '@/workbench/extensions/manager/components/manager/button/PackInstallButton.vue'
7062
import { useMissingNodes } from '@/workbench/extensions/manager/composables/nodePack/useMissingNodes'
7163
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
7264
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
7365
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
7466
75-
const props = defineProps<{
76-
missingNodeTypes: MissingNodeType[]
77-
}>()
67+
const dialogStore = useDialogStore()
68+
const { t } = useI18n()
7869
79-
// Get missing node packs from workflow with loading and error states
80-
const { missingNodePacks, isLoading, error, missingCoreNodes } =
81-
useMissingNodes()
70+
// Cloud mode handlers
71+
const handleLearnMoreClick = () => {
72+
window.open('https://www.comfy.org/cloud', '_blank')
73+
}
74+
75+
const handleGotItClick = () => {
76+
dialogStore.closeDialog({ key: 'global-cloud-missing-nodes' })
77+
}
8278
79+
// OSS mode logic
80+
const { missingNodePacks, isLoading, error } = useMissingNodes()
8381
const comfyManagerStore = useComfyManagerStore()
8482
const managerState = useManagerState()
8583
@@ -91,27 +89,6 @@ const isInstalling = computed(() => {
9189
)
9290
})
9391
94-
const uniqueNodes = computed(() => {
95-
const seenTypes = new Set()
96-
return props.missingNodeTypes
97-
.filter((node) => {
98-
const type = typeof node === 'object' ? node.type : node
99-
if (seenTypes.has(type)) return false
100-
seenTypes.add(type)
101-
return true
102-
})
103-
.map((node) => {
104-
if (typeof node === 'object') {
105-
return {
106-
label: node.type,
107-
hint: node.hint,
108-
action: node.action
109-
}
110-
}
111-
return { label: node }
112-
})
113-
})
114-
11592
// Show manager buttons unless manager is disabled
11693
const showManagerButtons = computed(() => {
11794
return managerState.shouldShowManagerButtons.value
@@ -129,9 +106,6 @@ const openManager = async () => {
129106
})
130107
}
131108
132-
const { t } = useI18n()
133-
const dialogStore = useDialogStore()
134-
135109
// Computed to check if all missing nodes have been installed
136110
const allMissingNodesInstalled = computed(() => {
137111
return (
@@ -140,13 +114,14 @@ const allMissingNodesInstalled = computed(() => {
140114
missingNodePacks.value?.length === 0
141115
)
142116
})
143-
// Watch for completion and close dialog
117+
118+
// Watch for completion and close dialog (OSS mode only)
144119
watch(allMissingNodesInstalled, async (allInstalled) => {
145-
if (allInstalled && showInstallAllButton.value) {
120+
if (!isCloud && allInstalled && showInstallAllButton.value) {
146121
// Use nextTick to ensure state updates are complete
147122
await nextTick()
148123
149-
dialogStore.closeDialog({ key: 'global-load-workflow-warning' })
124+
dialogStore.closeDialog({ key: 'global-cloud-missing-nodes' })
150125
151126
// Show success toast
152127
useToastStore().add({
@@ -158,20 +133,3 @@ watch(allMissingNodesInstalled, async (allInstalled) => {
158133
}
159134
})
160135
</script>
161-
162-
<style scoped>
163-
.comfy-missing-nodes {
164-
max-height: 300px;
165-
overflow-y: auto;
166-
}
167-
168-
.node-hint {
169-
margin-left: 0.5rem;
170-
font-style: italic;
171-
color: var(--text-color-secondary);
172-
}
173-
174-
:deep(.p-button) {
175-
margin-left: auto;
176-
}
177-
</style>

src/components/dialog/content/CloudMissingNodesHeader.vue renamed to src/components/dialog/content/MissingNodesHeader.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@
33
<div class="flex items-center gap-2">
44
<i class="icon-[lucide--triangle-alert] text-gold-600"></i>
55
<p class="m-0 text-sm">
6-
{{ $t('cloud.missingNodes.title') }}
6+
{{
7+
isCloud
8+
? $t('missingNodes.cloud.title')
9+
: $t('missingNodes.oss.title')
10+
}}
711
</p>
812
</div>
913
</div>
1014
</template>
15+
16+
<script setup lang="ts">
17+
import { isCloud } from '@/platform/distribution/types'
18+
</script>

src/locales/en/main.json

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@
289289
"lastUpdated": "Last Updated",
290290
"noDescription": "No description available",
291291
"installSelected": "Install Selected",
292-
"installAllMissingNodes": "Install All Missing Nodes",
292+
"installAllMissingNodes": "Install All",
293293
"allMissingNodesInstalled": "All missing nodes have been successfully installed",
294294
"packsSelected": "packs selected",
295295
"mixedSelectionMessage": "Cannot perform bulk action on mixed selection",
@@ -1400,13 +1400,6 @@
14001400
"missingModels": "Missing Models",
14011401
"missingModelsMessage": "When loading the graph, the following models were not found"
14021402
},
1403-
"loadWorkflowWarning": {
1404-
"missingNodesTitle": "Some Nodes Are Missing",
1405-
"missingNodesDescription": "When loading the graph, the following node types were not found.\nThis may also happen if your installed version is lower and that node type can’t be found.",
1406-
"outdatedVersion": "Some nodes require a newer version of ComfyUI (current: {version}). Please update to use all nodes.",
1407-
"outdatedVersionGeneric": "Some nodes require a newer version of ComfyUI. Please update to use all nodes.",
1408-
"coreNodesFromVersion": "Requires ComfyUI {version}:"
1409-
},
14101403
"versionMismatchWarning": {
14111404
"title": "Version Compatibility Warning",
14121405
"frontendOutdated": "Frontend version {frontendVersion} is outdated. Backend requires version {requiredVersion} or higher.",
@@ -2054,16 +2047,18 @@
20542047
"vueNodesMigrationMainMenu": {
20552048
"message": "Switch back to Nodes 2.0 anytime from the main menu."
20562049
},
2057-
"cloud": {
2058-
"missingNodes": {
2050+
"missingNodes": {
2051+
"cloud": {
20592052
"title": "These nodes aren't available on Comfy Cloud yet",
20602053
"description": "This workflow uses custom nodes that aren't supported in the Cloud version yet.",
20612054
"priorityMessage": "We've automatically flagged these nodes so we can prioritize adding them.",
2062-
"missingNodes": "Missing Nodes",
20632055
"replacementInstruction": "In the meantime, replace these nodes (highlighted red on the canvas) with supported ones if possible, or try a different workflow.",
20642056
"learnMore": "Learn more",
2065-
"gotIt": "Ok, got it",
2066-
"cannotRun": "Workflow contains unsupported nodes (highlighted red). Remove these to run the workflow. "
2057+
"gotIt": "Ok, got it"
2058+
},
2059+
"oss": {
2060+
"title": "Some Nodes Are Missing",
2061+
"description": "This workflow uses custom nodes you haven't installed yet."
20672062
}
20682063
}
20692064
}

src/scripts/app.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,11 +1018,7 @@ export class ComfyApp {
10181018

10191019
private showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
10201020
if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) {
1021-
if (isCloud) {
1022-
useDialogService().showCloudLoadWorkflowWarning({ missingNodeTypes })
1023-
} else {
1024-
useDialogService().showLoadWorkflowWarning({ missingNodeTypes })
1025-
}
1021+
useDialogService().showCloudLoadWorkflowWarning({ missingNodeTypes })
10261022
}
10271023
}
10281024

0 commit comments

Comments
 (0)