mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-27 03:19:56 +00:00
[backport core/1.35] feat(manager): add Try Update button for nightly packs (#7635)
Backport of #7610 to `core/1.35` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7635-backport-core-1-35-feat-manager-add-Try-Update-button-for-nightly-packs-2ce6d73d36508135a070e17689cee5e7) by [Unito](https://www.unito.io) Co-authored-by: Johnpaul Chiwetelu <49923152+Myestery@users.noreply.github.com>
This commit is contained in:
@@ -294,6 +294,8 @@
|
||||
"uninstall": "Uninstall",
|
||||
"uninstalling": "Uninstalling {id}",
|
||||
"update": "Update",
|
||||
"tryUpdate": "Try Update",
|
||||
"tryUpdateTooltip": "Pull latest changes from repository. Nightly versions may have updates that cannot be detected automatically.",
|
||||
"uninstallSelected": "Uninstall Selected",
|
||||
"updateSelected": "Update Selected",
|
||||
"updateAll": "Update All",
|
||||
|
||||
@@ -63,7 +63,7 @@ const {
|
||||
fill?: boolean
|
||||
}>()
|
||||
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(nodePack)
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(() => nodePack)
|
||||
const popoverRef = ref()
|
||||
|
||||
const managerStore = useComfyManagerStore()
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<IconTextButton
|
||||
v-tooltip.top="$t('manager.tryUpdateTooltip')"
|
||||
v-bind="$attrs"
|
||||
type="transparent"
|
||||
:label="computedLabel"
|
||||
:border="true"
|
||||
:size="size"
|
||||
:disabled="isUpdating"
|
||||
@click="tryUpdate"
|
||||
>
|
||||
<template v-if="isUpdating" #icon>
|
||||
<DotSpinner duration="1s" :size="size === 'sm' ? 12 : 16" />
|
||||
</template>
|
||||
</IconTextButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||
import DotSpinner from '@/components/common/DotSpinner.vue'
|
||||
import type { ButtonSize } from '@/types/buttonTypes'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
|
||||
const { nodePack, size = 'sm' } = defineProps<{
|
||||
nodePack: NodePack
|
||||
size?: ButtonSize
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const managerStore = useComfyManagerStore()
|
||||
|
||||
const isUpdating = ref(false)
|
||||
|
||||
async function tryUpdate() {
|
||||
if (!nodePack.id) {
|
||||
console.warn('Pack missing required id:', nodePack)
|
||||
return
|
||||
}
|
||||
|
||||
isUpdating.value = true
|
||||
try {
|
||||
await managerStore.updatePack.call({
|
||||
id: nodePack.id,
|
||||
version: 'nightly'
|
||||
})
|
||||
managerStore.updatePack.clear()
|
||||
} catch (error) {
|
||||
console.error('Nightly update failed:', error)
|
||||
} finally {
|
||||
isUpdating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const computedLabel = computed(() =>
|
||||
isUpdating.value ? t('g.updating') : t('manager.tryUpdate')
|
||||
)
|
||||
</script>
|
||||
@@ -5,7 +5,14 @@
|
||||
<InfoPanelHeader
|
||||
:node-packs="[nodePack]"
|
||||
:has-conflict="hasCompatibilityIssues"
|
||||
/>
|
||||
>
|
||||
<template v-if="canTryNightlyUpdate" #install-button>
|
||||
<div class="flex w-full justify-center gap-2">
|
||||
<PackTryUpdateButton :node-pack="nodePack" size="md" />
|
||||
<PackUninstallButton :node-packs="[nodePack]" size="md" />
|
||||
</div>
|
||||
</template>
|
||||
</InfoPanelHeader>
|
||||
</div>
|
||||
<div
|
||||
ref="scrollContainer"
|
||||
@@ -68,9 +75,12 @@ import type { components } from '@/types/comfyRegistryTypes'
|
||||
import PackStatusMessage from '@/workbench/extensions/manager/components/manager/PackStatusMessage.vue'
|
||||
import PackVersionBadge from '@/workbench/extensions/manager/components/manager/PackVersionBadge.vue'
|
||||
import PackEnableToggle from '@/workbench/extensions/manager/components/manager/button/PackEnableToggle.vue'
|
||||
import PackTryUpdateButton from '@/workbench/extensions/manager/components/manager/button/PackTryUpdateButton.vue'
|
||||
import PackUninstallButton from '@/workbench/extensions/manager/components/manager/button/PackUninstallButton.vue'
|
||||
import InfoPanelHeader from '@/workbench/extensions/manager/components/manager/infoPanel/InfoPanelHeader.vue'
|
||||
import InfoTabs from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTabs.vue'
|
||||
import MetadataRow from '@/workbench/extensions/manager/components/manager/infoPanel/MetadataRow.vue'
|
||||
import { usePackUpdateStatus } from '@/workbench/extensions/manager/composables/nodePack/usePackUpdateStatus'
|
||||
import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection'
|
||||
import { useImportFailedDetection } from '@/workbench/extensions/manager/composables/useImportFailedDetection'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
@@ -99,6 +109,8 @@ whenever(isInstalled, () => {
|
||||
isInstalling.value = false
|
||||
})
|
||||
|
||||
const { canTryNightlyUpdate } = usePackUpdateStatus(() => nodePack)
|
||||
|
||||
const { checkNodeCompatibility } = useConflictDetection()
|
||||
const { getConflictsForPackageByID } = useConflictDetectionStore()
|
||||
|
||||
|
||||
@@ -18,12 +18,27 @@
|
||||
<div v-if="isMixed" class="text-sm text-neutral-500">
|
||||
{{ $t('manager.mixedSelectionMessage') }}
|
||||
</div>
|
||||
<!-- All installed: Show uninstall button -->
|
||||
<PackUninstallButton
|
||||
<!-- All installed: Show update (if nightly) and uninstall buttons -->
|
||||
<div
|
||||
v-else-if="isAllInstalled"
|
||||
size="md"
|
||||
:node-packs="installedPacks"
|
||||
/>
|
||||
class="flex w-full justify-center gap-2"
|
||||
>
|
||||
<IconTextButton
|
||||
v-if="hasNightlyPacks"
|
||||
v-tooltip.top="$t('manager.tryUpdateTooltip')"
|
||||
type="transparent"
|
||||
:label="updateSelectedLabel"
|
||||
:border="true"
|
||||
size="md"
|
||||
:disabled="isUpdatingSelected"
|
||||
@click="updateSelectedNightlyPacks"
|
||||
>
|
||||
<template v-if="isUpdatingSelected" #icon>
|
||||
<DotSpinner duration="1s" :size="16" />
|
||||
</template>
|
||||
</IconTextButton>
|
||||
<PackUninstallButton size="md" :node-packs="installedPacks" />
|
||||
</div>
|
||||
<!-- None installed: Show install button -->
|
||||
<PackInstallButton
|
||||
v-else-if="isNoneInstalled"
|
||||
@@ -55,8 +70,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import { computed, onUnmounted, provide, toRef } from 'vue'
|
||||
import { computed, onUnmounted, provide, ref, toRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||
import DotSpinner from '@/components/common/DotSpinner.vue'
|
||||
import { useComfyRegistryStore } from '@/stores/comfyRegistryStore'
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import PackStatusMessage from '@/workbench/extensions/manager/components/manager/PackStatusMessage.vue'
|
||||
@@ -68,6 +86,7 @@ import PackIconStacked from '@/workbench/extensions/manager/components/manager/p
|
||||
import { usePacksSelection } from '@/workbench/extensions/manager/composables/nodePack/usePacksSelection'
|
||||
import { usePacksStatus } from '@/workbench/extensions/manager/composables/nodePack/usePacksStatus'
|
||||
import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import type { ConflictDetail } from '@/workbench/extensions/manager/types/conflictDetectionTypes'
|
||||
import { ImportFailedKey } from '@/workbench/extensions/manager/types/importFailedTypes'
|
||||
|
||||
@@ -75,6 +94,8 @@ const { nodePacks } = defineProps<{
|
||||
nodePacks: components['schemas']['Node'][]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const managerStore = useComfyManagerStore()
|
||||
const nodePacksRef = toRef(() => nodePacks)
|
||||
|
||||
// Use new composables for cleaner code
|
||||
@@ -83,11 +104,40 @@ const {
|
||||
notInstalledPacks,
|
||||
isAllInstalled,
|
||||
isNoneInstalled,
|
||||
isMixed
|
||||
isMixed,
|
||||
nightlyPacks,
|
||||
hasNightlyPacks
|
||||
} = usePacksSelection(nodePacksRef)
|
||||
|
||||
const { hasImportFailed, overallStatus } = usePacksStatus(nodePacksRef)
|
||||
|
||||
// Batch update state for nightly packs
|
||||
const isUpdatingSelected = ref(false)
|
||||
|
||||
async function updateSelectedNightlyPacks() {
|
||||
if (nightlyPacks.value.length === 0) return
|
||||
|
||||
isUpdatingSelected.value = true
|
||||
try {
|
||||
for (const pack of nightlyPacks.value) {
|
||||
if (!pack.id) continue
|
||||
await managerStore.updatePack.call({
|
||||
id: pack.id,
|
||||
version: 'nightly'
|
||||
})
|
||||
}
|
||||
managerStore.updatePack.clear()
|
||||
} catch (error) {
|
||||
console.error('Batch nightly update failed:', error)
|
||||
} finally {
|
||||
isUpdatingSelected.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const updateSelectedLabel = computed(() =>
|
||||
isUpdatingSelected.value ? t('g.updating') : t('manager.updateSelected')
|
||||
)
|
||||
|
||||
const { checkNodeCompatibility } = useConflictDetection()
|
||||
const { getNodeDefs } = useComfyRegistryStore()
|
||||
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { toValue } from '@vueuse/core'
|
||||
import { compare, valid } from 'semver'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
export const usePackUpdateStatus = (
|
||||
nodePack: components['schemas']['Node']
|
||||
nodePackSource: MaybeRefOrGetter<components['schemas']['Node']>
|
||||
) => {
|
||||
const { isPackInstalled, getInstalledPackVersion } = useComfyManagerStore()
|
||||
const { isPackInstalled, isPackEnabled, getInstalledPackVersion } =
|
||||
useComfyManagerStore()
|
||||
|
||||
const isInstalled = computed(() => isPackInstalled(nodePack?.id))
|
||||
// Use toValue to unwrap the source reactively inside computeds
|
||||
const nodePack = computed(() => toValue(nodePackSource))
|
||||
|
||||
const isInstalled = computed(() => isPackInstalled(nodePack.value?.id))
|
||||
const isEnabled = computed(() => isPackEnabled(nodePack.value?.id))
|
||||
const installedVersion = computed(() =>
|
||||
getInstalledPackVersion(nodePack.id ?? '')
|
||||
getInstalledPackVersion(nodePack.value?.id ?? '')
|
||||
)
|
||||
const latestVersion = computed(() => nodePack.latest_version?.version)
|
||||
const latestVersion = computed(() => nodePack.value?.latest_version?.version)
|
||||
|
||||
const isNightlyPack = computed(
|
||||
() => !!installedVersion.value && !valid(installedVersion.value)
|
||||
@@ -31,9 +38,19 @@ export const usePackUpdateStatus = (
|
||||
return compare(latestVersion.value, installedVersion.value) > 0
|
||||
})
|
||||
|
||||
/**
|
||||
* Nightly packs can always "try update" since we cannot compare git hashes
|
||||
* to determine if an update is actually available. This allows users to
|
||||
* pull the latest changes from the repository.
|
||||
*/
|
||||
const canTryNightlyUpdate = computed(
|
||||
() => isInstalled.value && isEnabled.value && isNightlyPack.value
|
||||
)
|
||||
|
||||
return {
|
||||
isUpdateAvailable,
|
||||
isNightlyPack,
|
||||
canTryNightlyUpdate,
|
||||
installedVersion,
|
||||
latestVersion
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { valid } from 'semver'
|
||||
import { computed } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
@@ -41,12 +42,30 @@ export function usePacksSelection(nodePacks: Ref<NodePack[]>) {
|
||||
return 'mixed'
|
||||
})
|
||||
|
||||
/**
|
||||
* Nightly packs are installed packs with a non-semver version (git hash)
|
||||
* that are also enabled
|
||||
*/
|
||||
const nightlyPacks = computed(() =>
|
||||
installedPacks.value.filter((pack) => {
|
||||
if (!pack.id) return false
|
||||
const version = managerStore.getInstalledPackVersion(pack.id)
|
||||
const isNightly = !!version && !valid(version)
|
||||
const isEnabled = managerStore.isPackEnabled(pack.id)
|
||||
return isNightly && isEnabled
|
||||
})
|
||||
)
|
||||
|
||||
const hasNightlyPacks = computed(() => nightlyPacks.value.length > 0)
|
||||
|
||||
return {
|
||||
installedPacks,
|
||||
notInstalledPacks,
|
||||
isAllInstalled,
|
||||
isNoneInstalled,
|
||||
isMixed,
|
||||
selectionState
|
||||
selectionState,
|
||||
nightlyPacks,
|
||||
hasNightlyPacks
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user