Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/sidebar/SidebarHelpCenterIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ import { storeToRefs } from 'pinia'
import { computed, onMounted, toRefs } from 'vue'

import HelpCenterMenuContent from '@/components/helpcenter/HelpCenterMenuContent.vue'
import { useNodeConflictDialog } from '@/composables/useNodeConflictDialog'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
import ReleaseNotificationToast from '@/platform/updates/components/ReleaseNotificationToast.vue'
import WhatsNewPopup from '@/platform/updates/components/WhatsNewPopup.vue'
import { useDialogService } from '@/services/dialogService'
import { useHelpCenterStore } from '@/stores/helpCenterStore'
import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment'
import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection'
Expand All @@ -84,7 +84,7 @@ const { shouldShowRedDot: showReleaseRedDot } = storeToRefs(releaseStore)

const conflictDetection = useConflictDetection()

const { showNodeConflictDialog } = useDialogService()
const { show: showNodeConflictDialog } = useNodeConflictDialog()

// Use conflict acknowledgment state from composable - call only once
const { shouldShowRedDot: shouldShowConflictRedDot, markConflictsAsSeen } =
Expand Down
29 changes: 29 additions & 0 deletions src/composables/useMissingNodesDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue'
import MissingNodesFooter from '@/components/dialog/content/MissingNodesFooter.vue'
import MissingNodesHeader from '@/components/dialog/content/MissingNodesHeader.vue'
import { useDialogService } from '@/services/dialogService'
import { useDialogStore } from '@/stores/dialogStore'
import type { ComponentProps } from 'vue-component-type-helpers'

const DIALOG_KEY = 'global-missing-nodes'

export const useMissingNodesDialog = () => {
const dialogService = useDialogService()
const dialogStore = useDialogStore()

function hide() {
dialogStore.closeDialog({ key: DIALOG_KEY })
}

function show(props: ComponentProps<typeof MissingNodesContent>) {
dialogService.showSmallDialog({
key: DIALOG_KEY,
headerComponent: MissingNodesHeader,
footerComponent: MissingNodesFooter,
component: MissingNodesContent,
props
})
}

return { show, hide }
}
48 changes: 48 additions & 0 deletions src/composables/useNodeConflictDialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import NodeConflictDialogContent from '@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue'
import NodeConflictFooter from '@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue'
import NodeConflictHeader from '@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue'
import { useDialogService } from '@/services/dialogService'
import { useDialogStore } from '@/stores/dialogStore'
import type { DialogComponentProps } from '@/stores/dialogStore'
import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes'

const DIALOG_KEY = 'global-node-conflict'

export const useNodeConflictDialog = () => {
const dialogService = useDialogService()
const dialogStore = useDialogStore()

function hide() {
dialogStore.closeDialog({ key: DIALOG_KEY })
}

function show(
options: {
showAfterWhatsNew?: boolean
conflictedPackages?: ConflictDetectionResult[]
dialogComponentProps?: DialogComponentProps
buttonText?: string
onButtonClick?: () => void
} = {}
) {
const { buttonText, onButtonClick, showAfterWhatsNew, conflictedPackages } =
options

return dialogService.showSmallDialog({
key: DIALOG_KEY,
headerComponent: NodeConflictHeader,
footerComponent: NodeConflictFooter,
component: NodeConflictDialogContent,
props: {
showAfterWhatsNew,
conflictedPackages
},
footerProps: {
buttonText,
onButtonClick
}
})
}

return { show, hide }
}
Comment on lines +1 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

LGTM with one concern! Well-structured composable with flexible options.

The composable follows Vue best practices and provides a clean API for displaying node conflict dialogs. However, there's a potential issue:

The dialogComponentProps from the options parameter (line 23) is destructured (line 28) but not passed to showSmallDialog() (lines 31-44).

Apply this diff to pass through dialog component props:

     return dialogService.showSmallDialog({
       key: DIALOG_KEY,
       headerComponent: NodeConflictHeader,
       footerComponent: NodeConflictFooter,
       component: NodeConflictDialogContent,
       props: {
         showAfterWhatsNew,
         conflictedPackages
       },
       footerProps: {
         buttonText,
         onButtonClick
-      }
+      },
+      dialogComponentProps
     })

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/composables/useNodeConflictDialog.ts around lines 1 to 48, the options
parameter accepts dialogComponentProps but that value is destructured and never
forwarded into dialogService.showSmallDialog; update the call to showSmallDialog
to merge or pass dialogComponentProps into the props object (e.g., spread
dialogComponentProps into props or assign it to a specific prop key) so the
dialog receives those component props, ensuring no other props are overwritten
when merging.

3 changes: 2 additions & 1 deletion src/scripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
isComboInputSpecV2
} from '@/schemas/nodeDefSchema'
import { type BaseDOMWidget, DOMWidgetImpl } from '@/scripts/domWidget'
import { useMissingNodesDialog } from '@/composables/useMissingNodesDialog'
import { useDialogService } from '@/services/dialogService'
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
import { useExtensionService } from '@/services/extensionService'
Expand Down Expand Up @@ -1018,7 +1019,7 @@ export class ComfyApp {

private showMissingNodesError(missingNodeTypes: MissingNodeType[]) {
if (useSettingStore().get('Comfy.Workflow.ShowMissingNodesWarning')) {
useDialogService().showLoadWorkflowWarning({ missingNodeTypes })
useMissingNodesDialog().show({ missingNodeTypes })
}
}

Expand Down
124 changes: 40 additions & 84 deletions src/services/dialogService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { merge } from 'es-toolkit/compat'
import type { Component } from 'vue'

import ApiNodesSignInContent from '@/components/dialog/content/ApiNodesSignInContent.vue'
import MissingNodesContent from '@/components/dialog/content/MissingNodesContent.vue'
import MissingNodesFooter from '@/components/dialog/content/MissingNodesFooter.vue'
import MissingNodesHeader from '@/components/dialog/content/MissingNodesHeader.vue'
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
import MissingModelsWarning from '@/components/dialog/content/MissingModelsWarning.vue'
Expand All @@ -30,10 +27,6 @@ import ManagerProgressFooter from '@/workbench/extensions/manager/components/Man
import ManagerProgressHeader from '@/workbench/extensions/manager/components/ManagerProgressHeader.vue'
import ManagerDialogContent from '@/workbench/extensions/manager/components/manager/ManagerDialogContent.vue'
import ManagerHeader from '@/workbench/extensions/manager/components/manager/ManagerHeader.vue'
import NodeConflictDialogContent from '@/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue'
import NodeConflictFooter from '@/workbench/extensions/manager/components/manager/NodeConflictFooter.vue'
import NodeConflictHeader from '@/workbench/extensions/manager/components/manager/NodeConflictHeader.vue'
import type { ConflictDetectionResult } from '@/workbench/extensions/manager/types/conflictDetectionTypes'
import type { ComponentProps } from 'vue-component-type-helpers'

export type ConfirmationDialogType =
Expand All @@ -47,32 +40,6 @@ export type ConfirmationDialogType =
export const useDialogService = () => {
const dialogStore = useDialogStore()

function showLoadWorkflowWarning(
props: ComponentProps<typeof MissingNodesContent>
) {
dialogStore.showDialog({
key: 'global-missing-nodes',
headerComponent: MissingNodesHeader,
footerComponent: MissingNodesFooter,
component: MissingNodesContent,
dialogComponentProps: {
closable: true,
pt: {
root: { class: 'bg-base-background border-border-default' },
header: { class: '!p-0 !m-0' },
content: { class: '!p-0 overflow-y-hidden' },
footer: { class: '!p-0' },
pcCloseButton: {
root: {
class: '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5'
}
}
}
},
props
})
}

function showMissingModelsWarning(
props: InstanceType<typeof MissingModelsWarning>['$props']
) {
Expand Down Expand Up @@ -449,6 +416,44 @@ export const useDialogService = () => {
}
}

function showSmallDialog<T extends Component>(options: {
key: string
component: T
headerComponent?: Component
footerComponent?: Component
props?: ComponentProps<T>
footerProps?: Record<string, unknown>
dialogComponentProps?: DialogComponentProps
}) {
const smallDialogDefaultProps: DialogComponentProps = {
closable: true,
pt: {
root: { class: 'bg-base-background border-border-default' },
header: { class: '!p-0 !m-0' },
content: { class: '!p-0 overflow-y-hidden' },
footer: { class: '!p-0' },
pcCloseButton: {
root: {
class: '!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5'
}
}
}
}

return dialogStore.showDialog({
key: options.key,
component: options.component,
headerComponent: options.headerComponent,
footerComponent: options.footerComponent,
props: options.props,
footerProps: options.footerProps,
dialogComponentProps: merge(
smallDialogDefaultProps,
options.dialogComponentProps || {}
)
})
}

function showLayoutDialog(options: {
key: string
component: Component
Expand Down Expand Up @@ -481,54 +486,6 @@ export const useDialogService = () => {
})
}

function showNodeConflictDialog(
options: {
showAfterWhatsNew?: boolean
conflictedPackages?: ConflictDetectionResult[]
dialogComponentProps?: DialogComponentProps
buttonText?: string
onButtonClick?: () => void
} = {}
) {
const {
dialogComponentProps,
buttonText,
onButtonClick,
showAfterWhatsNew,
conflictedPackages
} = options

return dialogStore.showDialog({
key: 'global-node-conflict',
headerComponent: NodeConflictHeader,
footerComponent: NodeConflictFooter,
component: NodeConflictDialogContent,
dialogComponentProps: {
closable: true,
pt: {
header: { class: '!p-0 !m-0' },
content: { class: '!p-0 overflow-y-hidden' },
footer: { class: '!p-0' },
pcCloseButton: {
root: {
class:
'!w-7 !h-7 !border-none !outline-none !p-2 !m-1.5 bg-dialog-surface text-white'
}
}
},
...dialogComponentProps
},
props: {
showAfterWhatsNew,
conflictedPackages
},
footerProps: {
buttonText,
onButtonClick
}
})
}

async function showSubscriptionRequiredDialog() {
if (!isCloud || !window.__CONFIG__?.subscription_required) {
return
Expand All @@ -542,7 +499,6 @@ export const useDialogService = () => {
}

return {
showLoadWorkflowWarning,
showMissingModelsWarning,
showSettingsDialog,
showAboutDialog,
Expand All @@ -560,7 +516,7 @@ export const useDialogService = () => {
confirm,
toggleManagerDialog,
toggleManagerProgressDialog,
showLayoutDialog,
showNodeConflictDialog
showSmallDialog,
showLayoutDialog
}
}
17 changes: 12 additions & 5 deletions src/stores/dialogStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,19 @@ export const useDialogStore = defineStore('dialog', () => {

function showDialog(options: ShowDialogOptions) {
const dialogKey = options.key || genDialogKey()

let dialog = dialogStack.value.find((d) => d.key === dialogKey)

const existingIndex = dialogStack.value.findIndex(
(d) => d.key === dialogKey
)
let dialog =
existingIndex !== -1 ? dialogStack.value[existingIndex] : undefined
if (dialog) {
dialog.visible = true
riseDialog(dialog)
if (!dialog.visible) {
dialogStack.value.splice(existingIndex, 1)
dialog = createDialog({ ...options, key: dialogKey })
} else {
dialog.visible = true
riseDialog(dialog)
}
} else {
dialog = createDialog({ ...options, key: dialogKey })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<!-- Import Failed List Wrapper -->
<div
v-if="importFailedConflicts.length > 0"
class="flex min-h-8 w-full flex-col rounded-lg bg-base-background"
class="flex min-h-8 w-full flex-col rounded-lg bg-secondary-background"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the NodeConflictDialogContent.vue file to understand its structure
cat -n src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue

Repository: Comfy-Org/ComfyUI_frontend

Length of output: 9603


Extension List Wrapper should conditionally render when data is empty, for consistency.

The component has three collapsible list sections with identical structure. The "Import Failed List Wrapper" and "Conflict List Wrapper" both use v-if to hide when their respective data arrays are empty (lines 16, 64), but the "Extension List Wrapper" (lines 111-112) renders unconditionally regardless of conflictData.length. This creates visual inconsistency—when conflictData.length === 0, the header displays "0 Extensions At Risk" with an empty expandable list, unlike the other sections which disappear entirely.

For UI consistency, add v-if="conflictData.length > 0" to line 111-112.

🤖 Prompt for AI Agents
In
src/workbench/extensions/manager/components/manager/NodeConflictDialogContent.vue
around lines 111 to 112, the Extension List Wrapper renders even when
conflictData is empty causing an inconsistent UI; update the wrapper element to
include a conditional rendering check (v-if="conflictData.length > 0") so the
entire Extension List section is omitted when there are no conflicts, matching
the other list wrappers that already use v-if.

>
<div
data-testid="conflict-dialog-panel-toggle"
Expand Down Expand Up @@ -50,7 +50,7 @@
<div
v-for="(packageName, i) in importFailedConflicts"
:key="i"
class="conflict-list-item flex h-6 flex-shrink-0 items-center justify-between px-4"
class="hover:bg-alpha-azure-600-30 flex h-6 flex-shrink-0 items-center justify-between px-4"
>
<span class="text-xs text-muted">
{{ packageName }}
Expand All @@ -60,7 +60,10 @@
</div>
</div>
<!-- Conflict List Wrapper -->
<div class="flex min-h-8 w-full flex-col rounded-lg bg-base-background">
<div
v-if="allConflictDetails.length > 0"
class="flex min-h-8 w-full flex-col rounded-lg bg-secondary-background"
>
<div
data-testid="conflict-dialog-panel-toggle"
class="flex h-8 w-full items-center justify-between gap-2 pl-4"
Expand Down Expand Up @@ -95,7 +98,7 @@
<div
v-for="(conflict, i) in allConflictDetails"
:key="i"
class="conflict-list-item flex h-6 flex-shrink-0 items-center justify-between px-4"
class="hover:bg-alpha-azure-600-30 flex h-6 flex-shrink-0 items-center justify-between px-4"
>
<span class="text-xs text-muted">{{
getConflictMessage(conflict, t)
Expand All @@ -105,7 +108,9 @@
</div>
</div>
<!-- Extension List Wrapper -->
<div class="flex min-h-8 w-full flex-col rounded-lg bg-base-background">
<div
class="flex min-h-8 w-full flex-col rounded-lg bg-secondary-background"
>
<div
data-testid="conflict-dialog-panel-toggle"
class="flex h-8 w-full items-center justify-between gap-2 pl-4"
Expand Down Expand Up @@ -140,7 +145,7 @@
<div
v-for="conflictResult in conflictData"
:key="conflictResult.package_id"
class="conflict-list-item flex h-6 flex-shrink-0 items-center justify-between px-4"
class="hover:bg-alpha-azure-600-30 flex h-6 flex-shrink-0 items-center justify-between px-4"
>
<span class="text-xs text-muted">
{{ conflictResult.package_name }}
Expand Down Expand Up @@ -230,8 +235,3 @@ const toggleExtensionsPanel = () => {
importFailedExpanded.value = false
}
</script>
<style scoped>
.conflict-list-item:hover {
background-color: rgb(0 122 255 / 0.2);
}
</style>
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<template>
<div class="flex h-12 w-full items-center justify-between pl-6">
<div class="flex w-full items-center justify-between p-4">
<div class="flex items-center gap-2">
<!-- Warning Icon -->
<i class="pi pi-exclamation-triangle text-lg"></i>
<!-- Title -->
<p class="text-base font-bold">
<i class="icon-[lucide--triangle-alert] text-gold-600"></i>
<p class="m-0 text-sm">
{{ $t('manager.conflicts.title') }}
</p>
</div>
Expand Down
Loading
Loading