mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
fix Vue node opacity conditions (user node opacity, bypass state, muted state) (#6022)
## Summary
Fixed Vue node opacity calculation to properly combine global opacity
setting with muted/bypassed state opacity.
**Root Cause**: When global opacity setting was added as inline style
(481aa8252), it began overriding CSS `opacity-50` classes due to higher
specificity.
**Solution**: Modified `nodeOpacity` computed property to calculate
effective opacity as `globalOpacity * 0.5` for muted/bypassed states,
removing conflicting CSS classes.
## Changes
- **What**: Fixed [CSS specificity
conflict](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)
where inline `opacity` style overrode `opacity-50` classes for
muted/bypassed nodes
- **Breaking**: None - restores intended opacity behavior
## Review Focus
Multiplicative opacity calculation ensuring muted/bypassed nodes apply
0.5 opacity on top of global opacity setting rather than being
overridden by it.
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6022-fix-Vue-node-opacity-conditions-user-node-opacity-bypass-state-muted-state-2896d73d365081c290f1da37c195c2f5)
by [Unito](https://www.unito.io)
---------
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -20,6 +20,9 @@ test.describe('Vue Node Bypass', () => {
|
|||||||
|
|
||||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'vue-node-bypassed-state.png'
|
||||||
|
)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
||||||
await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS)
|
await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -4,7 +4,7 @@ import {
|
|||||||
} from '../../../fixtures/ComfyPage'
|
} from '../../../fixtures/ComfyPage'
|
||||||
|
|
||||||
const MUTE_HOTKEY = 'Control+m'
|
const MUTE_HOTKEY = 'Control+m'
|
||||||
const MUTE_CLASS = /opacity-50/
|
const MUTE_OPACITY = '0.5'
|
||||||
|
|
||||||
test.describe('Vue Node Mute', () => {
|
test.describe('Vue Node Mute', () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
@@ -19,10 +19,11 @@ test.describe('Vue Node Mute', () => {
|
|||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
|
|
||||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
await expect(checkpointNode).toHaveClass(MUTE_CLASS)
|
await expect(checkpointNode).toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-muted-state.png')
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
await expect(checkpointNode).not.toHaveClass(MUTE_CLASS)
|
await expect(checkpointNode).not.toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should allow toggling mute on multiple selected nodes with hotkey', async ({
|
test('should allow toggling mute on multiple selected nodes with hotkey', async ({
|
||||||
@@ -35,11 +36,11 @@ test.describe('Vue Node Mute', () => {
|
|||||||
const ksamplerNode = comfyPage.vueNodes.getNodeByTitle('KSampler')
|
const ksamplerNode = comfyPage.vueNodes.getNodeByTitle('KSampler')
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
await expect(checkpointNode).toHaveClass(MUTE_CLASS)
|
await expect(checkpointNode).toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
await expect(ksamplerNode).toHaveClass(MUTE_CLASS)
|
await expect(ksamplerNode).toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
await expect(checkpointNode).not.toHaveClass(MUTE_CLASS)
|
await expect(checkpointNode).not.toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
await expect(ksamplerNode).not.toHaveClass(MUTE_CLASS)
|
await expect(ksamplerNode).not.toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -19,9 +19,9 @@
|
|||||||
outlineClass,
|
outlineClass,
|
||||||
{
|
{
|
||||||
'animate-pulse': executing,
|
'animate-pulse': executing,
|
||||||
'opacity-50 before:rounded-2xl before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0':
|
'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':
|
'before:rounded-2xl before:pointer-events-none before:absolute before:inset-0':
|
||||||
muted,
|
muted,
|
||||||
'will-change-transform': isDragging
|
'will-change-transform': isDragging
|
||||||
},
|
},
|
||||||
@@ -174,9 +174,6 @@ const {
|
|||||||
|
|
||||||
useVueElementTracking(() => nodeData.id, 'node')
|
useVueElementTracking(() => nodeData.id, 'node')
|
||||||
|
|
||||||
const { selectedNodeIds } = storeToRefs(useCanvasStore())
|
|
||||||
|
|
||||||
// Inject transform state for coordinate conversion
|
|
||||||
const transformState = inject(TransformStateKey)
|
const transformState = inject(TransformStateKey)
|
||||||
if (!transformState) {
|
if (!transformState) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -184,16 +181,13 @@ if (!transformState) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computed selection state - only this node re-evaluates when its selection changes
|
const { selectedNodeIds } = storeToRefs(useCanvasStore())
|
||||||
const isSelected = computed(() => {
|
const isSelected = computed(() => {
|
||||||
return selectedNodeIds.value.has(nodeData.id)
|
return selectedNodeIds.value.has(nodeData.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use execution state composable
|
|
||||||
const nodeLocatorId = computed(() => getLocatorIdFromNodeData(nodeData))
|
const nodeLocatorId = computed(() => getLocatorIdFromNodeData(nodeData))
|
||||||
const { executing, progress } = useNodeExecutionState(nodeLocatorId)
|
const { executing, progress } = useNodeExecutionState(nodeLocatorId)
|
||||||
|
|
||||||
// Direct access to execution store for error state
|
|
||||||
const executionStore = useExecutionStore()
|
const executionStore = useExecutionStore()
|
||||||
const hasExecutionError = computed(
|
const hasExecutionError = computed(
|
||||||
() => executionStore.lastExecutionErrorNodeId === nodeData.id
|
() => executionStore.lastExecutionErrorNodeId === nodeData.id
|
||||||
@@ -225,9 +219,16 @@ const nodeBodyBackgroundColor = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const nodeOpacity = computed(
|
const nodeOpacity = computed(() => {
|
||||||
() => useSettingStore().get('Comfy.Node.Opacity') ?? 1
|
const globalOpacity = useSettingStore().get('Comfy.Node.Opacity') ?? 1
|
||||||
)
|
|
||||||
|
// For muted/bypassed nodes, apply the 0.5 multiplier on top of global opacity
|
||||||
|
if (bypassed.value || muted.value) {
|
||||||
|
return globalOpacity * 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
return globalOpacity
|
||||||
|
})
|
||||||
|
|
||||||
// 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