feat: add Update ComfyUI option to Help Center for non-desktop environments (#7578)

## Summary

- Adds "Update ComfyUI" menu item to Help Center for portable/localhost
environments
- Wires existing `/v2/manager/queue/update_comfyui` endpoint to the
frontend
- Only visible in non-desktop, non-cloud distributions (where Electron
update mechanism isn't available)

## Changes

1. **Service layer**: Added `updateComfyUI()` method to
`comfyManagerService.ts`
2. **UI**: Added menu item with download icon to
`HelpCenterMenuContent.vue`
3. **i18n**: Added translation key for the new menu item

## Context

The new Manager UI (v4) lost the ability to update ComfyUI core in
non-desktop environments. This restores that functionality by
integrating the existing manager endpoint into the Help Center menu.

## Test plan

- [ ] Verify menu item appears in portable/localhost environments
- [ ] Verify menu item does NOT appear in desktop (Electron)
environments
- [ ] Verify menu item does NOT appear in cloud environments
- [ ] Test clicking the menu item triggers update and reboot

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7578-feat-add-Update-ComfyUI-option-to-Help-Center-for-non-desktop-environments-2cc6d73d3650811e9e4fe55515f50333)
by [Unito](https://www.unito.io)
This commit is contained in:
Johnpaul Chiwetelu
2025-12-17 21:54:05 +01:00
committed by GitHub
parent 3e111bd75c
commit 890ab2019f
4 changed files with 96 additions and 1 deletions

View File

@@ -152,6 +152,7 @@
</template>
<script setup lang="ts">
import { useToast } from 'primevue/usetoast'
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import type { CSSProperties, Component } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -168,6 +169,7 @@ import { electronAPI, isElectron } from '@/utils/envUtil'
import { formatVersionAnchor } from '@/utils/formatUtil'
import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment'
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
import { useComfyManagerService } from '@/workbench/extensions/manager/services/comfyManagerService'
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
// Types
@@ -201,6 +203,7 @@ const SUBMENU_CONFIG = {
// Composables
const { t } = useI18n()
const toast = useToast()
const { staticUrls, buildDocsUrl } = useExternalLink()
const releaseStore = useReleaseStore()
const commandStore = useCommandStore()
@@ -230,6 +233,7 @@ const showVersionUpdates = computed(() =>
// Use conflict acknowledgment state from composable
const { shouldShowRedDot: shouldShowManagerRedDot } =
useConflictAcknowledgment()
const { isNewManagerUI } = useManagerState()
const moreItems = computed<MenuItem[]>(() => {
const allMoreItems: MenuItem[] = [
@@ -369,6 +373,19 @@ const menuItems = computed<MenuItem[]>(() => {
}
})
}
// Update ComfyUI - only for non-desktop, non-cloud with new manager UI
if (!isElectron() && !isCloud && isNewManagerUI.value) {
items.push({
key: 'update-comfyui',
type: 'item',
icon: 'icon-[lucide--download]',
label: t('helpCenter.updateComfyUI'),
action: () => {
onUpdateComfyUI()
emit('close')
}
})
}
items.push({
key: 'more',
@@ -545,6 +562,47 @@ const onReinstall = (): void => {
}
}
const onUpdateComfyUI = async (): Promise<void> => {
const { updateComfyUI, rebootComfyUI, error } = useComfyManagerService()
toast.add({
severity: 'info',
summary: t('helpCenter.updateComfyUIStarted'),
detail: t('helpCenter.updateComfyUIStartedDetail'),
life: 3000
})
try {
const result = await updateComfyUI({ is_stable: true })
if (result === null || error.value) {
toast.add({
severity: 'error',
summary: t('g.error'),
detail: error.value || t('helpCenter.updateComfyUIFailed'),
life: 5000
})
return
}
toast.add({
severity: 'success',
summary: t('helpCenter.updateComfyUISuccess'),
detail: t('helpCenter.updateComfyUISuccessDetail'),
life: 3000
})
await rebootComfyUI()
} catch (err) {
toast.add({
severity: 'error',
summary: t('g.error'),
detail: err instanceof Error ? err.message : t('g.unknownError'),
life: 5000
})
}
}
const onReleaseClick = (release: ReleaseNote): void => {
trackResourceClick('release_notes', true)
void releaseStore.handleShowChangelog(release.version)

View File

@@ -775,7 +775,13 @@
"updateAvailable": "Update",
"desktopUserGuide": "Desktop User Guide",
"openDevTools": "Open Dev Tools",
"reinstall": "Re-Install"
"reinstall": "Re-Install",
"updateComfyUI": "Update ComfyUI",
"updateComfyUIStarted": "Update Started",
"updateComfyUIStartedDetail": "ComfyUI update has been queued. Please wait...",
"updateComfyUISuccess": "Update Complete",
"updateComfyUISuccessDetail": "ComfyUI has been updated. Rebooting...",
"updateComfyUIFailed": "Failed to update ComfyUI. Please try again."
},
"releaseToast": {
"newVersionAvailable": "New update is out!",

View File

@@ -12,6 +12,7 @@ type ManagerQueueStatus = components['schemas']['QueueStatus']
type InstallPackParams = components['schemas']['InstallPackParams']
type InstalledPacksResponse = components['schemas']['InstalledPacksResponse']
type UpdateAllPacksParams = components['schemas']['UpdateAllPacksParams']
type UpdateComfyUIParams = components['schemas']['UpdateComfyUIParams']
type ManagerTaskHistory = components['schemas']['HistoryResponse']
type QueueTaskItem = components['schemas']['QueueTaskItem']
@@ -26,6 +27,7 @@ enum ManagerRoute {
RESET_QUEUE = 'manager/queue/reset',
QUEUE_STATUS = 'manager/queue/status',
UPDATE_ALL = 'manager/queue/update_all',
UPDATE_COMFYUI = 'manager/queue/update_comfyui',
LIST_INSTALLED = 'customnode/installed',
GET_NODES = 'customnode/getmappings',
IMPORT_FAIL_INFO = 'customnode/import_fail_info',
@@ -271,6 +273,33 @@ export const useComfyManagerService = () => {
)
}
const updateComfyUI = async (
params: UpdateComfyUIParams = { is_stable: true },
ui_id?: string,
signal?: AbortSignal
) => {
const errorContext = 'Updating ComfyUI'
const routeSpecificErrors = {
400: 'Bad Request: Missing required parameters',
403: 'Forbidden: To use this action, a security_level of `middle or below` is required'
}
const queryParams = {
client_id: api.clientId ?? api.initialClientId ?? 'unknown',
ui_id: ui_id || uuidv4(),
...params
}
return executeRequest<null>(
() =>
managerApiClient.get(ManagerRoute.UPDATE_COMFYUI, {
params: queryParams,
signal
}),
{ errorContext, routeSpecificErrors, isQueueOperation: true }
)
}
const rebootComfyUI = async (signal?: AbortSignal) => {
const errorContext = 'Rebooting ComfyUI'
const routeSpecificErrors = {
@@ -335,6 +364,7 @@ export const useComfyManagerService = () => {
updateAllPacks,
// System operations
updateComfyUI,
rebootComfyUI,
isLegacyManagerUI
}

View File

@@ -95,6 +95,7 @@ describe('useComfyManagerStore', () => {
disablePack: vi.fn().mockResolvedValue(null),
updatePack: vi.fn().mockResolvedValue(null),
updateAllPacks: vi.fn().mockResolvedValue(null),
updateComfyUI: vi.fn().mockResolvedValue(null),
rebootComfyUI: vi.fn().mockResolvedValue(null),
isLegacyManagerUI: vi.fn().mockResolvedValue(false)
}