From 3166acb825966504cba8d7f90650c976eef68c57 Mon Sep 17 00:00:00 2001 From: bymyself Date: Wed, 11 Jun 2025 12:50:11 -0700 Subject: [PATCH] sync position, drag, re-render conditions --- src/components/graph/GraphCanvas.vue | 18 +- src/components/graph/nodes/VueNode.vue | 453 +++++++++++++++--- src/components/graph/nodes/VueNodeBody.vue | 120 ++--- src/components/graph/nodes/VueNodeHeader.vue | 121 +++-- src/components/graph/nodes/VueNodeOverlay.vue | 128 ++++- .../nodeRendering/useNodePositionSync.ts | 21 +- .../nodeRendering/useTestPhantomNodes.ts | 59 +++ src/composables/useCoreCommands.ts | 2 + src/scripts/app.ts | 2 + src/scripts/changeTracker.ts | 27 +- src/scripts/domWidget.ts | 3 +- 11 files changed, 724 insertions(+), 230 deletions(-) create mode 100644 src/composables/nodeRendering/useTestPhantomNodes.ts diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 4a855623a..6613355a9 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -37,7 +37,7 @@ - + @@ -54,15 +54,16 @@ import SubgraphBreadcrumb from '@/components/breadcrumb/SubgraphBreadcrumb.vue' import DomWidgets from '@/components/graph/DomWidgets.vue' import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue' import NodeTooltip from '@/components/graph/NodeTooltip.vue' -import VueNodeOverlay from '@/components/graph/nodes/VueNodeOverlay.vue' import SelectionOverlay from '@/components/graph/SelectionOverlay.vue' import SelectionToolbox from '@/components/graph/SelectionToolbox.vue' import TitleEditor from '@/components/graph/TitleEditor.vue' +import VueNodeOverlay from '@/components/graph/nodes/VueNodeOverlay.vue' import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vue' import SideToolbar from '@/components/sidebar/SideToolbar.vue' import SecondRowWorkflowTabs from '@/components/topbar/SecondRowWorkflowTabs.vue' import { useChainCallback } from '@/composables/functional/useChainCallback' import { useNodeBadge } from '@/composables/node/useNodeBadge' +import { useTestPhantomNodes } from '@/composables/nodeRendering/useTestPhantomNodes' import { useCanvasDrop } from '@/composables/useCanvasDrop' import { useContextMenuTranslation } from '@/composables/useContextMenuTranslation' import { useCopy } from '@/composables/useCopy' @@ -115,6 +116,17 @@ const selectionToolboxEnabled = computed(() => // Temporarily enable Vue node rendering for testing const vueNodeRenderingEnabled = computed(() => true) +// Use test helper for automatic phantom mode enabling +useTestPhantomNodes() + +// Debug logging +watchEffect(() => { + console.log( + '🖼️ GraphCanvas: Vue node rendering enabled:', + vueNodeRenderingEnabled.value + ) +}) + watchEffect(() => { nodeDefStore.showDeprecated = settingStore.get('Comfy.Node.ShowDeprecated') }) @@ -281,6 +293,7 @@ onMounted(async () => { useWorkflowAutoSave() comfyApp.vueAppReady = true + console.log('🖼️ GraphCanvas: comfyApp.vueAppReady:', comfyApp.vueAppReady) workspaceStore.spinner = true // ChangeTracker needs to be initialized before setup, as it will overwrite @@ -314,6 +327,7 @@ onMounted(async () => { window.graph = comfyApp.graph comfyAppReady.value = true + console.log('🖼️ GraphCanvas: comfyAppReady:', comfyAppReady.value) comfyApp.canvas.onSelectionChange = useChainCallback( comfyApp.canvas.onSelectionChange, diff --git a/src/components/graph/nodes/VueNode.vue b/src/components/graph/nodes/VueNode.vue index 068811d35..94b66ad70 100644 --- a/src/components/graph/nodes/VueNode.vue +++ b/src/components/graph/nodes/VueNode.vue @@ -1,50 +1,76 @@ \ No newline at end of file diff --git a/src/components/graph/nodes/VueNodeBody.vue b/src/components/graph/nodes/VueNodeBody.vue index 6d47f8679..346c3e2ea 100644 --- a/src/components/graph/nodes/VueNodeBody.vue +++ b/src/components/graph/nodes/VueNodeBody.vue @@ -1,55 +1,40 @@ \ No newline at end of file + diff --git a/src/components/graph/nodes/VueNodeHeader.vue b/src/components/graph/nodes/VueNodeHeader.vue index 9c6ddd12f..7169b3a4a 100644 --- a/src/components/graph/nodes/VueNodeHeader.vue +++ b/src/components/graph/nodes/VueNodeHeader.vue @@ -1,48 +1,42 @@ \ No newline at end of file + diff --git a/src/composables/nodeRendering/useNodePositionSync.ts b/src/composables/nodeRendering/useNodePositionSync.ts index b176b634a..6e0e4af72 100644 --- a/src/composables/nodeRendering/useNodePositionSync.ts +++ b/src/composables/nodeRendering/useNodePositionSync.ts @@ -86,16 +86,27 @@ export function useNodePositionSync() { // Get visible nodes (within viewport bounds) const visibleNodes = computed(() => { - if (!canvas.value?.graph) return [] + if (!canvas.value?.graph) { + console.log('🚫 useNodePositionSync: No canvas or graph available') + return [] + } - const nodes = canvas.value.graph._nodes.filter((node: LGraphNode) => { - // Only return nodes that have phantom_mode enabled - return node.phantom_mode === true + const allNodes = canvas.value.graph._nodes + console.log('🔍 useNodePositionSync: Checking', allNodes.length, 'total nodes') + + const phantomNodes = allNodes.filter((node: LGraphNode) => { + const isPhantom = node.phantom_mode === true + if (isPhantom) { + console.log('👻 Found phantom node:', { id: node.id, title: node.title, phantom_mode: node.phantom_mode }) + } + return isPhantom }) + console.log('📊 useNodePositionSync: Found', phantomNodes.length, 'phantom nodes out of', allNodes.length, 'total') + // TODO: Add viewport culling for performance // For now, return all phantom nodes - return nodes + return phantomNodes }) // Manual sync function for external triggers diff --git a/src/composables/nodeRendering/useTestPhantomNodes.ts b/src/composables/nodeRendering/useTestPhantomNodes.ts new file mode 100644 index 000000000..0e141d4d6 --- /dev/null +++ b/src/composables/nodeRendering/useTestPhantomNodes.ts @@ -0,0 +1,59 @@ +import { onMounted, onUnmounted } from 'vue' +import { usePhantomNodes } from './usePhantomNodes' +import { useCanvasStore } from '@/stores/graphStore' +import { api } from '@/scripts/api' + +/** + * Development helper to automatically enable phantom mode for testing + */ +export function useTestPhantomNodes() { + const { enableAllPhantomMode, getPhantomNodes } = usePhantomNodes() + const canvasStore = useCanvasStore() + + let graphChangeHandler: (() => void) | null = null + + onMounted(() => { + // Function to enable phantom mode for all nodes + const enablePhantomModeForAllNodes = () => { + if (canvasStore.canvas?.graph) { + const count = enableAllPhantomMode() + if (count > 0) { + console.log(`✅ Enabled phantom mode for ${count} nodes`) + } + return count + } + return 0 + } + + // Listen for graph changes to immediately enable phantom mode for new nodes + graphChangeHandler = () => { + enablePhantomModeForAllNodes() + } + + api.addEventListener('graphChanged', graphChangeHandler) + + // Initial attempt when mounted + setTimeout(() => { + enablePhantomModeForAllNodes() + }, 100) // Much shorter timeout just to ensure canvas is ready + }) + + onUnmounted(() => { + if (graphChangeHandler) { + api.removeEventListener('graphChanged', graphChangeHandler) + } + }) + + // Expose helper functions to global scope for manual testing + if (typeof window !== 'undefined') { + (window as any).testPhantomNodes = { + enableAll: enableAllPhantomMode, + getPhantom: getPhantomNodes, + enableSingle: (nodeId: string) => { + const { enablePhantomMode } = usePhantomNodes() + return enablePhantomMode(nodeId) + } + } + console.log('🚀 Phantom node testing helpers available on window.testPhantomNodes') + } +} \ No newline at end of file diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index 46070dfe9..feddaa728 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -255,6 +255,8 @@ export function useCoreCommands(): ComfyCommand[] { return } app.canvas.fitViewToSelectionAnimated() + // Trigger re-render of Vue nodes after view change + api.dispatchCustomEvent('graphChanged') } }, { diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 31d2ba1cc..d66e053af 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -1150,6 +1150,7 @@ export class ComfyApp { } useExtensionService().invokeExtensions('loadedGraphNode', node) + api.dispatchCustomEvent('graphChanged', { workflow: this.graph.serialize() as unknown as ComfyWorkflowJSON }) } if (missingNodeTypes.length && showMissingNodesDialog) { @@ -1169,6 +1170,7 @@ export class ComfyApp { ) requestAnimationFrame(() => { this.graph.setDirtyCanvas(true, true) + api.dispatchCustomEvent('graphChanged', { workflow: this.graph.serialize() as unknown as ComfyWorkflowJSON }) }) } diff --git a/src/scripts/changeTracker.ts b/src/scripts/changeTracker.ts index 30c2f3289..199ed0998 100644 --- a/src/scripts/changeTracker.ts +++ b/src/scripts/changeTracker.ts @@ -271,6 +271,27 @@ export class ChangeTracker { return v } + // Handle wheel events (zoom/pan with mouse wheel) + const processMouseWheel = LGraphCanvas.prototype.processMouseWheel + LGraphCanvas.prototype.processMouseWheel = function (e) { + const v = processMouseWheel.apply(this, [e]) + logger.debug('checkState on processMouseWheel') + checkState() + return v + } + + // Handle drag events (panning) + const processMouseMove = LGraphCanvas.prototype.processMouseMove + LGraphCanvas.prototype.processMouseMove = function (e) { + const v = processMouseMove.apply(this, [e]) + // Only check state if we're dragging the canvas (not a node) + if (this.dragging_canvas) { + logger.debug('checkState on processMouseMove (canvas drag)') + checkState() + } + return v + } + // Handle litegraph dialog popup for number/string widgets const prompt = LGraphCanvas.prototype.prompt LGraphCanvas.prototype.prompt = function ( @@ -369,10 +390,8 @@ export class ChangeTracker { return false } - // Compare extra properties ignoring ds - if ( - !_.isEqual(_.omit(a.extra ?? {}, ['ds']), _.omit(b.extra ?? {}, ['ds'])) - ) + // Compare extra properties including ds for Vue node position updates + if (!_.isEqual(a.extra ?? {}, b.extra ?? {})) return false // Compare other properties normally diff --git a/src/scripts/domWidget.ts b/src/scripts/domWidget.ts index 6acfcd95f..e8ac12790 100644 --- a/src/scripts/domWidget.ts +++ b/src/scripts/domWidget.ts @@ -241,7 +241,8 @@ export class ComponentWidgetImpl< }) { super({ ...obj, - type: 'custom' + type: 'custom', + options: { hideOnZoom: true, ...obj.options } }) this.component = obj.component this.inputSpec = obj.inputSpec