Skip to content

Commit 98b0d84

Browse files
benceruleanluMyestery
authored andcommitted
feat: active jobs context menu (#8216)
Add a right-click context menu to the active jobs button that clears the queue and matches the Queue Progress modal styling. Per [design](https://www.figma.com/design/LVilZgHGk5RwWOkVN6yCEK/Queue-Progress-Modal?node-id=3407-41345&m=dev) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8216-feat-active-jobs-context-menu-2ef6d73d365081e68386cf0f7c3c23f2) by [Unito](https://www.unito.io)
1 parent bea8138 commit 98b0d84

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

src/components/TopMenuSection.test.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createTestingPinia } from '@pinia/testing'
22
import { mount } from '@vue/test-utils'
3+
import type { MenuItem } from 'primevue/menuitem'
34
import { beforeEach, describe, expect, it, vi } from 'vitest'
45
import { computed, nextTick } from 'vue'
56
import { createI18n } from 'vue-i18n'
@@ -42,7 +43,8 @@ function createWrapper() {
4243
queueProgressOverlay: {
4344
viewJobHistory: 'View job history',
4445
expandCollapsedQueue: 'Expand collapsed queue',
45-
activeJobsShort: '{count} active | {count} active'
46+
activeJobsShort: '{count} active | {count} active',
47+
clearQueueTooltip: 'Clear queue'
4648
}
4749
}
4850
}
@@ -56,7 +58,12 @@ function createWrapper() {
5658
SubgraphBreadcrumb: true,
5759
QueueProgressOverlay: true,
5860
CurrentUserButton: true,
59-
LoginButton: true
61+
LoginButton: true,
62+
ContextMenu: {
63+
name: 'ContextMenu',
64+
props: ['model'],
65+
template: '<div />'
66+
}
6067
},
6168
directives: {
6269
tooltip: () => {}
@@ -134,4 +141,24 @@ describe('TopMenuSection', () => {
134141
const queueButton = wrapper.find('[data-testid="queue-overlay-toggle"]')
135142
expect(queueButton.text()).toContain('3 active')
136143
})
144+
145+
it('disables the clear queue context menu item when no queued jobs exist', () => {
146+
const wrapper = createWrapper()
147+
const menu = wrapper.findComponent({ name: 'ContextMenu' })
148+
const model = menu.props('model') as MenuItem[]
149+
expect(model[0]?.label).toBe('Clear queue')
150+
expect(model[0]?.disabled).toBe(true)
151+
})
152+
153+
it('enables the clear queue context menu item when queued jobs exist', async () => {
154+
const wrapper = createWrapper()
155+
const queueStore = useQueueStore()
156+
queueStore.pendingTasks = [createTask('pending-1', 'pending')]
157+
158+
await nextTick()
159+
160+
const menu = wrapper.findComponent({ name: 'ContextMenu' })
161+
const model = menu.props('model') as MenuItem[]
162+
expect(model[0]?.disabled).toBe(false)
163+
})
137164
})

src/components/TopMenuSection.vue

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
class="px-3"
5050
data-testid="queue-overlay-toggle"
5151
@click="toggleQueueOverlay"
52+
@contextmenu.stop.prevent="showQueueContextMenu"
5253
>
5354
<span class="text-sm font-normal tabular-nums">
5455
{{ activeJobsLabel }}
@@ -57,6 +58,7 @@
5758
{{ t('sideToolbar.queueProgressOverlay.expandCollapsedQueue') }}
5859
</span>
5960
</Button>
61+
<ContextMenu ref="queueContextMenu" :model="queueContextMenuItems" />
6062
<CurrentUserButton
6163
v-if="isLoggedIn && !isIntegratedTabBar"
6264
class="shrink-0"
@@ -84,6 +86,8 @@
8486

8587
<script setup lang="ts">
8688
import { storeToRefs } from 'pinia'
89+
import ContextMenu from 'primevue/contextmenu'
90+
import type { MenuItem } from 'primevue/menuitem'
8791
import { computed, onMounted, ref } from 'vue'
8892
import { useI18n } from 'vue-i18n'
8993
@@ -101,6 +105,7 @@ import { useSettingStore } from '@/platform/settings/settingStore'
101105
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
102106
import { app } from '@/scripts/app'
103107
import { useCommandStore } from '@/stores/commandStore'
108+
import { useExecutionStore } from '@/stores/executionStore'
104109
import { useQueueStore, useQueueUIStore } from '@/stores/queueStore'
105110
import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore'
106111
import { useWorkspaceStore } from '@/stores/workspaceStore'
@@ -119,6 +124,7 @@ const { t, n } = useI18n()
119124
const { toastErrorHandler } = useErrorHandling()
120125
const commandStore = useCommandStore()
121126
const queueStore = useQueueStore()
127+
const executionStore = useExecutionStore()
122128
const queueUIStore = useQueueUIStore()
123129
const { activeJobsCount } = storeToRefs(queueStore)
124130
const { isOverlayExpanded: isQueueOverlayExpanded } = storeToRefs(queueUIStore)
@@ -144,6 +150,18 @@ const queueHistoryTooltipConfig = computed(() =>
144150
const customNodesManagerTooltipConfig = computed(() =>
145151
buildTooltipConfig(t('menu.customNodesManager'))
146152
)
153+
const queueContextMenu = ref<InstanceType<typeof ContextMenu> | null>(null)
154+
const queueContextMenuItems = computed<MenuItem[]>(() => [
155+
{
156+
label: t('sideToolbar.queueProgressOverlay.clearQueueTooltip'),
157+
icon: 'icon-[lucide--list-x] text-destructive-background',
158+
class: '*:text-destructive-background',
159+
disabled: queueStore.pendingTasks.length === 0,
160+
command: () => {
161+
void handleClearQueue()
162+
}
163+
}
164+
])
147165
148166
// Use either release red dot or conflict red dot
149167
const shouldShowRedDot = computed((): boolean => {
@@ -170,6 +188,19 @@ const toggleQueueOverlay = () => {
170188
commandStore.execute('Comfy.Queue.ToggleOverlay')
171189
}
172190
191+
const showQueueContextMenu = (event: MouseEvent) => {
192+
queueContextMenu.value?.show(event)
193+
}
194+
195+
const handleClearQueue = async () => {
196+
const pendingPromptIds = queueStore.pendingTasks
197+
.map((task) => task.promptId)
198+
.filter((id): id is string => typeof id === 'string' && id.length > 0)
199+
200+
await commandStore.execute('Comfy.ClearPendingTasks')
201+
executionStore.clearInitializationByPromptIds(pendingPromptIds)
202+
}
203+
173204
const openCustomNodeManager = async () => {
174205
try {
175206
await managerState.openManager({

0 commit comments

Comments
 (0)