From 415ebfd67b5bce861e707a8ae40bd7a5396d6c4e Mon Sep 17 00:00:00 2001 From: Christian Byrne Date: Thu, 25 Sep 2025 13:07:29 -0700 Subject: [PATCH] Add muted state to Vue nodes (#5770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Added mute state support to Vue nodes with visual feedback and keyboard shortcut functionality. ## Changes - **What**: Implemented mute state (mode 2) for Vue nodes with opacity styling and `Ctrl+M` hotkey support ## Review Focus Visual consistency between bypass and mute states, and keyboard shortcut conflict detection with existing hotkeys. ## Test Coverage - Single node mute/unmute with `Ctrl+M` hotkey - Multi-selection mute/unmute operations - Visual state verification with opacity changes ## Related - https://github.com/Comfy-Org/ComfyUI_frontend/pull/5715 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5770-Add-muted-state-to-Vue-nodes-2796d73d36508143b3edfbcb782de7c1) by [Unito](https://www.unito.io) --- browser_tests/fixtures/VueNodeHelpers.ts | 7 +++ .../tests/vueNodes/nodeStates/bypass.spec.ts | 20 ++++----- .../tests/vueNodes/nodeStates/mute.spec.ts | 45 +++++++++++++++++++ .../vueNodes/components/LGraphNode.vue | 3 ++ 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 browser_tests/tests/vueNodes/nodeStates/mute.spec.ts diff --git a/browser_tests/fixtures/VueNodeHelpers.ts b/browser_tests/fixtures/VueNodeHelpers.ts index e3b3de542..b51750299 100644 --- a/browser_tests/fixtures/VueNodeHelpers.ts +++ b/browser_tests/fixtures/VueNodeHelpers.ts @@ -22,6 +22,13 @@ export class VueNodeHelpers { ) } + /** + * Get locator for a Vue node by the node's title (displayed name in the header) + */ + getNodeByTitle(title: string): Locator { + return this.page.locator(`[data-node-id]`).filter({ hasText: title }) + } + /** * Get total count of Vue nodes in the DOM */ diff --git a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts index c80a86503..74ec17cc9 100644 --- a/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts +++ b/browser_tests/tests/vueNodes/nodeStates/bypass.spec.ts @@ -15,11 +15,10 @@ test.describe('Vue Node Bypass', () => { test('should allow toggling bypass on a selected node with hotkey', async ({ comfyPage }) => { - const checkpointNode = comfyPage.page.locator('[data-node-id]').filter({ - hasText: 'Load Checkpoint' - }) - await checkpointNode.getByText('Load Checkpoint').click() + await comfyPage.page.getByText('Load Checkpoint').click() await comfyPage.page.keyboard.press(BYPASS_HOTKEY) + + const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') await expect(checkpointNode).toHaveClass(BYPASS_CLASS) await comfyPage.page.keyboard.press(BYPASS_HOTKEY) @@ -29,15 +28,12 @@ test.describe('Vue Node Bypass', () => { test('should allow toggling bypass on multiple selected nodes with hotkey', async ({ comfyPage }) => { - const checkpointNode = comfyPage.page.locator('[data-node-id]').filter({ - hasText: 'Load Checkpoint' - }) - const ksamplerNode = comfyPage.page.locator('[data-node-id]').filter({ - hasText: 'KSampler' - }) + await comfyPage.page.getByText('Load Checkpoint').click() + await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] }) + + const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') + const ksamplerNode = comfyPage.vueNodes.getNodeByTitle('KSampler') - await checkpointNode.getByText('Load Checkpoint').click() - await ksamplerNode.getByText('KSampler').click({ modifiers: ['Control'] }) await comfyPage.page.keyboard.press(BYPASS_HOTKEY) await expect(checkpointNode).toHaveClass(BYPASS_CLASS) await expect(ksamplerNode).toHaveClass(BYPASS_CLASS) diff --git a/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts new file mode 100644 index 000000000..37dcfd37b --- /dev/null +++ b/browser_tests/tests/vueNodes/nodeStates/mute.spec.ts @@ -0,0 +1,45 @@ +import { + comfyExpect as expect, + comfyPageFixture as test +} from '../../../fixtures/ComfyPage' + +const MUTE_HOTKEY = 'Control+m' +const MUTE_CLASS = /opacity-50/ + +test.describe('Vue Node Mute', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.VueNodes.Enabled', true) + await comfyPage.vueNodes.waitForNodes() + }) + + test('should allow toggling mute on a selected node with hotkey', async ({ + comfyPage + }) => { + await comfyPage.page.getByText('Load Checkpoint').click() + await comfyPage.page.keyboard.press(MUTE_HOTKEY) + + const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') + await expect(checkpointNode).toHaveClass(MUTE_CLASS) + + await comfyPage.page.keyboard.press(MUTE_HOTKEY) + await expect(checkpointNode).not.toHaveClass(MUTE_CLASS) + }) + + test('should allow toggling mute on multiple selected nodes with hotkey', async ({ + comfyPage + }) => { + await comfyPage.page.getByText('Load Checkpoint').click() + await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] }) + + const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint') + const ksamplerNode = comfyPage.vueNodes.getNodeByTitle('KSampler') + + await comfyPage.page.keyboard.press(MUTE_HOTKEY) + await expect(checkpointNode).toHaveClass(MUTE_CLASS) + await expect(ksamplerNode).toHaveClass(MUTE_CLASS) + + await comfyPage.page.keyboard.press(MUTE_HOTKEY) + await expect(checkpointNode).not.toHaveClass(MUTE_CLASS) + await expect(ksamplerNode).not.toHaveClass(MUTE_CLASS) + }) +}) diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 7171ec770..828ee3c8c 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -21,6 +21,8 @@ 'animate-pulse': executing, 'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0': bypassed, + 'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:inset-0': + muted, 'will-change-transform': isDragging }, @@ -213,6 +215,7 @@ const hasAnyError = computed((): boolean => { }) const bypassed = computed((): boolean => nodeData.mode === 4) +const muted = computed((): boolean => nodeData.mode === 2) // NEVER mode // Use canvas interactions for proper wheel event handling and pointer event capture control const { handleWheel, shouldHandleNodePointerEvents } = useCanvasInteractions()