From 4d012f5fd91b1deccedf0c98d50e030d9cd70b81 Mon Sep 17 00:00:00 2001 From: bymyself Date: Tue, 14 Oct 2025 11:46:54 -0700 Subject: [PATCH 1/2] allow copy and paste when minimap target --- browser_tests/fixtures/ComfyPage.ts | 7 +++ browser_tests/fixtures/components/Minimap.ts | 41 ++++++++++++++ browser_tests/tests/copyPaste.spec.ts | 31 +++++++++-- browser_tests/tests/minimap.spec.ts | 57 +++++++++++--------- src/components/graph/GraphCanvasMenu.vue | 1 + src/composables/useCopy.ts | 10 +++- src/composables/usePaste.ts | 10 +++- src/renderer/extensions/minimap/MiniMap.vue | 1 + src/services/keybindingService.ts | 8 +-- 9 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 browser_tests/fixtures/components/Minimap.ts diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index b01de101e5..3f960561c5 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -13,6 +13,7 @@ import { ComfyTemplates } from '../helpers/templates' import { ComfyMouse } from './ComfyMouse' import { VueNodeHelpers } from './VueNodeHelpers' import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox' +import { Minimap } from './components/Minimap' import { SettingDialog } from './components/SettingDialog' import { NodeLibrarySidebarTab, @@ -33,6 +34,7 @@ class ComfyMenu { private _workflowsTab: WorkflowsSidebarTab | null = null private _queueTab: QueueSidebarTab | null = null private _topbar: Topbar | null = null + private _minimap: Minimap | null = null public readonly sideToolbar: Locator public readonly themeToggleButton: Locator @@ -70,6 +72,11 @@ class ComfyMenu { return this._topbar } + get minimap() { + this._minimap ??= new Minimap(this.page) + return this._minimap + } + async toggleTheme() { await this.themeToggleButton.click() await this.page.evaluate(() => { diff --git a/browser_tests/fixtures/components/Minimap.ts b/browser_tests/fixtures/components/Minimap.ts new file mode 100644 index 0000000000..9554b008ff --- /dev/null +++ b/browser_tests/fixtures/components/Minimap.ts @@ -0,0 +1,41 @@ +import type { Locator, Page } from '@playwright/test' + +export class Minimap { + constructor(public readonly page: Page) {} + + get mainContainer(): Locator { + return this.page.locator('.minimap-main-container') + } + + get container(): Locator { + return this.page.locator('.litegraph-minimap') + } + + get canvas(): Locator { + return this.container.locator('.minimap-canvas') + } + + get viewport(): Locator { + return this.container.locator('.minimap-viewport') + } + + get settingsButton(): Locator { + return this.container.getByRole('button').first() + } + + get closeButton(): Locator { + return this.container.getByTestId('close-minmap-button') + } + + async clickCanvas(options?: Parameters[0]): Promise { + await this.canvas.click(options) + } + + async clickSettingsButton(): Promise { + await this.settingsButton.click() + } + + async close(): Promise { + await this.closeButton.click() + } +} diff --git a/browser_tests/tests/copyPaste.spec.ts b/browser_tests/tests/copyPaste.spec.ts index cabb849e80..877cab03a7 100644 --- a/browser_tests/tests/copyPaste.spec.ts +++ b/browser_tests/tests/copyPaste.spec.ts @@ -2,11 +2,11 @@ import { expect } from '@playwright/test' import { comfyPageFixture as test } from '../fixtures/ComfyPage' -test.beforeEach(async ({ comfyPage }) => { - await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled') -}) - test.describe('Copy Paste', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled') + }) + test('Can copy and paste node', async ({ comfyPage }) => { await comfyPage.clickEmptyLatentNode() await comfyPage.page.mouse.move(10, 10) @@ -15,6 +15,29 @@ test.describe('Copy Paste', () => { await expect(comfyPage.canvas).toHaveScreenshot('copied-node.png') }) + test('Can copy and paste node after clicking minimap', async ({ + comfyPage + }) => { + await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + await comfyPage.setSetting('Comfy.Minimap.Visible', true) + + const latentNodeTitle = 'Empty Latent Image' + const initialLatentNodeCt = + await comfyPage.getNodeRefsByTitle(latentNodeTitle) + + await comfyPage.clickEmptyLatentNode() + await comfyPage.ctrlC() + + // Click minimap to lose focus. + await comfyPage.menu.minimap.clickCanvas({ force: true }) + + // Paste node. + await comfyPage.ctrlV() + const expectedNodeCt = initialLatentNodeCt.length + 1 + const latentNodeCt = await comfyPage.getNodeRefsByTitle(latentNodeTitle) + expect(latentNodeCt.length).toBe(expectedNodeCt) + }) + test('Can copy and paste node with link', async ({ comfyPage }) => { await comfyPage.clickTextEncodeNode1() await comfyPage.page.mouse.move(10, 10) diff --git a/browser_tests/tests/minimap.spec.ts b/browser_tests/tests/minimap.spec.ts index cea5fe9a80..3b8d8b26ce 100644 --- a/browser_tests/tests/minimap.spec.ts +++ b/browser_tests/tests/minimap.spec.ts @@ -14,65 +14,70 @@ test.describe('Minimap', () => { }) test('Validate minimap is visible by default', async ({ comfyPage }) => { - const minimapContainer = comfyPage.page.locator('.litegraph-minimap') + const minimap = comfyPage.menu.minimap - await expect(minimapContainer).toBeVisible() - - const minimapCanvas = minimapContainer.locator('.minimap-canvas') - await expect(minimapCanvas).toBeVisible() - - const minimapViewport = minimapContainer.locator('.minimap-viewport') - await expect(minimapViewport).toBeVisible() - - await expect(minimapContainer).toHaveCSS('position', 'relative') + await expect(minimap.container).toBeVisible() + await expect(minimap.canvas).toBeVisible() + await expect(minimap.viewport).toBeVisible() + await expect(minimap.container).toHaveCSS('position', 'relative') // position and z-index validation moved to the parent container of the minimap - const minimapMainContainer = comfyPage.page.locator( - '.minimap-main-container' - ) - await expect(minimapMainContainer).toHaveCSS('position', 'absolute') - await expect(minimapMainContainer).toHaveCSS('z-index', '1000') + await expect(minimap.mainContainer).toHaveCSS('position', 'absolute') + await expect(minimap.mainContainer).toHaveCSS('z-index', '1000') }) test('Validate minimap toggle button state', async ({ comfyPage }) => { + const minimap = comfyPage.menu.minimap + const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button') await expect(toggleButton).toBeVisible() - - const minimapContainer = comfyPage.page.locator('.litegraph-minimap') - await expect(minimapContainer).toBeVisible() + await expect(minimap.container).toBeVisible() }) test('Validate minimap can be toggled off and on', async ({ comfyPage }) => { - const minimapContainer = comfyPage.page.locator('.litegraph-minimap') + const minimap = comfyPage.menu.minimap + + // Open zoom controls dropdown first + const zoomControlsButton = comfyPage.page.getByTestId( + 'zoom-controls-button' + ) + await zoomControlsButton.click() + const toggleButton = comfyPage.page.getByTestId('toggle-minimap-button') - await expect(minimapContainer).toBeVisible() + await expect(minimap.container).toBeVisible() await toggleButton.click() await comfyPage.nextFrame() - await expect(minimapContainer).not.toBeVisible() + await expect(minimap.container).not.toBeVisible() await toggleButton.click() await comfyPage.nextFrame() - await expect(minimapContainer).toBeVisible() + await expect(minimap.container).toBeVisible() + + // Open zoom controls dropdown again to verify button text + await zoomControlsButton.click() + await comfyPage.nextFrame() + + await expect(toggleButton).toContainText('Hide Minimap') }) test('Validate minimap keyboard shortcut Alt+M', async ({ comfyPage }) => { - const minimapContainer = comfyPage.page.locator('.litegraph-minimap') + const minimap = comfyPage.menu.minimap - await expect(minimapContainer).toBeVisible() + await expect(minimap.container).toBeVisible() await comfyPage.page.keyboard.press('Alt+KeyM') await comfyPage.nextFrame() - await expect(minimapContainer).not.toBeVisible() + await expect(minimap.container).not.toBeVisible() await comfyPage.page.keyboard.press('Alt+KeyM') await comfyPage.nextFrame() - await expect(minimapContainer).toBeVisible() + await expect(minimap.container).toBeVisible() }) }) diff --git a/src/components/graph/GraphCanvasMenu.vue b/src/components/graph/GraphCanvasMenu.vue index b4172cdcc2..18ed8e0cd8 100644 --- a/src/components/graph/GraphCanvasMenu.vue +++ b/src/components/graph/GraphCanvasMenu.vue @@ -10,6 +10,7 @@ > { // Default system copy return } + // Check if target is graph canvas or within graph UI (minimap, controls, etc.) const isTargetInGraph = - e.target.classList.contains('litegraph') || + e.target.id === 'graph-canvas' || + e.target.id === 'comfy-minimap' || + e.target.id === 'graph-canvas-controls' || e.target.classList.contains('graph-canvas-container') || - e.target.id === 'graph-canvas' + e.target.classList.contains('litegraph') || + e.target.closest('#comfy-minimap') !== null || + e.target.closest('#graph-canvas-controls') !== null || + e.target.closest('#graph-canvas-container') !== null // copy nodes and clear clipboard const canvas = canvasStore.canvas diff --git a/src/composables/usePaste.ts b/src/composables/usePaste.ts index c04901951a..69b87ecdeb 100644 --- a/src/composables/usePaste.ts +++ b/src/composables/usePaste.ts @@ -38,11 +38,17 @@ export const usePaste = () => { } useEventListener(document, 'paste', async (e) => { + // Check if target is graph canvas or within graph UI (minimap, controls, etc.) const isTargetInGraph = e.target instanceof Element && - (e.target.classList.contains('litegraph') || + (e.target.id === 'graph-canvas' || + e.target.id === 'comfy-minimap' || + e.target.id === 'graph-canvas-controls' || e.target.classList.contains('graph-canvas-container') || - e.target.id === 'graph-canvas') + e.target.classList.contains('litegraph') || + e.target.closest('#comfy-minimap') !== null || + e.target.closest('#graph-canvas-controls') !== null || + e.target.closest('#graph-canvas-container') !== null) // If the target is not in the graph, we don't want to handle the paste event if (!isTargetInGraph) return diff --git a/src/renderer/extensions/minimap/MiniMap.vue b/src/renderer/extensions/minimap/MiniMap.vue index 12f8d8c135..c6b10639f9 100644 --- a/src/renderer/extensions/minimap/MiniMap.vue +++ b/src/renderer/extensions/minimap/MiniMap.vue @@ -1,6 +1,7 @@