mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
[Manager] “Restarting” state after clicking restart button (#4269)
This commit is contained in:
130
src/components/common/DotSpinner.vue
Normal file
130
src/components/common/DotSpinner.vue
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="inline-flex items-center justify-center"
|
||||||
|
:style="{ width: size + 'px', height: size + 'px' }"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
:width="size"
|
||||||
|
:height="size"
|
||||||
|
viewBox="0 0 14 14"
|
||||||
|
fill="none"
|
||||||
|
class="animate-spin"
|
||||||
|
:style="{ animationDuration: duration }"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_776_9582)">
|
||||||
|
<!-- Top dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M7 2.21053C7.61042 2.21053 8.10526 1.71568 8.10526 1.10526C8.10526 0.494843 7.61042 0 7 0C6.38958 0 5.89474 0.494843 5.89474 1.10526C5.89474 1.71568 6.38958 2.21053 7 2.21053Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Left dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.25s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M2.21053 7C2.21053 7.61042 1.71568 8.10526 1.10526 8.10526C0.494843 8.10526 0 7.61042 0 7C0 6.38958 0.494843 5.89474 1.10526 5.89474C1.71568 5.89474 2.21053 6.38958 2.21053 7Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Right dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.5s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M14 7C14 7.61042 13.5052 8.10526 12.8947 8.10526C12.2843 8.10526 11.7895 7.61042 11.7895 7C11.7895 6.38958 12.2843 5.89474 12.8947 5.89474C13.5052 5.89474 14 6.38958 14 7Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Bottom dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.75s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8.10526 12.8947C8.10526 13.5052 7.61041 14 6.99999 14C6.38957 14 5.89473 13.5052 5.89473 12.8947C5.89473 12.2843 6.38957 11.7895 6.99999 11.7895C7.61041 11.7895 8.10526 12.2843 8.10526 12.8947Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Top-left dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.125s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M2.05039 3.61349C2.48203 4.04513 3.18184 4.04513 3.61347 3.61349C4.0451 3.18186 4.0451 2.48205 3.61347 2.05042C3.18184 1.61878 2.48203 1.61878 2.05039 2.05042C1.61876 2.48205 1.61876 3.18186 2.05039 3.61349Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Bottom-right dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.625s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M11.9496 11.9496C11.518 12.3812 10.8182 12.3812 10.3865 11.9496C9.9549 11.5179 9.9549 10.8181 10.3865 10.3865C10.8182 9.95485 11.518 9.95485 11.9496 10.3865C12.3812 10.8181 12.3812 11.5179 11.9496 11.9496Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Bottom-left dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.875s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M2.05039 11.9496C2.48203 12.3812 3.18184 12.3812 3.61347 11.9496C4.0451 11.5179 4.0451 10.8181 3.61347 10.3865C3.18184 9.95485 2.48203 9.95485 2.05039 10.3865C1.61876 10.8181 1.61876 11.5179 2.05039 11.9496Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
<!-- Top-right dot -->
|
||||||
|
<path
|
||||||
|
class="dot-animation"
|
||||||
|
style="animation-delay: 0.375s"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M11.9496 3.61349C11.518 4.04513 10.8182 4.04513 10.3865 3.61349C9.9549 3.18186 9.9549 2.48205 10.3865 2.05042C10.8182 1.61878 11.518 1.61878 11.9496 2.05042C12.3812 2.48205 12.3812 3.18186 11.9496 3.61349Z"
|
||||||
|
:fill="color"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_776_9582">
|
||||||
|
<rect width="14" height="14" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||||
|
|
||||||
|
const { size = 24, duration = '2s' } = defineProps<{
|
||||||
|
size?: number
|
||||||
|
duration?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const colorPaletteStore = useColorPaletteStore()
|
||||||
|
|
||||||
|
const color = computed(() =>
|
||||||
|
colorPaletteStore.completedActivePalette.light_theme ? '#2C2B30' : '#D4D4D4'
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dot-animation {
|
||||||
|
animation: dot-pulse 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dot-pulse {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,41 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="w-full px-6 py-4 shadow-lg flex items-center justify-between"
|
class="w-full px-6 py-2 shadow-lg flex items-center justify-between"
|
||||||
:class="{
|
:class="{
|
||||||
'rounded-t-none': progressDialogContent.isExpanded,
|
'rounded-t-none': progressDialogContent.isExpanded,
|
||||||
'rounded-lg': !progressDialogContent.isExpanded
|
'rounded-lg': !progressDialogContent.isExpanded
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="justify-center text-sm font-bold leading-none">
|
<div class="flex items-center text-base leading-none">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<template v-if="isInProgress">
|
<template v-if="isInProgress">
|
||||||
<i class="pi pi-spin pi-spinner mr-2 text-3xl" />
|
<DotSpinner duration="1s" class="mr-2" />
|
||||||
|
<span>{{ currentTaskName }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="isRestartCompleted">
|
||||||
|
<span class="mr-2">🎉</span>
|
||||||
<span>{{ currentTaskName }}</span>
|
<span>{{ currentTaskName }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<i class="pi pi-check-circle mr-2 text-green-500" />
|
<span class="mr-2">✅</span>
|
||||||
<span class="leading-none">{{
|
<span>{{ $t('manager.restartToApplyChanges') }}</span>
|
||||||
$t('manager.restartToApplyChanges')
|
|
||||||
}}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span v-if="isInProgress" class="text-xs font-bold text-neutral-600">
|
<span v-if="isInProgress" class="text-sm text-neutral-700">
|
||||||
{{ comfyManagerStore.uncompletedCount }} {{ $t('g.progressCountOf') }}
|
{{ completedTasksCount }} {{ $t('g.progressCountOf') }}
|
||||||
{{ comfyManagerStore.taskLogs.length }}
|
{{ comfyManagerStore.taskLogs.length }}
|
||||||
</span>
|
</span>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
v-if="!isInProgress"
|
v-if="!isInProgress && !isRestartCompleted"
|
||||||
rounded
|
rounded
|
||||||
outlined
|
outlined
|
||||||
class="px-4 py-2 rounded-md mr-4"
|
class="mr-4 rounded-md border-2 px-3 text-neutral-600 border-neutral-900 hover:bg-neutral-100 !dark-theme:bg-transparent dark-theme:text-white dark-theme:border-white dark-theme:hover:bg-neutral-800"
|
||||||
@click="handleRestart"
|
@click="handleRestart"
|
||||||
>
|
>
|
||||||
{{ $t('g.restart') }}
|
{{ $t('manager.applyChanges') }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
v-else-if="!isRestartCompleted"
|
||||||
:icon="
|
:icon="
|
||||||
progressDialogContent.isExpanded
|
progressDialogContent.isExpanded
|
||||||
? 'pi pi-chevron-up'
|
? 'pi pi-chevron-up'
|
||||||
@@ -44,6 +47,7 @@
|
|||||||
text
|
text
|
||||||
rounded
|
rounded
|
||||||
size="small"
|
size="small"
|
||||||
|
class="font-bold"
|
||||||
severity="secondary"
|
severity="secondary"
|
||||||
:aria-label="progressDialogContent.isExpanded ? 'Collapse' : 'Expand'"
|
:aria-label="progressDialogContent.isExpanded ? 'Collapse' : 'Expand'"
|
||||||
@click.stop="progressDialogContent.toggle"
|
@click.stop="progressDialogContent.toggle"
|
||||||
@@ -53,6 +57,7 @@
|
|||||||
text
|
text
|
||||||
rounded
|
rounded
|
||||||
size="small"
|
size="small"
|
||||||
|
class="font-bold"
|
||||||
severity="secondary"
|
severity="secondary"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
@click.stop="closeDialog"
|
@click.stop="closeDialog"
|
||||||
@@ -65,9 +70,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useEventListener } from '@vueuse/core'
|
import { useEventListener } from '@vueuse/core'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import DotSpinner from '@/components/common/DotSpinner.vue'
|
||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import { useComfyManagerService } from '@/services/comfyManagerService'
|
import { useComfyManagerService } from '@/services/comfyManagerService'
|
||||||
import { useWorkflowService } from '@/services/workflowService'
|
import { useWorkflowService } from '@/services/workflowService'
|
||||||
@@ -77,41 +83,93 @@ import {
|
|||||||
} from '@/stores/comfyManagerStore'
|
} from '@/stores/comfyManagerStore'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
|
import { useSettingStore } from '@/stores/settingStore'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const progressDialogContent = useManagerProgressDialogStore()
|
const progressDialogContent = useManagerProgressDialogStore()
|
||||||
const comfyManagerStore = useComfyManagerStore()
|
const comfyManagerStore = useComfyManagerStore()
|
||||||
|
const settingStore = useSettingStore()
|
||||||
|
|
||||||
const isInProgress = computed(() => comfyManagerStore.isProcessingTasks)
|
// State management for restart process
|
||||||
|
const isRestarting = ref<boolean>(false)
|
||||||
|
const isRestartCompleted = ref<boolean>(false)
|
||||||
|
|
||||||
|
const isInProgress = computed(
|
||||||
|
() => comfyManagerStore.isProcessingTasks || isRestarting.value
|
||||||
|
)
|
||||||
|
|
||||||
|
const completedTasksCount = computed(() => {
|
||||||
|
return (
|
||||||
|
comfyManagerStore.succeededTasksIds.length +
|
||||||
|
comfyManagerStore.failedTasksIds.length
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
dialogStore.closeDialog({ key: 'global-manager-progress-dialog' })
|
dialogStore.closeDialog({ key: 'global-manager-progress-dialog' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fallbackTaskName = t('g.installing')
|
const fallbackTaskName = t('manager.installingDependencies')
|
||||||
const currentTaskName = computed(() => {
|
const currentTaskName = computed(() => {
|
||||||
|
if (isRestarting.value) {
|
||||||
|
return t('manager.restartingBackend')
|
||||||
|
}
|
||||||
|
if (isRestartCompleted.value) {
|
||||||
|
return t('manager.extensionsSuccessfullyInstalled')
|
||||||
|
}
|
||||||
if (!comfyManagerStore.taskLogs.length) return fallbackTaskName
|
if (!comfyManagerStore.taskLogs.length) return fallbackTaskName
|
||||||
const task = comfyManagerStore.taskLogs.at(-1)
|
const task = comfyManagerStore.taskLogs.at(-1)
|
||||||
return task?.taskName ?? fallbackTaskName
|
return task?.taskName ?? fallbackTaskName
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleRestart = async () => {
|
const handleRestart = async () => {
|
||||||
const onReconnect = async () => {
|
// Store original toast setting value
|
||||||
// Refresh manager state
|
const originalToastSetting = settingStore.get(
|
||||||
|
'Comfy.Toast.DisableReconnectingToast'
|
||||||
|
)
|
||||||
|
|
||||||
comfyManagerStore.clearLogs()
|
try {
|
||||||
comfyManagerStore.setStale()
|
await settingStore.set('Comfy.Toast.DisableReconnectingToast', true)
|
||||||
|
|
||||||
// Refresh node definitions
|
isRestarting.value = true
|
||||||
await useCommandStore().execute('Comfy.RefreshNodeDefinitions')
|
|
||||||
|
|
||||||
// Reload workflow
|
const onReconnect = async () => {
|
||||||
await useWorkflowService().reloadCurrentWorkflow()
|
try {
|
||||||
|
comfyManagerStore.setStale()
|
||||||
|
|
||||||
|
await useCommandStore().execute('Comfy.RefreshNodeDefinitions')
|
||||||
|
|
||||||
|
await useWorkflowService().reloadCurrentWorkflow()
|
||||||
|
} finally {
|
||||||
|
await settingStore.set(
|
||||||
|
'Comfy.Toast.DisableReconnectingToast',
|
||||||
|
originalToastSetting
|
||||||
|
)
|
||||||
|
|
||||||
|
isRestarting.value = false
|
||||||
|
isRestartCompleted.value = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
closeDialog()
|
||||||
|
comfyManagerStore.resetTaskState()
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEventListener(api, 'reconnected', onReconnect, { once: true })
|
||||||
|
|
||||||
|
await useComfyManagerService().rebootComfyUI()
|
||||||
|
} catch (error) {
|
||||||
|
// If restart fails, restore settings and reset state
|
||||||
|
await settingStore.set(
|
||||||
|
'Comfy.Toast.DisableReconnectingToast',
|
||||||
|
originalToastSetting
|
||||||
|
)
|
||||||
|
isRestarting.value = false
|
||||||
|
isRestartCompleted.value = false
|
||||||
|
closeDialog() // Close dialog on error
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
useEventListener(api, 'reconnected', onReconnect, { once: true })
|
|
||||||
|
|
||||||
await useComfyManagerService().rebootComfyUI()
|
|
||||||
closeDialog()
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -160,11 +160,15 @@
|
|||||||
"dependencies": "Dependencies",
|
"dependencies": "Dependencies",
|
||||||
"inWorkflow": "In Workflow",
|
"inWorkflow": "In Workflow",
|
||||||
"infoPanelEmpty": "Click an item to see the info",
|
"infoPanelEmpty": "Click an item to see the info",
|
||||||
"restartToApplyChanges": "To apply changes, please restart ComfyUI",
|
"applyChanges": "Apply Changes",
|
||||||
|
"restartToApplyChanges": "Click 'Apply Changes' to finish setup",
|
||||||
|
"restartingBackend": "Restarting backend to apply changes...",
|
||||||
|
"extensionsSuccessfullyInstalled": "Extension(s) successfully installed and are ready to use!",
|
||||||
"loadingVersions": "Loading versions...",
|
"loadingVersions": "Loading versions...",
|
||||||
"selectVersion": "Select Version",
|
"selectVersion": "Select Version",
|
||||||
"downloads": "Downloads",
|
"downloads": "Downloads",
|
||||||
"repository": "Repository",
|
"repository": "Repository",
|
||||||
|
"installingDependencies": "Installing dependencies...",
|
||||||
"uninstall": "Uninstall",
|
"uninstall": "Uninstall",
|
||||||
"uninstalling": "Uninstalling",
|
"uninstalling": "Uninstalling",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
|
|||||||
@@ -299,12 +299,31 @@ export const useComfyManagerStore = defineStore('comfyManager', () => {
|
|||||||
taskLogs.value = []
|
taskLogs.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resetTaskState = () => {
|
||||||
|
// Clear all task-related reactive state for fresh start after restart
|
||||||
|
taskLogs.value = []
|
||||||
|
taskHistory.value = {}
|
||||||
|
succeededTasksIds.value = []
|
||||||
|
failedTasksIds.value = []
|
||||||
|
succeededTasksLogs.value = []
|
||||||
|
failedTasksLogs.value = []
|
||||||
|
|
||||||
|
// Reset task queue to initial state
|
||||||
|
taskQueue.value = {
|
||||||
|
history: {},
|
||||||
|
running_queue: [],
|
||||||
|
pending_queue: [],
|
||||||
|
installed_packs: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Manager state
|
// Manager state
|
||||||
isLoading: managerService.isLoading,
|
isLoading: managerService.isLoading,
|
||||||
error: managerService.error,
|
error: managerService.error,
|
||||||
taskLogs,
|
taskLogs,
|
||||||
clearLogs,
|
clearLogs,
|
||||||
|
resetTaskState,
|
||||||
setStale,
|
setStale,
|
||||||
|
|
||||||
// Installed packs state
|
// Installed packs state
|
||||||
|
|||||||
Reference in New Issue
Block a user