Skip to content

Commit fdf4ffd

Browse files
feat: Complete manager migration with conflict detection integration
This completes the integration of ComfyUI Manager migration features with enhanced conflict detection system. Key changes include: ## Manager Migration & Conflict Detection - Integrated PR #4637 (4-state manager restart workflow) with PR #4654 (comprehensive conflict detection) - Fixed conflict detection to properly check `latest_version` fields for registry API compatibility - Added conflict detection to PackCardFooter and InfoPanelHeader for comprehensive warning coverage - Merged missing English locale translations from main branch with proper conflict resolution ## Bug Fixes - Fixed double API path issue (`/api/v2/v2/`) in manager service routes - Corrected PackUpdateButton payload structure and service method calls - Enhanced conflict detection system to handle both installed and registry package structures ## Technical Improvements - Updated conflict detection composable to handle both installed and registry package structures - Enhanced manager service with proper error handling and route corrections - Improved type safety across manager components with proper TypeScript definitions
1 parent 183bba0 commit fdf4ffd

File tree

14 files changed

+388
-408
lines changed

14 files changed

+388
-408
lines changed

src/components/dialog/content/manager/PackVersionSelectorPopover.vue

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,33 @@ import VerifiedIcon from '@/components/icons/VerifiedIcon.vue'
9393
import { useConflictDetection } from '@/composables/useConflictDetection'
9494
import { useComfyRegistryService } from '@/services/comfyRegistryService'
9595
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
96-
import {
97-
ManagerChannel,
98-
ManagerDatabaseSource,
99-
SelectedVersion
100-
} from '@/types/comfyManagerTypes'
10196
import { components } from '@/types/comfyRegistryTypes'
97+
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
10298
import { getJoinedConflictMessages } from '@/utils/conflictMessageUtil'
10399
import { isSemVer } from '@/utils/formatUtil'
104100
101+
type ManagerChannel = ManagerComponents['schemas']['ManagerChannel']
102+
type ManagerDatabaseSource =
103+
ManagerComponents['schemas']['ManagerDatabaseSource']
104+
type SelectedVersion = ManagerComponents['schemas']['SelectedVersion']
105+
106+
// Enum values for runtime use
107+
const SelectedVersionValues = {
108+
LATEST: 'latest' as SelectedVersion,
109+
NIGHTLY: 'nightly' as SelectedVersion
110+
}
111+
112+
const ManagerChannelValues = {
113+
STABLE: 'stable' as ManagerChannel,
114+
DEV: 'dev' as ManagerChannel
115+
}
116+
117+
const ManagerDatabaseSourceValues = {
118+
CACHE: 'cache' as ManagerDatabaseSource,
119+
REMOTE: 'remote' as ManagerDatabaseSource,
120+
LOCAL: 'local' as ManagerDatabaseSource
121+
}
122+
105123
const { nodePack } = defineProps<{
106124
nodePack: components['schemas']['Node']
107125
}>()
@@ -118,19 +136,21 @@ const { checkNodeCompatibility } = useConflictDetection()
118136
119137
const isQueueing = ref(false)
120138
121-
const selectedVersion = ref<string>(SelectedVersion.LATEST)
139+
const selectedVersion = ref<string>(SelectedVersionValues.LATEST)
122140
onMounted(() => {
123-
const initialVersion = getInitialSelectedVersion() ?? SelectedVersion.LATEST
141+
const initialVersion =
142+
getInitialSelectedVersion() ?? SelectedVersionValues.LATEST
124143
selectedVersion.value =
125144
// Use NIGHTLY when version is a Git hash
126-
isSemVer(initialVersion) ? initialVersion : SelectedVersion.NIGHTLY
145+
isSemVer(initialVersion) ? initialVersion : SelectedVersionValues.NIGHTLY
127146
})
128147
129148
const getInitialSelectedVersion = () => {
130149
if (!nodePack.id) return
131150
132151
// If unclaimed, set selected version to nightly
133-
if (nodePack.publisher?.name === 'Unclaimed') return SelectedVersion.NIGHTLY
152+
if (nodePack.publisher?.name === 'Unclaimed')
153+
return SelectedVersionValues.NIGHTLY
134154
135155
// If node pack is installed, set selected version to the installed version
136156
if (managerStore.isPackInstalled(nodePack.id))
@@ -180,15 +200,15 @@ const onNodePackChange = async () => {
180200
// Add Latest option
181201
const defaultVersions = [
182202
{
183-
value: SelectedVersion.LATEST,
203+
value: SelectedVersionValues.LATEST,
184204
label: latestLabel
185205
}
186206
]
187207
188208
// Add Nightly option if there is a non-empty `repository` field
189209
if (nodePack.repository?.length) {
190210
defaultVersions.push({
191-
value: SelectedVersion.NIGHTLY,
211+
value: SelectedVersionValues.NIGHTLY,
192212
label: t('manager.nightlyVersion')
193213
})
194214
}
@@ -222,8 +242,8 @@ const handleSubmit = async () => {
222242
await managerStore.installPack.call({
223243
id: nodePack.id,
224244
repository: nodePack.repository ?? '',
225-
channel: ManagerChannel.DEFAULT,
226-
mode: ManagerDatabaseSource.CACHE,
245+
channel: ManagerChannelValues.STABLE,
246+
mode: ManagerDatabaseSourceValues.CACHE,
227247
version: actualVersion,
228248
selected_version: selectedVersion.value
229249
})

src/components/dialog/content/manager/button/PackEnableToggle.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgme
3838
import { useDialogService } from '@/services/dialogService'
3939
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
4040
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
41-
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
4241
import type { components } from '@/types/comfyRegistryTypes'
42+
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
4343
4444
const TOGGLE_DEBOUNCE_MS = 256
4545
@@ -108,8 +108,12 @@ const handleEnable = () => {
108108
}
109109
return enablePack.call({
110110
id: nodePack.id,
111-
version: version.value ?? ('latest' as ManagerComponents['schemas']['SelectedVersion']),
112-
selected_version: version.value ?? ('latest' as ManagerComponents['schemas']['SelectedVersion']),
111+
version:
112+
version.value ??
113+
('latest' as ManagerComponents['schemas']['SelectedVersion']),
114+
selected_version:
115+
version.value ??
116+
('latest' as ManagerComponents['schemas']['SelectedVersion']),
113117
repository: nodePack.repository ?? '',
114118
channel: 'default' as ManagerComponents['schemas']['ManagerChannel'],
115119
mode: 'cache' as ManagerComponents['schemas']['ManagerDatabaseSource'],
@@ -123,7 +127,9 @@ const handleDisable = () => {
123127
}
124128
return disablePack({
125129
id: nodePack.id,
126-
version: version.value ?? ('latest' as ManagerComponents['schemas']['SelectedVersion'])
130+
version:
131+
version.value ??
132+
('latest' as ManagerComponents['schemas']['SelectedVersion'])
127133
})
128134
}
129135

src/components/dialog/content/manager/button/PackInstallButton.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ import { useDialogService } from '@/services/dialogService'
3333
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
3434
import { ButtonSize } from '@/types/buttonTypes'
3535
import type { components } from '@/types/comfyRegistryTypes'
36-
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
3736
import {
3837
type ConflictDetail,
3938
ConflictDetectionResult
4039
} from '@/types/conflictDetectionTypes'
40+
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
4141
4242
type NodePack = components['schemas']['Node']
4343
@@ -123,7 +123,7 @@ const installAllPacks = async () => {
123123
})
124124
return
125125
}
126-
126+
127127
// No conflicts or conflicts acknowledged - proceed with installation
128128
const uninstalledPacks = nodePacks.filter(
129129
(pack) => !managerStore.isPackInstalled(pack.id)

src/components/dialog/content/manager/button/PackUpdateButton.vue

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
<template>
2-
<PackActionButton
2+
<IconTextButton
33
v-bind="$attrs"
4-
variant="black"
4+
type="transparent"
55
:label="$t('manager.updateAll')"
6-
:loading="isUpdating"
7-
:loading-message="$t('g.updating')"
8-
@action="updateAllPacks"
9-
/>
6+
:border="true"
7+
size="sm"
8+
:disabled="isUpdating"
9+
@click="updateAllPacks"
10+
>
11+
<template v-if="isUpdating" #icon>
12+
<DotSpinner duration="1s" :size="12" />
13+
</template>
14+
</IconTextButton>
1015
</template>
1116

1217
<script setup lang="ts">
1318
import { ref } from 'vue'
1419
15-
import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue'
20+
import IconTextButton from '@/components/button/IconTextButton.vue'
21+
import DotSpinner from '@/components/common/DotSpinner.vue'
1622
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
1723
import type { components } from '@/types/comfyRegistryTypes'
18-
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
1924
2025
type NodePack = components['schemas']['Node']
2126
@@ -27,16 +32,10 @@ const isUpdating = ref<boolean>(false)
2732
2833
const managerStore = useComfyManagerStore()
2934
30-
const createPayload = (
31-
updateItem: NodePack
32-
): ManagerComponents['schemas']['ManagerPackInfo'] => {
33-
if (!updateItem.id) {
34-
throw new Error('Node ID is required for update')
35-
}
36-
35+
const createPayload = (updateItem: NodePack) => {
3736
return {
38-
id: updateItem.id,
39-
version: updateItem.latest_version?.version || 'latest'
37+
id: updateItem.id!,
38+
version: updateItem.latest_version!.version!
4039
}
4140
}
4241
@@ -53,21 +52,16 @@ const updateAllPacks = async () => {
5352
console.warn('No packs provided for update')
5453
return
5554
}
56-
5755
isUpdating.value = true
58-
5956
const updatablePacks = nodePacks.filter((pack) =>
6057
managerStore.isPackInstalled(pack.id)
6158
)
62-
6359
if (!updatablePacks.length) {
6460
console.info('No installed packs available for update')
6561
isUpdating.value = false
6662
return
6763
}
68-
6964
console.info(`Starting update of ${updatablePacks.length} packs`)
70-
7165
try {
7266
await Promise.all(updatablePacks.map(updatePack))
7367
managerStore.updatePack.clear()

src/components/dialog/content/manager/infoPanel/InfoPanelHeader.vue

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
size="md"
2929
:is-installing="isInstalling"
3030
:node-packs="nodePacks"
31-
:has-conflict="hasConflict"
31+
:has-conflict="hasConflict || computedHasConflict"
32+
:conflict-info="conflictInfo"
3233
/>
3334
</slot>
3435
</div>
@@ -48,8 +49,10 @@ import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
4849
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
4950
import PackUninstallButton from '@/components/dialog/content/manager/button/PackUninstallButton.vue'
5051
import PackIcon from '@/components/dialog/content/manager/packIcon/PackIcon.vue'
52+
import { useConflictDetection } from '@/composables/useConflictDetection'
5153
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
5254
import { components } from '@/types/comfyRegistryTypes'
55+
import type { ConflictDetail } from '@/types/conflictDetectionTypes'
5356
import { ImportFailedKey } from '@/types/importFailedTypes'
5457
5558
const { nodePacks, hasConflict } = defineProps<{
@@ -79,4 +82,23 @@ const isInstalling = computed(() => {
7982
if (!nodePacks?.length) return false
8083
return nodePacks.some((pack) => managerStore.isPackInstalling(pack.id))
8184
})
85+
86+
// Add conflict detection for install button dialog
87+
const { checkNodeCompatibility } = useConflictDetection()
88+
89+
// Compute conflict info for all node packs
90+
const conflictInfo = computed<ConflictDetail[]>(() => {
91+
if (!nodePacks?.length) return []
92+
93+
const allConflicts: ConflictDetail[] = []
94+
for (const nodePack of nodePacks) {
95+
const compatibilityCheck = checkNodeCompatibility(nodePack)
96+
if (compatibilityCheck.conflicts) {
97+
allConflicts.push(...compatibilityCheck.conflicts)
98+
}
99+
}
100+
return allConflicts
101+
})
102+
103+
const computedHasConflict = computed(() => conflictInfo.value.length > 0)
82104
</script>

src/components/dialog/content/manager/packCard/PackCardFooter.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
v-if="!isInstalled"
1111
:node-packs="[nodePack]"
1212
:is-installing="isInstalling"
13+
:has-conflict="hasConflicts"
14+
:conflict-info="conflictInfo"
1315
/>
1416
<PackEnableToggle v-else :node-pack="nodePack" />
1517
</div>
@@ -21,9 +23,11 @@ import { useI18n } from 'vue-i18n'
2123
2224
import PackEnableToggle from '@/components/dialog/content/manager/button/PackEnableToggle.vue'
2325
import PackInstallButton from '@/components/dialog/content/manager/button/PackInstallButton.vue'
26+
import { useConflictDetection } from '@/composables/useConflictDetection'
2427
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
2528
import { IsInstallingKey } from '@/types/comfyManagerTypes'
2629
import type { components } from '@/types/comfyRegistryTypes'
30+
import type { ConflictDetail } from '@/types/conflictDetectionTypes'
2731
2832
const { nodePack } = defineProps<{
2933
nodePack: components['schemas']['Node']
@@ -38,4 +42,16 @@ const { n } = useI18n()
3842
const formattedDownloads = computed(() =>
3943
nodePack.downloads ? n(nodePack.downloads) : ''
4044
)
45+
46+
// Add conflict detection for the card button
47+
const { checkNodeCompatibility } = useConflictDetection()
48+
49+
// Check for conflicts with this specific node pack
50+
const conflictInfo = computed<ConflictDetail[]>(() => {
51+
if (!nodePack) return []
52+
const compatibilityCheck = checkNodeCompatibility(nodePack)
53+
return compatibilityCheck.conflicts || []
54+
})
55+
56+
const hasConflicts = computed(() => conflictInfo.value.length > 0)
4157
</script>

src/composables/useConflictDetection.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -712,16 +712,27 @@ export function useConflictDetection() {
712712
const conflicts: ConflictDetail[] = []
713713

714714
// Check OS compatibility using centralized function
715-
if (node.supported_os && node.supported_os.length > 0) {
715+
// First try latest_version (most accurate), then fallback to top level
716+
const supportedOS =
717+
('latest_version' in node ? node.latest_version?.supported_os : null) ||
718+
node.supported_os
719+
720+
if (supportedOS && supportedOS.length > 0) {
716721
const currentOS = systemStats.system?.os || 'unknown'
717-
const osConflict = checkOSConflict(node.supported_os, currentOS)
722+
const osConflict = checkOSConflict(supportedOS, currentOS)
718723
if (osConflict) {
719724
conflicts.push(osConflict)
720725
}
721726
}
722727

723728
// Check accelerator compatibility using centralized function
724-
if (node.supported_accelerators && node.supported_accelerators.length > 0) {
729+
// First try latest_version (most accurate), then fallback to top level
730+
const supportedAccelerators =
731+
('latest_version' in node
732+
? node.latest_version?.supported_accelerators
733+
: null) || node.supported_accelerators
734+
735+
if (supportedAccelerators && supportedAccelerators.length > 0) {
725736
// Extract available accelerators from system stats
726737
const acceleratorInfo = extractAcceleratorInfo(systemStats)
727738
const availableAccelerators: Node['supported_accelerators'] = []
@@ -733,7 +744,7 @@ export function useConflictDetection() {
733744
})
734745

735746
const acceleratorConflict = checkAcceleratorConflict(
736-
node.supported_accelerators,
747+
supportedAccelerators,
737748
availableAccelerators
738749
)
739750
if (acceleratorConflict) {
@@ -742,13 +753,19 @@ export function useConflictDetection() {
742753
}
743754

744755
// Check ComfyUI version compatibility
745-
if (node.supported_comfyui_version) {
756+
// First try latest_version (most accurate), then fallback to top level
757+
const comfyuiVersionRequirement =
758+
('latest_version' in node
759+
? node.latest_version?.supported_comfyui_version
760+
: null) || node.supported_comfyui_version
761+
762+
if (comfyuiVersionRequirement) {
746763
const currentComfyUIVersion = systemStats.system?.comfyui_version
747764
if (currentComfyUIVersion && currentComfyUIVersion !== 'unknown') {
748765
const versionConflict = utilCheckVersionCompatibility(
749766
'comfyui_version',
750767
currentComfyUIVersion,
751-
node.supported_comfyui_version
768+
comfyuiVersionRequirement
752769
)
753770
if (versionConflict) {
754771
conflicts.push(versionConflict)
@@ -757,13 +774,19 @@ export function useConflictDetection() {
757774
}
758775

759776
// Check ComfyUI Frontend version compatibility
760-
if (node.supported_comfyui_frontend_version) {
777+
// First try latest_version (most accurate), then fallback to top level
778+
const frontendVersionRequirement =
779+
('latest_version' in node
780+
? node.latest_version?.supported_comfyui_frontend_version
781+
: null) || node.supported_comfyui_frontend_version
782+
783+
if (frontendVersionRequirement) {
761784
const currentFrontendVersion = config.app_version
762785
if (currentFrontendVersion && currentFrontendVersion !== 'unknown') {
763786
const versionConflict = utilCheckVersionCompatibility(
764787
'frontend_version',
765788
currentFrontendVersion,
766-
node.supported_comfyui_frontend_version
789+
frontendVersionRequirement
767790
)
768791
if (versionConflict) {
769792
conflicts.push(versionConflict)

0 commit comments

Comments
 (0)