diff --git a/locales/en.json b/locales/en.json index 3f330f395..7e3e66ae7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -614,6 +614,7 @@ "updateConfirmMessage": "Update from {installed} to {latest}?\n\nLocal changes will be stashed and a backup branch created.", "updateConfirmMessageLatest": "Update from {installed} to latest on master ({latest})?\n\nThis will pull the newest commits from the master branch. Local changes will be stashed and a backup branch created.", "updateConfirmMessageDowngrade": "Roll back from {installed} to stable release {latest}?\n\nYour install is currently ahead of the latest stable release. This will check out the stable tag, removing any newer commits. Local changes will be stashed and a backup branch created.", + "updateRestartNotice": "ComfyUI is currently running and will be restarted to apply this update.", "updatingTitle": "Updating to {version}", "updatePrepare": "Prepare", "updatePrepareSnapshot": "Saving snapshot of current state…", @@ -831,6 +832,7 @@ "stopRequired": "This installation is currently running and must be stopped first.", "stopRequiredConfirm": "This installation is currently running. Would you like to stop it to continue?", "stopRunning": "Stop Running", + "stopTimeout": "ComfyUI is still shutting down. Please wait a moment and try again.", "folderPermissionDenied": "macOS denied access to \"{path}\". Please open System Settings → Privacy & Security → Files and Folders, grant access to ComfyUI Desktop 2.0, then retry." }, diff --git a/src/renderer/src/composables/useDeepLinkRouter.ts b/src/renderer/src/composables/useDeepLinkRouter.ts index 7ff33c754..17c9f616e 100644 --- a/src/renderer/src/composables/useDeepLinkRouter.ts +++ b/src/renderer/src/composables/useDeepLinkRouter.ts @@ -48,11 +48,16 @@ export function useDeepLinkRouter(opts: DeepLinkRouterOpts): void { await opts.bootstrapReady const inst = installationStore.getById(id) if (!inst) return + // Issue #557 — pill click drives straight to the update + // confirm dialog. The Update tab still opens behind the + // dialog so a cancel returns the user to the surface that + // shows release notes / channel info. await opts.openOverlay({ kind: 'settings', installation: inst, initialTab: 'comfy', initialDetailTab: 'update', + autoAction: 'update-comfyui', }) return } diff --git a/src/renderer/src/panel/PanelApp.test.ts b/src/renderer/src/panel/PanelApp.test.ts index 858ce9dd8..361afe7f1 100644 --- a/src/renderer/src/panel/PanelApp.test.ts +++ b/src/renderer/src/panel/PanelApp.test.ts @@ -34,7 +34,7 @@ vi.mock('../views/DetailModal.vue', () => ({ // parent owns the close behaviour. props: ['installation', 'initialTab', 'autoAction'], template: - '
', + '', }, })) vi.mock('../views/ProgressModal.vue', () => ({ @@ -722,7 +722,9 @@ describe('PanelApp', () => { // `kind: 'install-update'` and the host's installationId. The // panel renderer routes that into a Tier 1 manage overlay with // initialTab='update' so the DetailModal opens directly on the - // update tab. + // update tab. Issue #557: it also pre-arms the `update-comfyui` + // auto-action so the confirm dialog fires immediately instead of + // making the user click "Update Now" inside the tab. const wrapper = mountPanel() await flushPromises() expect(wrapper.find('[data-testid="detail-modal"]').exists()).toBe(false) @@ -734,6 +736,7 @@ describe('PanelApp', () => { const detail = wrapper.find('[data-testid="detail-modal"]') expect(detail.exists()).toBe(true) expect(detail.attributes('data-installation-id')).toBe('test-id') + expect(detail.attributes('data-auto-action')).toBe('update-comfyui') }) it('shows the "Desktop Update Ready" confirm modal when a restart-prompt event arrives, and installs on confirm', async () => { diff --git a/src/renderer/src/views/DetailModal.vue b/src/renderer/src/views/DetailModal.vue index 64b418507..d1699827d 100644 --- a/src/renderer/src/views/DetailModal.vue +++ b/src/renderer/src/views/DetailModal.vue @@ -318,12 +318,36 @@ async function runAction(action: ActionDef, btn: HTMLButtonElement | null): Prom // Pre-flight: check if the installation is busy or running // Skip for migrate-to-standalone — the useMigrateAction composable handles its own guard - if (action.id !== 'migrate-to-standalone' && REQUIRES_STOPPED.has(action.id)) { + // Skip for update-comfyui — the smooth update flow folds the + // stop-required prompt into the action's own confirm dialog and + // chains stopComfyUI → update → launch in `apiCall` (issue #557). + if ( + action.id !== 'migrate-to-standalone' && + action.id !== 'update-comfyui' && + REQUIRES_STOPPED.has(action.id) + ) { if (!await actionGuard.checkBeforeAction(props.installation.id, action.label)) return } let mutableAction = { ...action } + // Smooth update flow — when the install is running, the post-update + // auto-launch is implied by the action; tell the user that in the + // single confirm dialog rather than as a separate stop-required + // prompt. The chained stop+launch lives in the showProgress branch. + const updateWillRestart = + mutableAction.id === 'update-comfyui' && sessionStore.isRunning(props.installation.id) + if (updateWillRestart && mutableAction.confirm) { + const baseMsg = mutableAction.confirm.message ?? '' + mutableAction = { + ...mutableAction, + confirm: { + ...mutableAction.confirm, + message: `${t('standalone.updateRestartNotice')}\n\n${baseMsg}`, + }, + } + } + // fieldSelects chain if (mutableAction.fieldSelects) { const selections: Record