diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index bcffa3cc6..dbc06b999 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -168,7 +168,7 @@ export class ComfyPage { this.menu = new ComfyMenu(page) this.actionbar = new ComfyActionbar(page) this.templates = new ComfyTemplates(page) - this.settingDialog = new SettingDialog(page) + this.settingDialog = new SettingDialog(page, this) this.confirmDialog = new ConfirmDialog(page) } diff --git a/browser_tests/fixtures/components/SettingDialog.ts b/browser_tests/fixtures/components/SettingDialog.ts index 82d47e78e..afaf86154 100644 --- a/browser_tests/fixtures/components/SettingDialog.ts +++ b/browser_tests/fixtures/components/SettingDialog.ts @@ -1,15 +1,19 @@ import { Page } from '@playwright/test' +import { ComfyPage } from '../ComfyPage' + export class SettingDialog { - constructor(public readonly page: Page) {} + constructor( + public readonly page: Page, + public readonly comfyPage: ComfyPage + ) {} get root() { return this.page.locator('div.settings-container') } async open() { - const button = this.page.locator('button.comfy-settings-btn:visible') - await button.click() + await this.comfyPage.executeCommand('Comfy.ShowSettingsDialog') await this.page.waitForSelector('div.settings-container') } diff --git a/browser_tests/fixtures/components/Topbar.ts b/browser_tests/fixtures/components/Topbar.ts index c72177d32..81fcf6764 100644 --- a/browser_tests/fixtures/components/Topbar.ts +++ b/browser_tests/fixtures/components/Topbar.ts @@ -15,10 +15,6 @@ export class Topbar { .innerText() } - async openSubmenuMobile() { - await this.page.locator('.p-menubar-mobile .p-menubar-button').click() - } - getMenuItem(itemLabel: string): Locator { return this.page.locator(`.p-menubar-item-label:text-is("${itemLabel}")`) } @@ -68,31 +64,41 @@ export class Topbar { await this.getSaveDialog().waitFor({ state: 'hidden', timeout: 500 }) } + async openTopbarMenu() { + await this.page.locator('.comfyui-logo-wrapper').click() + const menu = this.page.locator('.comfy-command-menu') + await menu.waitFor({ state: 'visible' }) + return menu + } + async triggerTopbarCommand(path: string[]) { if (path.length < 2) { throw new Error('Path is too short') } + const menu = await this.openTopbarMenu() const tabName = path[0] - const topLevelMenu = this.page.locator( - `.top-menubar .p-menubar-item-label:text-is("${tabName}")` + const topLevelMenuItem = this.page.locator( + `.p-menubar-item-label:text-is("${tabName}")` ) + const topLevelMenu = menu + .locator('.p-tieredmenu-item') + .filter({ has: topLevelMenuItem }) await topLevelMenu.waitFor({ state: 'visible' }) - await topLevelMenu.click() + await topLevelMenu.hover() + let currentMenu = topLevelMenu for (let i = 1; i < path.length; i++) { const commandName = path[i] - const menuItem = this.page + const menuItem = currentMenu .locator( - `.top-menubar .p-menubar-submenu .p-menubar-item:has-text("${commandName}")` + `.p-tieredmenu-submenu .p-tieredmenu-item:has-text("${commandName}")` ) .first() await menuItem.waitFor({ state: 'visible' }) await menuItem.hover() - - if (i === path.length - 1) { - await menuItem.click() - } + currentMenu = menuItem } + await currentMenu.click() } } diff --git a/browser_tests/tests/interaction.spec.ts b/browser_tests/tests/interaction.spec.ts index fc1b5e8e1..9469e7960 100644 --- a/browser_tests/tests/interaction.spec.ts +++ b/browser_tests/tests/interaction.spec.ts @@ -769,7 +769,8 @@ test.describe('Viewport settings', () => { }) => { // Screenshot the canvas element await comfyPage.menu.topbar.saveWorkflow('Workflow A') - await expect(comfyPage.canvas).toHaveScreenshot('viewport-workflow-a.png') + await comfyPage.nextFrame() + const screenshotA = (await comfyPage.canvas.screenshot()).toString('base64') // Save workflow as a new file, then zoom out before screen shot await comfyPage.menu.topbar.saveWorkflowAs('Workflow B') @@ -777,7 +778,12 @@ test.describe('Viewport settings', () => { for (let i = 0; i < 4; i++) { await comfyMouse.wheel(0, 60) } - await expect(comfyPage.canvas).toHaveScreenshot('viewport-workflow-b.png') + + await comfyPage.nextFrame() + const screenshotB = (await comfyPage.canvas.screenshot()).toString('base64') + + // Ensure that the screenshots are different due to zoom level + expect(screenshotB).not.toBe(screenshotA) const tabA = comfyPage.menu.topbar.getWorkflowTab('Workflow A') const tabB = comfyPage.menu.topbar.getWorkflowTab('Workflow B') @@ -785,11 +791,15 @@ test.describe('Viewport settings', () => { // Go back to Workflow A await tabA.click() await comfyPage.nextFrame() - await expect(comfyPage.canvas).toHaveScreenshot('viewport-workflow-a.png') + expect((await comfyPage.canvas.screenshot()).toString('base64')).toBe( + screenshotA + ) // And back to Workflow B await tabB.click() await comfyPage.nextFrame() - await expect(comfyPage.canvas).toHaveScreenshot('viewport-workflow-b.png') + expect((await comfyPage.canvas.screenshot()).toString('base64')).toBe( + screenshotB + ) }) }) diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-a-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-a-chromium-linux.png deleted file mode 100644 index be9f8d341..000000000 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-a-chromium-linux.png and /dev/null differ diff --git a/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-b-chromium-linux.png b/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-b-chromium-linux.png deleted file mode 100644 index 1d45b9a5d..000000000 Binary files a/browser_tests/tests/interaction.spec.ts-snapshots/viewport-workflow-b-chromium-linux.png and /dev/null differ diff --git a/browser_tests/tests/menu.spec.ts b/browser_tests/tests/menu.spec.ts index 7c999f879..a771257f2 100644 --- a/browser_tests/tests/menu.spec.ts +++ b/browser_tests/tests/menu.spec.ts @@ -63,7 +63,7 @@ test.describe('Menu', () => { test('@mobile Items fully visible on mobile screen width', async ({ comfyPage }) => { - await comfyPage.menu.topbar.openSubmenuMobile() + await comfyPage.menu.topbar.openTopbarMenu() const topLevelMenuItem = comfyPage.page .locator('a.p-menubar-item-link') .first() @@ -74,8 +74,9 @@ test.describe('Menu', () => { }) test('Displays keybinding next to item', async ({ comfyPage }) => { + await comfyPage.menu.topbar.openTopbarMenu() const workflowMenuItem = comfyPage.menu.topbar.getMenuItem('Workflow') - await workflowMenuItem.click() + await workflowMenuItem.hover() const exportTag = comfyPage.page.locator('.keybinding-tag', { hasText: 'Ctrl + s' }) diff --git a/src/components/actionbar/ComfyActionbar.vue b/src/components/actionbar/ComfyActionbar.vue index c9d75d009..be113867a 100644 --- a/src/components/actionbar/ComfyActionbar.vue +++ b/src/components/actionbar/ComfyActionbar.vue @@ -30,10 +30,11 @@ import ComfyQueueButton from './ComfyQueueButton.vue' const settingsStore = useSettingStore() -const visible = computed( - () => settingsStore.get('Comfy.UseNewMenu') !== 'Disabled' -) +const position = computed(() => settingsStore.get('Comfy.UseNewMenu')) +const visible = computed(() => position.value !== 'Disabled') + +const topMenuRef = inject>('topMenuRef') const panelRef = ref(null) const dragHandleRef = ref(null) const isDocked = useLocalStorage('Comfy.MenuPosition.Docked', false) @@ -49,7 +50,16 @@ const { } = useDraggable(panelRef, { initialValue: { x: 0, y: 0 }, handle: dragHandleRef, - containerElement: document.body + containerElement: document.body, + onMove: (event) => { + // Prevent dragging the menu over the top of the tabs + if (position.value === 'Top') { + const minY = topMenuRef?.value?.getBoundingClientRect().top ?? 40 + if (event.y < minY) { + event.y = minY + } + } + } }) // Update storedPosition when x or y changes @@ -182,7 +192,6 @@ const adjustMenuPosition = () => { useEventListener(window, 'resize', adjustMenuPosition) -const topMenuRef = inject>('topMenuRef') const topMenuBounds = useElementBounding(topMenuRef) const overlapThreshold = 20 // pixels const isOverlappingWithTopMenu = computed(() => { diff --git a/src/components/breadcrumb/SubgraphBreadcrumb.vue b/src/components/breadcrumb/SubgraphBreadcrumb.vue index 63ee9c269..141d30303 100644 --- a/src/components/breadcrumb/SubgraphBreadcrumb.vue +++ b/src/components/breadcrumb/SubgraphBreadcrumb.vue @@ -1,34 +1,70 @@ - + + diff --git a/src/components/breadcrumb/SubgraphBreadcrumbItem.vue b/src/components/breadcrumb/SubgraphBreadcrumbItem.vue new file mode 100644 index 000000000..f541db432 --- /dev/null +++ b/src/components/breadcrumb/SubgraphBreadcrumbItem.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index c6aa2aea7..fbb9500ae 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -16,7 +16,6 @@ - @@ -50,7 +49,6 @@ import { computed, onMounted, ref, watch, watchEffect } from 'vue' import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue' import BottomPanel from '@/components/bottomPanel/BottomPanel.vue' -import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue' import DomWidgets from '@/components/graph/DomWidgets.vue' import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue' import NodeTooltip from '@/components/graph/NodeTooltip.vue' diff --git a/src/components/sidebar/SideToolbar.vue b/src/components/sidebar/SideToolbar.vue index 4b7e6df00..e455ae74a 100644 --- a/src/components/sidebar/SideToolbar.vue +++ b/src/components/sidebar/SideToolbar.vue @@ -14,9 +14,8 @@ />
- - +
@@ -32,6 +31,7 @@ import { computed } from 'vue' import ExtensionSlot from '@/components/common/ExtensionSlot.vue' +import SidebarBottomPanelToggleButton from '@/components/sidebar/SidebarBottomPanelToggleButton.vue' import { useKeybindingStore } from '@/stores/keybindingStore' import { useSettingStore } from '@/stores/settingStore' import { useUserStore } from '@/stores/userStore' @@ -41,8 +41,6 @@ import type { SidebarTabExtension } from '@/types/extensionTypes' import SidebarHelpCenterIcon from './SidebarHelpCenterIcon.vue' import SidebarIcon from './SidebarIcon.vue' import SidebarLogoutIcon from './SidebarLogoutIcon.vue' -import SidebarSettingsToggleIcon from './SidebarSettingsToggleIcon.vue' -import SidebarThemeToggleIcon from './SidebarThemeToggleIcon.vue' const workspaceStore = useWorkspaceStore() const settingStore = useSettingStore() diff --git a/src/components/sidebar/SidebarBottomPanelToggleButton.vue b/src/components/sidebar/SidebarBottomPanelToggleButton.vue new file mode 100644 index 000000000..6ca891e6e --- /dev/null +++ b/src/components/sidebar/SidebarBottomPanelToggleButton.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/sidebar/SidebarIcon.vue b/src/components/sidebar/SidebarIcon.vue index 1c1dffc1b..5002d7724 100644 --- a/src/components/sidebar/SidebarIcon.vue +++ b/src/components/sidebar/SidebarIcon.vue @@ -19,10 +19,12 @@ @click="emit('click', $event)" > diff --git a/src/components/sidebar/SidebarSettingsToggleIcon.vue b/src/components/sidebar/SidebarSettingsToggleIcon.vue deleted file mode 100644 index 3b214fb43..000000000 --- a/src/components/sidebar/SidebarSettingsToggleIcon.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/src/components/sidebar/SidebarThemeToggleIcon.vue b/src/components/sidebar/SidebarThemeToggleIcon.vue deleted file mode 100644 index 8d64ff4ef..000000000 --- a/src/components/sidebar/SidebarThemeToggleIcon.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/src/components/topbar/BottomPanelToggleButton.vue b/src/components/topbar/BottomPanelToggleButton.vue deleted file mode 100644 index 8af15331b..000000000 --- a/src/components/topbar/BottomPanelToggleButton.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/src/components/topbar/CommandMenubar.vue b/src/components/topbar/CommandMenubar.vue index faf795b9b..02029d307 100644 --- a/src/components/topbar/CommandMenubar.vue +++ b/src/components/topbar/CommandMenubar.vue @@ -1,51 +1,116 @@