mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-27 01:39:47 +00:00
Add muted state to Vue nodes (#5770)
## 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)
This commit is contained in:
@@ -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
|
* Get total count of Vue nodes in the DOM
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ test.describe('Vue Node Bypass', () => {
|
|||||||
test('should allow toggling bypass on a selected node with hotkey', async ({
|
test('should allow toggling bypass on a selected node with hotkey', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
const checkpointNode = comfyPage.page.locator('[data-node-id]').filter({
|
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||||
hasText: 'Load Checkpoint'
|
|
||||||
})
|
|
||||||
await checkpointNode.getByText('Load Checkpoint').click()
|
|
||||||
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
||||||
|
|
||||||
|
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
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 ({
|
test('should allow toggling bypass on multiple selected nodes with hotkey', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
const checkpointNode = comfyPage.page.locator('[data-node-id]').filter({
|
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||||
hasText: 'Load Checkpoint'
|
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
|
||||||
})
|
|
||||||
const ksamplerNode = comfyPage.page.locator('[data-node-id]').filter({
|
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
hasText: 'KSampler'
|
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 comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
||||||
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
||||||
await expect(ksamplerNode).toHaveClass(BYPASS_CLASS)
|
await expect(ksamplerNode).toHaveClass(BYPASS_CLASS)
|
||||||
|
|||||||
45
browser_tests/tests/vueNodes/nodeStates/mute.spec.ts
Normal file
45
browser_tests/tests/vueNodes/nodeStates/mute.spec.ts
Normal file
@@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
'animate-pulse': executing,
|
'animate-pulse': executing,
|
||||||
'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0':
|
'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0':
|
||||||
bypassed,
|
bypassed,
|
||||||
|
'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:inset-0':
|
||||||
|
muted,
|
||||||
'will-change-transform': isDragging
|
'will-change-transform': isDragging
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -213,6 +215,7 @@ const hasAnyError = computed((): boolean => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const bypassed = computed((): boolean => nodeData.mode === 4)
|
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
|
// Use canvas interactions for proper wheel event handling and pointer event capture control
|
||||||
const { handleWheel, shouldHandleNodePointerEvents } = useCanvasInteractions()
|
const { handleWheel, shouldHandleNodePointerEvents } = useCanvasInteractions()
|
||||||
|
|||||||
Reference in New Issue
Block a user