From e2feb5739a9775b923074281af64f5a199fd770f Mon Sep 17 00:00:00 2001 From: AEmotionStudio Date: Mon, 5 Jan 2026 16:26:56 -0800 Subject: [PATCH] fix: explicitly set selected node in state manager when `onNodeSelected` is invoked. --- src/info-panel/InfoPanel.ts | 3 +++ web/info-panel/InfoPanel.js | 1 + web/info-panel/InfoPanel.js.map | 2 +- web/popout-viewer.html | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/info-panel/InfoPanel.ts b/src/info-panel/InfoPanel.ts index d6fd81c..64584c7 100644 --- a/src/info-panel/InfoPanel.ts +++ b/src/info-panel/InfoPanel.ts @@ -278,6 +278,9 @@ export class InfoPanel { onNodeSelected(nodeId: number): void { Logger.debug(`Node selected from dropdown: ${nodeId}`); + // Set the selected node in state manager + this.stateManager.setSelectedNode(nodeId); + // Force an immediate info update this.updateInfo(); } diff --git a/web/info-panel/InfoPanel.js b/web/info-panel/InfoPanel.js index ff94941..be20a19 100644 --- a/web/info-panel/InfoPanel.js +++ b/web/info-panel/InfoPanel.js @@ -190,6 +190,7 @@ class InfoPanel { */ onNodeSelected(nodeId) { Logger.debug(`Node selected from dropdown: ${nodeId}`); + this.stateManager.setSelectedNode(nodeId); this.updateInfo(); } /** diff --git a/web/info-panel/InfoPanel.js.map b/web/info-panel/InfoPanel.js.map index 6334957..4082226 100644 --- a/web/info-panel/InfoPanel.js.map +++ b/web/info-panel/InfoPanel.js.map @@ -1 +1 @@ -{"version":3,"file":"InfoPanel.js","sources":["../../src/info-panel/InfoPanel.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel Orchestrator (TypeScript)\n * \n * Complete Info Panel Manager extracted from magnify_info_panel.js\n * Main class that orchestrates all info panel components.\n */\n\nimport type { MagnifyGlassInstance } from '../types/comfyui';\nimport { registerPanelSettings } from '../shared/settings';\nimport { Logger } from '../shared/logger';\nimport { StateManager } from './StateManager';\nimport { UIManager } from './UIManager';\nimport { PositionManager } from './PositionManager';\nimport { EventManager } from './EventManager';\nimport { InformationGatherer } from './InformationGatherer';\n\n/**\n * Professional Info Panel Manager.\n * Orchestrates all the other components for the info panel.\n */\nexport class InfoPanel {\n magnifyGlass: MagnifyGlassInstance;\n stateManager: StateManager;\n uiManager: UIManager;\n positionManager: PositionManager;\n eventManager: EventManager;\n informationGatherer: InformationGatherer;\n\n constructor(magnifyGlass: MagnifyGlassInstance) {\n this.magnifyGlass = magnifyGlass;\n\n // Initialize Managers\n this.stateManager = new StateManager();\n this.uiManager = new UIManager(this.stateManager);\n this.informationGatherer = new InformationGatherer();\n\n // Position Manager needs the UI elements\n this.positionManager = new PositionManager(this.stateManager, this.uiManager.elements.panel!);\n\n // Event Manager needs state and UI manager\n this.eventManager = new EventManager(this.stateManager, this.uiManager.elements.panel!, this.positionManager, {\n toggleVisibility: () => {\n if (this.stateManager.state.isPanelVisible) {\n this.uiManager.hide();\n } else {\n this.uiManager.show();\n }\n },\n togglePin: () => {\n this.stateManager.togglePinning();\n this.uiManager.updatePinnedState();\n },\n updatePosition: () => this.positionManager.positionPanel()\n });\n\n this.hookIntoMagnifyGlass();\n\n // Register settings with ComfyUI\n registerPanelSettings(this.stateManager, this.uiManager, this.positionManager);\n\n // Listen for popout state changes to update UI buttons\n if (this.magnifyGlass.popOutManager) {\n this.magnifyGlass.popOutManager.onStateChange = (isOpen: boolean) => {\n this.uiManager.updateControlStates();\n };\n // Listen for node selection from popout\n this.magnifyGlass.popOutManager.onNodeSelect = (nodeId: number) => {\n this.onNodeSelected(nodeId);\n };\n }\n\n // Set up node selection callback\n this.uiManager.onNodeSelected = (nodeId: number) => {\n this.onNodeSelected(nodeId);\n };\n\n Logger.info('Info Panel initialized successfully');\n }\n\n hookIntoMagnifyGlass(): void {\n // Hook into magnify glass update cycle\n const originalUpdateMagnifiedView = this.magnifyGlass.updateMagnifiedView.bind(this.magnifyGlass);\n\n this.magnifyGlass.updateMagnifiedView = (() => {\n originalUpdateMagnifiedView();\n\n // Schedule info update if panel is enabled and glass is active\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] &&\n this.magnifyGlass.state.active) {\n this.scheduleInfoUpdate();\n }\n }).bind(this);\n\n // Hook into show/hide\n const originalShow = this.magnifyGlass.ui.show.bind(this.magnifyGlass.ui);\n const originalHide = this.magnifyGlass.ui.hide.bind(this.magnifyGlass.ui);\n\n this.magnifyGlass.ui.show = (() => {\n originalShow();\n\n // CRITICAL: Only restore panel if the magnify glass is actually active\n // This prevents the panel from showing when toggle() is switching it OFF\n if (!this.magnifyGlass.state.active) {\n return;\n }\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"]) {\n // Restore state: If it was visible before hide, make it visible now.\n // Otherwise only show controls.\n if (this.stateManager.state.wasPanelVisibleBeforeHide) {\n this.uiManager.show();\n\n // Position properly\n setTimeout(() => {\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n }, 10);\n } else {\n // Do NOT force panel visible. Only show controls.\n const showControls = this.stateManager.state.settings[\"🔍MagnifyGlass.ShowHoveringControls\"] !== false;\n if (showControls && this.uiManager.elements.controls) {\n this.uiManager.elements.controls.style.display = 'flex';\n }\n\n // Ensure states are updated\n this.uiManager.updateControlStates();\n\n // Position controls after showing (relative to glass since panel is hidden)\n setTimeout(() => {\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n }, 10);\n }\n }\n }).bind(this);\n\n this.magnifyGlass.ui.hide = (() => {\n // Save state before hiding\n this.stateManager.state.wasPanelVisibleBeforeHide = this.stateManager.state.isPanelVisible;\n\n originalHide();\n this.uiManager.hide();\n\n // FORCE HIDE: Directly set panel display to none as a safety measure\n if (this.uiManager.elements.panel) {\n this.uiManager.elements.panel.style.display = 'none';\n }\n\n // Explicitly hide floating controls when glass is disabled\n if (this.uiManager.elements.controls) {\n this.uiManager.elements.controls.style.display = 'none';\n }\n }).bind(this);\n }\n\n scheduleInfoUpdate(): void {\n if (this.stateManager.state.updateScheduled) return;\n\n this.stateManager.state.updateScheduled = true;\n requestAnimationFrame(() => {\n this.updateInfo();\n this.stateManager.state.updateScheduled = false;\n });\n }\n\n // State for persistence\n private lastValidNodeInfo: any = null;\n\n updateInfo(): void {\n const settings = this.stateManager.state.settings;\n const isActive = this.magnifyGlass.state.active;\n\n // Safety: If disabled or inactive, ensure hidden and return\n if (!settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] || !isActive) {\n // Hide the panel completely when the glass is inactive\n this.uiManager.hide();\n if (this.uiManager.elements.controls && this.uiManager.elements.controls.style.display !== 'none') {\n this.uiManager.elements.controls.style.display = 'none';\n }\n return;\n }\n\n // Logic for \"Hold Info\" (Freeze)\n if (this.stateManager.state.isInfoHeld) {\n // If held, we skip gathering new info and skip updating the display content.\n // But we MUST still update positioning to keep the UI responsive to movement.\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n return;\n }\n\n let info = this.informationGatherer.gatherInformation();\n\n // NODE SELECTION OVERRIDE - check if user has selected a specific node\n const selectedNodeId = this.stateManager.state.selectedNodeId;\n if (selectedNodeId !== null) {\n const selectedNode = this.uiManager.nodeSelector.getNodeById(selectedNodeId);\n if (selectedNode) {\n // Override with selected node info\n const detailedInfo = this.informationGatherer.getDetailedNodeInfo(\n selectedNode,\n { x: 0, y: 0 } // No local position when manually selected\n );\n info = {\n ...info,\n hoveredNode: detailedInfo,\n hoveredWidget: null\n };\n }\n }\n // END NODE SELECTION OVERRIDE\n\n // PERSISTENCE LOGIC START\n if (info.hoveredNode) {\n // We have a valid node, update our cache\n this.lastValidNodeInfo = info;\n } else if (settings[\"🔍MagnifyGlass.InfoPanelPersist\"] && this.lastValidNodeInfo) {\n // No node hovered, but persistence is ON and we have a cache.\n // Merge the CACHED node info into the CURRENT info (so we keep current cursor pos/zoom but show old node)\n info = {\n ...info, // Current timestamp, cursor, zoom\n hoveredNode: this.lastValidNodeInfo.hoveredNode,\n hoveredWidget: this.lastValidNodeInfo.hoveredWidget\n };\n }\n // PERSISTENCE LOGIC END\n\n this.stateManager.setCurrentInfo(info);\n\n // Update the UI\n this.uiManager.displayInfo(info);\n\n // Send info to pop-out viewer if active\n if (this.magnifyGlass.popOutManager && this.magnifyGlass.popOutManager.isPopOutOpen()) {\n this.magnifyGlass.popOutManager.sendInfo(info as any);\n }\n\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n\n // Handle node hover state logic if needed for internal state\n if (info.hoveredNode) {\n this.stateManager.state.isHoveringNode = true;\n this.stateManager.expandNodeSections();\n if (this.stateManager.state.lastNodeId !== info.hoveredNode.id) {\n this.stateManager.state.lastNodeId = info.hoveredNode.id;\n }\n } else {\n this.stateManager.state.isHoveringNode = false;\n // Only collapse if we are NOT persisting\n if (!settings[\"🔍MagnifyGlass.InfoPanelPersist\"]) {\n if (this.stateManager.state.lastNodeId !== null) {\n this.stateManager.state.lastNodeId = null;\n if (!this.stateManager.state.isPanelHovered) {\n this.stateManager.scheduleAutoCollapse();\n }\n }\n }\n }\n }\n\n updateSettings(): void {\n const changes = this.stateManager.updateSettings();\n this.uiManager.applyStyles();\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] && this.magnifyGlass.state.active) {\n this.uiManager.show();\n // Ensure position is updated after potential dimension changes\n this.positionManager.positionPanel();\n } else {\n this.uiManager.hide();\n }\n }\n\n /**\n * Handle node selection from dropdown.\n * @param nodeId - ID of the selected node\n */\n onNodeSelected(nodeId: number): void {\n Logger.debug(`Node selected from dropdown: ${nodeId}`);\n\n // Force an immediate info update\n this.updateInfo();\n }\n\n /**\n * Clear the selected node, returning to hover-based detection.\n */\n clearSelectedNode(): void {\n this.stateManager.clearSelectedNode();\n this.updateInfo();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"InfoPanel.js","sources":["../../src/info-panel/InfoPanel.ts"],"sourcesContent":["/**\n * ComfyUI MagnifyGlass - Info Panel Orchestrator (TypeScript)\n * \n * Complete Info Panel Manager extracted from magnify_info_panel.js\n * Main class that orchestrates all info panel components.\n */\n\nimport type { MagnifyGlassInstance } from '../types/comfyui';\nimport { registerPanelSettings } from '../shared/settings';\nimport { Logger } from '../shared/logger';\nimport { StateManager } from './StateManager';\nimport { UIManager } from './UIManager';\nimport { PositionManager } from './PositionManager';\nimport { EventManager } from './EventManager';\nimport { InformationGatherer } from './InformationGatherer';\n\n/**\n * Professional Info Panel Manager.\n * Orchestrates all the other components for the info panel.\n */\nexport class InfoPanel {\n magnifyGlass: MagnifyGlassInstance;\n stateManager: StateManager;\n uiManager: UIManager;\n positionManager: PositionManager;\n eventManager: EventManager;\n informationGatherer: InformationGatherer;\n\n constructor(magnifyGlass: MagnifyGlassInstance) {\n this.magnifyGlass = magnifyGlass;\n\n // Initialize Managers\n this.stateManager = new StateManager();\n this.uiManager = new UIManager(this.stateManager);\n this.informationGatherer = new InformationGatherer();\n\n // Position Manager needs the UI elements\n this.positionManager = new PositionManager(this.stateManager, this.uiManager.elements.panel!);\n\n // Event Manager needs state and UI manager\n this.eventManager = new EventManager(this.stateManager, this.uiManager.elements.panel!, this.positionManager, {\n toggleVisibility: () => {\n if (this.stateManager.state.isPanelVisible) {\n this.uiManager.hide();\n } else {\n this.uiManager.show();\n }\n },\n togglePin: () => {\n this.stateManager.togglePinning();\n this.uiManager.updatePinnedState();\n },\n updatePosition: () => this.positionManager.positionPanel()\n });\n\n this.hookIntoMagnifyGlass();\n\n // Register settings with ComfyUI\n registerPanelSettings(this.stateManager, this.uiManager, this.positionManager);\n\n // Listen for popout state changes to update UI buttons\n if (this.magnifyGlass.popOutManager) {\n this.magnifyGlass.popOutManager.onStateChange = (isOpen: boolean) => {\n this.uiManager.updateControlStates();\n };\n // Listen for node selection from popout\n this.magnifyGlass.popOutManager.onNodeSelect = (nodeId: number) => {\n this.onNodeSelected(nodeId);\n };\n }\n\n // Set up node selection callback\n this.uiManager.onNodeSelected = (nodeId: number) => {\n this.onNodeSelected(nodeId);\n };\n\n Logger.info('Info Panel initialized successfully');\n }\n\n hookIntoMagnifyGlass(): void {\n // Hook into magnify glass update cycle\n const originalUpdateMagnifiedView = this.magnifyGlass.updateMagnifiedView.bind(this.magnifyGlass);\n\n this.magnifyGlass.updateMagnifiedView = (() => {\n originalUpdateMagnifiedView();\n\n // Schedule info update if panel is enabled and glass is active\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] &&\n this.magnifyGlass.state.active) {\n this.scheduleInfoUpdate();\n }\n }).bind(this);\n\n // Hook into show/hide\n const originalShow = this.magnifyGlass.ui.show.bind(this.magnifyGlass.ui);\n const originalHide = this.magnifyGlass.ui.hide.bind(this.magnifyGlass.ui);\n\n this.magnifyGlass.ui.show = (() => {\n originalShow();\n\n // CRITICAL: Only restore panel if the magnify glass is actually active\n // This prevents the panel from showing when toggle() is switching it OFF\n if (!this.magnifyGlass.state.active) {\n return;\n }\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"]) {\n // Restore state: If it was visible before hide, make it visible now.\n // Otherwise only show controls.\n if (this.stateManager.state.wasPanelVisibleBeforeHide) {\n this.uiManager.show();\n\n // Position properly\n setTimeout(() => {\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n }, 10);\n } else {\n // Do NOT force panel visible. Only show controls.\n const showControls = this.stateManager.state.settings[\"🔍MagnifyGlass.ShowHoveringControls\"] !== false;\n if (showControls && this.uiManager.elements.controls) {\n this.uiManager.elements.controls.style.display = 'flex';\n }\n\n // Ensure states are updated\n this.uiManager.updateControlStates();\n\n // Position controls after showing (relative to glass since panel is hidden)\n setTimeout(() => {\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n }, 10);\n }\n }\n }).bind(this);\n\n this.magnifyGlass.ui.hide = (() => {\n // Save state before hiding\n this.stateManager.state.wasPanelVisibleBeforeHide = this.stateManager.state.isPanelVisible;\n\n originalHide();\n this.uiManager.hide();\n\n // FORCE HIDE: Directly set panel display to none as a safety measure\n if (this.uiManager.elements.panel) {\n this.uiManager.elements.panel.style.display = 'none';\n }\n\n // Explicitly hide floating controls when glass is disabled\n if (this.uiManager.elements.controls) {\n this.uiManager.elements.controls.style.display = 'none';\n }\n }).bind(this);\n }\n\n scheduleInfoUpdate(): void {\n if (this.stateManager.state.updateScheduled) return;\n\n this.stateManager.state.updateScheduled = true;\n requestAnimationFrame(() => {\n this.updateInfo();\n this.stateManager.state.updateScheduled = false;\n });\n }\n\n // State for persistence\n private lastValidNodeInfo: any = null;\n\n updateInfo(): void {\n const settings = this.stateManager.state.settings;\n const isActive = this.magnifyGlass.state.active;\n\n // Safety: If disabled or inactive, ensure hidden and return\n if (!settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] || !isActive) {\n // Hide the panel completely when the glass is inactive\n this.uiManager.hide();\n if (this.uiManager.elements.controls && this.uiManager.elements.controls.style.display !== 'none') {\n this.uiManager.elements.controls.style.display = 'none';\n }\n return;\n }\n\n // Logic for \"Hold Info\" (Freeze)\n if (this.stateManager.state.isInfoHeld) {\n // If held, we skip gathering new info and skip updating the display content.\n // But we MUST still update positioning to keep the UI responsive to movement.\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n return;\n }\n\n let info = this.informationGatherer.gatherInformation();\n\n // NODE SELECTION OVERRIDE - check if user has selected a specific node\n const selectedNodeId = this.stateManager.state.selectedNodeId;\n if (selectedNodeId !== null) {\n const selectedNode = this.uiManager.nodeSelector.getNodeById(selectedNodeId);\n if (selectedNode) {\n // Override with selected node info\n const detailedInfo = this.informationGatherer.getDetailedNodeInfo(\n selectedNode,\n { x: 0, y: 0 } // No local position when manually selected\n );\n info = {\n ...info,\n hoveredNode: detailedInfo,\n hoveredWidget: null\n };\n }\n }\n // END NODE SELECTION OVERRIDE\n\n // PERSISTENCE LOGIC START\n if (info.hoveredNode) {\n // We have a valid node, update our cache\n this.lastValidNodeInfo = info;\n } else if (settings[\"🔍MagnifyGlass.InfoPanelPersist\"] && this.lastValidNodeInfo) {\n // No node hovered, but persistence is ON and we have a cache.\n // Merge the CACHED node info into the CURRENT info (so we keep current cursor pos/zoom but show old node)\n info = {\n ...info, // Current timestamp, cursor, zoom\n hoveredNode: this.lastValidNodeInfo.hoveredNode,\n hoveredWidget: this.lastValidNodeInfo.hoveredWidget\n };\n }\n // PERSISTENCE LOGIC END\n\n this.stateManager.setCurrentInfo(info);\n\n // Update the UI\n this.uiManager.displayInfo(info);\n\n // Send info to pop-out viewer if active\n if (this.magnifyGlass.popOutManager && this.magnifyGlass.popOutManager.isPopOutOpen()) {\n this.magnifyGlass.popOutManager.sendInfo(info as any);\n }\n\n this.positionManager.positionPanel();\n this.positionManager.positionFloatingControls(this.uiManager.elements.controls);\n\n // Handle node hover state logic if needed for internal state\n if (info.hoveredNode) {\n this.stateManager.state.isHoveringNode = true;\n this.stateManager.expandNodeSections();\n if (this.stateManager.state.lastNodeId !== info.hoveredNode.id) {\n this.stateManager.state.lastNodeId = info.hoveredNode.id;\n }\n } else {\n this.stateManager.state.isHoveringNode = false;\n // Only collapse if we are NOT persisting\n if (!settings[\"🔍MagnifyGlass.InfoPanelPersist\"]) {\n if (this.stateManager.state.lastNodeId !== null) {\n this.stateManager.state.lastNodeId = null;\n if (!this.stateManager.state.isPanelHovered) {\n this.stateManager.scheduleAutoCollapse();\n }\n }\n }\n }\n }\n\n updateSettings(): void {\n const changes = this.stateManager.updateSettings();\n this.uiManager.applyStyles();\n\n if (this.stateManager.state.settings[\"🔍MagnifyGlass.InfoPanelEnabled\"] && this.magnifyGlass.state.active) {\n this.uiManager.show();\n // Ensure position is updated after potential dimension changes\n this.positionManager.positionPanel();\n } else {\n this.uiManager.hide();\n }\n }\n\n /**\n * Handle node selection from dropdown.\n * @param nodeId - ID of the selected node\n */\n onNodeSelected(nodeId: number): void {\n Logger.debug(`Node selected from dropdown: ${nodeId}`);\n\n // Set the selected node in state manager\n this.stateManager.setSelectedNode(nodeId);\n\n // Force an immediate info update\n this.updateInfo();\n }\n\n /**\n * Clear the selected node, returning to hover-based detection.\n */\n clearSelectedNode(): void {\n this.stateManager.clearSelectedNode();\n this.updateInfo();\n }\n}\n"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/web/popout-viewer.html b/web/popout-viewer.html index 4515db1..e1e2d08 100644 --- a/web/popout-viewer.html +++ b/web/popout-viewer.html @@ -1340,8 +1340,10 @@ } function selectNode(nodeId) { + console.log('[Popout] selectNode called with nodeId:', nodeId); // Send node selection to main window if (channel) { + console.log('[Popout] Sending node-select message'); channel.postMessage({ type: 'node-select', data: nodeId }); } }