diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 96841ebc0..493e6a04b 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -44,7 +44,6 @@ :node-data="nodeData" :position="nodePositions.get(nodeData.id)" :size="nodeSizes.get(nodeData.id)" - :selected="nodeData.selected" :readonly="false" :executing="executionStore.executingNodeId === nodeData.id" :error=" @@ -79,6 +78,7 @@ import { computed, onMounted, onUnmounted, + provide, ref, shallowRef, watch, @@ -112,6 +112,7 @@ import { useWorkflowPersistence } from '@/composables/useWorkflowPersistence' import { CORE_SETTINGS } from '@/constants/coreSettings' import { i18n, t } from '@/i18n' import type { LGraphNode } from '@/lib/litegraph/src/litegraph' +import { SelectedNodeIdsKey } from '@/renderer/core/canvas/injectionKeys' import TransformPane from '@/renderer/core/layout/TransformPane.vue' import MiniMap from '@/renderer/extensions/minimap/MiniMap.vue' import VueGraphNode from '@/renderer/extensions/vueNodes/components/LGraphNode.vue' @@ -189,6 +190,21 @@ const handleNodeSelect = nodeEventHandlers.handleNodeSelect const handleNodeCollapse = nodeEventHandlers.handleNodeCollapse const handleNodeTitleUpdate = nodeEventHandlers.handleNodeTitleUpdate +// Provide selection state to all Vue nodes +const selectedNodeIds = ref(new Set()) +provide(SelectedNodeIdsKey, selectedNodeIds) +watch( + () => canvasStore.selectedItems, + (newSelectedItems) => { + selectedNodeIds.value = new Set( + newSelectedItems + .filter((item) => item.id !== undefined) + .map((item) => String(item.id)) + ) + }, + { immediate: true } +) + watchEffect(() => { nodeDefStore.showDeprecated = settingStore.get('Comfy.Node.ShowDeprecated') }) diff --git a/src/composables/graph/useNodeEventHandlers.ts b/src/composables/graph/useNodeEventHandlers.ts index 377965fe2..e00f67b5b 100644 --- a/src/composables/graph/useNodeEventHandlers.ts +++ b/src/composables/graph/useNodeEventHandlers.ts @@ -33,12 +33,20 @@ export function useNodeEventHandlers(nodeManager: Ref) { const node = nodeManager.value.getNode(nodeData.id) if (!node) return - // Handle multi-select with Ctrl/Cmd key - if (!event.ctrlKey && !event.metaKey) { - canvasStore.canvas.deselectAllNodes() - } + const isMultiSelect = event.ctrlKey || event.metaKey - canvasStore.canvas.selectNode(node) + if (isMultiSelect) { + // Ctrl/Cmd+click -> toggle selection + if (node.selected) { + canvasStore.canvas.deselect(node) + } else { + canvasStore.canvas.select(node) + } + } else { + // Regular click -> single select + canvasStore.canvas.deselectAll() + canvasStore.canvas.select(node) + } // Bring node to front when clicked (similar to LiteGraph behavior) // Skip if node is pinned to avoid unwanted movement @@ -47,9 +55,6 @@ export function useNodeEventHandlers(nodeManager: Ref) { layoutMutations.bringNodeToFront(nodeData.id) } - // Ensure node selection state is set - node.selected = true - // Update canvas selection tracking canvasStore.updateSelectedItems() } diff --git a/src/renderer/core/canvas/injectionKeys.ts b/src/renderer/core/canvas/injectionKeys.ts new file mode 100644 index 000000000..4134846c3 --- /dev/null +++ b/src/renderer/core/canvas/injectionKeys.ts @@ -0,0 +1,8 @@ +import type { InjectionKey, Ref } from 'vue' + +/** + * Injection key for providing selected node IDs to Vue node components. + * Contains a reactive Set of selected node IDs (as strings). + */ +export const SelectedNodeIdsKey: InjectionKey>> = + Symbol('selectedNodeIds') diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 4ce4e2100..2fb26fdca 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -10,10 +10,13 @@ 'bg-white dark-theme:bg-[#15161A]', 'min-w-[445px]', 'lg-node absolute border border-solid rounded-2xl', - 'outline outline-transparent outline-2 hover:outline-black dark-theme:hover:outline-white', + 'outline outline-transparent outline-2', { - 'border-blue-500 ring-2 ring-blue-300': selected, - 'border-[#e1ded5] dark-theme:border-[#292A30]': !selected, + 'outline-black dark-theme:outline-white': isSelected + }, + { + 'border-blue-500 ring-2 ring-blue-300': isSelected, + 'border-[#e1ded5] dark-theme:border-[#292A30]': !isSelected, 'animate-pulse': executing, 'opacity-50': nodeData.mode === 4, 'border-red-500 bg-red-50': error, @@ -107,12 +110,13 @@