mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-06 16:10:09 +00:00
Add cloud-specific missing nodes warning dialog (#6659)
## Summary Implements a cloud-specific dialog to warn users when loading workflows with unsupported custom nodes in Comfy Cloud. The new dialog follows the visual style of the node conflict dialog and provides appropriate messaging and actions. ## Changes - Add `CloudMissingNodesHeader`, `CloudMissingNodesContent`, and `CloudMissingNodesFooter` components - Add `showCloudLoadWorkflowWarning` to dialogService - Update app.ts to show cloud dialog when in cloud environment - Add `cloud.missingNodes` translations ## Screenshots The dialog displays: - Warning icon and title - Description of the issue - List of missing nodes - "Learn more" link and "Ok, got it" button ## Test plan 1. Load a workflow with custom nodes in cloud environment 2. Verify cloud-specific dialog appears with appropriate styling 3. Verify "Learn more" button opens cloud documentation 4. Verify "Ok, got it" button closes dialog ## Notes - Two unused i18n keys (`cloud.missingNodes.cannotRun` and `cloud.missingNodes.missingNodes`) are included for future PR that will add breadcrumb warning icons and run button disable functionality <img width="1367" height="988" alt="스크린샷 2025-11-12 오후 4 33 38" src="https://github.com/user-attachments/assets/75a6fced-959f-4e93-9b82-4e61b53a9ee4" /> 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6659-Add-cloud-specific-missing-nodes-warning-dialog-2a96d73d36508161ae55fe157f55cd17) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
70
src/components/dialog/content/CloudMissingNodesContent.vue
Normal file
70
src/components/dialog/content/CloudMissingNodesContent.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="flex w-[490px] flex-col">
|
||||
<ContentDivider :width="1" />
|
||||
<div class="flex h-full w-full flex-col gap-4 p-4">
|
||||
<!-- Description -->
|
||||
<div>
|
||||
<p class="m-0 text-sm leading-4 text-muted-foreground">
|
||||
{{ $t('cloud.missingNodes.description') }}
|
||||
<br /><br />
|
||||
{{ $t('cloud.missingNodes.priorityMessage') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Missing Nodes List Wrapper -->
|
||||
<div
|
||||
class="flex flex-col max-h-[256px] rounded-lg py-2 scrollbar-custom bg-component-node-widget-background"
|
||||
>
|
||||
<div
|
||||
v-for="(node, i) in uniqueNodes"
|
||||
:key="i"
|
||||
class="flex min-h-8 items-center justify-between px-4 py-2 bg-component-node-widget-background text-text-secondary"
|
||||
>
|
||||
<span class="text-xs">
|
||||
{{ node.label }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom instruction -->
|
||||
<div>
|
||||
<p class="m-0 text-sm leading-4 text-muted-foreground">
|
||||
{{ $t('cloud.missingNodes.replacementInstruction') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ContentDivider :width="1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import ContentDivider from '@/components/common/ContentDivider.vue'
|
||||
import type { MissingNodeType } from '@/types/comfy'
|
||||
|
||||
const props = defineProps<{
|
||||
missingNodeTypes: MissingNodeType[]
|
||||
}>()
|
||||
|
||||
const uniqueNodes = computed(() => {
|
||||
const seenTypes = new Set()
|
||||
return props.missingNodeTypes
|
||||
.filter((node) => {
|
||||
const type = typeof node === 'object' ? node.type : node
|
||||
if (seenTypes.has(type)) return false
|
||||
seenTypes.add(type)
|
||||
return true
|
||||
})
|
||||
.map((node) => {
|
||||
if (typeof node === 'object') {
|
||||
return {
|
||||
label: node.type,
|
||||
hint: node.hint,
|
||||
action: node.action
|
||||
}
|
||||
}
|
||||
return { label: node }
|
||||
})
|
||||
})
|
||||
</script>
|
||||
37
src/components/dialog/content/CloudMissingNodesFooter.vue
Normal file
37
src/components/dialog/content/CloudMissingNodesFooter.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="flex w-full items-center justify-between gap-2 py-2 px-4">
|
||||
<IconTextButton
|
||||
:label="$t('cloud.missingNodes.learnMore')"
|
||||
type="transparent"
|
||||
size="sm"
|
||||
icon-position="left"
|
||||
@click="handleLearnMoreClick"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--info]"></i>
|
||||
</template>
|
||||
</IconTextButton>
|
||||
<TextButton
|
||||
:label="$t('cloud.missingNodes.gotIt')"
|
||||
type="secondary"
|
||||
size="md"
|
||||
@click="handleGotItClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||
import TextButton from '@/components/button/TextButton.vue'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
|
||||
const dialogStore = useDialogStore()
|
||||
|
||||
const handleLearnMoreClick = () => {
|
||||
window.open('https://www.comfy.org/cloud', '_blank')
|
||||
}
|
||||
|
||||
const handleGotItClick = () => {
|
||||
dialogStore.closeDialog({ key: 'global-cloud-missing-nodes' })
|
||||
}
|
||||
</script>
|
||||
10
src/components/dialog/content/CloudMissingNodesHeader.vue
Normal file
10
src/components/dialog/content/CloudMissingNodesHeader.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div class="flex w-full items-center justify-between p-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="icon-[lucide--triangle-alert] text-gold-600"></i>
|
||||
<p class="m-0 text-sm">
|
||||
{{ $t('cloud.missingNodes.title') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user