[refactor] WIP: checkNodeCompatibility refactor

This commit is contained in:
Jin Yi
2025-07-30 12:48:58 +09:00
parent 6c58d08f08
commit b93d7aecc0
7 changed files with 110 additions and 198 deletions

View File

@@ -25,7 +25,7 @@
v-model="selectedVersion"
option-label="label"
option-value="value"
:options="versionOptions"
:options="processedVersionOptions"
:highlight-on-select="false"
class="w-full max-h-[50vh] border-none shadow-none rounded-md"
:pt="{
@@ -35,19 +35,14 @@
<template #option="slotProps">
<div class="flex justify-between items-center w-full p-1">
<div class="flex items-center gap-2">
<!-- Show no icon for nightly versions since compatibility is uncertain -->
<template v-if="slotProps.option.value === 'nightly'">
<div class="w-4"></div>
<!-- Empty space to maintain alignment -->
</template>
<template v-else>
<i
v-if="
getVersionCompatibility(slotProps.option.value).hasConflict
"
v-if="slotProps.option.hasConflict"
v-tooltip="{
value: getVersionCompatibility(slotProps.option.value)
.conflictMessage,
value: slotProps.option.conflictMessage,
showDelay: 300
}"
class="pi pi-exclamation-triangle text-yellow-500"
@@ -57,7 +52,7 @@
<span>{{ slotProps.option.label }}</span>
</div>
<i
v-if="selectedVersion === slotProps.option.value"
v-if="slotProps.option.isSelected"
class="pi pi-check text-highlight"
/>
</div>
@@ -89,7 +84,7 @@ import { whenever } from '@vueuse/core'
import Button from 'primevue/button'
import Listbox from 'primevue/listbox'
import ProgressSpinner from 'primevue/progressspinner'
import { onMounted, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import ContentDivider from '@/components/common/ContentDivider.vue'
@@ -115,11 +110,12 @@ const emit = defineEmits<{
const { t } = useI18n()
const registryService = useComfyRegistryService()
const managerStore = useComfyManagerStore()
const { checkVersionCompatibility } = useConflictDetection()
const { checkNodeCompatibility } = useConflictDetection()
const isQueueing = ref(false)
const selectedVersion = ref<string>('latest')
onMounted(() => {
const initialVersion = getInitialSelectedVersion() ?? 'latest'
selectedVersion.value =
@@ -130,14 +126,14 @@ onMounted(() => {
const getInitialSelectedVersion = () => {
if (!nodePack.id) return
// If unclaimed, set selected version to nightly
if (nodePack.publisher?.name === 'Unclaimed')
return 'nightly' as ManagerComponents['schemas']['SelectedVersion']
// If node pack is installed, set selected version to the installed version
if (managerStore.isPackInstalled(nodePack.id))
return managerStore.getInstalledPackVersion(nodePack.id)
// If unclaimed, set selected version to nightly
if (nodePack.publisher?.name === 'Unclaimed')
return 'nightly' as ManagerComponents['schemas']['SelectedVersion']
// If node pack is not installed, set selected version to latest
return nodePack.latest_version?.version
}
@@ -235,76 +231,35 @@ const handleSubmit = async () => {
emit('submit')
}
// Function to get version data (either from nodePack or fetchedVersions)
const getVersionData = (version: string) => {
// Use latest_version data for both "latest" and the actual latest version number
const latestVersionNumber = nodePack.latest_version?.version
const useLatestVersionData =
version === 'latest' || version === latestVersionNumber
if (useLatestVersionData) {
// For "latest" and the actual latest version number, use consistent data from latest_version
const latestVersionData = nodePack.latest_version
return {
supported_os: latestVersionData?.supported_os ?? nodePack.supported_os,
supported_accelerators:
latestVersionData?.supported_accelerators ??
nodePack.supported_accelerators,
supported_comfyui_version:
latestVersionData?.supported_comfyui_version ??
nodePack.supported_comfyui_version,
supported_comfyui_frontend_version:
latestVersionData?.supported_comfyui_frontend_version ??
nodePack.supported_comfyui_frontend_version
...latestVersionData
}
}
if (version === 'nightly') {
// For nightly, we can't determine exact compatibility since it's dynamic Git HEAD
// But we can assume it's generally compatible (nightly = latest development)
// Use nodePack data as fallback, but nightly is typically more permissive
return {
supported_os: nodePack.supported_os || [], // If no OS restrictions, assume all supported
supported_accelerators: nodePack.supported_accelerators || [], // If no accelerator restrictions, assume all supported
supported_comfyui_version: nodePack.supported_comfyui_version, // Use latest known requirement
supported_comfyui_frontend_version:
nodePack.supported_comfyui_frontend_version // Use latest known requirement
}
}
// For specific versions, find in fetched versions
const versionData = fetchedVersions.value.find((v) => v.version === version)
if (versionData) {
return {
supported_os: versionData.supported_os,
supported_accelerators: versionData.supported_accelerators,
supported_comfyui_version: versionData.supported_comfyui_version,
supported_comfyui_frontend_version:
versionData.supported_comfyui_frontend_version
...versionData
}
}
// Fallback to nodePack data
return {
supported_os: nodePack.supported_os,
supported_accelerators: nodePack.supported_accelerators,
supported_comfyui_version: nodePack.supported_comfyui_version,
supported_comfyui_frontend_version:
nodePack.supported_comfyui_frontend_version
...nodePack
}
}
// Function to check version compatibility using centralized logic
const checkVersionCompatibilityLocal = (
versionData: ReturnType<typeof getVersionData>
) => {
return checkVersionCompatibility(versionData)
}
// Main function to get version compatibility info
const getVersionCompatibility = (version: string) => {
const versionData = getVersionData(version)
const compatibility = checkVersionCompatibilityLocal(versionData)
const compatibility = checkNodeCompatibility(versionData)
const conflictMessage = compatibility.hasConflict
? getJoinedConflictMessages(compatibility.conflicts, t)
@@ -315,4 +270,33 @@ const getVersionCompatibility = (version: string) => {
conflictMessage
}
}
// Helper to determine if an option is selected.
const isOptionSelected = (optionValue: string) => {
if (selectedVersion.value === optionValue) {
return true
}
if (
optionValue === 'latest' &&
selectedVersion.value === nodePack.latest_version?.version
) {
return true
}
return false
}
// Checks if an option is selected, treating 'latest' as an alias for the actual latest version number.
const processedVersionOptions = computed(() => {
return versionOptions.value.map((option) => {
const compatibility = getVersionCompatibility(option.value)
const isSelected = isOptionSelected(option.value)
return {
...option,
hasConflict: compatibility.hasConflict,
conflictMessage: compatibility.conflictMessage,
isSelected: isSelected
}
})
})
</script>

View File

@@ -17,36 +17,24 @@
<script setup lang="ts">
import { inject, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import PackActionButton from '@/components/dialog/content/manager/button/PackActionButton.vue'
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
import { useConflictDetection } from '@/composables/useConflictDetection'
import { useDialogService } from '@/services/dialogService'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { IsInstallingKey } from '@/types/comfyManagerTypes'
import type { components } from '@/types/comfyRegistryTypes'
import type { ConflictDetectionResult } from '@/types/conflictDetectionTypes'
import { components as ManagerComponents } from '@/types/generatedManagerTypes'
type NodePack = components['schemas']['Node']
const { nodePacks, variant, label, hasConflict, skipConflictCheck } =
defineProps<{
nodePacks: NodePack[]
variant?: 'default' | 'black'
label?: string
hasConflict?: boolean
skipConflictCheck?: boolean
}>()
const { nodePacks, variant, label, hasConflict } = defineProps<{
nodePacks: NodePack[]
variant?: 'default' | 'black'
label?: string
hasConflict?: boolean
}>()
const { t } = useI18n()
const isInstalling = inject(IsInstallingKey, ref(false))
const managerStore = useComfyManagerStore()
const { showNodeConflictDialog } = useDialogService()
const { checkVersionCompatibility } = useConflictDetection()
const { acknowledgeConflict, isConflictAcknowledged } =
useConflictAcknowledgment()
const onClick = (): void => {
isInstalling.value = true
@@ -78,81 +66,37 @@ const createPayload = (
const installPack = (item: NodePack) =>
managerStore.installPack.call(createPayload(item))
// Function to check compatibility for uninstalled packages using centralized logic
function checkUninstalledPackageCompatibility(
pack: NodePack
): ConflictDetectionResult | null {
const compatibility = checkVersionCompatibility({
supported_os: pack.supported_os,
supported_accelerators: pack.supported_accelerators,
supported_comfyui_version: pack.supported_comfyui_version,
supported_comfyui_frontend_version: pack.supported_comfyui_frontend_version
})
if (compatibility.hasConflict) {
return {
package_id: pack.id || 'unknown',
package_name: pack.name || 'unknown',
has_conflict: true,
conflicts: compatibility.conflicts,
is_compatible: false
}
}
return null
}
const installAllPacks = async () => {
if (!nodePacks?.length) return
// TBD Install Anyway modal
// if (hasConflict && !isConflictAcknowledged) {
// showNodeConflictDialog({
// conflictedPackages: nodePacks,
// buttonText: t('manager.conflicts.installAnyway'),
// onButtonClick: async () => {
// // User chose "Install Anyway" - acknowledge all conflicts and proceed
// for (const conflictedPack of packsWithConflicts) {
// for (const conflict of conflictedPack.conflicts) {
// acknowledgeConflict(
// conflictedPack.package_id,
// conflict.type,
// '0.1.0'
// )
// }
// }
// // Proceed with installation
// await performInstallation(uninstalledPacks)
// }
// })
// return
// }
const uninstalledPacks = nodePacks.filter(
(pack) => !managerStore.isPackInstalled(pack.id)
)
if (!uninstalledPacks.length) return
// Skip conflict check if explicitly requested (e.g., from "Install Anyway" button)
if (!skipConflictCheck) {
// Check for conflicts in uninstalled packages
const packsWithConflicts: ConflictDetectionResult[] = []
for (const pack of uninstalledPacks) {
const conflicts = checkUninstalledPackageCompatibility(pack)
if (conflicts) {
// Check if conflicts have been acknowledged
const hasUnacknowledgedConflicts = conflicts.conflicts.some(
(conflict) => !isConflictAcknowledged(pack.id || '', conflict.type)
)
if (hasUnacknowledgedConflicts) {
packsWithConflicts.push(conflicts)
}
}
}
// If there are unacknowledged conflicts, show modal
if (packsWithConflicts.length > 0) {
showNodeConflictDialog({
conflictedPackages: packsWithConflicts,
buttonText: t('manager.conflicts.installAnyway'),
onButtonClick: async () => {
// User chose "Install Anyway" - acknowledge all conflicts and proceed
for (const conflictedPack of packsWithConflicts) {
for (const conflict of conflictedPack.conflicts) {
acknowledgeConflict(
conflictedPack.package_id,
conflict.type,
'0.1.0'
)
}
}
// Proceed with installation
await performInstallation(uninstalledPacks)
}
})
return
}
}
// No conflicts or conflicts acknowledged - proceed with installation
await performInstallation(uninstalledPacks)
}

View File

@@ -94,7 +94,7 @@ whenever(isInstalled, () => {
isInstalling.value = false
})
const { checkVersionCompatibility } = useConflictDetection()
const { checkNodeCompatibility } = useConflictDetection()
const { getConflictsForPackageByID } = useConflictDetectionStore()
const { t, d, n } = useI18n()
@@ -107,22 +107,12 @@ const conflictResult = computed((): ConflictDetectionResult | null => {
}
// For non-installed packages, perform compatibility check
const compatibility = checkVersionCompatibility({
supported_os: nodePack.supported_os,
supported_accelerators: nodePack.supported_accelerators,
supported_comfyui_version: nodePack.supported_comfyui_version,
supported_comfyui_frontend_version:
nodePack.supported_comfyui_frontend_version
// TODO: Add when API provides these fields
// supported_python_version: nodePack.supported_python_version,
// is_banned: nodePack.is_banned,
// has_registry_data: nodePack.has_registry_data
})
const compatibility = checkNodeCompatibility(nodePack)
if (compatibility.hasConflict && nodePack.id && nodePack.name) {
if (compatibility.hasConflict) {
return {
package_id: nodePack.id,
package_name: nodePack.name,
package_id: nodePack.id || '',
package_name: nodePack.name || '',
has_conflict: true,
conflicts: compatibility.conflicts,
is_compatible: false
@@ -133,7 +123,7 @@ const conflictResult = computed((): ConflictDetectionResult | null => {
})
const hasCompatibilityIssues = computed(() => {
return isInstalled.value && conflictResult.value?.has_conflict ? true : false
return conflictResult.value?.has_conflict
})
const infoItems = computed<InfoItem[]>(() => [

View File

@@ -5,7 +5,7 @@
:key="index"
class="p-3 bg-yellow-800/20 rounded-md"
>
<div class="text-sm">
<div class="text-sm break-words">
{{ getConflictMessage(conflict, $t) }}
</div>
</div>

View File

@@ -45,7 +45,7 @@ const formattedDownloads = computed(() =>
)
const { getConflictsForPackageByID } = useConflictDetectionStore()
const { checkVersionCompatibility } = useConflictDetection()
const { checkNodeCompatibility } = useConflictDetection()
const hasConflict = computed(() => {
if (!nodePack.id) return false
@@ -60,9 +60,7 @@ const hasConflict = computed(() => {
}
// For uninstalled packages, check compatibility directly
const compatibility = checkVersionCompatibility(nodePack)
console.log(compatibility)
const compatibility = checkNodeCompatibility(nodePack)
return compatibility.hasConflict
})
</script>

View File

@@ -1,31 +1,24 @@
import { uniqBy } from 'lodash'
import { computed, getCurrentInstance, onUnmounted, readonly, ref } from 'vue'
import { uniqBy } from 'lodash';
import { computed, getCurrentInstance, onUnmounted, readonly, ref } from 'vue';
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks';
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment';
import config from '@/config';
import { useComfyManagerService } from '@/services/comfyManagerService';
import { useComfyRegistryService } from '@/services/comfyRegistryService';
import { useComfyManagerStore } from '@/stores/comfyManagerStore';
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore';
import { useSystemStatsStore } from '@/stores/systemStatsStore';
import type { SystemStats } from '@/types';
import type { components } from '@/types/comfyRegistryTypes';
import type { ConflictDetail, ConflictDetectionResponse, ConflictDetectionResult, ConflictDetectionSummary, ConflictType, Node, NodePackRequirements, SystemEnvironment } from '@/types/conflictDetectionTypes';
import { cleanVersion, satisfiesVersion, utilCheckVersionCompatibility } from '@/utils/versionUtil';
import { useInstalledPacks } from '@/composables/nodePack/useInstalledPacks'
import { useConflictAcknowledgment } from '@/composables/useConflictAcknowledgment'
import config from '@/config'
import { useComfyManagerService } from '@/services/comfyManagerService'
import { useComfyRegistryService } from '@/services/comfyRegistryService'
import { useComfyManagerStore } from '@/stores/comfyManagerStore'
import { useConflictDetectionStore } from '@/stores/conflictDetectionStore'
import { useSystemStatsStore } from '@/stores/systemStatsStore'
import type { SystemStats } from '@/types'
import type { components } from '@/types/comfyRegistryTypes'
import type {
ConflictDetail,
ConflictDetectionResponse,
ConflictDetectionResult,
ConflictDetectionSummary,
ConflictType,
Node,
NodePackRequirements,
SystemEnvironment
} from '@/types/conflictDetectionTypes'
import {
cleanVersion,
satisfiesVersion,
checkVersionCompatibility as utilCheckVersionCompatibility
} from '@/utils/versionUtil'
/**
* Composable for conflict detection system.
@@ -734,10 +727,12 @@ export function useConflictDetection() {
}
/**
* Check compatibility for a specific version of a package.
* Check compatibility for a node.
* Used by components like PackVersionSelectorPopover.
*/
function checkVersionCompatibility(node: Node) {
function checkNodeCompatibility(
node: Node | components['schemas']['NodeVersion']
) {
const systemStatsStore = useSystemStatsStore()
const systemStats = systemStatsStore.systemStats
if (!systemStats) return { hasConflict: false, conflicts: [] }
@@ -856,7 +851,7 @@ export function useConflictDetection() {
acknowledgePackageConflict,
// Helper functions for other components
checkVersionCompatibility
checkNodeCompatibility
}
}
@@ -1302,6 +1297,7 @@ function generateSummary(
const conflictsByType: Record<ConflictType, number> = {
comfyui_version: 0,
frontend_version: 0,
import_failed: 0,
os: 0,
accelerator: 0,
banned: 0,
@@ -1374,4 +1370,4 @@ function getEmptySummary(): ConflictDetectionSummary {
last_check_timestamp: new Date().toISOString(),
check_duration_ms: 0
}
}
}

View File

@@ -65,7 +65,7 @@ export function isValidVersion(version: string): boolean {
* @param supportedVersion Required version range string
* @returns ConflictDetail object if incompatible, null if compatible
*/
export function checkVersionCompatibility(
export function utilCheckVersionCompatibility(
type: ConflictType,
currentVersion: string,
supportedVersion: string