diff --git a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png index eaa359731..c7334eb16 100644 Binary files a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png and b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-default-workflow-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png index ff608be64..e3acabfcb 100644 Binary files a/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png and b/browser_tests/tests/mobileBaseline.spec.ts-snapshots/mobile-empty-canvas-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png index f1d41cf13..915fb36d9 100644 Binary files a/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/canvas/pan.spec.ts-snapshots/vue-nodes-paned-with-touch-mobile-chrome-linux.png differ diff --git a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png index 390def0a2..f4bac78b7 100644 Binary files a/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png and b/browser_tests/tests/vueNodes/interactions/node/move.spec.ts-snapshots/vue-node-moved-node-touch-mobile-chrome-linux.png differ diff --git a/src/components/TopMenuSection.test.ts b/src/components/TopMenuSection.test.ts index 8d4f3673d..b2bf7041c 100644 --- a/src/components/TopMenuSection.test.ts +++ b/src/components/TopMenuSection.test.ts @@ -2,7 +2,8 @@ import { createTestingPinia } from '@pinia/testing' import { mount } from '@vue/test-utils' import type { MenuItem } from 'primevue/menuitem' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { computed, nextTick } from 'vue' +import { computed, defineComponent, h, nextTick, onMounted } from 'vue' +import type { Component } from 'vue' import { createI18n } from 'vue-i18n' import TopMenuSection from '@/components/TopMenuSection.vue' @@ -14,6 +15,7 @@ import type { } from '@/platform/remote/comfyui/jobs/jobTypes' import { useSettingStore } from '@/platform/settings/settingStore' import { useCommandStore } from '@/stores/commandStore' +import { useExecutionStore } from '@/stores/executionStore' import { TaskItemImpl, useQueueStore } from '@/stores/queueStore' import { useSidebarTabStore } from '@/stores/workspace/sidebarTabStore' import { isElectron } from '@/utils/envUtil' @@ -36,7 +38,17 @@ vi.mock('@/stores/firebaseAuthStore', () => ({ })) })) -function createWrapper(pinia = createTestingPinia({ createSpy: vi.fn })) { +type WrapperOptions = { + pinia?: ReturnType + stubs?: Record + attachTo?: HTMLElement +} + +function createWrapper({ + pinia = createTestingPinia({ createSpy: vi.fn }), + stubs = {}, + attachTo +}: WrapperOptions = {}) { const i18n = createI18n({ legacy: false, locale: 'en', @@ -55,18 +67,21 @@ function createWrapper(pinia = createTestingPinia({ createSpy: vi.fn })) { }) return mount(TopMenuSection, { + attachTo, global: { plugins: [pinia, i18n], stubs: { SubgraphBreadcrumb: true, QueueProgressOverlay: true, + QueueInlineProgressSummary: true, CurrentUserButton: true, LoginButton: true, ContextMenu: { name: 'ContextMenu', props: ['model'], template: '
' - } + }, + ...stubs }, directives: { tooltip: () => {} @@ -91,6 +106,7 @@ function createTask(id: string, status: JobStatus): TaskItemImpl { describe('TopMenuSection', () => { beforeEach(() => { vi.resetAllMocks() + localStorage.clear() }) describe('authentication state', () => { @@ -151,7 +167,7 @@ describe('TopMenuSection', () => { vi.mocked(settingStore.get).mockImplementation((key) => key === 'Comfy.Queue.QPOV2' ? true : undefined ) - const wrapper = createWrapper(pinia) + const wrapper = createWrapper({ pinia }) await nextTick() @@ -169,7 +185,7 @@ describe('TopMenuSection', () => { vi.mocked(settingStore.get).mockImplementation((key) => key === 'Comfy.Queue.QPOV2' ? false : undefined ) - const wrapper = createWrapper(pinia) + const wrapper = createWrapper({ pinia }) const commandStore = useCommandStore(pinia) await wrapper.find('[data-testid="queue-overlay-toggle"]').trigger('click') @@ -185,7 +201,7 @@ describe('TopMenuSection', () => { vi.mocked(settingStore.get).mockImplementation((key) => key === 'Comfy.Queue.QPOV2' ? true : undefined ) - const wrapper = createWrapper(pinia) + const wrapper = createWrapper({ pinia }) const sidebarTabStore = useSidebarTabStore(pinia) await wrapper.find('[data-testid="queue-overlay-toggle"]').trigger('click') @@ -199,7 +215,7 @@ describe('TopMenuSection', () => { vi.mocked(settingStore.get).mockImplementation((key) => key === 'Comfy.Queue.QPOV2' ? true : undefined ) - const wrapper = createWrapper(pinia) + const wrapper = createWrapper({ pinia }) const sidebarTabStore = useSidebarTabStore(pinia) const toggleButton = wrapper.find('[data-testid="queue-overlay-toggle"]') @@ -210,6 +226,84 @@ describe('TopMenuSection', () => { expect(sidebarTabStore.activeSidebarTabId).toBe(null) }) + describe('inline progress summary', () => { + const configureSettings = ( + pinia: ReturnType, + qpoV2Enabled: boolean + ) => { + const settingStore = useSettingStore(pinia) + vi.mocked(settingStore.get).mockImplementation((key) => { + if (key === 'Comfy.Queue.QPOV2') return qpoV2Enabled + if (key === 'Comfy.UseNewMenu') return 'Top' + return undefined + }) + } + + it('renders inline progress summary when QPO V2 is enabled', async () => { + const pinia = createTestingPinia({ createSpy: vi.fn }) + configureSettings(pinia, true) + + const wrapper = createWrapper({ pinia }) + + await nextTick() + + expect( + wrapper.findComponent({ name: 'QueueInlineProgressSummary' }).exists() + ).toBe(true) + }) + + it('does not render inline progress summary when QPO V2 is disabled', async () => { + const pinia = createTestingPinia({ createSpy: vi.fn }) + configureSettings(pinia, false) + + const wrapper = createWrapper({ pinia }) + + await nextTick() + + expect( + wrapper.findComponent({ name: 'QueueInlineProgressSummary' }).exists() + ).toBe(false) + }) + + it('teleports inline progress summary when actionbar is floating', async () => { + localStorage.setItem('Comfy.MenuPosition.Docked', 'false') + const actionbarTarget = document.createElement('div') + document.body.appendChild(actionbarTarget) + const pinia = createTestingPinia({ createSpy: vi.fn }) + configureSettings(pinia, true) + const executionStore = useExecutionStore(pinia) + executionStore.activePromptId = 'prompt-1' + + const ComfyActionbarStub = defineComponent({ + name: 'ComfyActionbar', + setup(_, { emit }) { + onMounted(() => { + emit('update:progressTarget', actionbarTarget) + }) + return () => h('div') + } + }) + + const wrapper = createWrapper({ + pinia, + attachTo: document.body, + stubs: { + ComfyActionbar: ComfyActionbarStub, + QueueInlineProgressSummary: false + } + }) + + try { + await nextTick() + + expect(actionbarTarget.querySelector('[role="status"]')).not.toBeNull() + } finally { + wrapper.unmount() + actionbarTarget.remove() + } + }) + }) + it('disables the clear queue context menu item when no queued jobs exist', () => { const wrapper = createWrapper() const menu = wrapper.findComponent({ name: 'ContextMenu' }) diff --git a/src/components/TopMenuSection.vue b/src/components/TopMenuSection.vue index 05149589c..a76d94f46 100644 --- a/src/components/TopMenuSection.vue +++ b/src/components/TopMenuSection.vue @@ -1,101 +1,130 @@ diff --git a/src/components/queue/QueueInlineProgressSummary.vue b/src/components/queue/QueueInlineProgressSummary.vue new file mode 100644 index 000000000..758c5d316 --- /dev/null +++ b/src/components/queue/QueueInlineProgressSummary.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/components/rightSidePanel/RightSidePanel.vue b/src/components/rightSidePanel/RightSidePanel.vue index b23346bf4..1ba514f66 100644 --- a/src/components/rightSidePanel/RightSidePanel.vue +++ b/src/components/rightSidePanel/RightSidePanel.vue @@ -8,12 +8,14 @@ import Tab from '@/components/tab/Tab.vue' import TabList from '@/components/tab/TabList.vue' import Button from '@/components/ui/button/Button.vue' import { useGraphHierarchy } from '@/composables/graph/useGraphHierarchy' +import { st } from '@/i18n' import { SubgraphNode } from '@/lib/litegraph/src/litegraph' import type { LGraphNode } from '@/lib/litegraph/src/litegraph' import { useSettingStore } from '@/platform/settings/settingStore' import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import { useRightSidePanelStore } from '@/stores/workspace/rightSidePanelStore' import type { RightSidePanelTab } from '@/stores/workspace/rightSidePanelStore' +import { resolveNodeDisplayName } from '@/utils/nodeTitleUtil' import { cn } from '@/utils/tailwindUtil' import TabInfo from './info/TabInfo.vue' @@ -146,9 +148,12 @@ function resolveTitle() { return groups[0].title || t('rightSidePanel.fallbackGroupTitle') } if (nodes.length === 1) { - return ( - nodes[0].title || nodes[0].type || t('rightSidePanel.fallbackNodeTitle') - ) + const fallbackNodeTitle = t('rightSidePanel.fallbackNodeTitle') + return resolveNodeDisplayName(nodes[0], { + emptyLabel: fallbackNodeTitle, + untitledLabel: fallbackNodeTitle, + st + }) } } return t('rightSidePanel.title', { count: items.length }) diff --git a/src/components/rightSidePanel/parameters/WidgetItem.vue b/src/components/rightSidePanel/parameters/WidgetItem.vue index 6a2eca748..4cd05636a 100644 --- a/src/components/rightSidePanel/parameters/WidgetItem.vue +++ b/src/components/rightSidePanel/parameters/WidgetItem.vue @@ -1,9 +1,11 @@