From f495f074693f1b8aedac94d0ff09bc41e33ad2a9 Mon Sep 17 00:00:00 2001 From: Benjamin Lu Date: Thu, 26 Feb 2026 19:00:10 -0800 Subject: [PATCH] fix: move active jobs button into actionbar (#9211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Move the top menu `N active` queue button into `ComfyActionbar` so it stays attached to the actionbar when docked, dragged, or floating. ## Changes - Moved the `queue-overlay-toggle` button UI from `TopMenuSection.vue` into `ComfyActionbar.vue` - Moved queue button behavior and context menu handling (`toggle`, right-click clear queue, active count badge/label) into `ComfyActionbar.vue` - Removed now-unused queue button state/handlers/imports from `TopMenuSection.vue` image image ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9211-fix-move-active-jobs-button-into-actionbar-3126d73d365081ceb553c172db479e3b) by [Unito](https://www.unito.io) --- src/components/TopMenuSection.vue | 97 +-------------------- src/components/actionbar/ComfyActionbar.vue | 87 +++++++++++++++++- 2 files changed, 87 insertions(+), 97 deletions(-) diff --git a/src/components/TopMenuSection.vue b/src/components/TopMenuSection.vue index 2b94d5d509..fd14b11f0e 100644 --- a/src/components/TopMenuSection.vue +++ b/src/components/TopMenuSection.vue @@ -56,43 +56,6 @@ :queue-overlay-expanded="isQueueOverlayExpanded" @update:progress-target="updateProgressTarget" /> - - import { useLocalStorage } from '@vueuse/core' import { storeToRefs } from 'pinia' -import ContextMenu from 'primevue/contextmenu' -import type { MenuItem } from 'primevue/menuitem' import { computed, onMounted, ref } from 'vue' import { useI18n } from 'vue-i18n' import ComfyActionbar from '@/components/actionbar/ComfyActionbar.vue' import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue' -import StatusBadge from '@/components/common/StatusBadge.vue' import QueueInlineProgressSummary from '@/components/queue/QueueInlineProgressSummary.vue' import QueueNotificationBannerHost from '@/components/queue/QueueNotificationBannerHost.vue' import QueueProgressOverlay from '@/components/queue/QueueProgressOverlay.vue' @@ -169,12 +129,9 @@ import { useErrorHandling } from '@/composables/useErrorHandling' import { buildTooltipConfig } from '@/composables/useTooltipConfig' import { useSettingStore } from '@/platform/settings/settingStore' import { app } from '@/scripts/app' -import { useCommandStore } from '@/stores/commandStore' -import { useExecutionStore } from '@/stores/executionStore' import { useExecutionErrorStore } from '@/stores/executionErrorStore' -import { useQueueStore, useQueueUIStore } from '@/stores/queueStore' +import { useQueueUIStore } from '@/stores/queueStore' import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore' -import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore' import { useWorkspaceStore } from '@/stores/workspaceStore' import { isDesktop } from '@/platform/distribution/types' import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment' @@ -187,17 +144,11 @@ const workspaceStore = useWorkspaceStore() const rightSidePanelStore = useRightSidePanelStore() const managerState = useManagerState() const { isLoggedIn } = useCurrentUser() -const { t, n } = useI18n() +const { t } = useI18n() const { toastErrorHandler } = useErrorHandling() -const commandStore = useCommandStore() -const queueStore = useQueueStore() -const executionStore = useExecutionStore() const executionErrorStore = useExecutionErrorStore() const queueUIStore = useQueueUIStore() -const sidebarTabStore = useSidebarTabStore() -const { activeJobsCount } = storeToRefs(queueStore) const { isOverlayExpanded: isQueueOverlayExpanded } = storeToRefs(queueUIStore) -const { activeSidebarTabId } = storeToRefs(sidebarTabStore) const { shouldShowRedDot: shouldShowConflictRedDot } = useConflictAcknowledgment() const isTopMenuHovered = ref(false) @@ -210,14 +161,6 @@ const isActionbarEnabled = computed( const isActionbarFloating = computed( () => isActionbarEnabled.value && !isActionbarDocked.value ) -const activeJobsLabel = computed(() => { - const count = activeJobsCount.value - return t( - 'sideToolbar.queueProgressOverlay.activeJobsShort', - { count: n(count) }, - count - ) -}) const isIntegratedTabBar = computed( () => settingStore.get('Comfy.UI.TabBarLayout') === 'Integrated' ) @@ -246,24 +189,9 @@ const inlineProgressSummaryTarget = computed(() => { const shouldHideInlineProgressSummary = computed( () => isQueueProgressOverlayEnabled.value && isQueueOverlayExpanded.value ) -const queueHistoryTooltipConfig = computed(() => - buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory')) -) const customNodesManagerTooltipConfig = computed(() => buildTooltipConfig(t('menu.manageExtensions')) ) -const queueContextMenu = ref | null>(null) -const queueContextMenuItems = computed(() => [ - { - label: t('sideToolbar.queueProgressOverlay.clearQueueTooltip'), - icon: 'icon-[lucide--list-x] text-destructive-background', - class: '*:text-destructive-background', - disabled: queueStore.pendingTasks.length === 0, - command: () => { - void handleClearQueue() - } - } -]) const shouldShowRedDot = computed((): boolean => { return shouldShowConflictRedDot.value @@ -286,27 +214,6 @@ onMounted(() => { } }) -const toggleQueueOverlay = () => { - if (isQueuePanelV2Enabled.value) { - sidebarTabStore.toggleSidebarTab('job-history') - return - } - commandStore.execute('Comfy.Queue.ToggleOverlay') -} - -const showQueueContextMenu = (event: MouseEvent) => { - queueContextMenu.value?.show(event) -} - -const handleClearQueue = async () => { - const pendingJobIds = queueStore.pendingTasks - .map((task) => task.jobId) - .filter((id): id is string => typeof id === 'string' && id.length > 0) - - await commandStore.execute('Comfy.ClearPendingTasks') - executionStore.clearInitializationByJobIds(pendingJobIds) -} - const openCustomNodeManager = async () => { try { await managerState.openManager({ diff --git a/src/components/actionbar/ComfyActionbar.vue b/src/components/actionbar/ComfyActionbar.vue index 652e3cf171..cf7ae875de 100644 --- a/src/components/actionbar/ComfyActionbar.vue +++ b/src/components/actionbar/ComfyActionbar.vue @@ -42,6 +42,38 @@ > + + @@ -65,11 +97,14 @@ import { } from '@vueuse/core' import { clamp } from 'es-toolkit/compat' import { storeToRefs } from 'pinia' +import ContextMenu from 'primevue/contextmenu' +import type { MenuItem } from 'primevue/menuitem' import Panel from 'primevue/panel' import { computed, nextTick, ref, watch } from 'vue' import type { ComponentPublicInstance } from 'vue' import { useI18n } from 'vue-i18n' +import StatusBadge from '@/components/common/StatusBadge.vue' import QueueInlineProgress from '@/components/queue/QueueInlineProgress.vue' import Button from '@/components/ui/button/Button.vue' import { buildTooltipConfig } from '@/composables/useTooltipConfig' @@ -77,6 +112,8 @@ import { useSettingStore } from '@/platform/settings/settingStore' import { useTelemetry } from '@/platform/telemetry' import { useCommandStore } from '@/stores/commandStore' import { useExecutionStore } from '@/stores/executionStore' +import { useQueueStore } from '@/stores/queueStore' +import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore' import { cn } from '@/utils/tailwindUtil' import ComfyRunButton from './ComfyRunButton' @@ -92,8 +129,13 @@ const emit = defineEmits<{ const settingsStore = useSettingStore() const commandStore = useCommandStore() -const { t } = useI18n() -const { isIdle: isExecutionIdle } = storeToRefs(useExecutionStore()) +const executionStore = useExecutionStore() +const queueStore = useQueueStore() +const sidebarTabStore = useSidebarTabStore() +const { t, n } = useI18n() +const { isIdle: isExecutionIdle } = storeToRefs(executionStore) +const { activeJobsCount } = storeToRefs(queueStore) +const { activeSidebarTabId } = storeToRefs(sidebarTabStore) const position = computed(() => settingsStore.get('Comfy.UseNewMenu')) const visible = computed(() => position.value !== 'Disabled') @@ -318,11 +360,52 @@ watch(isDragging, (dragging) => { const cancelJobTooltipConfig = computed(() => buildTooltipConfig(t('menu.interrupt')) ) +const queueHistoryTooltipConfig = computed(() => + buildTooltipConfig(t('sideToolbar.queueProgressOverlay.viewJobHistory')) +) +const activeJobsLabel = computed(() => { + const count = activeJobsCount.value + return t( + 'sideToolbar.queueProgressOverlay.activeJobsShort', + { count: n(count) }, + count + ) +}) +const queueContextMenu = ref | null>(null) +const queueContextMenuItems = computed(() => [ + { + label: t('sideToolbar.queueProgressOverlay.clearQueueTooltip'), + icon: 'icon-[lucide--list-x] text-destructive-background', + class: '*:text-destructive-background', + disabled: queueStore.pendingTasks.length === 0, + command: () => { + void handleClearQueue() + } + } +]) const cancelCurrentJob = async () => { if (isExecutionIdle.value) return await commandStore.execute('Comfy.Interrupt') } +const toggleQueueOverlay = () => { + if (isQueuePanelV2Enabled.value) { + sidebarTabStore.toggleSidebarTab('job-history') + return + } + commandStore.execute('Comfy.Queue.ToggleOverlay') +} +const showQueueContextMenu = (event: MouseEvent) => { + queueContextMenu.value?.show(event) +} +const handleClearQueue = async () => { + const pendingJobIds = queueStore.pendingTasks + .map((task) => task.jobId) + .filter((id): id is string => typeof id === 'string' && id.length > 0) + + await commandStore.execute('Comfy.ClearPendingTasks') + executionStore.clearInitializationByJobIds(pendingJobIds) +} const actionbarClass = computed(() => cn(