import { app } from "../../scripts/app.js"; if ( !window.__comfyflowHighlightPatch ) { window.__comfyflowHighlightPatch = true; const originalDrawNode = LGraphCanvas.prototype.drawNode; LGraphCanvas.prototype.drawNode = function( node, ctx ) { originalDrawNode.call( this, node, ctx ); if ( node && node.__comfyflowHighlight ) { ctx.save(); const pulse = ( Math.sin( Date.now() / 150 ) + 1 ) / 2; ctx.strokeStyle = `rgba( 255, 255, 0, ${0.5 + pulse * 0.5} )`; ctx.lineWidth = 8; ctx.strokeRect( node.pos[0] - 12, node.pos[1] - 12, node.size[0] + 24, node.size[1] + 24 ); ctx.restore(); } }; } export function locateNode( nodeId ) { const target = app.graph._nodes.find( node => node.id === nodeId ); if (!target) { alert( "Node not found." ); return; } try { if ( app.canvas.selectNode ) { app.canvas.selectNode( target ); } if ( target.pos ) { app.canvas.ds.offset[0] = -target.pos[0] + 600; app.canvas.ds.offset[1] = -target.pos[1] + 350; } app.canvas.ds.scale = 1.2; target.__comfyflowHighlight = true; app.canvas.setDirty( true, true ); setTimeout( () => { target.__comfyflowHighlight = false; app.canvas.setDirty( true, true ); }, 3000 ); } catch(error) { console.error( "Locate Node Error:", error ); } } function safeArray( value ) { if ( Array.isArray( value ) ) { return value; } return []; } function safePosition( value ) { if ( Array.isArray( value ) && value.length >= 2 ) { return value; } return [0, 0]; } function createList( values ) { const list = document.createElement( "ul" ); safeArray( values ).forEach( value => { const item = document.createElement( "li" ); item.innerText = String( value ); list.appendChild( item ); } ); if ( list.children.length === 0 ) { const item = document.createElement( "li" ); item.innerText = "None"; list.appendChild( item ); } return list; } function createWidgetList( widgetValues ) { const list = document.createElement( "ul" ); if ( Array.isArray( widgetValues ) ) { widgetValues.forEach( value => { const item = document.createElement( "li" ); item.innerText = String( value ); list.appendChild( item ); } ); } else if ( widgetValues !== null && widgetValues !== undefined ) { const item = document.createElement( "li" ); item.innerText = JSON.stringify( widgetValues ); list.appendChild( item ); } else { const item = document.createElement( "li" ); item.innerText = "No widget values"; list.appendChild( item ); } return list; } function createNodeCard( node ) { const card = document.createElement( "div" ); card.style.border = "1px solid #444"; card.style.padding = "15px"; card.style.marginBottom = "15px"; card.style.borderRadius = "8px"; card.style.background = "#252526"; const position = safePosition( node.position ); const size = safePosition( node.size ); card.innerHTML = `

${node.type || "Unknown Node"}

Category: ${node.category || "Unknown"}

Execution Order: ${node.execution_order ?? 0}

Node ID: ${node.id ?? "Unknown"}

Position: X=${Math.round(position[0])} Y=${Math.round(position[1])}

Size: ${Math.round(size[0])} x ${Math.round(size[1])}

`; const inputTitle = document.createElement( "h4" ); inputTitle.innerText = "Input Ports"; card.appendChild( inputTitle ); card.appendChild( createList( node.input_names ) ); const outputTitle = document.createElement( "h4" ); outputTitle.innerText = "Output Ports"; card.appendChild( outputTitle ); card.appendChild( createList( node.output_names ) ); const widgetTitle = document.createElement( "h4" ); widgetTitle.innerText = "Widget Values"; card.appendChild( widgetTitle ); card.appendChild( createWidgetList( node.widget_values ) ); const locateButton = document.createElement( "button" ); locateButton.innerText = "Locate Node"; locateButton.style.marginTop = "10px"; locateButton.onclick = () => { locateNode( node.id ); }; card.appendChild( locateButton ); return card; } export function buildNodeExplorer( nodes ) { const wrapper = document.createElement( "div" ); const searchBox = document.createElement( "input" ); searchBox.placeholder = "Search nodes..."; searchBox.style.width = "100%"; searchBox.style.padding = "10px"; searchBox.style.marginBottom = "20px"; wrapper.appendChild( searchBox ); const results = document.createElement( "div" ); wrapper.appendChild( results ); function renderNodes( filter = "" ) { results.innerHTML = ""; const filtered = safeArray( nodes ).filter( node => String( node.type || "" ) .toLowerCase() .includes( filter .toLowerCase() ) ); filtered.forEach( node => { results.appendChild( createNodeCard( node ) ); } ); } searchBox.addEventListener( "input", function() { renderNodes( this.value ); } ); renderNodes(); return wrapper; }