mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
Display a separate indicator for pending jobs in app mode (#10382)
Previously, a pending, but not yet running job was displayed as an empty skeleton. It is instead updated to display an animated `lucide--loader` icon. <img width="1544" height="1347" alt="image" src="https://github.com/user-attachments/assets/4f82185c-97dc-44af-8ea1-a012fb992fe2" /> As this shifts the activeQueueItem component to display even when only the single pending item is in the queue, the count badge is also update to not display when this is the case. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-10382-Display-a-separate-indicator-for-pending-jobs-in-app-mode-32a6d73d36508189b3a9ff4b84993a98) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -14,6 +14,11 @@ const meta: Meta<typeof Loader> = {
|
||||
control: 'select',
|
||||
options: ['sm', 'md', 'lg'],
|
||||
description: 'Spinner size: sm (16px), md (32px), lg (48px)'
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['loader', 'loader-circle'],
|
||||
description: 'The type of loader displayed'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ useEventListener(document.body, 'keydown', (e: KeyboardEvent) => {
|
||||
class="flex h-15 shrink-0 items-start gap-0.5"
|
||||
>
|
||||
<OutputHistoryActiveQueueItem
|
||||
v-if="queueCount > 1"
|
||||
v-if="queueCount > 1 || queueStore.pendingTasks.length"
|
||||
class="mr-3"
|
||||
:queue-count="queueCount"
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import { setActivePinia } from 'pinia'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import OutputHistoryActiveQueueItem from './OutputHistoryActiveQueueItem.vue'
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({ t: (key: string) => key })
|
||||
}))
|
||||
const i18n = createI18n({ legacy: false, locale: 'en' })
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
|
||||
vi.mock('@/stores/commandStore', () => ({
|
||||
useCommandStore: () => ({
|
||||
@@ -15,16 +17,16 @@ vi.mock('@/stores/commandStore', () => ({
|
||||
|
||||
function mountComponent(queueCount: number) {
|
||||
return shallowMount(OutputHistoryActiveQueueItem, {
|
||||
props: { queueCount }
|
||||
props: { queueCount },
|
||||
global: { plugins: [i18n] }
|
||||
})
|
||||
}
|
||||
|
||||
describe('OutputHistoryActiveQueueItem', () => {
|
||||
it('shows badge when queueCount is 1', () => {
|
||||
it('hides badge when queueCount is 1', () => {
|
||||
const wrapper = mountComponent(1)
|
||||
const badge = wrapper.find('[aria-hidden="true"]')
|
||||
expect(badge.exists()).toBe(true)
|
||||
expect(badge.text()).toBe('1')
|
||||
expect(badge.exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows badge with correct count when queueCount is 3', () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import Loader from '@/components/loader/Loader.vue'
|
||||
import Popover from '@/components/ui/Popover.vue'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useQueueStore } from '@/stores/queueStore'
|
||||
|
||||
const { queueCount } = defineProps<{
|
||||
queueCount: number
|
||||
@@ -12,6 +13,7 @@ const { queueCount } = defineProps<{
|
||||
|
||||
const { t } = useI18n()
|
||||
const commandStore = useCommandStore()
|
||||
const queueStore = useQueueStore()
|
||||
|
||||
function clearQueue(close: () => void) {
|
||||
void commandStore.execute('Comfy.ClearPendingTasks')
|
||||
@@ -32,7 +34,13 @@ function clearQueue(close: () => void) {
|
||||
size="unset"
|
||||
class="flex size-10 items-center justify-center rounded-sm bg-secondary-background"
|
||||
>
|
||||
<Loader size="sm" class="text-muted-foreground" />
|
||||
<Loader
|
||||
:variant="
|
||||
queueStore.runningTasks.length ? 'loader-circle' : 'loader'
|
||||
"
|
||||
size="sm"
|
||||
class="text-muted-foreground"
|
||||
/>
|
||||
</Button>
|
||||
</template>
|
||||
<template #default="{ close }">
|
||||
@@ -48,7 +56,7 @@ function clearQueue(close: () => void) {
|
||||
</template>
|
||||
</Popover>
|
||||
<div
|
||||
v-if="queueCount > 0"
|
||||
v-if="queueCount > 1"
|
||||
aria-hidden="true"
|
||||
class="absolute top-0 right-0 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary-background text-xs text-text-primary"
|
||||
v-text="queueCount"
|
||||
|
||||
@@ -438,12 +438,12 @@ describe(useOutputHistory, () => {
|
||||
expect(mayBeActiveWorkflowPending.value).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true when a pending task matches the active workflow', () => {
|
||||
it('returns false when only pending tasks exist', () => {
|
||||
pendingTasksRef.value = [{ jobId: 'job-1' }]
|
||||
jobIdToPathRef.value = new Map([['job-1', 'workflows/test.json']])
|
||||
|
||||
const { mayBeActiveWorkflowPending } = useOutputHistory()
|
||||
expect(mayBeActiveWorkflowPending.value).toBe(true)
|
||||
expect(mayBeActiveWorkflowPending.value).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false when tasks belong to another workflow', () => {
|
||||
|
||||
@@ -43,10 +43,7 @@ export function useOutputHistory(): {
|
||||
|
||||
function hasActiveWorkflowJobs(): boolean {
|
||||
if (!workflowStore.activeWorkflow?.path) return false
|
||||
return (
|
||||
queueStore.runningTasks.some(matchesActiveWorkflow) ||
|
||||
queueStore.pendingTasks.some(matchesActiveWorkflow)
|
||||
)
|
||||
return queueStore.runningTasks.some(matchesActiveWorkflow)
|
||||
}
|
||||
|
||||
// True when there are queued/running jobs for the active workflow but no
|
||||
|
||||
Reference in New Issue
Block a user