mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-19 22:09:37 +00:00
Compare commits
3 Commits
remove-que
...
task-runne
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
628b44051b | ||
|
|
a7a5e3cf67 | ||
|
|
64e218a9f3 |
@@ -5,12 +5,12 @@
|
|||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
class="max-w-48 relative h-full overflow-hidden"
|
class="max-w-48 relative h-full overflow-hidden"
|
||||||
:class="{ 'opacity-65': state.state !== 'error' }"
|
:class="{ 'opacity-65': runner.state !== 'error' }"
|
||||||
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
|
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<i
|
<i
|
||||||
v-if="state.state === 'error'"
|
v-if="runner.state === 'error'"
|
||||||
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
|
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
|
||||||
style="font-size: 10rem"
|
style="font-size: 10rem"
|
||||||
/>
|
/>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<i
|
<i
|
||||||
v-if="!isLoading && state.state === 'OK'"
|
v-if="!isLoading && runner.state === 'OK'"
|
||||||
class="task-card-ok pi pi-check"
|
class="task-card-ok pi pi-check"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,7 +54,7 @@ import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes'
|
|||||||
import { useMinLoadingDurationRef } from '@/utils/refUtil'
|
import { useMinLoadingDurationRef } from '@/utils/refUtil'
|
||||||
|
|
||||||
const taskStore = useMaintenanceTaskStore()
|
const taskStore = useMaintenanceTaskStore()
|
||||||
const state = computed(() => taskStore.getState(props.task))
|
const runner = computed(() => taskStore.getRunner(props.task))
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -68,14 +68,14 @@ defineEmits<{
|
|||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
const description = computed(() =>
|
const description = computed(() =>
|
||||||
state.value.state === 'error'
|
runner.value.state === 'error'
|
||||||
? props.task.errorDescription ?? props.task.shortDescription
|
? props.task.errorDescription ?? props.task.shortDescription
|
||||||
: props.task.shortDescription
|
: props.task.shortDescription
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use a minimum run time to ensure tasks "feel" like they have run
|
// Use a minimum run time to ensure tasks "feel" like they have run
|
||||||
const reactiveLoading = computed(() => state.value.refreshing)
|
const reactiveLoading = computed(() => runner.value.refreshing)
|
||||||
const reactiveExecuting = computed(() => state.value.executing)
|
const reactiveExecuting = computed(() => runner.value.executing)
|
||||||
|
|
||||||
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
|
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
|
||||||
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)
|
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
<tr
|
<tr
|
||||||
class="border-neutral-700 border-solid border-y"
|
class="border-neutral-700 border-solid border-y"
|
||||||
:class="{
|
:class="{
|
||||||
'opacity-50': state.state === 'resolved',
|
'opacity-50': runner.resolved,
|
||||||
'opacity-75': isLoading && state.state !== 'resolved'
|
'opacity-75': isLoading && runner.resolved
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<td class="text-center w-16">
|
<td class="text-center w-16">
|
||||||
<TaskListStatusIcon :state="state.state" :loading="isLoading" />
|
<TaskListStatusIcon :state="runner.state" :loading="isLoading" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p class="inline-block">{{ task.name }}</p>
|
<p class="inline-block">{{ task.name }}</p>
|
||||||
@@ -51,7 +51,7 @@ import { useMinLoadingDurationRef } from '@/utils/refUtil'
|
|||||||
import TaskListStatusIcon from './TaskListStatusIcon.vue'
|
import TaskListStatusIcon from './TaskListStatusIcon.vue'
|
||||||
|
|
||||||
const taskStore = useMaintenanceTaskStore()
|
const taskStore = useMaintenanceTaskStore()
|
||||||
const state = computed(() => taskStore.getState(props.task))
|
const runner = computed(() => taskStore.getRunner(props.task))
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -65,14 +65,14 @@ defineEmits<{
|
|||||||
|
|
||||||
// Binding
|
// Binding
|
||||||
const severity = computed<VueSeverity>(() =>
|
const severity = computed<VueSeverity>(() =>
|
||||||
state.value.state === 'error' || state.value.state === 'warning'
|
runner.value.state === 'error' || runner.value.state === 'warning'
|
||||||
? 'primary'
|
? 'primary'
|
||||||
: 'secondary'
|
: 'secondary'
|
||||||
)
|
)
|
||||||
|
|
||||||
// Use a minimum run time to ensure tasks "feel" like they have run
|
// Use a minimum run time to ensure tasks "feel" like they have run
|
||||||
const reactiveLoading = computed(() => state.value.refreshing)
|
const reactiveLoading = computed(() => runner.value.refreshing)
|
||||||
const reactiveExecuting = computed(() => state.value.executing)
|
const reactiveExecuting = computed(() => runner.value.executing)
|
||||||
|
|
||||||
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
|
const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
|
||||||
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)
|
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import ProgressSpinner from 'primevue/progressspinner'
|
|||||||
import { MaybeRef, computed } from 'vue'
|
import { MaybeRef, computed } from 'vue'
|
||||||
|
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
|
import { MaintenanceTaskState } from '@/stores/maintenanceTaskStore'
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
const tooltip = computed(() => {
|
const tooltip = computed(() => {
|
||||||
@@ -38,7 +39,7 @@ const cssClasses = computed(() => {
|
|||||||
|
|
||||||
// Model
|
// Model
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
state: 'warning' | 'error' | 'resolved' | 'OK' | 'skipped' | undefined
|
state?: MaintenanceTaskState
|
||||||
loading?: MaybeRef<boolean>
|
loading?: MaybeRef<boolean>
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,12 +3,77 @@ import { defineStore } from 'pinia'
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import { DESKTOP_MAINTENANCE_TASKS } from '@/constants/desktopMaintenanceTasks'
|
import { DESKTOP_MAINTENANCE_TASKS } from '@/constants/desktopMaintenanceTasks'
|
||||||
import type {
|
import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes'
|
||||||
MaintenanceTask,
|
|
||||||
MaintenanceTaskState
|
|
||||||
} from '@/types/desktop/maintenanceTypes'
|
|
||||||
import { electronAPI } from '@/utils/envUtil'
|
import { electronAPI } from '@/utils/envUtil'
|
||||||
|
|
||||||
|
/** State of a maintenance task, managed by the maintenance task store. */
|
||||||
|
export type MaintenanceTaskState = 'warning' | 'error' | 'OK' | 'skipped'
|
||||||
|
|
||||||
|
// Type not exported by API
|
||||||
|
type ValidationState = InstallValidation['basePath']
|
||||||
|
// Add index to API type
|
||||||
|
type IndexedUpdate = InstallValidation & Record<string, ValidationState>
|
||||||
|
|
||||||
|
/** State of a maintenance task, managed by the maintenance task store. */
|
||||||
|
export class MaintenanceTaskRunner {
|
||||||
|
constructor(readonly task: MaintenanceTask) {}
|
||||||
|
|
||||||
|
private _state?: MaintenanceTaskState
|
||||||
|
/** The current state of the task. Setter also controls {@link resolved} as a side-effect. */
|
||||||
|
get state() {
|
||||||
|
return this._state
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Updates the task state and {@link resolved} status. */
|
||||||
|
setState(value: MaintenanceTaskState) {
|
||||||
|
// Mark resolved
|
||||||
|
if (this._state === 'error' && value === 'OK') this.resolved = true
|
||||||
|
// Mark unresolved (if previously resolved)
|
||||||
|
if (value === 'error') this.resolved &&= false
|
||||||
|
|
||||||
|
this._state = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/** `true` if the task has been resolved (was `error`, now `OK`). This is a side-effect of the {@link state} setter. */
|
||||||
|
resolved?: boolean
|
||||||
|
|
||||||
|
/** Whether the task state is currently being refreshed. */
|
||||||
|
refreshing?: boolean
|
||||||
|
/** Whether the task is currently running. */
|
||||||
|
executing?: boolean
|
||||||
|
/** The error message that occurred when the task failed. */
|
||||||
|
error?: string
|
||||||
|
|
||||||
|
update(update: IndexedUpdate) {
|
||||||
|
const state = update[this.task.id]
|
||||||
|
|
||||||
|
this.refreshing = state === undefined
|
||||||
|
if (state) this.setState(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
finaliseUpdate(update: IndexedUpdate) {
|
||||||
|
this.refreshing = false
|
||||||
|
this.setState(update[this.task.id] ?? 'skipped')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wraps the execution of a maintenance task, updating state and rethrowing errors. */
|
||||||
|
async execute(task: MaintenanceTask) {
|
||||||
|
try {
|
||||||
|
this.executing = true
|
||||||
|
const success = await task.execute()
|
||||||
|
if (!success) return false
|
||||||
|
|
||||||
|
this.error = undefined
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
this.error = (error as Error)?.message
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
this.executing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User-initiated maintenance tasks. Currently only used by the desktop app maintenance view.
|
* User-initiated maintenance tasks. Currently only used by the desktop app maintenance view.
|
||||||
*
|
*
|
||||||
@@ -24,78 +89,46 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
|
|||||||
const isRunningTerminalCommand = computed(() =>
|
const isRunningTerminalCommand = computed(() =>
|
||||||
tasks.value
|
tasks.value
|
||||||
.filter((task) => task.usesTerminal)
|
.filter((task) => task.usesTerminal)
|
||||||
.some((task) => getState(task)?.executing)
|
.some((task) => getRunner(task)?.executing)
|
||||||
)
|
)
|
||||||
const isRunningInstallationFix = computed(() =>
|
const isRunningInstallationFix = computed(() =>
|
||||||
tasks.value
|
tasks.value
|
||||||
.filter((task) => task.isInstallationFix)
|
.filter((task) => task.isInstallationFix)
|
||||||
.some((task) => getState(task)?.executing)
|
.some((task) => getRunner(task)?.executing)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Task list
|
// Task list
|
||||||
const tasks = ref(DESKTOP_MAINTENANCE_TASKS)
|
const tasks = ref(DESKTOP_MAINTENANCE_TASKS)
|
||||||
|
|
||||||
const taskStates = ref(
|
const taskStates = ref(
|
||||||
new Map<MaintenanceTask['id'], MaintenanceTaskState>(
|
new Map<MaintenanceTask['id'], MaintenanceTaskRunner>(
|
||||||
DESKTOP_MAINTENANCE_TASKS.map((x) => [x.id, {}])
|
DESKTOP_MAINTENANCE_TASKS.map((x) => [x.id, new MaintenanceTaskRunner(x)])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
/** True if any tasks are in an error state. */
|
/** True if any tasks are in an error state. */
|
||||||
const anyErrors = computed(() =>
|
const anyErrors = computed(() =>
|
||||||
tasks.value.some((task) => getState(task).state === 'error')
|
tasks.value.some((task) => getRunner(task).state === 'error')
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Wraps the execution of a maintenance task, updating state and rethrowing errors. */
|
|
||||||
const execute = async (task: MaintenanceTask) => {
|
|
||||||
const state = getState(task)
|
|
||||||
|
|
||||||
try {
|
|
||||||
state.executing = true
|
|
||||||
const success = await task.execute()
|
|
||||||
if (!success) return false
|
|
||||||
|
|
||||||
state.error = undefined
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
state.error = (error as Error)?.message
|
|
||||||
throw error
|
|
||||||
} finally {
|
|
||||||
state.executing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the matching state object for a task.
|
* Returns the matching state object for a task.
|
||||||
* @param task Task to get the matching state object for
|
* @param task Task to get the matching state object for
|
||||||
* @returns The state object for this task
|
* @returns The state object for this task
|
||||||
*/
|
*/
|
||||||
const getState = (task: MaintenanceTask) => taskStates.value.get(task.id)!
|
const getRunner = (task: MaintenanceTask) => taskStates.value.get(task.id)!
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the task list with the latest validation state.
|
* Updates the task list with the latest validation state.
|
||||||
* @param validationUpdate Update details passed in by electron
|
* @param validationUpdate Update details passed in by electron
|
||||||
*/
|
*/
|
||||||
const processUpdate = (validationUpdate: InstallValidation) => {
|
const processUpdate = (validationUpdate: InstallValidation) => {
|
||||||
// Type not exported by API
|
|
||||||
type ValidationState = InstallValidation['basePath']
|
|
||||||
// Add index to API type
|
|
||||||
type IndexedUpdate = InstallValidation & Record<string, ValidationState>
|
|
||||||
|
|
||||||
const update = validationUpdate as IndexedUpdate
|
const update = validationUpdate as IndexedUpdate
|
||||||
isRefreshing.value = true
|
isRefreshing.value = true
|
||||||
|
|
||||||
// Update each task state
|
// Update each task state
|
||||||
for (const task of tasks.value) {
|
for (const task of tasks.value) {
|
||||||
const state = getState(task)
|
getRunner(task).update(update)
|
||||||
|
|
||||||
state.refreshing = update[task.id] === undefined
|
|
||||||
// Mark resolved
|
|
||||||
if (state.state === 'error' && update[task.id] === 'OK')
|
|
||||||
state.state = 'resolved'
|
|
||||||
if (update[task.id] === 'OK' && state.state === 'resolved') continue
|
|
||||||
|
|
||||||
if (update[task.id]) state.state = update[task.id]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final update
|
// Final update
|
||||||
@@ -103,11 +136,7 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
|
|||||||
isRefreshing.value = false
|
isRefreshing.value = false
|
||||||
|
|
||||||
for (const task of tasks.value) {
|
for (const task of tasks.value) {
|
||||||
const state = getState(task)
|
getRunner(task).finaliseUpdate(update)
|
||||||
state.refreshing = false
|
|
||||||
if (state.state === 'resolved') continue
|
|
||||||
|
|
||||||
state.state = update[task.id] ?? 'skipped'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,8 +144,7 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
|
|||||||
/** Clears the resolved status of tasks (when changing filters) */
|
/** Clears the resolved status of tasks (when changing filters) */
|
||||||
const clearResolved = () => {
|
const clearResolved = () => {
|
||||||
for (const task of tasks.value) {
|
for (const task of tasks.value) {
|
||||||
const state = getState(task)
|
getRunner(task).resolved &&= false
|
||||||
if (state?.state === 'resolved') state.state = 'OK'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,13 +155,17 @@ export const useMaintenanceTaskStore = defineStore('maintenanceTask', () => {
|
|||||||
await electron.Validation.validateInstallation(processUpdate)
|
await electron.Validation.validateInstallation(processUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const execute = async (task: MaintenanceTask) => {
|
||||||
|
return getRunner(task).execute(task)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tasks,
|
tasks,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
isRunningTerminalCommand,
|
isRunningTerminalCommand,
|
||||||
isRunningInstallationFix,
|
isRunningInstallationFix,
|
||||||
execute,
|
execute,
|
||||||
getState,
|
getRunner,
|
||||||
processUpdate,
|
processUpdate,
|
||||||
clearResolved,
|
clearResolved,
|
||||||
/** True if any tasks are in an error state. */
|
/** True if any tasks are in an error state. */
|
||||||
|
|||||||
@@ -39,18 +39,6 @@ export interface MaintenanceTask {
|
|||||||
isInstallationFix?: boolean
|
isInstallationFix?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** State of a maintenance task, managed by the maintenance task store. */
|
|
||||||
export interface MaintenanceTaskState {
|
|
||||||
/** The current state of the task. */
|
|
||||||
state?: 'warning' | 'error' | 'resolved' | 'OK' | 'skipped'
|
|
||||||
/** Whether the task state is currently being refreshed. */
|
|
||||||
refreshing?: boolean
|
|
||||||
/** Whether the task is currently running. */
|
|
||||||
executing?: boolean
|
|
||||||
/** The error message that occurred when the task failed. */
|
|
||||||
error?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The filter options for the maintenance task list. */
|
/** The filter options for the maintenance task list. */
|
||||||
export interface MaintenanceFilter {
|
export interface MaintenanceFilter {
|
||||||
/** CSS classes used for the filter button icon, e.g. 'pi pi-cross' */
|
/** CSS classes used for the filter button icon, e.g. 'pi pi-cross' */
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ const displayAsList = ref(PrimeIcons.TH_LARGE)
|
|||||||
|
|
||||||
const errorFilter = computed(() =>
|
const errorFilter = computed(() =>
|
||||||
taskStore.tasks.filter((x) => {
|
taskStore.tasks.filter((x) => {
|
||||||
const { state } = taskStore.getState(x)
|
const { state, resolved } = taskStore.getRunner(x)
|
||||||
return state === 'error' || state === 'resolved'
|
return state === 'error' || resolved
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user