html-overlay-node 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HTMLOverlayNode={})}(this,function(e){"use strict";var t=Object.defineProperty,n=(e,n,o)=>((e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o)(e,"symbol"!=typeof n?n+"":n,o);class o{constructor(){this.types=new Map}register(e,t){if(!e||"string"!=typeof e)throw new Error("Invalid node type: type must be a non-empty string, got "+typeof e);if(!t||"object"!=typeof t)throw new Error(`Invalid definition for type "${e}": definition must be an object`);if(this.types.has(e))throw new Error(`Node type "${e}" is already registered. Use unregister() first to replace it.`);this.types.set(e,t)}unregister(e){if(!this.types.has(e))throw new Error(`Cannot unregister type "${e}": type is not registered`);this.types.delete(e)}removeAll(){this.types.clear()}createInstance(e){const t=this.types.get(e);if(!t){const t=Array.from(this.types.keys()).join(", ")||"none";throw new Error(`Unknown node type: "${e}". Available types: ${t}`)}return t}}function s(){const e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t=e.crypto||e.msCrypto;if(t&&"function"==typeof t.randomUUID)return t.randomUUID();if(t&&"function"==typeof t.getRandomValues){const e=new Uint8Array(16);t.getRandomValues(e),e[6]=15&e[6]|64,e[8]=63&e[8]|128;const n=Array.from(e,e=>e.toString(16).padStart(2,"0"));return n.slice(0,4).join("")+"-"+n.slice(4,6).join("")+"-"+n.slice(6,8).join("")+"-"+n.slice(8,10).join("")+"-"+n.slice(10,16).join("")}try{const e=Function('return typeof require === "function" ? require : null')();if(e){const t=e("crypto");if("function"==typeof t.randomUUID)return t.randomUUID();const n=t.randomBytes(16);n[6]=15&n[6]|64,n[8]=63&n[8]|128;const o=Array.from(n,e=>e.toString(16).padStart(2,"0"));return o.slice(0,4).join("")+"-"+o.slice(4,6).join("")+"-"+o.slice(6,8).join("")+"-"+o.slice(8,10).join("")+"-"+o.slice(10,16).join("")}}catch{}const n=new Uint8Array(16);for(let s=0;s<16;s++)n[s]=Math.floor(256*Math.random());n[6]=15&n[6]|64,n[8]=63&n[8]|128;const o=Array.from(n,e=>e.toString(16).padStart(2,"0"));return o.slice(0,4).join("")+"-"+o.slice(4,6).join("")+"-"+o.slice(6,8).join("")+"-"+o.slice(8,10).join("")+"-"+o.slice(10,16).join("")}class i{constructor({id:e,type:t,title:n,x:o=0,y:i=0,width:r=160,height:a=60}){if(!t)throw new Error("Node type is required");this.id=e??s(),this.type=t,this.title=n??t,this.pos={x:o,y:i},this.size={width:r,height:a},this.inputs=[],this.outputs=[],this.state={},this.parent=null,this.children=new Set,this.computed={x:0,y:0,w:0,h:0}}addInput(e,t="any",n="data"){if(!e||"string"!=typeof e)throw new Error("Input port name must be a non-empty string");const o={id:s(),name:e,datatype:t,portType:n,dir:"in"};return this.inputs.push(o),o}addOutput(e,t="any",n="data"){if(!e||"string"!=typeof e)throw new Error("Output port name must be a non-empty string");const o={id:s(),name:e,datatype:t,portType:n,dir:"out"};return this.outputs.push(o),o}}class r{constructor({id:e,fromNode:t,fromPort:n,toNode:o,toPort:i}){if(!(t&&n&&o&&i))throw new Error("Edge requires fromNode, fromPort, toNode, and toPort");this.id=e??s(),this.fromNode=t,this.fromPort=n,this.toNode=o,this.toPort=i}}class a{constructor({graph:e,hooks:t}){this.graph=e,this.hooks=t,this._groups=[]}addGroup({title:e="Group",x:t=0,y:n=0,width:o=240,height:s=160,color:i="#39424e",members:r=[]}={}){var a;(o<100||s<60)&&(console.warn("Group size too small, using minimum size"),o=Math.max(100,o),s=Math.max(60,s));const l=this.graph.addNode("core/Group",{title:e,x:t,y:n,width:o,height:s});l.state.color=i;for(const d of r){const e=this.graph.getNodeById(d);if(e){if("core/Group"===e.type){console.warn(`Cannot add group ${d} as member of another group`);continue}this.graph.reparent(e,l)}else console.warn(`Member node ${d} not found, skipping`)}return this._groups.push(l),null==(a=this.hooks)||a.emit("group:change"),l}addGroupFromSelection({title:e="Group",margin:t={x:12,y:12}}={}){return null}removeGroup(e){var t;const n=this.graph.getNodeById(e);if(!n||"core/Group"!==n.type)return;const o=[...n.children];for(const s of o)this.graph.reparent(s,n.parent);this.graph.removeNode(e),null==(t=this.hooks)||t.emit("group:change")}resizeGroup(e,t,n){var o;const s=this.graph.getNodeById(e);if(!s||"core/Group"!==s.type)return;s.size.width=Math.max(100,s.size.width+t),s.size.height=Math.max(60,s.size.height+n),this.graph.updateWorldTransforms(),null==(o=this.hooks)||o.emit("group:change")}hitTestResizeHandle(e,t){const n=[...this.graph.nodes.values()].reverse();for(const o of n){if("core/Group"!==o.type)continue;const{x:n,y:s,w:i,h:r}=o.computed;if(e>=n+i-10&&e<=n+i&&t>=s+r-10&&t<=s+r)return{group:o,handle:"se"}}return null}}class l{constructor({hooks:e,registry:t}){if(!t)throw new Error("Graph requires a registry");this.nodes=new Map,this.edges=new Map,this.hooks=e,this.registry=t,this._valuesA=new Map,this._valuesB=new Map,this._useAasCurrent=!0,this.groupManager=new a({graph:this,hooks:this.hooks})}getNodeById(e){return this.nodes.get(e)||null}addNode(e,t={}){var n,o,s,r;const a=this.registry.types.get(e);if(!a){const t=Array.from(this.registry.types.keys()).join(", ")||"none";throw new Error(`Unknown node type: "${e}". Available types: ${t}`)}const l=new i({type:e,title:a.title,width:null==(n=a.size)?void 0:n.w,height:null==(o=a.size)?void 0:o.h,...t});for(const i of a.inputs||[])l.addInput(i.name,i.datatype,i.portType||"data");for(const i of a.outputs||[])l.addOutput(i.name,i.datatype,i.portType||"data");return null==(s=a.onCreate)||s.call(a,l),this.nodes.set(l.id,l),null==(r=this.hooks)||r.emit("node:create",l),l}removeNode(e){for(const[t,n]of this.edges)n.fromNode!==e&&n.toNode!==e||this.edges.delete(t);this.nodes.delete(e)}addEdge(e,t,n,o){var s;if(!this.nodes.has(e))throw new Error(`Cannot create edge: source node "${e}" not found`);if(!this.nodes.has(n))throw new Error(`Cannot create edge: target node "${n}" not found`);const i=new r({fromNode:e,fromPort:t,toNode:n,toPort:o});return this.edges.set(i.id,i),null==(s=this.hooks)||s.emit("edge:create",i),i}clear(){this.nodes.clear(),this.edges.clear()}updateWorldTransforms(){const e=[];for(const n of this.nodes.values())n.parent||e.push(n);const t=e.map(e=>({node:e,px:0,py:0}));for(;t.length>0;){const{node:e,px:n,py:o}=t.pop();e.computed.x=n+e.pos.x,e.computed.y=o+e.pos.y,e.computed.w=e.size.width,e.computed.h=e.size.height;for(const s of e.children)t.push({node:s,px:e.computed.x,py:e.computed.y})}}reparent(e,t){if(e.parent===t)return;const n=e.computed.x,o=e.computed.y;e.parent&&e.parent.children.delete(e),e.parent=t,t?(t.children.add(e),e.pos.x=n-t.computed.x,e.pos.y=o-t.computed.y):(e.pos.x=n,e.pos.y=o),this.updateWorldTransforms()}_curBuf(){return this._useAasCurrent?this._valuesA:this._valuesB}_nextBuf(){return this._useAasCurrent?this._valuesB:this._valuesA}swapBuffers(){this._useAasCurrent=!this._useAasCurrent,this._nextBuf().clear()}setOutput(e,t,n){console.log(`[Graph.setOutput] nodeId: ${e}, portId: ${t}, value:`,n);const o=`${e}:${t}`;this._nextBuf().set(o,n)}getInput(e,t){for(const n of this.edges.values())if(n.toNode===e&&n.toPort===t){const o=`${n.fromNode}:${n.fromPort}`,s=this._curBuf().get(o);return console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, reading from ${n.fromNode}:${n.fromPort}, value:`,s),s}console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, no edge found, returning undefined`)}toJSON(){var e;const t={nodes:[...this.nodes.values()].map(e=>{var t;return{id:e.id,type:e.type,title:e.title,x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height,inputs:e.inputs,outputs:e.outputs,state:e.state,parentId:(null==(t=e.parent)?void 0:t.id)||null}}),edges:[...this.edges.values()]};return null==(e=this.hooks)||e.emit("graph:serialize",t),t}fromJSON(e){var t,n,o;this.clear();for(const s of e.nodes){const e=new i({id:s.id,type:s.type,title:s.title,x:s.x,y:s.y,width:s.w,height:s.h}),o=null==(n=null==(t=this.registry)?void 0:t.types)?void 0:n.get(s.type);(null==o?void 0:o.onCreate)&&o.onCreate(e),e.inputs=s.inputs,e.outputs=s.outputs,e.state={...e.state,...s.state||{}},this.nodes.set(e.id,e)}for(const s of e.nodes)if(s.parentId){const e=this.nodes.get(s.id),t=this.nodes.get(s.parentId);e&&t&&(e.parent=t,t.children.add(e))}for(const s of e.edges)this.edges.set(s.id,new r(s));return this.updateWorldTransforms(),null==(o=this.hooks)||o.emit("graph:deserialize",e),this}}function d(e,t,n,o){const{x:s,y:i,w:r,h:a}=e.computed||{x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height},l="in"===o?e.inputs.length:e.outputs.length,d=i+28+((a||e.size.height)-28-16)/(l+1)*(n+1);return"in"===o?{x:s-6,y:d-6,w:12,h:12}:"out"===o?{x:s+r-6,y:d-6,w:12,h:12}:void 0}const c=class e{constructor(e,{theme:t={},registry:n,edgeStyle:o="orthogonal"}={}){this.canvas=e,this.ctx=e.getContext("2d"),this.registry=n,this.scale=1,this.minScale=.25,this.maxScale=3,this.offsetX=0,this.offsetY=0,this.edgeStyle=o,this.theme=Object.assign({bg:"#0d0d0f",grid:"#1a1a1d",node:"#16161a",nodeBorder:"#2a2a2f",title:"#1f1f24",text:"#e4e4e7",textMuted:"#a1a1aa",port:"#6366f1",portExec:"#10b981",edge:"#52525b",edgeActive:"#8b5cf6",accent:"#6366f1",accentBright:"#818cf8"},t)}setEdgeStyle(e){this.edgeStyle="line"===e||"orthogonal"===e?e:"bezier"}setRegistry(e){this.registry=e}resize(e,t){this.canvas.width=e,this.canvas.height=t}setTransform({scale:e=this.scale,offsetX:t=this.offsetX,offsetY:n=this.offsetY}={}){this.scale=Math.min(this.maxScale,Math.max(this.minScale,e)),this.offsetX=t,this.offsetY=n}panBy(e,t){this.offsetX+=e,this.offsetY+=t}zoomAt(e,t,n){const o=this.scale,s=Math.min(this.maxScale,Math.max(this.minScale,o*e));if(s===o)return;const i=(t-this.offsetX)/o,r=(n-this.offsetY)/o;this.offsetX=t-i*s,this.offsetY=n-r*s,this.scale=s}screenToWorld(e,t){return{x:(e-this.offsetX)/this.scale,y:(t-this.offsetY)/this.scale}}worldToScreen(e,t){return{x:e*this.scale+this.offsetX,y:t*this.scale+this.offsetY}}_applyTransform(){const{ctx:e}=this;e.setTransform(this.scale,0,0,this.scale,this.offsetX,this.offsetY)}_resetTransform(){this.ctx.setTransform(1,0,0,1,0,0)}_drawArrowhead(e,t,n,o,s=10){const{ctx:i}=this,r=s/this.scale,a=Math.atan2(o-t,n-e);i.beginPath(),i.moveTo(n,o),i.lineTo(n-r*Math.cos(a-Math.PI/6),o-r*Math.sin(a-Math.PI/6)),i.lineTo(n-r*Math.cos(a+Math.PI/6),o-r*Math.sin(a+Math.PI/6)),i.closePath(),i.fill()}_drawScreenText(e,t,n,{fontPx:o=12,color:s=this.theme.text,align:i="left",baseline:r="alphabetic",dpr:a=1}={}){const{ctx:l}=this,{x:d,y:c}=this.worldToScreen(t,n);l.save(),this._resetTransform();const h=Math.round(d)+.5,u=Math.round(c)+.5;l.font=o*this.scale+"px system-ui",l.fillStyle=s,l.textAlign=i,l.textBaseline=r,l.fillText(e,h,u),l.restore()}drawGrid(){const{ctx:e,canvas:t,theme:n,scale:o,offsetX:s,offsetY:i}=this;this._resetTransform(),e.fillStyle=n.bg,e.fillRect(0,0,t.width,t.height),this._applyTransform(),e.strokeStyle=this._rgba(n.grid,.35),e.lineWidth=1/o;const r=20,a=-s/o,l=-i/o,d=(t.width-s)/o,c=(t.height-i)/o,h=Math.floor(a/r)*r,u=Math.floor(l/r)*r;e.beginPath();for(let p=h;p<=d;p+=r)e.moveTo(p,l),e.lineTo(p,c);for(let p=u;p<=c;p+=r)e.moveTo(a,p),e.lineTo(d,p);e.stroke(),this._resetTransform()}draw(t,{selection:n=new Set,tempEdge:o=null,running:s=!1,time:i=performance.now(),dt:r=0,groups:a=null,activeEdges:l=new Set}={}){var d,c,h,u,p,g;t.updateWorldTransforms(),this.drawGrid();const{ctx:f,theme:m}=this;this._applyTransform(),f.save();for(const e of t.nodes.values())if("core/Group"===e.type){const t=n.has(e.id),o=null==(c=null==(d=this.registry)?void 0:d.types)?void 0:c.get(e.type);(null==o?void 0:o.onDraw)?o.onDraw(e,{ctx:f,theme:m}):this._drawNode(e,t)}f.lineWidth=1.5/this.scale;let y=null,x=0;if(s){const t=i/1e3*120/this.scale%e.FONT_SIZE;y=[6/this.scale,6/this.scale],x=-t}for(const e of t.edges.values()){const n=l&&l.size>0&&l.has(e.id);s&&n&&y?(f.setLineDash(y),f.lineDashOffset=x):(f.setLineDash([]),f.lineDashOffset=0);l&&l.has(e.id)?(f.strokeStyle="#00ffff",f.lineWidth=3*this.scale):(f.strokeStyle=m.edge,f.lineWidth=1.5/this.scale),this._drawEdge(t,e)}if(o){const e=this.screenToWorld(o.x1,o.y1),t=this.screenToWorld(o.x2,o.y2),n=this.ctx.getLineDash();this.ctx.setLineDash([6/this.scale,6/this.scale]);let s=null;if("line"===this.edgeStyle?(this._drawLine(e.x,e.y,t.x,t.y),s=[{x:e.x,y:e.y},{x:t.x,y:t.y}]):"orthogonal"===this.edgeStyle?s=this._drawOrthogonal(e.x,e.y,t.x,t.y):(this._drawCurve(e.x,e.y,t.x,t.y),s=[{x:e.x,y:e.y},{x:t.x,y:t.y}]),this.ctx.setLineDash(n),s&&s.length>=2){const e=s[s.length-2],t=s[s.length-1];this.ctx.fillStyle=this.theme.edge,this.ctx.strokeStyle=this.theme.edge,this._drawArrowhead(e.x,e.y,t.x,t.y,12)}}for(const e of t.nodes.values())if("core/Group"!==e.type){const t=n.has(e.id),o=null==(u=null==(h=this.registry)?void 0:h.types)?void 0:u.get(e.type),s=!!(null==o?void 0:o.html);this._drawNode(e,t,s),(null==o?void 0:o.onDraw)&&o.onDraw(e,{ctx:f,theme:m})}for(const e of t.nodes.values())if("core/Group"!==e.type){const t=null==(g=null==(p=this.registry)?void 0:p.types)?void 0:g.get(e.type);!!(null==t?void 0:t.html)&&this._drawPorts(e)}this._resetTransform()}_rgba(e,t){const n=e.replace("#",""),o=parseInt(3===n.length?n.split("").map(e=>e+e).join(""):n,16);return`rgba(${o>>16&255},${o>>8&255},${255&o},${t})`}_drawNode(t,n,o=!1){const{ctx:s,theme:i}=this,{x:r,y:a,w:l,h:c}=t.computed;n||(s.save(),s.shadowColor="rgba(0, 0, 0, 0.3)",s.shadowBlur=8/this.scale,s.shadowOffsetY=2/this.scale,s.fillStyle="rgba(0, 0, 0, 0.2)",u(s,r,a,l,c,8),s.fill(),s.restore()),s.fillStyle=i.node,s.strokeStyle=n?i.accentBright:i.nodeBorder,s.lineWidth=(n?1.5:1)/this.scale,u(s,r,a,l,c,8),s.fill(),s.stroke(),s.fillStyle=i.title,u(s,r,a,l,24,{tl:8,tr:8,br:0,bl:0}),s.fill(),s.strokeStyle=n?i.accentBright:i.nodeBorder,s.lineWidth=(n?1.5:1)/this.scale,s.beginPath(),s.moveTo(r+8,a),s.lineTo(r+l-8,a),s.quadraticCurveTo(r+l,a,r+l,a+8),s.lineTo(r+l,a+24),s.moveTo(r,a+24),s.lineTo(r,a+8),s.quadraticCurveTo(r,a,r+8,a),s.stroke(),this._drawScreenText(t.title,r+8,a+e.FONT_SIZE,{fontPx:e.FONT_SIZE,color:i.text,baseline:"middle",align:"left"}),o||(t.inputs.forEach((e,n)=>{const o=d(t,0,n,"in"),r=o.x+o.w/2,a=o.y+o.h/2;if("exec"===e.portType){const e=8;s.fillStyle=i.portExec,s.strokeStyle="rgba(16, 185, 129, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.roundRect(r-e/2,a-e/2,e,e,2),s.fill(),s.stroke()}else s.fillStyle=i.port,s.strokeStyle="rgba(99, 102, 241, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.arc(r,a,5,0,2*Math.PI),s.fill(),s.stroke()}),t.outputs.forEach((e,n)=>{const o=d(t,0,n,"out"),r=o.x+o.w/2,a=o.y+o.h/2;if("exec"===e.portType){const e=8;s.fillStyle=i.portExec,s.strokeStyle="rgba(16, 185, 129, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.roundRect(r-e/2,a-e/2,e,e,2),s.fill(),s.stroke()}else s.fillStyle=i.port,s.strokeStyle="rgba(99, 102, 241, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.arc(r,a,5,0,2*Math.PI),s.fill(),s.stroke()}))}_drawPorts(e){const{ctx:t,theme:n}=this;e.inputs.forEach((o,s)=>{const i=d(e,0,s,"in"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===o.portType){const e=8;t.fillStyle=n.portExec,t.strokeStyle="rgba(16, 185, 129, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.roundRect(r-e/2,a-e/2,e,e,2),t.fill(),t.stroke()}else t.fillStyle=n.port,t.strokeStyle="rgba(99, 102, 241, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.arc(r,a,5,0,2*Math.PI),t.fill()}),e.outputs.forEach((o,s)=>{const i=d(e,0,s,"out"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===o.portType){const e=8;t.fillStyle=n.portExec,t.strokeStyle="rgba(16, 185, 129, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.roundRect(r-e/2,a-e/2,e,e,2),t.fill(),t.stroke()}else t.fillStyle=n.port,t.strokeStyle="rgba(99, 102, 241, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.arc(r,a,5,0,2*Math.PI),t.fill(),t.stroke()})}_drawEdge(e,t){const n=e.nodes.get(t.fromNode),o=e.nodes.get(t.toNode);if(!n||!o)return;const s=n.outputs.findIndex(e=>e.id===t.fromPort),i=o.inputs.findIndex(e=>e.id===t.toPort),r=d(n,0,s,"out"),a=d(o,0,i,"in"),l=r.x,c=r.y+7,h=a.x,u=a.y+7;"line"===this.edgeStyle?this._drawLine(l,c,h,u):"orthogonal"===this.edgeStyle?this._drawOrthogonal(l,c,h,u):this._drawCurve(l,c,h,u)}_drawLine(e,t,n,o){const{ctx:s}=this;s.beginPath(),s.moveTo(e,t),s.lineTo(n,o),s.stroke()}_drawPolyline(e){const{ctx:t}=this;t.beginPath(),t.moveTo(e[0].x,e[0].y);for(let n=1;n<e.length;n++)t.lineTo(e[n].x,e[n].y);t.stroke()}_drawOrthogonal(e,t,n,o){const s=(e+n)/2;let i;i=[{x:e,y:t},{x:s,y:t},{x:s,y:o},{x:n,y:o}];const{ctx:r}=this,a=r.lineJoin,l=r.lineCap;return r.lineJoin="round",r.lineCap="round",this._drawPolyline(i),r.lineJoin=a,r.lineCap=l,i}_drawCurve(e,t,n,o){const{ctx:s}=this,i=Math.max(40,.4*Math.abs(n-e));s.beginPath(),s.moveTo(e,t),s.bezierCurveTo(e+i,t,n-i,o,n,o),s.stroke()}};n(c,"FONT_SIZE",12),n(c,"SELECTED_NODE_COLOR","#6cf");let h=c;function u(e,t,n,o,s,i=6){"number"==typeof i&&(i={tl:i,tr:i,br:i,bl:i}),e.beginPath(),e.moveTo(t+i.tl,n),e.lineTo(t+o-i.tr,n),e.quadraticCurveTo(t+o,n,t+o,n+i.tr),e.lineTo(t+o,n+s-i.br),e.quadraticCurveTo(t+o,n+s,t+o-i.br,n+s),e.lineTo(t+i.bl,n+s),e.quadraticCurveTo(t,n+s,t,n+s-i.bl),e.lineTo(t,n+i.tl),e.quadraticCurveTo(t,n,t+i.tl,n),e.closePath()}function p(e,t,n,o,s){for(const[i,r]of e.edges)if(r.fromNode===t&&r.fromPort===n&&r.toNode===o&&r.toPort===s)return i;return null}function g(e,t){let n=null,o=[];return{do(){n=t,o=e.edges?[...e.edges.values()].filter(e=>e.fromNode===t.id||e.toNode===t.id):[];for(const t of o)e.edges.delete(t.id);e.nodes.delete(t.id)},undo(){n&&e.nodes.set(n.id,n);for(const t of o)e.edges.set(t.id,t)}}}class f{constructor(){this.undoStack=[],this.redoStack=[]}exec(e){e.do(),this.undoStack.push(e),this.redoStack.length=0}undo(){const e=this.undoStack.pop();e&&(e.undo(),this.redoStack.push(e))}redo(){const e=this.redoStack.pop();e&&(e.do(),this.undoStack.push(e))}}const m=class e{constructor({graph:e,renderer:t,hooks:n,htmlOverlay:o,contextMenu:s,portRenderer:i}){this.graph=e,this.renderer=t,this.hooks=n,this.htmlOverlay=o,this.contextMenu=s,this.portRenderer=i,this.stack=new f,this.selection=new Set,this.dragging=null,this.connecting=null,this.panning=null,this.resizing=null,this.gDragging=null,this.gResizing=null,this.boxSelecting=null,this.snapToGrid=!0,this.gridSize=20,this._cursor="default",this._onKeyPressEvt=this._onKeyPress.bind(this),this._onDownEvt=this._onDown.bind(this),this._onWheelEvt=this._onWheel.bind(this),this._onMoveEvt=this._onMove.bind(this),this._onUpEvt=this._onUp.bind(this),this._onContextMenuEvt=this._onContextMenu.bind(this),this._onDblClickEvt=this._onDblClick.bind(this),this._bindEvents()}destroy(){const e=this.renderer.canvas;e.removeEventListener("mousedown",this._onDownEvt),e.removeEventListener("dblclick",this._onDblClickEvt),e.removeEventListener("wheel",this._onWheelEvt,{passive:!1}),e.removeEventListener("contextmenu",this._onContextMenuEvt),window.removeEventListener("mousemove",this._onMoveEvt),window.removeEventListener("mouseup",this._onUpEvt),window.removeEventListener("keydown",this._onKeyPressEvt)}_bindEvents(){const e=this.renderer.canvas;e.addEventListener("mousedown",this._onDownEvt),e.addEventListener("dblclick",this._onDblClickEvt),e.addEventListener("wheel",this._onWheelEvt,{passive:!1}),e.addEventListener("contextmenu",this._onContextMenuEvt),window.addEventListener("mousemove",this._onMoveEvt),window.addEventListener("mouseup",this._onUpEvt),window.addEventListener("keydown",this._onKeyPressEvt)}_onKeyPress(e){return this.isAlt=e.altKey,this.isShift=e.shiftKey,this.isCtrl=e.ctrlKey,"g"!==e.key.toLowerCase()||e.ctrlKey||e.metaKey?(e.ctrlKey||e.metaKey)&&"g"===e.key.toLowerCase()?(e.preventDefault(),void this._createGroupFromSelection()):(e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()?(e.preventDefault(),e.shiftKey?this.stack.redo():this.stack.undo(),void this.render()):(e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()?(e.preventDefault(),this.stack.redo(),void this.render()):"a"===e.key.toLowerCase()&&this.selection.size>1?(e.preventDefault(),void(e.shiftKey?this._alignNodesVertical():this._alignNodesHorizontal())):void("Delete"===e.key&&([...this.selection].forEach(e=>{const t=this.graph.getNodeById(e);this.stack.exec(g(this.graph,t)),this.graph.removeNode(e)}),this.render())):(this.snapToGrid=!this.snapToGrid,void this.render())}_setCursor(e){this._cursor!==e&&(this._cursor=e,this.renderer.canvas.style.cursor=e)}_posScreen(e){const t=this.renderer.canvas.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}_posWorld(e){const t=this._posScreen(e);return this.renderer.screenToWorld(t.x,t.y)}_findNodeAtWorld(e,t){const n=[...this.graph.nodes.values()].reverse();for(const o of n){const{x:n,y:s,w:i,h:r}=o.computed;if(e>=n&&e<=n+i&&t>=s&&t<=s+r){if("core/Group"===o.type){const n=this._findChildNodeAtWorld(o,e,t);if(n)return n}return o}}return null}_findChildNodeAtWorld(e,t,n){const o=[];for(const s of this.graph.nodes.values())s.parent===e&&o.push(s);for(let s=o.length-1;s>=0;s--){const e=o[s],{x:i,y:r,w:a,h:l}=e.computed;if(t>=i&&t<=i+a&&n>=r&&n<=r+l){if("core/Group"===e.type){const o=this._findChildNodeAtWorld(e,t,n);if(o)return o}return e}}return null}_findPortAtWorld(e,t){for(const n of this.graph.nodes.values()){for(let o=0;o<n.inputs.length;o++){if(x(d(n,n.inputs[o],o,"in"),e,t))return{node:n,port:n.inputs[o],dir:"in",idx:o}}for(let o=0;o<n.outputs.length;o++){if(x(d(n,n.outputs[o],o,"out"),e,t))return{node:n,port:n.outputs[o],dir:"out",idx:o}}}return null}_findIncomingEdge(e,t){for(const[n,o]of this.graph.edges)if(o.toNode===e&&o.toPort===t)return{id:n,edge:o};return null}_onWheel(e){e.preventDefault();const{x:t,y:n}=this._posScreen(e),o=Math.pow(1.0015,-e.deltaY);this.renderer.zoomAt(o,t,n),this.render()}_onContextMenu(e){if(e.preventDefault(),!this.contextMenu)return;const t=this._posWorld(e),n=this._findNodeAtWorld(t.x,t.y);this.contextMenu.show(n,e.clientX,e.clientY,t)}_onDblClick(e){var t;const n=this._posWorld(e),o=this._findNodeAtWorld(n.x,n.y);o&&(null==(t=this.hooks)||t.emit("node:dblclick",o))}_resizeHandleRect(e){const{x:t,y:n,w:o,h:s}=e.computed;return{x:t+o-10,y:n+s-10,w:10,h:10}}_hitResizeHandle(e,t,n){const o=this._resizeHandleRect(e);return t>=o.x&&t<=o.x+o.w&&n>=o.y&&n<=o.y+o.h}_onDown(e){const t=this._posScreen(e),n=this._posWorld(e);if(1===e.button)return void(this.panning={x:t.x,y:t.y});const o=this._findNodeAtWorld(n.x,n.y);if(0===e.button&&o&&this._hitResizeHandle(o,n.x,n.y))return this.resizing={nodeId:o.id,startW:o.size.width,startH:o.size.height,startX:n.x,startY:n.y},e.shiftKey||this.selection.clear(),this.selection.add(o.id),this._setCursor("se-resize"),void this.render();const s=this._findPortAtWorld(n.x,n.y);if(0===e.button&&s&&"in"===s.dir){const e=this._findIncomingEdge(s.node.id,s.port.id);if(e)return this.stack.exec(function(e,t){const n=e.edges.get(t);if(!n)return null;const{fromNode:o,fromPort:s,toNode:i,toPort:r}=n;return{do(){e.edges.delete(t)},undo(){e.addEdge(o,s,i,r)}}}(this.graph,e.id)),void this.render()}if(0===e.button&&s&&"out"===s.dir){const e=d(s.node,s.port,s.idx,"out"),t=this.renderer.worldToScreen(e.x,e.y+7);return void(this.connecting={fromNode:s.node.id,fromPort:s.port.id,x:t.x,y:t.y})}if(0!==e.button||!o)return 0===e.button?(this.selection.size&&this.selection.clear(),e.ctrlKey||e.metaKey?this.boxSelecting={startX:n.x,startY:n.y,currentX:n.x,currentY:n.y}:this.panning={x:t.x,y:t.y},void this.render()):void 0;e.shiftKey||this.selection.clear(),this.selection.add(o.id),this.dragging={nodeId:o.id,offsetX:n.x-o.computed.x,offsetY:n.y-o.computed.y,startPos:{...o.pos},selectedNodes:[]};for(const i of this.selection){const e=this.graph.nodes.get(i);e&&this.dragging.selectedNodes.push({node:e,startWorldX:e.computed.x,startWorldY:e.computed.y,startLocalX:e.pos.x,startLocalY:e.pos.y})}if("core/Group"===o.type){this.dragging.childrenWorldPos=[];for(const e of this.graph.nodes.values())e.parent===o&&this.dragging.childrenWorldPos.push({node:e,worldX:e.computed.x,worldY:e.computed.y})}this.render()}_onMove(t){var n,o;this.isAlt=t.altKey,this.isShift=t.shiftKey,this.isCtrl=t.ctrlKey;const s=this._posScreen(t),i=this.renderer.screenToWorld(s.x,s.y);if(this.resizing){const t=this.graph.nodes.get(this.resizing.nodeId),o=i.x-this.resizing.startX,s=i.y-this.resizing.startY,r=e.MIN_NODE_WIDTH,a=e.MIN_NODE_HEIGHT;return t.size.width=Math.max(r,this.resizing.startW+o),t.size.height=Math.max(a,this.resizing.startH+s),null==(n=this.hooks)||n.emit("node:resize",t),this._setCursor("se-resize"),void this.render()}if(this.panning){const e=s.x-this.panning.x,t=s.y-this.panning.y;return this.panning={x:s.x,y:s.y},this.renderer.panBy(e,t),void this.render()}if(this.dragging){const e=this.graph.nodes.get(this.dragging.nodeId);let t=i.x-this.dragging.offsetX,n=this.isShift?i.y-0:i.y-this.dragging.offsetY;this.snapToGrid&&(t=this._snapToGrid(t),n=this._snapToGrid(n));const s=t-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldX,r=n-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldY;this.graph.updateWorldTransforms();for(const{node:o,startWorldX:i,startWorldY:a}of this.dragging.selectedNodes){if(this.isShift&&"core/Group"===o.type)continue;let e=i+s,t=a+r,n=0,l=0;o.parent&&(n=o.parent.computed.x,l=o.parent.computed.y),o.pos.x=e-n,o.pos.y=t-l}if(this.isAlt&&"core/Group"===e.type&&this.dragging.childrenWorldPos){this.graph.updateWorldTransforms();for(const t of this.dragging.childrenWorldPos){const n=t.node,o=e.computed.x,s=e.computed.y;n.pos.x=t.worldX-o,n.pos.y=t.worldY-s}}return null==(o=this.hooks)||o.emit("node:move",e),void this.render()}if(this.boxSelecting)return this.boxSelecting.currentX=i.x,this.boxSelecting.currentY=i.y,void this.render();this.connecting&&(this.connecting.x=s.x,this.connecting.y=s.y,this.render());const r=this._findPortAtWorld(i.x,i.y),a=this._findNodeAtWorld(i.x,i.y);a&&this._hitResizeHandle(a,i.x,i.y)?this._setCursor("se-resize"):r?this._setCursor("pointer"):this._setCursor("default")}_onUp(e){this.isAlt=e.altKey,this.isShift=e.shiftKey,this.isCtrl=e.ctrlKey;const t=this._posWorld(e);if(this.panning)this.panning=null;else{if(this.connecting){const e=this.connecting,n=this._findPortAtWorld(t.x,t.y);n&&"in"===n.dir&&this.stack.exec(function(e,t,n,o,s){let i=null;return{do(){e.addEdge(t,n,o,s),i=p(e,t,n,o,s)},undo(){const r=i??p(e,t,n,o,s);null!=r&&e.edges.delete(r)}}}(this.graph,e.fromNode,e.fromPort,n.node.id,n.port.id)),this.connecting=null,this.render()}if(this.resizing){const e=this.graph.nodes.get(this.resizing.nodeId),t={w:this.resizing.startW,h:this.resizing.startH},i={w:e.size.width,h:e.size.height};t.w===i.w&&t.h===i.h||this.stack.exec((n=e,o=t,s=i,{do(){n.size.width=s.w,n.size.height=s.h},undo(){n.size.width=o.w,n.size.height=o.h}})),this.resizing=null,this._setCursor("default")}var n,o,s;if(this.dragging){const e=this.graph.nodes.get(this.dragging.nodeId);if("core/Group"===e.type&&this.isAlt&&this.dragging.childrenWorldPos)for(const t of this.dragging.childrenWorldPos){const n=t.node;this.graph.updateWorldTransforms();const o=e.computed.x,s=e.computed.y;n.pos.x=t.worldX-o,n.pos.y=t.worldY-s}else if("core/Group"!==e.type||this.isAlt){if("core/Group"!==e.type){const n=this._findPotentialParent(t.x,t.y,e);n&&n!==e.parent?this.graph.reparent(e,n):!n&&e.parent&&this.graph.reparent(e,null)}}else this._autoParentNodesInGroup(e);this.dragging=null,this.render()}if(this.boxSelecting){const{startX:e,startY:t,currentX:n,currentY:o}=this.boxSelecting,s=Math.min(e,n),i=Math.max(e,n),r=Math.min(t,o),a=Math.max(t,o);for(const l of this.graph.nodes.values()){const{x:e,y:t,w:n,h:o}=l.computed;e+n>=s&&e<=i&&t+o>=r&&t<=a&&this.selection.add(l.id)}this.boxSelecting=null,this.render()}}}_autoParentNodesInGroup(e){const{x:t,y:n,w:o,h:s}=e.computed;for(const i of this.graph.nodes.values()){if(i===e)continue;if(i.parent===e)continue;if("core/Group"===i.type)continue;const{x:r,y:a,w:l,h:d}=i.computed,c=r+l/2,h=a+d/2;c>=t&&c<=t+o&&h>=n&&h<=n+s&&this.graph.reparent(i,e)}}_findPotentialParent(e,t,n){const o=[...this.graph.nodes.values()].reverse();for(const s of o){if("core/Group"!==s.type)continue;if(s===n)continue;let o=s.parent,i=!1;for(;o;){if(o===n){i=!0;break}o=o.parent}if(i)continue;const{x:r,y:a,w:l,h:d}=s.computed;if(e>=r&&e<=r+l&&t>=a&&t<=a+d)return s}return null}_snapToGrid(e){return Math.round(e/this.gridSize)*this.gridSize}_createGroupFromSelection(){if(0===this.selection.size)return void console.warn("No nodes selected to group");const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e));let t=1/0,n=1/0,o=-1/0,s=-1/0;for(const d of e){const{x:e,y:i,w:r,h:a}=d.computed;t=Math.min(t,e),n=Math.min(n,i),o=Math.max(o,e+r),s=Math.max(s,i+a)}const i=t-20,r=n-20,a=o-t+40,l=s-n+40;this.graph.groupManager&&(this.graph.groupManager.addGroup({title:"Group",x:i,y:r,width:a,height:l,members:Array.from(this.selection)}),this.selection.clear(),this.render())}_alignNodesHorizontal(){if(this.selection.size<2)return;const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e)),t=e.reduce((e,t)=>e+t.computed.y,0)/e.length;for(const n of e){const e=n.parent?n.parent.computed.y:0;n.pos.y=t-e}this.graph.updateWorldTransforms(),this.render()}_alignNodesVertical(){if(this.selection.size<2)return;const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e)),t=e.reduce((e,t)=>e+t.computed.x,0)/e.length;for(const n of e){const e=n.parent?n.parent.computed.x:0;n.pos.x=t-e}this.graph.updateWorldTransforms(),this.render()}render(){var e,t,n;const o=this.renderTempEdge();if(this.renderer.draw(this.graph,{selection:this.selection,tempEdge:o,boxSelecting:this.boxSelecting,activeEdges:this.activeEdges||new Set}),null==(e=this.htmlOverlay)||e.draw(this.graph,this.selection),this.boxSelecting){const{startX:e,startY:t,currentX:n,currentY:o}=this.boxSelecting,s=Math.min(e,n),i=Math.min(t,o),r=Math.abs(n-e),a=Math.abs(o-t),l=this.renderer.worldToScreen(s,i),d=this.renderer.worldToScreen(s+r,i+a),c=this.renderer.ctx;c.save(),this.renderer._resetTransform(),c.strokeStyle="#6cf",c.fillStyle="rgba(102, 204, 255, 0.1)",c.lineWidth=2,c.strokeRect(l.x,l.y,d.x-l.x,d.y-l.y),c.fillRect(l.x,l.y,d.x-l.x,d.y-l.y),c.restore()}if(this.portRenderer){this.portRenderer.ctx.clearRect(0,0,this.portRenderer.canvas.width,this.portRenderer.canvas.height),this.portRenderer.scale=this.renderer.scale,this.portRenderer.offsetX=this.renderer.offsetX,this.portRenderer.offsetY=this.renderer.offsetY,this.portRenderer._applyTransform();for(const e of this.graph.nodes.values())if("core/Group"!==e.type){const o=null==(n=null==(t=this.portRenderer.registry)?void 0:t.types)?void 0:n.get(e.type);!!(null==o?void 0:o.html)&&this.portRenderer._drawPorts(e)}this.portRenderer._resetTransform()}}renderTempEdge(){if(!this.connecting)return null;const e=this._portAnchorScreen(this.connecting.fromNode,this.connecting.fromPort);return{x1:e.x,y1:e.y,x2:this.connecting.x,y2:this.connecting.y}}_portAnchorScreen(e,t){const n=this.graph.nodes.get(e),o=n.outputs.findIndex(e=>e.id===t),s=d(n,0,o,"out");return this.renderer.worldToScreen(s.x,s.y+7)}};n(m,"MIN_NODE_WIDTH",80),n(m,"MIN_NODE_HEIGHT",60);let y=m;function x(e,t,n){return t>=e.x&&t<=e.x+e.w&&n>=e.y&&n<=e.y+e.h}class v{constructor({graph:e,hooks:t,renderer:n,commandStack:o}){this.graph=e,this.hooks=t,this.renderer=n,this.commandStack=o,this.items=[],this.visible=!1,this.target=null,this.position={x:0,y:0},this.menuElement=this._createMenuElement(),this._onDocumentClick=e=>{this.menuElement.contains(e.target)||this.hide()}}addItem(e,t,n={}){const{action:o,submenu:s,condition:i,order:r=100}=n;o||s?(this.removeItem(e),this.items.push({id:e,label:t,action:o,submenu:s,condition:i,order:r}),this.items.sort((e,t)=>e.order-t.order)):console.error("ContextMenu.addItem: either action or submenu is required")}removeItem(e){this.items=this.items.filter(t=>t.id!==e)}show(e,t,n,o=null){this.target=e,this.position={x:t,y:n},this.worldPosition=o,this.visible=!0,this._renderItems(),this.menuElement.style.left=`${t}px`,this.menuElement.style.top=`${n}px`,this.menuElement.style.display="block",requestAnimationFrame(()=>{const e=this.menuElement.getBoundingClientRect(),o=window.innerWidth,s=window.innerHeight;let i=t,r=n;e.right>o&&(i=o-e.width-5),e.bottom>s&&(r=s-e.height-5),this.menuElement.style.left=`${i}px`,this.menuElement.style.top=`${r}px`}),document.addEventListener("click",this._onDocumentClick)}hide(){this.visible=!1,this.target=null;document.querySelectorAll(".context-submenu").forEach(e=>e.remove()),this.menuElement.style.display="none",document.removeEventListener("click",this._onDocumentClick)}destroy(){this.hide(),this.menuElement&&this.menuElement.parentNode&&this.menuElement.parentNode.removeChild(this.menuElement)}_createMenuElement(){const e=document.createElement("div");return e.className="html-overlay-node-context-menu",Object.assign(e.style,{position:"fixed",display:"none",minWidth:"180px",backgroundColor:"#2a2a2e",border:"1px solid #444",borderRadius:"6px",boxShadow:"0 4px 16px rgba(0, 0, 0, 0.4)",zIndex:"10000",padding:"4px 0",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:"13px",color:"#e9e9ef"}),document.body.appendChild(e),e}_renderItems(){this.menuElement.innerHTML="";const e=this.items.filter(e=>!e.condition||e.condition(this.target));0!==e.length?e.forEach(e=>{const t=document.createElement("div");t.className="context-menu-item";const n=document.createElement("div");Object.assign(n.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"});const o=document.createElement("span");if(o.textContent=e.label,n.appendChild(o),e.submenu){const e=document.createElement("span");e.textContent="▶",e.style.marginLeft="12px",e.style.fontSize="10px",e.style.opacity="0.7",n.appendChild(e)}t.appendChild(n),Object.assign(t.style,{padding:"4px 8px",cursor:"pointer",transition:"background-color 0.15s ease",userSelect:"none",position:"relative"}),t.addEventListener("mouseenter",()=>{t.style.backgroundColor="#3a3a3e",t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null),e.submenu&&this._showSubmenu(e.submenu,t)}),t.addEventListener("mouseleave",n=>{if(t.style.backgroundColor="transparent",e.submenu){const e=t._submenuElement;e&&(t._hideTimeout=setTimeout(()=>{e.contains(document.elementFromPoint(n.clientX,n.clientY))||this._hideSubmenu(t)},150))}}),e.submenu||t.addEventListener("click",t=>{t.stopPropagation(),e.action(this.target),this.hide()}),this.menuElement.appendChild(t)}):this.hide()}_showSubmenu(e,t){this._hideSubmenu(t);const n=document.createElement("div");n.className="context-submenu",Object.assign(n.style,{position:"fixed",minWidth:"140px",backgroundColor:"#2a2a2e",border:"1px solid #444",borderRadius:"6px",boxShadow:"0 4px 16px rgba(0, 0, 0, 0.4)",zIndex:"10001",padding:"4px 0",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:"13px",color:"#e9e9ef"}),e.forEach(e=>{const t=document.createElement("div");t.className="context-submenu-item";const o=document.createElement("div");if(Object.assign(o.style,{display:"flex",alignItems:"center",gap:"8px"}),e.color){const t=document.createElement("div");Object.assign(t.style,{width:"16px",height:"16px",borderRadius:"3px",backgroundColor:e.color,border:"1px solid #555",flexShrink:"0"}),o.appendChild(t)}const s=document.createElement("span");s.textContent=e.label,o.appendChild(s),t.appendChild(o),Object.assign(t.style,{padding:"4px 8px",cursor:"pointer",transition:"background-color 0.15s ease",userSelect:"none"}),t.addEventListener("mouseenter",()=>{t.style.backgroundColor="#3a3a3e"}),t.addEventListener("mouseleave",()=>{t.style.backgroundColor="transparent"}),t.addEventListener("click",t=>{t.stopPropagation(),e.action(this.target),this.hide()}),n.appendChild(t)}),n.addEventListener("mouseenter",()=>{t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null)}),n.addEventListener("mouseleave",e=>{t.contains(e.relatedTarget)||this._hideSubmenu(t)}),document.body.appendChild(n),t._submenuElement=n,requestAnimationFrame(()=>{const e=t.getBoundingClientRect(),o=n.getBoundingClientRect();let s=e.right+2,i=e.top;s+o.width>window.innerWidth&&(s=e.left-o.width-2),i+o.height>window.innerHeight&&(i=window.innerHeight-o.height-5),n.style.left=`${s}px`,n.style.top=`${i}px`})}_hideSubmenu(e){e._submenuElement&&(e._submenuElement.remove(),e._submenuElement=null)}}class b{constructor({graph:e,registry:t,hooks:n,cyclesPerFrame:o=1}){this.graph=e,this.registry=t,this.hooks=n,this.running=!1,this._raf=null,this._last=0,this.cyclesPerFrame=Math.max(1,0|o)}isRunning(){return this.running}setCyclesPerFrame(e){this.cyclesPerFrame=Math.max(1,0|e)}step(e=1,t=0){var n,o;const s=Math.max(1,0|e);for(let r=0;r<s;r++){for(const e of this.graph.nodes.values()){const s=this.registry.types.get(e.type);if(null==s?void 0:s.onExecute)try{s.onExecute(e,{dt:t,graph:this.graph,getInput:t=>{const n=e.inputs.find(e=>e.name===t)||e.inputs[0];return n?this.graph.getInput(e.id,n.id):void 0},setOutput:(t,n)=>{const o=e.outputs.find(e=>e.name===t)||e.outputs[0];o&&this.graph.setOutput(e.id,o.id,n)}})}catch(i){null==(o=null==(n=this.hooks)?void 0:n.emit)||o.call(n,"error",i)}}this.graph.swapBuffers()}}runOnce(e,t=0){console.log("[Runner.runOnce] Starting exec flow from node:",e);const n=[],o=new Set;let s=e;for(;s;){const e=this.graph.nodes.get(s);if(!e){console.warn(`[Runner.runOnce] Node not found: ${s}`);break}n.push(s),o.add(s),console.log(`[Runner.runOnce] Executing: ${e.title} (${e.type})`);for(const n of e.inputs)if("data"===n.portType)for(const e of this.graph.edges.values())if(e.toNode===s&&e.toPort===n.id){this.graph.nodes.get(e.fromNode)&&!o.has(e.fromNode)&&(o.add(e.fromNode),this.executeNode(e.fromNode,t))}this.executeNode(s,t),s=this.findNextExecNode(s)}console.log("[Runner.runOnce] Executed nodes:",n.length);const i=new Set;for(const r of this.graph.edges.values())o.has(r.fromNode)&&o.has(r.toNode)&&i.add(r.id);return console.log("[Runner.runOnce] Connected edges count:",i.size),{connectedNodes:o,connectedEdges:i}}findNextExecNode(e){const t=this.graph.nodes.get(e);if(!t)return null;const n=t.outputs.find(e=>"exec"===e.portType);if(!n)return null;for(const o of this.graph.edges.values())if(o.fromNode===e&&o.fromPort===n.id)return o.toNode;return null}executeNode(e,t){var n,o;const s=this.graph.nodes.get(e);if(!s)return;const i=this.registry.types.get(s.type);if(null==i?void 0:i.onExecute)try{i.onExecute(s,{dt:t,graph:this.graph,getInput:e=>{const t=s.inputs.find(t=>t.name===e)||s.inputs[0];return t?this.graph.getInput(s.id,t.id):void 0},setOutput:(e,t)=>{const n=s.outputs.find(t=>t.name===e)||s.outputs[0];if(n){const e=`${s.id}:${n.id}`;this.graph._curBuf().set(e,t)}}})}catch(r){null==(o=null==(n=this.hooks)?void 0:n.emit)||o.call(n,"error",r)}}start(){var e,t;if(this.running)return;this.running=!0,this._last=0,null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:start");const n=e=>{var t,o;if(!this.running)return;const s=this._last?e-this._last:0;this._last=e;const i=s/1e3;this.step(this.cyclesPerFrame,i),null==(o=null==(t=this.hooks)?void 0:t.emit)||o.call(t,"runner:tick",{time:e,dt:i,running:!0,cps:this.cyclesPerFrame}),this._raf=requestAnimationFrame(n)};this._raf=requestAnimationFrame(n)}stop(){var e,t;this.running&&(this.running=!1,this._raf&&cancelAnimationFrame(this._raf),this._raf=null,this._last=0,null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:stop"))}}class w{constructor(e,t,n){this.host=e,this.renderer=t,this.registry=n,this.container=document.createElement("div"),Object.assign(this.container.style,{position:"absolute",inset:"0",pointerEvents:"none",zIndex:"10"}),e.appendChild(this.container),this.nodes=new Map}_createDefaultNodeLayout(e){const t=document.createElement("div");t.className="node-overlay",Object.assign(t.style,{position:"absolute",display:"flex",flexDirection:"column",boxSizing:"border-box",pointerEvents:"none",overflow:"hidden"});const n=document.createElement("div");n.className="node-header",Object.assign(n.style,{height:"24px",flexShrink:"0",display:"flex",alignItems:"center",padding:"0 8px",cursor:"grab",userSelect:"none",pointerEvents:"none"});const o=document.createElement("div");return o.className="node-body",Object.assign(o.style,{flex:"1",position:"relative",overflow:"hidden",pointerEvents:"auto",pointerEvents:"none"}),t.appendChild(n),t.appendChild(o),t._domParts={header:n,body:o},t}_ensureNodeElement(e,t){var n;let o=this.nodes.get(e.id);if(!o){if(null==(n=t.html)?void 0:n.render)o=t.html.render(e);else{if(!t.html)return null;o=this._createDefaultNodeLayout(e),t.html.init&&t.html.init(e,o,o._domParts)}if(!o)return null;o.style.position="absolute",o.style.pointerEvents="none",this.container.appendChild(o),this.nodes.set(e.id,o)}return o}draw(e,t=new Set){const{scale:n,offsetX:o,offsetY:s}=this.renderer;this.container.style.transform=`translate(${o}px, ${s}px) scale(${n})`,this.container.style.transformOrigin="0 0";const i=new Set;for(const r of e.nodes.values()){const e=this.registry.types.get(r.type);if(!!!(null==e?void 0:e.html))continue;const n=this._ensureNodeElement(r,e);if(n){if(n.style.left=`${r.computed.x}px`,n.style.top=`${r.computed.y}px`,n.style.width=`${r.computed.w}px`,n.style.height=`${r.computed.h}px`,e.html.update){const o=n._domParts||{};e.html.update(r,n,{selected:t.has(r.id),header:o.header,body:o.body})}i.add(r.id)}}for(const[r,a]of this.nodes)i.has(r)||(a.remove(),this.nodes.delete(r))}clear(){for(const[,e]of this.nodes)e.remove();this.nodes.clear()}destroy(){this.clear(),this.container.remove()}}class E{constructor(e,{graph:t,renderer:n,width:o=200,height:s=150}={}){this.graph=t,this.renderer=n,this.width=o,this.height=s,this.canvas=document.createElement("canvas"),this.canvas.id="minimap",this.canvas.width=o,this.canvas.height=s,this.canvas.style.position="fixed",this.canvas.style.bottom="20px",this.canvas.style.right="20px",this.canvas.style.border="2px solid #444",this.canvas.style.borderRadius="8px",this.canvas.style.background="rgba(20, 20, 23, 0.9)",this.canvas.style.boxShadow="0 4px 12px rgba(0, 0, 0, 0.5)",this.canvas.style.pointerEvents="none",this.ctx=this.canvas.getContext("2d"),e.appendChild(this.canvas)}render(){const{graph:e,renderer:t,ctx:n,width:o,height:s}=this;if(n.fillStyle="#141417",n.fillRect(0,0,o,s),0===e.nodes.size)return;let i=1/0,r=1/0,a=-1/0,l=-1/0;for(const v of e.nodes.values()){const{x:e,y:t,w:n,h:o}=v.computed;i=Math.min(i,e),r=Math.min(r,t),a=Math.max(a,e+n),l=Math.max(l,t+o)}const d=100,c=Math.max(300,a-i+200),h=Math.max(200,l-r+200);i-=d,r-=d;const u=Math.min((o-20)/c,(s-20)/h),p=(o-c*u)/2,g=(s-h*u)/2;n.strokeStyle="rgba(127, 140, 255, 0.5)",n.lineWidth=1;for(const v of e.edges.values()){const t=e.nodes.get(v.fromNode),o=e.nodes.get(v.toNode);if(!t||!o)continue;const s=(t.computed.x+t.computed.w/2-i)*u+p,a=(t.computed.y+t.computed.h/2-r)*u+g,l=(o.computed.x+o.computed.w/2-i)*u+p,d=(o.computed.y+o.computed.h/2-r)*u+g;n.beginPath(),n.moveTo(s,a),n.lineTo(l,d),n.stroke()}n.fillStyle="#6cf";for(const v of e.nodes.values()){const{x:e,y:t,w:o,h:s}=v.computed,a=(e-i)*u+p,l=(t-r)*u+g,d=o*u,c=s*u;"core/Group"===v.type?(n.fillStyle="rgba(102, 204, 255, 0.2)",n.strokeStyle="#6cf",n.lineWidth=1,n.fillRect(a,l,d,c),n.strokeRect(a,l,d,c)):(n.fillStyle="#6cf",n.fillRect(a,l,Math.max(2,d),Math.max(2,c)))}const f=(-t.offsetX/t.scale-i)*u+p,m=(-t.offsetY/t.scale-r)*u+g,y=t.canvas.width/t.scale*u,x=t.canvas.height/t.scale*u;n.strokeStyle="#ff6b6b",n.lineWidth=2,n.strokeRect(f,m,y,x)}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}}class _{constructor(e,{graph:t,hooks:n,registry:o,render:s}){this.container=e,this.graph=t,this.hooks=n,this.registry=o,this.render=s,this.panel=null,this.currentNode=null,this.isVisible=!1,this._createPanel()}_createPanel(){this.panel=document.createElement("div"),this.panel.className="property-panel",this.panel.style.display="none",this.panel.innerHTML='\n <div class="panel-inner">\n <div class="panel-header">\n <div class="panel-title">\n <span class="title-text">Node Properties</span>\n </div>\n <button class="panel-close" type="button">×</button>\n </div>\n <div class="panel-content">\n \x3c!-- Content will be dynamically generated --\x3e\n </div>\n </div>\n ',this.container.appendChild(this.panel),this.panel.querySelector(".panel-close").addEventListener("click",()=>{this.close()}),document.addEventListener("keydown",e=>{"Escape"===e.key&&this.isVisible&&this.close()})}open(e){e&&(this.currentNode=e,this.isVisible=!0,this._renderContent(),this.panel.style.display="block",this.panel.classList.add("panel-visible"))}close(){this.isVisible=!1,this.panel.classList.remove("panel-visible"),setTimeout(()=>{this.panel.style.display="none",this.currentNode=null},200)}_renderContent(){var e,t;const n=this.currentNode;if(!n)return;const o=this.panel.querySelector(".panel-content");null==(t=null==(e=this.registry)?void 0:e.types)||t.get(n.type),o.innerHTML=`\n <div class="section">\n <div class="section-title">Basic Info</div>\n <div class="section-body">\n <div class="field">\n <label>Type</label>\n <input type="text" value="${n.type}" readonly />\n </div>\n <div class="field">\n <label>Title</label>\n <input type="text" data-field="title" value="${n.title||""}" />\n </div>\n <div class="field">\n <label>ID</label>\n <input type="text" value="${n.id}" readonly />\n </div>\n </div>\n </div>\n \n <div class="section">\n <div class="section-title">Position & Size</div>\n <div class="section-body">\n <div class="field-row">\n <div class="field">\n <label>X</label>\n <input type="number" data-field="x" value="${Math.round(n.computed.x)}" />\n </div>\n <div class="field">\n <label>Y</label>\n <input type="number" data-field="y" value="${Math.round(n.computed.y)}" />\n </div>\n </div>\n <div class="field-row">\n <div class="field">\n <label>Width</label>\n <input type="number" data-field="width" value="${n.computed.w}" />\n </div>\n <div class="field">\n <label>Height</label>\n <input type="number" data-field="height" value="${n.computed.h}" />\n </div>\n </div>\n </div>\n </div>\n \n ${this._renderPorts(n)}\n ${this._renderState(n)}\n \n <div class="panel-actions">\n <button class="btn-secondary panel-close-btn">Close</button>\n </div>\n `,this._attachInputListeners()}_renderPorts(e){return e.inputs.length||e.outputs.length?`\n <div class="section">\n <div class="section-title">Ports</div>\n <div class="section-body">\n ${e.inputs.length?`\n <div class="port-group">\n <div class="port-group-title">Inputs (${e.inputs.length})</div>\n ${e.inputs.map(e=>`\n <div class="port-item">\n <span class="port-icon ${e.portType||"data"}"></span>\n <span class="port-name">${e.name}</span>\n ${e.datatype?`<span class="port-type">${e.datatype}</span>`:""}\n </div>\n `).join("")}\n </div>\n `:""}\n \n ${e.outputs.length?`\n <div class="port-group">\n <div class="port-group-title">Outputs (${e.outputs.length})</div>\n ${e.outputs.map(e=>`\n <div class="port-item">\n <span class="port-icon ${e.portType||"data"}"></span>\n <span class="port-name">${e.name}</span>\n ${e.datatype?`<span class="port-type">${e.datatype}</span>`:""}\n </div>\n `).join("")}\n </div>\n `:""}\n </div>\n </div>\n `:""}_renderState(e){return e.state&&0!==Object.keys(e.state).length?`\n <div class="section">\n <div class="section-title">State</div>\n <div class="section-body">\n ${Object.entries(e.state).map(([e,t])=>`\n <div class="field">\n <label>${e}</label>\n <input \n type="${"number"==typeof t?"number":"text"}" \n data-field="state.${e}" \n value="${t}" \n />\n </div>\n `).join("")}\n </div>\n </div>\n `:""}_attachInputListeners(){this.panel.querySelectorAll("[data-field]").forEach(e=>{e.addEventListener("change",()=>{this._handleFieldChange(e.dataset.field,e.value)})}),this.panel.querySelector(".panel-close-btn").addEventListener("click",()=>{this.close()})}_handleFieldChange(e,t){var n;const o=this.currentNode;if(o){switch(e){case"title":o.title=t;break;case"x":o.pos.x=parseFloat(t),this.graph.updateWorldTransforms();break;case"y":o.pos.y=parseFloat(t),this.graph.updateWorldTransforms();break;case"width":o.size.width=parseFloat(t);break;case"height":o.size.height=parseFloat(t);break;default:if(e.startsWith("state.")){const n=e.substring(6);if(o.state){const e=o.state[n];o.state[n]="number"==typeof e?parseFloat(t):t}}}null==(n=this.hooks)||n.emit("node:updated",o),this.render&&this.render()}}destroy(){this.panel&&this.panel.remove()}}e.createGraphEditor=function(e,{theme:t,hooks:n,autorun:s=!0,showMinimap:i=!0,enablePropertyPanel:r=!0,propertyPanelContainer:a=null}={}){let d,c;if("string"==typeof e&&(e=document.querySelector(e)),!e)throw new Error("createGraphEditor: target element not found");e instanceof HTMLCanvasElement?(d=e,c=d.parentElement):(c=e,d=c.querySelector("canvas"),d||(d=document.createElement("canvas"),d.style.display="block",d.style.width="100%",d.style.height="100%",c.appendChild(d))),"static"===getComputedStyle(c).position&&(c.style.position="relative");const u=n??function(e){const t=Object.fromEntries(e.map(e=>[e,new Set]));return{on:(e,n)=>(t[e].add(n),()=>t[e].delete(n)),async emit(e,...n){for(const o of t[e])await o(...n)}}}(["node:create","node:move","node:click","node:dblclick","edge:create","edge:delete","graph:serialize","graph:deserialize","error","runner:tick","runner:start","runner:stop","node:resize","group:change","node:updated"]),p=new o,f=new l({hooks:u,registry:p}),m=new h(d,{theme:t,registry:p}),x=new w(d.parentElement,m,p),k=document.createElement("canvas");k.id="port-canvas",Object.assign(k.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"20"}),d.parentElement.appendChild(k);const T=new h(k,{theme:t,registry:p});T.setTransform=m.setTransform.bind(m),T.scale=m.scale,T.offsetX=m.offsetX,T.offsetY=m.offsetY;const S=new y({graph:f,renderer:m,hooks:u,htmlOverlay:x,portRenderer:T}),C=new v({graph:f,hooks:u,renderer:m,commandStack:S.stack});S.contextMenu=C;let N=null;i&&(N=new E(c,{graph:f,renderer:m}));let P=null;r&&(P=new _(a||c,{graph:f,hooks:u,registry:p,render:()=>S.render()}),u.on("node:dblclick",e=>{P.open(e)}));const z=new b({graph:f,registry:p,hooks:u});u.on("runner:tick",({time:e,dt:t})=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!0,time:e,dt:t}),x.draw(f,S.selection)}),u.on("runner:start",()=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!0,time:performance.now(),dt:0}),x.draw(f,S.selection)}),u.on("runner:stop",()=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!1,time:performance.now(),dt:0}),x.draw(f,S.selection)}),u.on("node:updated",()=>{S.render()}),p.register("core/Note",{title:"Note",size:{w:180,h:80},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],onCreate(e){e.state.text="hello"},onExecute(e,{dt:t,getInput:n,setOutput:o}){o("out",(n("in")??e.state.text??"").toString().toUpperCase()+` · ${Math.floor(performance.now()/1e3%100)}`)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.pos,{width:i}=e.size}}),p.register("core/HtmlNote",{title:"HTML Note",size:{w:200,h:150},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#222",t.style.borderRadius="8px",t.style.border="1px solid #444",t.style.boxShadow="0 4px 12px rgba(0,0,0,0.3)",n.style.backgroundColor="#333",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.style.fontWeight="bold",n.textContent="My HTML Node",o.style.padding="8px",o.style.color="#ccc",o.style.fontSize="12px";const s=document.createElement("div");s.textContent="Event Name",o.appendChild(s);const i=document.createElement("input");Object.assign(i.style,{marginTop:"4px",padding:"4px",background:"#111",border:"1px solid #555",color:"#fff",borderRadius:"4px",pointerEvents:"auto"}),i.placeholder="Type here...",i.addEventListener("input",t=>{e.state.text=t.target.value}),i.addEventListener("mousedown",e=>e.stopPropagation()),o.appendChild(i),t._input=i},update(e,t,{header:n,body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#333",t._input.value!==(e.state.text||"")&&(t._input.value=e.state.text||"")}},onCreate(e){e.state.text=""},onExecute(e,{getInput:t,setOutput:n}){n("out",t("in"))}}),p.register("core/TodoNode",{title:"Todo List",size:{w:240,h:300},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.borderRadius="8px",t.style.boxShadow="0 4px 12px rgba(0,0,0,0.5)",t.style.border="1px solid #333",n.style.backgroundColor="#2a2a31",n.style.padding="8px",n.style.fontWeight="bold",n.style.color="#e9e9ef",n.textContent=e.title,o.style.display="flex",o.style.flexDirection="column",o.style.padding="8px",o.style.color="#e9e9ef";const s=document.createElement("div");Object.assign(s.style,{display:"flex",gap:"4px",marginBottom:"8px"});const i=document.createElement("input");Object.assign(i.style,{flex:"1",padding:"6px",borderRadius:"4px",border:"1px solid #444",background:"#141417",color:"#fff",pointerEvents:"auto"}),i.placeholder="Add task...";const r=document.createElement("button");r.textContent="+",Object.assign(r.style,{padding:"0 12px",cursor:"pointer",background:"#4f5b66",color:"#fff",border:"none",borderRadius:"4px",pointerEvents:"auto"}),s.append(i,r);const a=document.createElement("ul");Object.assign(a.style,{listStyle:"none",padding:"0",margin:"0",overflow:"hidden",flex:"1"}),o.append(s,a);const l=()=>{const t=i.value.trim();if(!t)return;const n=e.state.todos||[];e.state.todos=[...n,{id:Date.now(),text:t,done:!1}],i.value="",u.emit("node:updated",e)};r.onclick=l,i.onkeydown=e=>{"Enter"===e.key&&l(),e.stopPropagation()},i.onmousedown=e=>e.stopPropagation(),t._refs={list:a}},update(e,t,{selected:n}){t.style.borderColor=n?"#6cf":"#333";const{list:o}=t._refs,s=e.state.todos||[];o.innerHTML="",s.forEach(t=>{const n=document.createElement("li");Object.assign(n.style,{display:"flex",alignItems:"center",padding:"6px 0",borderBottom:"1px solid #2a2a31"});const s=document.createElement("input");s.type="checkbox",s.checked=t.done,s.style.marginRight="8px",s.style.pointerEvents="auto",s.onchange=()=>{t.done=s.checked,u.emit("node:updated",e)},s.onmousedown=e=>e.stopPropagation();const i=document.createElement("span");i.textContent=t.text,i.style.flex="1",i.style.textDecoration=t.done?"line-through":"none",i.style.color=t.done?"#777":"#eee";const r=document.createElement("button");r.textContent="×",Object.assign(r.style,{background:"none",border:"none",color:"#f44",cursor:"pointer",fontSize:"16px",pointerEvents:"auto"}),r.onclick=()=>{e.state.todos=e.state.todos.filter(e=>e.id!==t.id),u.emit("node:updated",e)},r.onmousedown=e=>e.stopPropagation(),n.append(s,i,r),o.appendChild(n)})}},onCreate(e){e.state.todos=[{id:1,text:"Welcome to Free Node",done:!1},{id:2,text:"Try adding a task",done:!0}]}}),p.register("math/Add",{title:"Add",size:{w:140,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"number"},{name:"b",portType:"data",datatype:"number"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"number"}],onCreate(e){e.state.a=0,e.state.b=0},onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??0,i=o+s;console.log("[Add] a:",o,"b:",s,"result:",i),n("result",i)}}),p.register("math/Subtract",{title:"Subtract",size:{w:140,h:80},inputs:[{name:"a",datatype:"number"},{name:"b",datatype:"number"}],outputs:[{name:"result",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){n("result",(t("a")??0)-(t("b")??0))}}),p.register("math/Multiply",{title:"Multiply",size:{w:140,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"number"},{name:"b",portType:"data",datatype:"number"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??0,i=o*s;console.log("[Multiply] a:",o,"b:",s,"result:",i),n("result",i)}}),p.register("math/Divide",{title:"Divide",size:{w:140,h:80},inputs:[{name:"a",datatype:"number"},{name:"b",datatype:"number"}],outputs:[{name:"result",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??1;n("result",0!==s?o/s:0)}}),p.register("logic/AND",{title:"AND",size:{w:120,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"boolean"},{name:"b",portType:"data",datatype:"boolean"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??!1,s=t("b")??!1;console.log("[AND] Inputs - a:",o,"b:",s);const i=o&&s;console.log("[AND] Result:",i),n("result",i)}}),p.register("logic/OR",{title:"OR",size:{w:120,h:80},inputs:[{name:"a",datatype:"boolean"},{name:"b",datatype:"boolean"}],outputs:[{name:"result",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??!1,s=t("b")??!1;n("result",o||s)}}),p.register("logic/NOT",{title:"NOT",size:{w:120,h:70},inputs:[{name:"in",datatype:"boolean"}],outputs:[{name:"out",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){n("out",!(t("in")??!1))}}),p.register("value/Number",{title:"Number",size:{w:140,h:60},outputs:[{name:"value",portType:"data",datatype:"number"}],onCreate(e){e.state.value=0},onExecute(e,{setOutput:t}){console.log("[Number] Outputting value:",e.state.value??0),t("value",e.state.value??0)},html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.border="1px solid #444",t.style.borderRadius="8px",n.style.backgroundColor="#2a2a31",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.textContent="Number",o.style.padding="12px",o.style.display="flex",o.style.alignItems="center",o.style.justifyContent="center";const s=document.createElement("input");s.type="number",s.value=e.state.value??0,Object.assign(s.style,{width:"100%",padding:"6px",background:"#141417",border:"1px solid #444",borderRadius:"4px",color:"#fff",fontSize:"14px",textAlign:"center",pointerEvents:"auto"}),s.addEventListener("change",t=>{e.state.value=parseFloat(t.target.value)||0}),s.addEventListener("mousedown",e=>e.stopPropagation()),s.addEventListener("keydown",e=>e.stopPropagation()),o.appendChild(s)},update(e,t,{header:n,body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#2a2a31"}},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#8f8",t.font="14px sans-serif",t.textAlign="center",t.fillText(String(e.state.value??0),o+70,s+42)}}),p.register("value/String",{title:"String",size:{w:160,h:60},outputs:[{name:"value",datatype:"string"}],onCreate(e){e.state.value="Hello"},onExecute(e,{setOutput:t}){t("value",e.state.value??"")},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#8f8",t.font="12px sans-serif",t.textAlign="center";const i=String(e.state.value??""),r=i.length>15?i.substring(0,15)+"...":i;t.fillText(r,o+80,s+42)}}),p.register("value/Boolean",{title:"Boolean",size:{w:140,h:60},outputs:[{name:"value",portType:"data",datatype:"boolean"}],onCreate(e){e.state.value=!0},onExecute(e,{setOutput:t}){console.log("[Boolean] Outputting value:",e.state.value??!1),t("value",e.state.value??!1)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle=e.state.value?"#8f8":"#f88",t.font="14px sans-serif",t.textAlign="center",t.fillText(String(e.state.value),o+70,s+42)}}),p.register("util/Print",{title:"Print",size:{w:140,h:80},inputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],onCreate(e){e.state.lastValue=null},onExecute(e,{getInput:t}){const n=t("value");n!==e.state.lastValue&&(console.log("[Print]",n),e.state.lastValue=n)}}),p.register("util/Watch",{title:"Watch",size:{w:180,h:110},inputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],outputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],onCreate(e){e.state.displayValue="---"},onExecute(e,{getInput:t,setOutput:n}){const o=t("value");console.log("[Watch] onExecute called, value:",o),e.state.displayValue=String(o??"---"),n("value",o)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#fa3",t.font="11px monospace",t.textAlign="left";const i=String(e.state.displayValue??"---"),r=i.length>20?i.substring(0,20)+"...":i;t.fillText(r,o+8,s+50)}}),p.register("util/Timer",{title:"Timer",size:{w:140,h:60},outputs:[{name:"time",datatype:"number"}],onCreate(e){e.state.startTime=performance.now()},onExecute(e,{setOutput:t}){t("time",((performance.now()-(e.state.startTime??0))/1e3).toFixed(2))}}),p.register("util/Trigger",{title:"Trigger",size:{w:140,h:80},outputs:[{name:"exec",portType:"exec"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.border="1px solid #444",t.style.borderRadius="8px",n.style.backgroundColor="#2a2a31",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.textContent="Trigger",o.style.padding="12px",o.style.display="flex",o.style.alignItems="center",o.style.justifyContent="center";const s=document.createElement("button");s.textContent="Fire!",Object.assign(s.style,{padding:"8px 16px",background:"#4a9eff",border:"none",borderRadius:"4px",color:"#fff",fontWeight:"bold",cursor:"pointer",pointerEvents:"auto",transition:"background 0.2s"}),s.addEventListener("mousedown",e=>{e.stopPropagation(),s.style.background="#2a7ede"}),s.addEventListener("mouseup",()=>{s.style.background="#4a9eff"}),s.addEventListener("click",t=>{if(t.stopPropagation(),e.state.triggered=!0,console.log("[Trigger] Button clicked!"),e.__runnerRef&&e.__controllerRef){console.log("[Trigger] Runner and controller found");const t=e.__runnerRef,n=e.__controllerRef,o=n.graph;console.log("[Trigger] Calling runner.runOnce with node.id:",e.id);const s=t.runOnce(e.id,0).connectedEdges,i=performance.now(),r=500,a=()=>{var t;performance.now()-i<r?(n.renderer.draw(o,{selection:n.selection,tempEdge:null,running:!0,time:performance.now(),dt:0,activeEdges:s}),null==(t=n.htmlOverlay)||t.draw(o,n.selection),requestAnimationFrame(a)):(n.render(),e.state.triggered=!1)};a()}}),o.appendChild(s)},update(e,t,{header:n,body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#2a2a31"}},onCreate(e){e.state.triggered=!1},onExecute(e,{setOutput:t}){console.log("[Trigger] Outputting triggered:",e.state.triggered),t("triggered",e.state.triggered)}}),p.register("core/Group",{title:"Group",size:{w:240,h:160},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s,w:i,h:r}=e.computed,a=e.state.color||"#39424e",l=n.text||"#e9e9ef",d=(e,t)=>{const n=e.replace("#",""),o=parseInt(3===n.length?n.split("").map(e=>e+e).join(""):n,16);return`rgba(${o>>16&255},${o>>8&255},${255&o},${t})`};var c,h,u,p,g,f;t.fillStyle=d(a,.5),h=o,u=s,(p=i)<2*(f=10)&&(f=p/2),(g=r)<2*f&&(f=g/2),(c=t).beginPath(),c.moveTo(h+f,u),c.arcTo(h+p,u,h+p,u+g,f),c.arcTo(h+p,u+g,h,u+g,f),c.arcTo(h,u+g,h,u,f),c.arcTo(h,u,h+p,u,f),c.closePath(),t.fill(),t.fillStyle=d(a,.3),t.beginPath(),t.roundRect(o,s,i,24,[10,10,0,0]),t.fill(),t.fillStyle=l,t.font="600 13px system-ui",t.textBaseline="top",t.fillText(e.title,o+12,s+6)}}),function(e,{controller:t,graph:n,hooks:o}){const s=[];for(const[i,r]of n.registry.types.entries())s.push({id:`add-${i}`,label:r.title||i,action:()=>{const s=e.worldPosition||{x:100,y:100},r=n.addNode(i,{x:s.x,y:s.y});null==o||o.emit("node:updated",r),t.render()}});e.addItem("add-node","Add Node",{condition:e=>!e,submenu:s,order:5}),e.addItem("delete-node","Delete Node",{condition:e=>e&&"core/Group"!==e.type,action:e=>{const s=g(n,e);t.stack.exec(s),null==o||o.emit("node:updated",e)},order:10}),e.addItem("change-group-color","Change Color",{condition:e=>e&&"core/Group"===e.type,submenu:[{name:"Default",color:"#39424e"},{name:"Slate",color:"#4a5568"},{name:"Gray",color:"#2d3748"},{name:"Blue",color:"#1a365d"},{name:"Green",color:"#22543d"},{name:"Red",color:"#742a2a"},{name:"Purple",color:"#44337a"}].map(e=>({id:`color-${e.color}`,label:e.name,color:e.color,action:n=>{const s=n.state.color||"#39424e",i=(r=n,a=s,l=e.color,{do(){r.state.color=l},undo(){r.state.color=a}});var r,a,l;t.stack.exec(i),null==o||o.emit("node:updated",n)}})),order:20}),e.addItem("delete-group","Delete Group",{condition:e=>e&&"core/Group"===e.type,action:e=>{const s=g(n,e);t.stack.exec(s),null==o||o.emit("node:updated",e)},order:20})}(C,{controller:S,graph:f,hooks:u}),m.resize(d.clientWidth,d.clientHeight),T.resize(d.clientWidth,d.clientHeight),S.render();const M=new ResizeObserver(()=>{m.resize(d.clientWidth,d.clientHeight),T.resize(d.clientWidth,d.clientHeight),S.render()});M.observe(d);const I=S.render.bind(S);S.render=function(){I(),N&&N.render()};const O={addGroup:(e={})=>{S.graph.groupManager.addGroup(e),S.render()},graph:f,renderer:m,controller:S,runner:z,minimap:N,contextMenu:C,hooks:u,registry:p,htmlOverlay:x,propertyPanel:P,render:()=>S.render(),start:()=>z.start(),stop:()=>z.stop(),destroy:()=>{z.stop(),M.disconnect(),S.destroy(),x.destroy(),C.destroy(),P&&P.destroy(),N&&N.destroy()}};return s&&z.start(),O},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HTMLOverlayNode={})}(this,function(e){"use strict";var t=Object.defineProperty,n=(e,n,o)=>((e,n,o)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[n]=o)(e,"symbol"!=typeof n?n+"":n,o);class o{constructor(){this.types=new Map}register(e,t){if(!e||"string"!=typeof e)throw new Error("Invalid node type: type must be a non-empty string, got "+typeof e);if(!t||"object"!=typeof t)throw new Error(`Invalid definition for type "${e}": definition must be an object`);if(this.types.has(e))throw new Error(`Node type "${e}" is already registered. Use unregister() first to replace it.`);this.types.set(e,t)}unregister(e){if(!this.types.has(e))throw new Error(`Cannot unregister type "${e}": type is not registered`);this.types.delete(e)}removeAll(){this.types.clear()}createInstance(e){const t=this.types.get(e);if(!t){const t=Array.from(this.types.keys()).join(", ")||"none";throw new Error(`Unknown node type: "${e}". Available types: ${t}`)}return t}}function s(){const e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},t=e.crypto||e.msCrypto;if(t&&"function"==typeof t.randomUUID)return t.randomUUID();if(t&&"function"==typeof t.getRandomValues){const e=new Uint8Array(16);t.getRandomValues(e),e[6]=15&e[6]|64,e[8]=63&e[8]|128;const n=Array.from(e,e=>e.toString(16).padStart(2,"0"));return n.slice(0,4).join("")+"-"+n.slice(4,6).join("")+"-"+n.slice(6,8).join("")+"-"+n.slice(8,10).join("")+"-"+n.slice(10,16).join("")}try{const e=Function('return typeof require === "function" ? require : null')();if(e){const t=e("crypto");if("function"==typeof t.randomUUID)return t.randomUUID();const n=t.randomBytes(16);n[6]=15&n[6]|64,n[8]=63&n[8]|128;const o=Array.from(n,e=>e.toString(16).padStart(2,"0"));return o.slice(0,4).join("")+"-"+o.slice(4,6).join("")+"-"+o.slice(6,8).join("")+"-"+o.slice(8,10).join("")+"-"+o.slice(10,16).join("")}}catch{}const n=new Uint8Array(16);for(let s=0;s<16;s++)n[s]=Math.floor(256*Math.random());n[6]=15&n[6]|64,n[8]=63&n[8]|128;const o=Array.from(n,e=>e.toString(16).padStart(2,"0"));return o.slice(0,4).join("")+"-"+o.slice(4,6).join("")+"-"+o.slice(6,8).join("")+"-"+o.slice(8,10).join("")+"-"+o.slice(10,16).join("")}class i{constructor({id:e,type:t,title:n,x:o=0,y:i=0,width:r=160,height:a=60}){if(!t)throw new Error("Node type is required");this.id=e??s(),this.type=t,this.title=n??t,this.pos={x:o,y:i},this.size={width:r,height:a},this.inputs=[],this.outputs=[],this.state={},this.parent=null,this.children=new Set,this.computed={x:0,y:0,w:0,h:0}}addInput(e,t="any",n="data"){if(!e||"string"!=typeof e)throw new Error("Input port name must be a non-empty string");const o={id:s(),name:e,datatype:t,portType:n,dir:"in"};return this.inputs.push(o),o}addOutput(e,t="any",n="data"){if(!e||"string"!=typeof e)throw new Error("Output port name must be a non-empty string");const o={id:s(),name:e,datatype:t,portType:n,dir:"out"};return this.outputs.push(o),o}}class r{constructor({id:e,fromNode:t,fromPort:n,toNode:o,toPort:i}){if(!(t&&n&&o&&i))throw new Error("Edge requires fromNode, fromPort, toNode, and toPort");this.id=e??s(),this.fromNode=t,this.fromPort=n,this.toNode=o,this.toPort=i}}class a{constructor({graph:e,hooks:t}){this.graph=e,this.hooks=t,this._groups=[]}addGroup({title:e="Group",x:t=0,y:n=0,width:o=240,height:s=160,color:i="#39424e",members:r=[]}={}){var a;(o<100||s<60)&&(console.warn("Group size too small, using minimum size"),o=Math.max(100,o),s=Math.max(60,s));const l=this.graph.addNode("core/Group",{title:e,x:t,y:n,width:o,height:s});l.state.color=i;for(const d of r){const e=this.graph.getNodeById(d);if(e){if("core/Group"===e.type){console.warn(`Cannot add group ${d} as member of another group`);continue}this.graph.reparent(e,l)}else console.warn(`Member node ${d} not found, skipping`)}return this._groups.push(l),null==(a=this.hooks)||a.emit("group:change"),l}addGroupFromSelection({_title:e="Group",_margin:t={x:12,y:12}}={}){return null}removeGroup(e){var t;const n=this.graph.getNodeById(e);if(!n||"core/Group"!==n.type)return;const o=[...n.children];for(const s of o)this.graph.reparent(s,n.parent);this.graph.removeNode(e),null==(t=this.hooks)||t.emit("group:change")}resizeGroup(e,t,n){var o;const s=this.graph.getNodeById(e);if(!s||"core/Group"!==s.type)return;s.size.width=Math.max(100,s.size.width+t),s.size.height=Math.max(60,s.size.height+n),this.graph.updateWorldTransforms(),null==(o=this.hooks)||o.emit("group:change")}hitTestResizeHandle(e,t){const n=[...this.graph.nodes.values()].reverse();for(const o of n){if("core/Group"!==o.type)continue;const{x:n,y:s,w:i,h:r}=o.computed;if(e>=n+i-10&&e<=n+i&&t>=s+r-10&&t<=s+r)return{group:o,handle:"se"}}return null}}class l{constructor({hooks:e,registry:t}){if(!t)throw new Error("Graph requires a registry");this.nodes=new Map,this.edges=new Map,this.hooks=e,this.registry=t,this._valuesA=new Map,this._valuesB=new Map,this._useAasCurrent=!0,this.groupManager=new a({graph:this,hooks:this.hooks})}getNodeById(e){return this.nodes.get(e)||null}addNode(e,t={}){var n,o,s,r;const a=this.registry.types.get(e);if(!a){const t=Array.from(this.registry.types.keys()).join(", ")||"none";throw new Error(`Unknown node type: "${e}". Available types: ${t}`)}const l=new i({type:e,title:a.title,width:null==(n=a.size)?void 0:n.w,height:null==(o=a.size)?void 0:o.h,...t});for(const i of a.inputs||[])l.addInput(i.name,i.datatype,i.portType||"data");for(const i of a.outputs||[])l.addOutput(i.name,i.datatype,i.portType||"data");return null==(s=a.onCreate)||s.call(a,l),this.nodes.set(l.id,l),null==(r=this.hooks)||r.emit("node:create",l),l}removeNode(e){for(const[t,n]of this.edges)n.fromNode!==e&&n.toNode!==e||this.edges.delete(t);this.nodes.delete(e)}addEdge(e,t,n,o){var s;if(!this.nodes.has(e))throw new Error(`Cannot create edge: source node "${e}" not found`);if(!this.nodes.has(n))throw new Error(`Cannot create edge: target node "${n}" not found`);const i=new r({fromNode:e,fromPort:t,toNode:n,toPort:o});return this.edges.set(i.id,i),null==(s=this.hooks)||s.emit("edge:create",i),i}clear(){this.nodes.clear(),this.edges.clear()}updateWorldTransforms(){const e=[];for(const n of this.nodes.values())n.parent||e.push(n);const t=e.map(e=>({node:e,px:0,py:0}));for(;t.length>0;){const{node:e,px:n,py:o}=t.pop();e.computed.x=n+e.pos.x,e.computed.y=o+e.pos.y,e.computed.w=e.size.width,e.computed.h=e.size.height;for(const s of e.children)t.push({node:s,px:e.computed.x,py:e.computed.y})}}reparent(e,t){if(e.parent===t)return;const n=e.computed.x,o=e.computed.y;e.parent&&e.parent.children.delete(e),e.parent=t,t?(t.children.add(e),e.pos.x=n-t.computed.x,e.pos.y=o-t.computed.y):(e.pos.x=n,e.pos.y=o),this.updateWorldTransforms()}_curBuf(){return this._useAasCurrent?this._valuesA:this._valuesB}_nextBuf(){return this._useAasCurrent?this._valuesB:this._valuesA}swapBuffers(){this._useAasCurrent=!this._useAasCurrent,this._nextBuf().clear()}setOutput(e,t,n){console.log(`[Graph.setOutput] nodeId: ${e}, portId: ${t}, value:`,n);const o=`${e}:${t}`;this._nextBuf().set(o,n)}getInput(e,t){for(const n of this.edges.values())if(n.toNode===e&&n.toPort===t){const o=`${n.fromNode}:${n.fromPort}`,s=this._curBuf().get(o);return console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, reading from ${n.fromNode}:${n.fromPort}, value:`,s),s}console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, no edge found, returning undefined`)}toJSON(){var e;const t={nodes:[...this.nodes.values()].map(e=>{var t;return{id:e.id,type:e.type,title:e.title,x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height,inputs:e.inputs,outputs:e.outputs,state:e.state,parentId:(null==(t=e.parent)?void 0:t.id)||null}}),edges:[...this.edges.values()]};return null==(e=this.hooks)||e.emit("graph:serialize",t),t}fromJSON(e){var t,n,o;this.clear();for(const s of e.nodes){const e=new i({id:s.id,type:s.type,title:s.title,x:s.x,y:s.y,width:s.w,height:s.h}),o=null==(n=null==(t=this.registry)?void 0:t.types)?void 0:n.get(s.type);(null==o?void 0:o.onCreate)&&o.onCreate(e),e.inputs=s.inputs,e.outputs=s.outputs,e.state={...e.state,...s.state||{}},this.nodes.set(e.id,e)}for(const s of e.nodes)if(s.parentId){const e=this.nodes.get(s.id),t=this.nodes.get(s.parentId);e&&t&&(e.parent=t,t.children.add(e))}for(const s of e.edges)this.edges.set(s.id,new r(s));return this.updateWorldTransforms(),null==(o=this.hooks)||o.emit("graph:deserialize",e),this}}function d(e,t,n,o){const{x:s,y:i,w:r,h:a}=e.computed||{x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height},l="in"===o?e.inputs.length:e.outputs.length,d=i+28+((a||e.size.height)-28-16)/(l+1)*(n+1);return"in"===o?{x:s-6,y:d-6,w:12,h:12}:"out"===o?{x:s+r-6,y:d-6,w:12,h:12}:void 0}const c=class e{constructor(e,{theme:t={},registry:n,edgeStyle:o="orthogonal"}={}){this.canvas=e,this.ctx=e.getContext("2d"),this.registry=n,this.scale=1,this.minScale=.25,this.maxScale=3,this.offsetX=0,this.offsetY=0,this.edgeStyle=o,this.theme=Object.assign({bg:"#0d0d0f",grid:"#1a1a1d",node:"#16161a",nodeBorder:"#2a2a2f",title:"#1f1f24",text:"#e4e4e7",textMuted:"#a1a1aa",port:"#6366f1",portExec:"#10b981",edge:"#52525b",edgeActive:"#8b5cf6",accent:"#6366f1",accentBright:"#818cf8"},t)}setEdgeStyle(e){this.edgeStyle="line"===e||"orthogonal"===e?e:"bezier"}setRegistry(e){this.registry=e}resize(e,t){this.canvas.width=e,this.canvas.height=t}setTransform({scale:e=this.scale,offsetX:t=this.offsetX,offsetY:n=this.offsetY}={}){this.scale=Math.min(this.maxScale,Math.max(this.minScale,e)),this.offsetX=t,this.offsetY=n}panBy(e,t){this.offsetX+=e,this.offsetY+=t}zoomAt(e,t,n){const o=this.scale,s=Math.min(this.maxScale,Math.max(this.minScale,o*e));if(s===o)return;const i=(t-this.offsetX)/o,r=(n-this.offsetY)/o;this.offsetX=t-i*s,this.offsetY=n-r*s,this.scale=s}screenToWorld(e,t){return{x:(e-this.offsetX)/this.scale,y:(t-this.offsetY)/this.scale}}worldToScreen(e,t){return{x:e*this.scale+this.offsetX,y:t*this.scale+this.offsetY}}_applyTransform(){const{ctx:e}=this;e.setTransform(this.scale,0,0,this.scale,this.offsetX,this.offsetY)}_resetTransform(){this.ctx.setTransform(1,0,0,1,0,0)}_drawArrowhead(e,t,n,o,s=10){const{ctx:i}=this,r=s/this.scale,a=Math.atan2(o-t,n-e);i.beginPath(),i.moveTo(n,o),i.lineTo(n-r*Math.cos(a-Math.PI/6),o-r*Math.sin(a-Math.PI/6)),i.lineTo(n-r*Math.cos(a+Math.PI/6),o-r*Math.sin(a+Math.PI/6)),i.closePath(),i.fill()}_drawScreenText(e,t,n,{fontPx:o=12,color:s=this.theme.text,align:i="left",baseline:r="alphabetic",dpr:a=1}={}){const{ctx:l}=this,{x:d,y:c}=this.worldToScreen(t,n);l.save(),this._resetTransform();const h=Math.round(d)+.5,u=Math.round(c)+.5;l.font=o*this.scale+"px system-ui",l.fillStyle=s,l.textAlign=i,l.textBaseline=r,l.fillText(e,h,u),l.restore()}drawGrid(){const{ctx:e,canvas:t,theme:n,scale:o,offsetX:s,offsetY:i}=this;this._resetTransform(),e.fillStyle=n.bg,e.fillRect(0,0,t.width,t.height),this._applyTransform(),e.strokeStyle=this._rgba(n.grid,.35),e.lineWidth=1/o;const r=20,a=-s/o,l=-i/o,d=(t.width-s)/o,c=(t.height-i)/o,h=Math.floor(a/r)*r,u=Math.floor(l/r)*r;e.beginPath();for(let p=h;p<=d;p+=r)e.moveTo(p,l),e.lineTo(p,c);for(let p=u;p<=c;p+=r)e.moveTo(a,p),e.lineTo(d,p);e.stroke(),this._resetTransform()}draw(t,{selection:n=new Set,tempEdge:o=null,running:s=!1,time:i=performance.now(),dt:r=0,groups:a=null,activeEdges:l=new Set}={}){var d,c,h,u,p,g;t.updateWorldTransforms(),this.drawGrid();const{ctx:f,theme:m}=this;this._applyTransform(),f.save();for(const e of t.nodes.values())if("core/Group"===e.type){const t=n.has(e.id),o=null==(c=null==(d=this.registry)?void 0:d.types)?void 0:c.get(e.type);(null==o?void 0:o.onDraw)?o.onDraw(e,{ctx:f,theme:m}):this._drawNode(e,t)}f.lineWidth=1.5/this.scale;let y=null,x=0;if(s){const t=i/1e3*120/this.scale%e.FONT_SIZE;y=[6/this.scale,6/this.scale],x=-t}for(const e of t.edges.values()){const n=l&&l.size>0&&l.has(e.id);s&&n&&y?(f.setLineDash(y),f.lineDashOffset=x):(f.setLineDash([]),f.lineDashOffset=0);l&&l.has(e.id)?(f.strokeStyle="#00ffff",f.lineWidth=3*this.scale):(f.strokeStyle=m.edge,f.lineWidth=1.5/this.scale),this._drawEdge(t,e)}if(o){const e=this.screenToWorld(o.x1,o.y1),t=this.screenToWorld(o.x2,o.y2),n=this.ctx.getLineDash();this.ctx.setLineDash([6/this.scale,6/this.scale]);let s=null;if("line"===this.edgeStyle?(this._drawLine(e.x,e.y,t.x,t.y),s=[{x:e.x,y:e.y},{x:t.x,y:t.y}]):"orthogonal"===this.edgeStyle?s=this._drawOrthogonal(e.x,e.y,t.x,t.y):(this._drawCurve(e.x,e.y,t.x,t.y),s=[{x:e.x,y:e.y},{x:t.x,y:t.y}]),this.ctx.setLineDash(n),s&&s.length>=2){const e=s[s.length-2],t=s[s.length-1];this.ctx.fillStyle=this.theme.edge,this.ctx.strokeStyle=this.theme.edge,this._drawArrowhead(e.x,e.y,t.x,t.y,12)}}for(const e of t.nodes.values())if("core/Group"!==e.type){const t=n.has(e.id),o=null==(u=null==(h=this.registry)?void 0:h.types)?void 0:u.get(e.type),s=!!(null==o?void 0:o.html);this._drawNode(e,t,s),(null==o?void 0:o.onDraw)&&o.onDraw(e,{ctx:f,theme:m})}for(const e of t.nodes.values())if("core/Group"!==e.type){const t=null==(g=null==(p=this.registry)?void 0:p.types)?void 0:g.get(e.type);!!(null==t?void 0:t.html)&&this._drawPorts(e)}this._resetTransform()}_rgba(e,t){const n=e.replace("#",""),o=parseInt(3===n.length?n.split("").map(e=>e+e).join(""):n,16);return`rgba(${o>>16&255},${o>>8&255},${255&o},${t})`}_drawNode(t,n,o=!1){const{ctx:s,theme:i}=this,{x:r,y:a,w:l,h:c}=t.computed;n||(s.save(),s.shadowColor="rgba(0, 0, 0, 0.3)",s.shadowBlur=8/this.scale,s.shadowOffsetY=2/this.scale,s.fillStyle="rgba(0, 0, 0, 0.2)",u(s,r,a,l,c,8),s.fill(),s.restore()),s.fillStyle=i.node,s.strokeStyle=n?i.accentBright:i.nodeBorder,s.lineWidth=(n?1.5:1)/this.scale,u(s,r,a,l,c,8),s.fill(),s.stroke(),s.fillStyle=i.title,u(s,r,a,l,24,{tl:8,tr:8,br:0,bl:0}),s.fill(),s.strokeStyle=n?i.accentBright:i.nodeBorder,s.lineWidth=(n?1.5:1)/this.scale,s.beginPath(),s.moveTo(r+8,a),s.lineTo(r+l-8,a),s.quadraticCurveTo(r+l,a,r+l,a+8),s.lineTo(r+l,a+24),s.moveTo(r,a+24),s.lineTo(r,a+8),s.quadraticCurveTo(r,a,r+8,a),s.stroke(),this._drawScreenText(t.title,r+8,a+e.FONT_SIZE,{fontPx:e.FONT_SIZE,color:i.text,baseline:"middle",align:"left"}),o||(t.inputs.forEach((e,n)=>{const o=d(t,0,n,"in"),r=o.x+o.w/2,a=o.y+o.h/2;if("exec"===e.portType){const e=8;s.fillStyle=i.portExec,s.strokeStyle="rgba(16, 185, 129, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.roundRect(r-e/2,a-e/2,e,e,2),s.fill(),s.stroke()}else s.fillStyle=i.port,s.strokeStyle="rgba(99, 102, 241, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.arc(r,a,5,0,2*Math.PI),s.fill(),s.stroke()}),t.outputs.forEach((e,n)=>{const o=d(t,0,n,"out"),r=o.x+o.w/2,a=o.y+o.h/2;if("exec"===e.portType){const e=8;s.fillStyle=i.portExec,s.strokeStyle="rgba(16, 185, 129, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.roundRect(r-e/2,a-e/2,e,e,2),s.fill(),s.stroke()}else s.fillStyle=i.port,s.strokeStyle="rgba(99, 102, 241, 0.3)",s.lineWidth=2/this.scale,s.beginPath(),s.arc(r,a,5,0,2*Math.PI),s.fill(),s.stroke()}))}_drawPorts(e){const{ctx:t,theme:n}=this;e.inputs.forEach((o,s)=>{const i=d(e,0,s,"in"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===o.portType){const e=8;t.fillStyle=n.portExec,t.strokeStyle="rgba(16, 185, 129, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.roundRect(r-e/2,a-e/2,e,e,2),t.fill(),t.stroke()}else t.fillStyle=n.port,t.strokeStyle="rgba(99, 102, 241, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.arc(r,a,5,0,2*Math.PI),t.fill()}),e.outputs.forEach((o,s)=>{const i=d(e,0,s,"out"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===o.portType){const e=8;t.fillStyle=n.portExec,t.strokeStyle="rgba(16, 185, 129, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.roundRect(r-e/2,a-e/2,e,e,2),t.fill(),t.stroke()}else t.fillStyle=n.port,t.strokeStyle="rgba(99, 102, 241, 0.3)",t.lineWidth=2/this.scale,t.beginPath(),t.arc(r,a,5,0,2*Math.PI),t.fill(),t.stroke()})}_drawEdge(e,t){const n=e.nodes.get(t.fromNode),o=e.nodes.get(t.toNode);if(!n||!o)return;const s=n.outputs.findIndex(e=>e.id===t.fromPort),i=o.inputs.findIndex(e=>e.id===t.toPort),r=d(n,0,s,"out"),a=d(o,0,i,"in"),l=r.x,c=r.y+7,h=a.x,u=a.y+7;"line"===this.edgeStyle?this._drawLine(l,c,h,u):"orthogonal"===this.edgeStyle?this._drawOrthogonal(l,c,h,u):this._drawCurve(l,c,h,u)}_drawLine(e,t,n,o){const{ctx:s}=this;s.beginPath(),s.moveTo(e,t),s.lineTo(n,o),s.stroke()}_drawPolyline(e){const{ctx:t}=this;t.beginPath(),t.moveTo(e[0].x,e[0].y);for(let n=1;n<e.length;n++)t.lineTo(e[n].x,e[n].y);t.stroke()}_drawOrthogonal(e,t,n,o){const s=(e+n)/2;let i;i=[{x:e,y:t},{x:s,y:t},{x:s,y:o},{x:n,y:o}];const{ctx:r}=this,a=r.lineJoin,l=r.lineCap;return r.lineJoin="round",r.lineCap="round",this._drawPolyline(i),r.lineJoin=a,r.lineCap=l,i}_drawCurve(e,t,n,o){const{ctx:s}=this,i=Math.max(40,.4*Math.abs(n-e));s.beginPath(),s.moveTo(e,t),s.bezierCurveTo(e+i,t,n-i,o,n,o),s.stroke()}};n(c,"FONT_SIZE",12),n(c,"SELECTED_NODE_COLOR","#6cf");let h=c;function u(e,t,n,o,s,i=6){"number"==typeof i&&(i={tl:i,tr:i,br:i,bl:i}),e.beginPath(),e.moveTo(t+i.tl,n),e.lineTo(t+o-i.tr,n),e.quadraticCurveTo(t+o,n,t+o,n+i.tr),e.lineTo(t+o,n+s-i.br),e.quadraticCurveTo(t+o,n+s,t+o-i.br,n+s),e.lineTo(t+i.bl,n+s),e.quadraticCurveTo(t,n+s,t,n+s-i.bl),e.lineTo(t,n+i.tl),e.quadraticCurveTo(t,n,t+i.tl,n),e.closePath()}function p(e,t,n,o,s){for(const[i,r]of e.edges)if(r.fromNode===t&&r.fromPort===n&&r.toNode===o&&r.toPort===s)return i;return null}function g(e,t){let n=null,o=[];return{do(){n=t,o=e.edges?[...e.edges.values()].filter(e=>e.fromNode===t.id||e.toNode===t.id):[];for(const t of o)e.edges.delete(t.id);e.nodes.delete(t.id)},undo(){n&&e.nodes.set(n.id,n);for(const t of o)e.edges.set(t.id,t)}}}class f{constructor(){this.undoStack=[],this.redoStack=[]}exec(e){e.do(),this.undoStack.push(e),this.redoStack.length=0}undo(){const e=this.undoStack.pop();e&&(e.undo(),this.redoStack.push(e))}redo(){const e=this.redoStack.pop();e&&(e.do(),this.undoStack.push(e))}}const m=class e{constructor({graph:e,renderer:t,hooks:n,htmlOverlay:o,contextMenu:s,portRenderer:i}){this.graph=e,this.renderer=t,this.hooks=n,this.htmlOverlay=o,this.contextMenu=s,this.portRenderer=i,this.stack=new f,this.selection=new Set,this.dragging=null,this.connecting=null,this.panning=null,this.resizing=null,this.gDragging=null,this.gResizing=null,this.boxSelecting=null,this.snapToGrid=!0,this.gridSize=20,this._cursor="default",this._onKeyPressEvt=this._onKeyPress.bind(this),this._onDownEvt=this._onDown.bind(this),this._onWheelEvt=this._onWheel.bind(this),this._onMoveEvt=this._onMove.bind(this),this._onUpEvt=this._onUp.bind(this),this._onContextMenuEvt=this._onContextMenu.bind(this),this._onDblClickEvt=this._onDblClick.bind(this),this._bindEvents()}destroy(){const e=this.renderer.canvas;e.removeEventListener("mousedown",this._onDownEvt),e.removeEventListener("dblclick",this._onDblClickEvt),e.removeEventListener("wheel",this._onWheelEvt,{passive:!1}),e.removeEventListener("contextmenu",this._onContextMenuEvt),window.removeEventListener("mousemove",this._onMoveEvt),window.removeEventListener("mouseup",this._onUpEvt),window.removeEventListener("keydown",this._onKeyPressEvt)}_bindEvents(){const e=this.renderer.canvas;e.addEventListener("mousedown",this._onDownEvt),e.addEventListener("dblclick",this._onDblClickEvt),e.addEventListener("wheel",this._onWheelEvt,{passive:!1}),e.addEventListener("contextmenu",this._onContextMenuEvt),window.addEventListener("mousemove",this._onMoveEvt),window.addEventListener("mouseup",this._onUpEvt),window.addEventListener("keydown",this._onKeyPressEvt)}_onKeyPress(e){return this.isAlt=e.altKey,this.isShift=e.shiftKey,this.isCtrl=e.ctrlKey,"g"!==e.key.toLowerCase()||e.ctrlKey||e.metaKey?(e.ctrlKey||e.metaKey)&&"g"===e.key.toLowerCase()?(e.preventDefault(),void this._createGroupFromSelection()):(e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()?(e.preventDefault(),e.shiftKey?this.stack.redo():this.stack.undo(),void this.render()):(e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()?(e.preventDefault(),this.stack.redo(),void this.render()):"a"===e.key.toLowerCase()&&this.selection.size>1?(e.preventDefault(),void(e.shiftKey?this._alignNodesVertical():this._alignNodesHorizontal())):void("Delete"===e.key&&([...this.selection].forEach(e=>{const t=this.graph.getNodeById(e);this.stack.exec(g(this.graph,t)),this.graph.removeNode(e)}),this.render())):(this.snapToGrid=!this.snapToGrid,void this.render())}_setCursor(e){this._cursor!==e&&(this._cursor=e,this.renderer.canvas.style.cursor=e)}_posScreen(e){const t=this.renderer.canvas.getBoundingClientRect();return{x:e.clientX-t.left,y:e.clientY-t.top}}_posWorld(e){const t=this._posScreen(e);return this.renderer.screenToWorld(t.x,t.y)}_findNodeAtWorld(e,t){const n=[...this.graph.nodes.values()].reverse();for(const o of n){const{x:n,y:s,w:i,h:r}=o.computed;if(e>=n&&e<=n+i&&t>=s&&t<=s+r){if("core/Group"===o.type){const n=this._findChildNodeAtWorld(o,e,t);if(n)return n}return o}}return null}_findChildNodeAtWorld(e,t,n){const o=[];for(const s of this.graph.nodes.values())s.parent===e&&o.push(s);for(let s=o.length-1;s>=0;s--){const e=o[s],{x:i,y:r,w:a,h:l}=e.computed;if(t>=i&&t<=i+a&&n>=r&&n<=r+l){if("core/Group"===e.type){const o=this._findChildNodeAtWorld(e,t,n);if(o)return o}return e}}return null}_findPortAtWorld(e,t){for(const n of this.graph.nodes.values()){for(let o=0;o<n.inputs.length;o++){if(x(d(n,n.inputs[o],o,"in"),e,t))return{node:n,port:n.inputs[o],dir:"in",idx:o}}for(let o=0;o<n.outputs.length;o++){if(x(d(n,n.outputs[o],o,"out"),e,t))return{node:n,port:n.outputs[o],dir:"out",idx:o}}}return null}_findIncomingEdge(e,t){for(const[n,o]of this.graph.edges)if(o.toNode===e&&o.toPort===t)return{id:n,edge:o};return null}_onWheel(e){e.preventDefault();const{x:t,y:n}=this._posScreen(e),o=Math.pow(1.0015,-e.deltaY);this.renderer.zoomAt(o,t,n),this.render()}_onContextMenu(e){if(e.preventDefault(),!this.contextMenu)return;const t=this._posWorld(e),n=this._findNodeAtWorld(t.x,t.y);this.contextMenu.show(n,e.clientX,e.clientY,t)}_onDblClick(e){var t;const n=this._posWorld(e),o=this._findNodeAtWorld(n.x,n.y);o&&(null==(t=this.hooks)||t.emit("node:dblclick",o))}_resizeHandleRect(e){const{x:t,y:n,w:o,h:s}=e.computed;return{x:t+o-10,y:n+s-10,w:10,h:10}}_hitResizeHandle(e,t,n){const o=this._resizeHandleRect(e);return t>=o.x&&t<=o.x+o.w&&n>=o.y&&n<=o.y+o.h}_onDown(e){const t=this._posScreen(e),n=this._posWorld(e);if(1===e.button)return void(this.panning={x:t.x,y:t.y});const o=this._findNodeAtWorld(n.x,n.y);if(0===e.button&&o&&this._hitResizeHandle(o,n.x,n.y))return this.resizing={nodeId:o.id,startW:o.size.width,startH:o.size.height,startX:n.x,startY:n.y},e.shiftKey||this.selection.clear(),this.selection.add(o.id),this._setCursor("se-resize"),void this.render();const s=this._findPortAtWorld(n.x,n.y);if(0===e.button&&s&&"in"===s.dir){const e=this._findIncomingEdge(s.node.id,s.port.id);if(e)return this.stack.exec(function(e,t){const n=e.edges.get(t);if(!n)return null;const{fromNode:o,fromPort:s,toNode:i,toPort:r}=n;return{do(){e.edges.delete(t)},undo(){e.addEdge(o,s,i,r)}}}(this.graph,e.id)),void this.render()}if(0===e.button&&s&&"out"===s.dir){const e=d(s.node,s.port,s.idx,"out"),t=this.renderer.worldToScreen(e.x,e.y+7);return void(this.connecting={fromNode:s.node.id,fromPort:s.port.id,x:t.x,y:t.y})}if(0!==e.button||!o)return 0===e.button?(this.selection.size&&this.selection.clear(),e.ctrlKey||e.metaKey?this.boxSelecting={startX:n.x,startY:n.y,currentX:n.x,currentY:n.y}:this.panning={x:t.x,y:t.y},void this.render()):void 0;e.shiftKey||this.selection.clear(),this.selection.add(o.id),this.dragging={nodeId:o.id,offsetX:n.x-o.computed.x,offsetY:n.y-o.computed.y,startPos:{...o.pos},selectedNodes:[]};for(const i of this.selection){const e=this.graph.nodes.get(i);e&&this.dragging.selectedNodes.push({node:e,startWorldX:e.computed.x,startWorldY:e.computed.y,startLocalX:e.pos.x,startLocalY:e.pos.y})}if("core/Group"===o.type){this.dragging.childrenWorldPos=[];for(const e of this.graph.nodes.values())e.parent===o&&this.dragging.childrenWorldPos.push({node:e,worldX:e.computed.x,worldY:e.computed.y})}this.render()}_onMove(t){var n,o;this.isAlt=t.altKey,this.isShift=t.shiftKey,this.isCtrl=t.ctrlKey;const s=this._posScreen(t),i=this.renderer.screenToWorld(s.x,s.y);if(this.resizing){const t=this.graph.nodes.get(this.resizing.nodeId),o=i.x-this.resizing.startX,s=i.y-this.resizing.startY,r=e.MIN_NODE_WIDTH,a=e.MIN_NODE_HEIGHT;return t.size.width=Math.max(r,this.resizing.startW+o),t.size.height=Math.max(a,this.resizing.startH+s),null==(n=this.hooks)||n.emit("node:resize",t),this._setCursor("se-resize"),void this.render()}if(this.panning){const e=s.x-this.panning.x,t=s.y-this.panning.y;return this.panning={x:s.x,y:s.y},this.renderer.panBy(e,t),void this.render()}if(this.dragging){const e=this.graph.nodes.get(this.dragging.nodeId);let t=i.x-this.dragging.offsetX,n=this.isShift?i.y-0:i.y-this.dragging.offsetY;this.snapToGrid&&(t=this._snapToGrid(t),n=this._snapToGrid(n));const s=t-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldX,r=n-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldY;this.graph.updateWorldTransforms();for(const{node:o,startWorldX:i,startWorldY:a}of this.dragging.selectedNodes){if(this.isShift&&"core/Group"===o.type)continue;const e=i+s,t=a+r;let n=0,l=0;o.parent&&(n=o.parent.computed.x,l=o.parent.computed.y),o.pos.x=e-n,o.pos.y=t-l}if(this.isAlt&&"core/Group"===e.type&&this.dragging.childrenWorldPos){this.graph.updateWorldTransforms();for(const t of this.dragging.childrenWorldPos){const n=t.node,o=e.computed.x,s=e.computed.y;n.pos.x=t.worldX-o,n.pos.y=t.worldY-s}}return null==(o=this.hooks)||o.emit("node:move",e),void this.render()}if(this.boxSelecting)return this.boxSelecting.currentX=i.x,this.boxSelecting.currentY=i.y,void this.render();this.connecting&&(this.connecting.x=s.x,this.connecting.y=s.y,this.render());const r=this._findPortAtWorld(i.x,i.y),a=this._findNodeAtWorld(i.x,i.y);a&&this._hitResizeHandle(a,i.x,i.y)?this._setCursor("se-resize"):r?this._setCursor("pointer"):this._setCursor("default")}_onUp(e){this.isAlt=e.altKey,this.isShift=e.shiftKey,this.isCtrl=e.ctrlKey;const t=this._posWorld(e);if(this.panning)this.panning=null;else{if(this.connecting){const e=this.connecting,n=this._findPortAtWorld(t.x,t.y);n&&"in"===n.dir&&this.stack.exec(function(e,t,n,o,s){let i=null;return{do(){e.addEdge(t,n,o,s),i=p(e,t,n,o,s)},undo(){const r=i??p(e,t,n,o,s);null!=r&&e.edges.delete(r)}}}(this.graph,e.fromNode,e.fromPort,n.node.id,n.port.id)),this.connecting=null,this.render()}if(this.resizing){const e=this.graph.nodes.get(this.resizing.nodeId),t={w:this.resizing.startW,h:this.resizing.startH},i={w:e.size.width,h:e.size.height};t.w===i.w&&t.h===i.h||this.stack.exec((n=e,o=t,s=i,{do(){n.size.width=s.w,n.size.height=s.h},undo(){n.size.width=o.w,n.size.height=o.h}})),this.resizing=null,this._setCursor("default")}var n,o,s;if(this.dragging){const e=this.graph.nodes.get(this.dragging.nodeId);if("core/Group"===e.type&&this.isAlt&&this.dragging.childrenWorldPos)for(const t of this.dragging.childrenWorldPos){const n=t.node;this.graph.updateWorldTransforms();const o=e.computed.x,s=e.computed.y;n.pos.x=t.worldX-o,n.pos.y=t.worldY-s}else if("core/Group"!==e.type||this.isAlt){if("core/Group"!==e.type){const n=this._findPotentialParent(t.x,t.y,e);n&&n!==e.parent?this.graph.reparent(e,n):!n&&e.parent&&this.graph.reparent(e,null)}}else this._autoParentNodesInGroup(e);this.dragging=null,this.render()}if(this.boxSelecting){const{startX:e,startY:t,currentX:n,currentY:o}=this.boxSelecting,s=Math.min(e,n),i=Math.max(e,n),r=Math.min(t,o),a=Math.max(t,o);for(const l of this.graph.nodes.values()){const{x:e,y:t,w:n,h:o}=l.computed;e+n>=s&&e<=i&&t+o>=r&&t<=a&&this.selection.add(l.id)}this.boxSelecting=null,this.render()}}}_autoParentNodesInGroup(e){const{x:t,y:n,w:o,h:s}=e.computed;for(const i of this.graph.nodes.values()){if(i===e)continue;if(i.parent===e)continue;if("core/Group"===i.type)continue;const{x:r,y:a,w:l,h:d}=i.computed,c=r+l/2,h=a+d/2;c>=t&&c<=t+o&&h>=n&&h<=n+s&&this.graph.reparent(i,e)}}_findPotentialParent(e,t,n){const o=[...this.graph.nodes.values()].reverse();for(const s of o){if("core/Group"!==s.type)continue;if(s===n)continue;let o=s.parent,i=!1;for(;o;){if(o===n){i=!0;break}o=o.parent}if(i)continue;const{x:r,y:a,w:l,h:d}=s.computed;if(e>=r&&e<=r+l&&t>=a&&t<=a+d)return s}return null}_snapToGrid(e){return Math.round(e/this.gridSize)*this.gridSize}_createGroupFromSelection(){if(0===this.selection.size)return void console.warn("No nodes selected to group");const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e));let t=1/0,n=1/0,o=-1/0,s=-1/0;for(const d of e){const{x:e,y:i,w:r,h:a}=d.computed;t=Math.min(t,e),n=Math.min(n,i),o=Math.max(o,e+r),s=Math.max(s,i+a)}const i=t-20,r=n-20,a=o-t+40,l=s-n+40;this.graph.groupManager&&(this.graph.groupManager.addGroup({title:"Group",x:i,y:r,width:a,height:l,members:Array.from(this.selection)}),this.selection.clear(),this.render())}_alignNodesHorizontal(){if(this.selection.size<2)return;const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e)),t=e.reduce((e,t)=>e+t.computed.y,0)/e.length;for(const n of e){const e=n.parent?n.parent.computed.y:0;n.pos.y=t-e}this.graph.updateWorldTransforms(),this.render()}_alignNodesVertical(){if(this.selection.size<2)return;const e=Array.from(this.selection).map(e=>this.graph.getNodeById(e)),t=e.reduce((e,t)=>e+t.computed.x,0)/e.length;for(const n of e){const e=n.parent?n.parent.computed.x:0;n.pos.x=t-e}this.graph.updateWorldTransforms(),this.render()}render(){var e,t,n;const o=this.renderTempEdge();if(this.renderer.draw(this.graph,{selection:this.selection,tempEdge:o,boxSelecting:this.boxSelecting,activeEdges:this.activeEdges||new Set}),null==(e=this.htmlOverlay)||e.draw(this.graph,this.selection),this.boxSelecting){const{startX:e,startY:t,currentX:n,currentY:o}=this.boxSelecting,s=Math.min(e,n),i=Math.min(t,o),r=Math.abs(n-e),a=Math.abs(o-t),l=this.renderer.worldToScreen(s,i),d=this.renderer.worldToScreen(s+r,i+a),c=this.renderer.ctx;c.save(),this.renderer._resetTransform(),c.strokeStyle="#6cf",c.fillStyle="rgba(102, 204, 255, 0.1)",c.lineWidth=2,c.strokeRect(l.x,l.y,d.x-l.x,d.y-l.y),c.fillRect(l.x,l.y,d.x-l.x,d.y-l.y),c.restore()}if(this.portRenderer){this.portRenderer.ctx.clearRect(0,0,this.portRenderer.canvas.width,this.portRenderer.canvas.height),this.portRenderer.scale=this.renderer.scale,this.portRenderer.offsetX=this.renderer.offsetX,this.portRenderer.offsetY=this.renderer.offsetY,this.portRenderer._applyTransform();for(const e of this.graph.nodes.values())if("core/Group"!==e.type){const o=null==(n=null==(t=this.portRenderer.registry)?void 0:t.types)?void 0:n.get(e.type);!!(null==o?void 0:o.html)&&this.portRenderer._drawPorts(e)}this.portRenderer._resetTransform()}}renderTempEdge(){if(!this.connecting)return null;const e=this._portAnchorScreen(this.connecting.fromNode,this.connecting.fromPort);return{x1:e.x,y1:e.y,x2:this.connecting.x,y2:this.connecting.y}}_portAnchorScreen(e,t){const n=this.graph.nodes.get(e),o=n.outputs.findIndex(e=>e.id===t),s=d(n,0,o,"out");return this.renderer.worldToScreen(s.x,s.y+7)}};n(m,"MIN_NODE_WIDTH",80),n(m,"MIN_NODE_HEIGHT",60);let y=m;function x(e,t,n){return t>=e.x&&t<=e.x+e.w&&n>=e.y&&n<=e.y+e.h}class v{constructor({graph:e,hooks:t,renderer:n,commandStack:o}){this.graph=e,this.hooks=t,this.renderer=n,this.commandStack=o,this.items=[],this.visible=!1,this.target=null,this.position={x:0,y:0},this.menuElement=this._createMenuElement(),this._onDocumentClick=e=>{this.menuElement.contains(e.target)||this.hide()}}addItem(e,t,n={}){const{action:o,submenu:s,condition:i,order:r=100}=n;o||s?(this.removeItem(e),this.items.push({id:e,label:t,action:o,submenu:s,condition:i,order:r}),this.items.sort((e,t)=>e.order-t.order)):console.error("ContextMenu.addItem: either action or submenu is required")}removeItem(e){this.items=this.items.filter(t=>t.id!==e)}show(e,t,n,o=null){this.target=e,this.position={x:t,y:n},this.worldPosition=o,this.visible=!0,this._renderItems(),this.menuElement.style.left=`${t}px`,this.menuElement.style.top=`${n}px`,this.menuElement.style.display="block",requestAnimationFrame(()=>{const e=this.menuElement.getBoundingClientRect(),o=window.innerWidth,s=window.innerHeight;let i=t,r=n;e.right>o&&(i=o-e.width-5),e.bottom>s&&(r=s-e.height-5),this.menuElement.style.left=`${i}px`,this.menuElement.style.top=`${r}px`}),document.addEventListener("click",this._onDocumentClick)}hide(){this.visible=!1,this.target=null;document.querySelectorAll(".context-submenu").forEach(e=>e.remove()),this.menuElement.style.display="none",document.removeEventListener("click",this._onDocumentClick)}destroy(){this.hide(),this.menuElement&&this.menuElement.parentNode&&this.menuElement.parentNode.removeChild(this.menuElement)}_createMenuElement(){const e=document.createElement("div");return e.className="html-overlay-node-context-menu",Object.assign(e.style,{position:"fixed",display:"none",minWidth:"180px",backgroundColor:"#2a2a2e",border:"1px solid #444",borderRadius:"6px",boxShadow:"0 4px 16px rgba(0, 0, 0, 0.4)",zIndex:"10000",padding:"4px 0",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:"13px",color:"#e9e9ef"}),document.body.appendChild(e),e}_renderItems(){this.menuElement.innerHTML="";const e=this.items.filter(e=>!e.condition||e.condition(this.target));0!==e.length?e.forEach(e=>{const t=document.createElement("div");t.className="context-menu-item";const n=document.createElement("div");Object.assign(n.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"});const o=document.createElement("span");if(o.textContent=e.label,n.appendChild(o),e.submenu){const e=document.createElement("span");e.textContent="▶",e.style.marginLeft="12px",e.style.fontSize="10px",e.style.opacity="0.7",n.appendChild(e)}t.appendChild(n),Object.assign(t.style,{padding:"4px 8px",cursor:"pointer",transition:"background-color 0.15s ease",userSelect:"none",position:"relative"}),t.addEventListener("mouseenter",()=>{t.style.backgroundColor="#3a3a3e",t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null),e.submenu&&this._showSubmenu(e.submenu,t)}),t.addEventListener("mouseleave",n=>{if(t.style.backgroundColor="transparent",e.submenu){const e=t._submenuElement;e&&(t._hideTimeout=setTimeout(()=>{e.contains(document.elementFromPoint(n.clientX,n.clientY))||this._hideSubmenu(t)},150))}}),e.submenu||t.addEventListener("click",t=>{t.stopPropagation(),e.action(this.target),this.hide()}),this.menuElement.appendChild(t)}):this.hide()}_showSubmenu(e,t){this._hideSubmenu(t);const n=document.createElement("div");n.className="context-submenu",Object.assign(n.style,{position:"fixed",minWidth:"140px",backgroundColor:"#2a2a2e",border:"1px solid #444",borderRadius:"6px",boxShadow:"0 4px 16px rgba(0, 0, 0, 0.4)",zIndex:"10001",padding:"4px 0",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:"13px",color:"#e9e9ef"}),e.forEach(e=>{const t=document.createElement("div");t.className="context-submenu-item";const o=document.createElement("div");if(Object.assign(o.style,{display:"flex",alignItems:"center",gap:"8px"}),e.color){const t=document.createElement("div");Object.assign(t.style,{width:"16px",height:"16px",borderRadius:"3px",backgroundColor:e.color,border:"1px solid #555",flexShrink:"0"}),o.appendChild(t)}const s=document.createElement("span");s.textContent=e.label,o.appendChild(s),t.appendChild(o),Object.assign(t.style,{padding:"4px 8px",cursor:"pointer",transition:"background-color 0.15s ease",userSelect:"none"}),t.addEventListener("mouseenter",()=>{t.style.backgroundColor="#3a3a3e"}),t.addEventListener("mouseleave",()=>{t.style.backgroundColor="transparent"}),t.addEventListener("click",t=>{t.stopPropagation(),e.action(this.target),this.hide()}),n.appendChild(t)}),n.addEventListener("mouseenter",()=>{t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null)}),n.addEventListener("mouseleave",e=>{t.contains(e.relatedTarget)||this._hideSubmenu(t)}),document.body.appendChild(n),t._submenuElement=n,requestAnimationFrame(()=>{const e=t.getBoundingClientRect(),o=n.getBoundingClientRect();let s=e.right+2,i=e.top;s+o.width>window.innerWidth&&(s=e.left-o.width-2),i+o.height>window.innerHeight&&(i=window.innerHeight-o.height-5),n.style.left=`${s}px`,n.style.top=`${i}px`})}_hideSubmenu(e){e._submenuElement&&(e._submenuElement.remove(),e._submenuElement=null)}}class b{constructor({graph:e,registry:t,hooks:n,cyclesPerFrame:o=1}){this.graph=e,this.registry=t,this.hooks=n,this.running=!1,this._raf=null,this._last=0,this.cyclesPerFrame=Math.max(1,0|o)}isRunning(){return this.running}setCyclesPerFrame(e){this.cyclesPerFrame=Math.max(1,0|e)}step(e=1,t=0){var n,o;const s=Math.max(1,0|e);for(let r=0;r<s;r++){for(const e of this.graph.nodes.values()){const s=this.registry.types.get(e.type);if(null==s?void 0:s.onExecute)try{s.onExecute(e,{dt:t,graph:this.graph,getInput:t=>{const n=e.inputs.find(e=>e.name===t)||e.inputs[0];return n?this.graph.getInput(e.id,n.id):void 0},setOutput:(t,n)=>{const o=e.outputs.find(e=>e.name===t)||e.outputs[0];o&&this.graph.setOutput(e.id,o.id,n)}})}catch(i){null==(o=null==(n=this.hooks)?void 0:n.emit)||o.call(n,"error",i)}}this.graph.swapBuffers()}}runOnce(e,t=0){console.log("[Runner.runOnce] Starting exec flow from node:",e);const n=[],o=new Set;let s=e;for(;s;){const e=this.graph.nodes.get(s);if(!e){console.warn(`[Runner.runOnce] Node not found: ${s}`);break}n.push(s),o.add(s),console.log(`[Runner.runOnce] Executing: ${e.title} (${e.type})`);for(const n of e.inputs)if("data"===n.portType)for(const e of this.graph.edges.values())if(e.toNode===s&&e.toPort===n.id){this.graph.nodes.get(e.fromNode)&&!o.has(e.fromNode)&&(o.add(e.fromNode),this.executeNode(e.fromNode,t))}this.executeNode(s,t),s=this.findNextExecNode(s)}console.log("[Runner.runOnce] Executed nodes:",n.length);const i=new Set;for(const r of this.graph.edges.values())o.has(r.fromNode)&&o.has(r.toNode)&&i.add(r.id);return console.log("[Runner.runOnce] Connected edges count:",i.size),{connectedNodes:o,connectedEdges:i}}findNextExecNode(e){const t=this.graph.nodes.get(e);if(!t)return null;const n=t.outputs.find(e=>"exec"===e.portType);if(!n)return null;for(const o of this.graph.edges.values())if(o.fromNode===e&&o.fromPort===n.id)return o.toNode;return null}executeNode(e,t){var n,o;const s=this.graph.nodes.get(e);if(!s)return;const i=this.registry.types.get(s.type);if(null==i?void 0:i.onExecute)try{i.onExecute(s,{dt:t,graph:this.graph,getInput:e=>{const t=s.inputs.find(t=>t.name===e)||s.inputs[0];return t?this.graph.getInput(s.id,t.id):void 0},setOutput:(e,t)=>{const n=s.outputs.find(t=>t.name===e)||s.outputs[0];if(n){const e=`${s.id}:${n.id}`;this.graph._curBuf().set(e,t)}}})}catch(r){null==(o=null==(n=this.hooks)?void 0:n.emit)||o.call(n,"error",r)}}start(){var e,t;if(this.running)return;this.running=!0,this._last=0,null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:start");const n=e=>{var t,o;if(!this.running)return;const s=this._last?e-this._last:0;this._last=e;const i=s/1e3;this.step(this.cyclesPerFrame,i),null==(o=null==(t=this.hooks)?void 0:t.emit)||o.call(t,"runner:tick",{time:e,dt:i,running:!0,cps:this.cyclesPerFrame}),this._raf=requestAnimationFrame(n)};this._raf=requestAnimationFrame(n)}stop(){var e,t;this.running&&(this.running=!1,this._raf&&cancelAnimationFrame(this._raf),this._raf=null,this._last=0,null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:stop"))}}class w{constructor(e,t,n){this.host=e,this.renderer=t,this.registry=n,this.container=document.createElement("div"),Object.assign(this.container.style,{position:"absolute",inset:"0",pointerEvents:"none",zIndex:"10"}),e.appendChild(this.container),this.nodes=new Map}_createDefaultNodeLayout(e){const t=document.createElement("div");t.className="node-overlay",Object.assign(t.style,{position:"absolute",display:"flex",flexDirection:"column",boxSizing:"border-box",pointerEvents:"none",overflow:"hidden"});const n=document.createElement("div");n.className="node-header",Object.assign(n.style,{height:"24px",flexShrink:"0",display:"flex",alignItems:"center",padding:"0 8px",cursor:"grab",userSelect:"none",pointerEvents:"none"});const o=document.createElement("div");return o.className="node-body",Object.assign(o.style,{flex:"1",position:"relative",overflow:"hidden",pointerEvents:"none"}),t.appendChild(n),t.appendChild(o),t._domParts={header:n,body:o},t}_ensureNodeElement(e,t){var n;let o=this.nodes.get(e.id);if(!o){if(null==(n=t.html)?void 0:n.render)o=t.html.render(e);else{if(!t.html)return null;o=this._createDefaultNodeLayout(e),t.html.init&&t.html.init(e,o,o._domParts)}if(!o)return null;o.style.position="absolute",o.style.pointerEvents="none",this.container.appendChild(o),this.nodes.set(e.id,o)}return o}draw(e,t=new Set){const{scale:n,offsetX:o,offsetY:s}=this.renderer;this.container.style.transform=`translate(${o}px, ${s}px) scale(${n})`,this.container.style.transformOrigin="0 0";const i=new Set;for(const r of e.nodes.values()){const e=this.registry.types.get(r.type);if(!!!(null==e?void 0:e.html))continue;const n=this._ensureNodeElement(r,e);if(n){if(n.style.left=`${r.computed.x}px`,n.style.top=`${r.computed.y}px`,n.style.width=`${r.computed.w}px`,n.style.height=`${r.computed.h}px`,e.html.update){const o=n._domParts||{};e.html.update(r,n,{selected:t.has(r.id),header:o.header,body:o.body})}i.add(r.id)}}for(const[r,a]of this.nodes)i.has(r)||(a.remove(),this.nodes.delete(r))}clear(){for(const[,e]of this.nodes)e.remove();this.nodes.clear()}destroy(){this.clear(),this.container.remove()}}class E{constructor(e,{graph:t,renderer:n,width:o=200,height:s=150}={}){this.graph=t,this.renderer=n,this.width=o,this.height=s,this.canvas=document.createElement("canvas"),this.canvas.id="minimap",this.canvas.width=o,this.canvas.height=s,this.canvas.style.position="fixed",this.canvas.style.bottom="20px",this.canvas.style.right="20px",this.canvas.style.border="2px solid #444",this.canvas.style.borderRadius="8px",this.canvas.style.background="rgba(20, 20, 23, 0.9)",this.canvas.style.boxShadow="0 4px 12px rgba(0, 0, 0, 0.5)",this.canvas.style.pointerEvents="none",this.ctx=this.canvas.getContext("2d"),e.appendChild(this.canvas)}render(){const{graph:e,renderer:t,ctx:n,width:o,height:s}=this;if(n.fillStyle="#141417",n.fillRect(0,0,o,s),0===e.nodes.size)return;let i=1/0,r=1/0,a=-1/0,l=-1/0;for(const v of e.nodes.values()){const{x:e,y:t,w:n,h:o}=v.computed;i=Math.min(i,e),r=Math.min(r,t),a=Math.max(a,e+n),l=Math.max(l,t+o)}const d=100,c=Math.max(300,a-i+200),h=Math.max(200,l-r+200);i-=d,r-=d;const u=Math.min((o-20)/c,(s-20)/h),p=(o-c*u)/2,g=(s-h*u)/2;n.strokeStyle="rgba(127, 140, 255, 0.5)",n.lineWidth=1;for(const v of e.edges.values()){const t=e.nodes.get(v.fromNode),o=e.nodes.get(v.toNode);if(!t||!o)continue;const s=(t.computed.x+t.computed.w/2-i)*u+p,a=(t.computed.y+t.computed.h/2-r)*u+g,l=(o.computed.x+o.computed.w/2-i)*u+p,d=(o.computed.y+o.computed.h/2-r)*u+g;n.beginPath(),n.moveTo(s,a),n.lineTo(l,d),n.stroke()}n.fillStyle="#6cf";for(const v of e.nodes.values()){const{x:e,y:t,w:o,h:s}=v.computed,a=(e-i)*u+p,l=(t-r)*u+g,d=o*u,c=s*u;"core/Group"===v.type?(n.fillStyle="rgba(102, 204, 255, 0.2)",n.strokeStyle="#6cf",n.lineWidth=1,n.fillRect(a,l,d,c),n.strokeRect(a,l,d,c)):(n.fillStyle="#6cf",n.fillRect(a,l,Math.max(2,d),Math.max(2,c)))}const f=(-t.offsetX/t.scale-i)*u+p,m=(-t.offsetY/t.scale-r)*u+g,y=t.canvas.width/t.scale*u,x=t.canvas.height/t.scale*u;n.strokeStyle="#ff6b6b",n.lineWidth=2,n.strokeRect(f,m,y,x)}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}}class _{constructor(e,{graph:t,hooks:n,registry:o,render:s}){this.container=e,this.graph=t,this.hooks=n,this.registry=o,this.render=s,this.panel=null,this.currentNode=null,this.isVisible=!1,this._createPanel()}_createPanel(){this.panel=document.createElement("div"),this.panel.className="property-panel",this.panel.style.display="none",this.panel.innerHTML='\n <div class="panel-inner">\n <div class="panel-header">\n <div class="panel-title">\n <span class="title-text">Node Properties</span>\n </div>\n <button class="panel-close" type="button">×</button>\n </div>\n <div class="panel-content">\n \x3c!-- Content will be dynamically generated --\x3e\n </div>\n </div>\n ',this.container.appendChild(this.panel),this.panel.querySelector(".panel-close").addEventListener("click",()=>{this.close()}),document.addEventListener("keydown",e=>{"Escape"===e.key&&this.isVisible&&this.close()})}open(e){e&&(this.currentNode=e,this.isVisible=!0,this._renderContent(),this.panel.style.display="block",this.panel.classList.add("panel-visible"))}close(){this.isVisible=!1,this.panel.classList.remove("panel-visible"),setTimeout(()=>{this.panel.style.display="none",this.currentNode=null},200)}_renderContent(){const e=this.currentNode;if(!e)return;this.panel.querySelector(".panel-content").innerHTML=`\n <div class="section">\n <div class="section-title">Basic Info</div>\n <div class="section-body">\n <div class="field">\n <label>Type</label>\n <input type="text" value="${e.type}" readonly />\n </div>\n <div class="field">\n <label>Title</label>\n <input type="text" data-field="title" value="${e.title||""}" />\n </div>\n <div class="field">\n <label>ID</label>\n <input type="text" value="${e.id}" readonly />\n </div>\n </div>\n </div>\n \n <div class="section">\n <div class="section-title">Position & Size</div>\n <div class="section-body">\n <div class="field-row">\n <div class="field">\n <label>X</label>\n <input type="number" data-field="x" value="${Math.round(e.computed.x)}" />\n </div>\n <div class="field">\n <label>Y</label>\n <input type="number" data-field="y" value="${Math.round(e.computed.y)}" />\n </div>\n </div>\n <div class="field-row">\n <div class="field">\n <label>Width</label>\n <input type="number" data-field="width" value="${e.computed.w}" />\n </div>\n <div class="field">\n <label>Height</label>\n <input type="number" data-field="height" value="${e.computed.h}" />\n </div>\n </div>\n </div>\n </div>\n \n ${this._renderPorts(e)}\n ${this._renderState(e)}\n \n <div class="panel-actions">\n <button class="btn-secondary panel-close-btn">Close</button>\n </div>\n `,this._attachInputListeners()}_renderPorts(e){return e.inputs.length||e.outputs.length?`\n <div class="section">\n <div class="section-title">Ports</div>\n <div class="section-body">\n ${e.inputs.length?`\n <div class="port-group">\n <div class="port-group-title">Inputs (${e.inputs.length})</div>\n ${e.inputs.map(e=>`\n <div class="port-item">\n <span class="port-icon ${e.portType||"data"}"></span>\n <span class="port-name">${e.name}</span>\n ${e.datatype?`<span class="port-type">${e.datatype}</span>`:""}\n </div>\n `).join("")}\n </div>\n `:""}\n \n ${e.outputs.length?`\n <div class="port-group">\n <div class="port-group-title">Outputs (${e.outputs.length})</div>\n ${e.outputs.map(e=>`\n <div class="port-item">\n <span class="port-icon ${e.portType||"data"}"></span>\n <span class="port-name">${e.name}</span>\n ${e.datatype?`<span class="port-type">${e.datatype}</span>`:""}\n </div>\n `).join("")}\n </div>\n `:""}\n </div>\n </div>\n `:""}_renderState(e){return e.state&&0!==Object.keys(e.state).length?`\n <div class="section">\n <div class="section-title">State</div>\n <div class="section-body">\n ${Object.entries(e.state).map(([e,t])=>`\n <div class="field">\n <label>${e}</label>\n <input \n type="${"number"==typeof t?"number":"text"}" \n data-field="state.${e}" \n value="${t}" \n />\n </div>\n `).join("")}\n </div>\n </div>\n `:""}_attachInputListeners(){this.panel.querySelectorAll("[data-field]").forEach(e=>{e.addEventListener("change",()=>{this._handleFieldChange(e.dataset.field,e.value)})}),this.panel.querySelector(".panel-close-btn").addEventListener("click",()=>{this.close()})}_handleFieldChange(e,t){var n;const o=this.currentNode;if(o){switch(e){case"title":o.title=t;break;case"x":o.pos.x=parseFloat(t),this.graph.updateWorldTransforms();break;case"y":o.pos.y=parseFloat(t),this.graph.updateWorldTransforms();break;case"width":o.size.width=parseFloat(t);break;case"height":o.size.height=parseFloat(t);break;default:if(e.startsWith("state.")){const n=e.substring(6);if(o.state){const e=o.state[n];o.state[n]="number"==typeof e?parseFloat(t):t}}}null==(n=this.hooks)||n.emit("node:updated",o),this.render&&this.render()}}destroy(){this.panel&&this.panel.remove()}}e.createGraphEditor=function(e,{theme:t,hooks:n,autorun:s=!0,showMinimap:i=!0,enablePropertyPanel:r=!0,propertyPanelContainer:a=null}={}){let d,c;if("string"==typeof e&&(e=document.querySelector(e)),!e)throw new Error("createGraphEditor: target element not found");e instanceof HTMLCanvasElement?(d=e,c=d.parentElement):(c=e,d=c.querySelector("canvas"),d||(d=document.createElement("canvas"),d.style.display="block",d.style.width="100%",d.style.height="100%",c.appendChild(d))),"static"===getComputedStyle(c).position&&(c.style.position="relative");const u=n??function(e){const t=Object.fromEntries(e.map(e=>[e,new Set]));return{on:(e,n)=>(t[e]||(t[e]=new Set),t[e].add(n),()=>t[e].delete(n)),off(e,n){t[e]&&t[e].delete(n)},emit(e,...n){if(t[e])for(const o of t[e])o(...n)}}}(["node:create","node:move","node:click","node:dblclick","edge:create","edge:delete","graph:serialize","graph:deserialize","error","runner:tick","runner:start","runner:stop","node:resize","group:change","node:updated"]),p=new o,f=new l({hooks:u,registry:p}),m=new h(d,{theme:t,registry:p}),x=new w(d.parentElement,m,p),k=document.createElement("canvas");k.id="port-canvas",Object.assign(k.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"20"}),d.parentElement.appendChild(k);const T=new h(k,{theme:t,registry:p});T.setTransform=m.setTransform.bind(m),T.scale=m.scale,T.offsetX=m.offsetX,T.offsetY=m.offsetY;const S=new y({graph:f,renderer:m,hooks:u,htmlOverlay:x,portRenderer:T}),C=new v({graph:f,hooks:u,renderer:m,commandStack:S.stack});S.contextMenu=C;let N=null;i&&(N=new E(c,{graph:f,renderer:m}));let P=null;r&&(P=new _(a||c,{graph:f,hooks:u,registry:p,render:()=>S.render()}),u.on("node:dblclick",e=>{P.open(e)}));const z=new b({graph:f,registry:p,hooks:u});u.on("runner:tick",({time:e,dt:t})=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!0,time:e,dt:t}),x.draw(f,S.selection)}),u.on("runner:start",()=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!0,time:performance.now(),dt:0}),x.draw(f,S.selection)}),u.on("runner:stop",()=>{m.draw(f,{selection:S.selection,tempEdge:S.connecting?S.renderTempEdge():null,running:!1,time:performance.now(),dt:0}),x.draw(f,S.selection)}),u.on("node:updated",()=>{S.render()}),p.register("core/Note",{title:"Note",size:{w:180,h:80},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],onCreate(e){e.state.text="hello"},onExecute(e,{dt:t,getInput:n,setOutput:o}){o("out",(n("in")??e.state.text??"").toString().toUpperCase()+` · ${Math.floor(performance.now()/1e3%100)}`)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.pos,{width:i}=e.size}}),p.register("core/HtmlNote",{title:"HTML Note",size:{w:200,h:150},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#222",t.style.borderRadius="8px",t.style.border="1px solid #444",t.style.boxShadow="0 4px 12px rgba(0,0,0,0.3)",n.style.backgroundColor="#333",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.style.fontWeight="bold",n.textContent="My HTML Node",o.style.padding="8px",o.style.color="#ccc",o.style.fontSize="12px";const s=document.createElement("div");s.textContent="Event Name",o.appendChild(s);const i=document.createElement("input");Object.assign(i.style,{marginTop:"4px",padding:"4px",background:"#111",border:"1px solid #555",color:"#fff",borderRadius:"4px",pointerEvents:"auto"}),i.placeholder="Type here...",i.addEventListener("input",t=>{e.state.text=t.target.value}),i.addEventListener("mousedown",e=>e.stopPropagation()),o.appendChild(i),t._input=i},update(e,t,{header:n,_body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#333",t._input.value!==(e.state.text||"")&&(t._input.value=e.state.text||"")}},onCreate(e){e.state.text=""},onExecute(e,{getInput:t,setOutput:n}){n("out",t("in"))}}),p.register("core/TodoNode",{title:"Todo List",size:{w:240,h:300},inputs:[{name:"in",datatype:"any"}],outputs:[{name:"out",datatype:"any"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.borderRadius="8px",t.style.boxShadow="0 4px 12px rgba(0,0,0,0.5)",t.style.border="1px solid #333",n.style.backgroundColor="#2a2a31",n.style.padding="8px",n.style.fontWeight="bold",n.style.color="#e9e9ef",n.textContent=e.title,o.style.display="flex",o.style.flexDirection="column",o.style.padding="8px",o.style.color="#e9e9ef";const s=document.createElement("div");Object.assign(s.style,{display:"flex",gap:"4px",marginBottom:"8px"});const i=document.createElement("input");Object.assign(i.style,{flex:"1",padding:"6px",borderRadius:"4px",border:"1px solid #444",background:"#141417",color:"#fff",pointerEvents:"auto"}),i.placeholder="Add task...";const r=document.createElement("button");r.textContent="+",Object.assign(r.style,{padding:"0 12px",cursor:"pointer",background:"#4f5b66",color:"#fff",border:"none",borderRadius:"4px",pointerEvents:"auto"}),s.append(i,r);const a=document.createElement("ul");Object.assign(a.style,{listStyle:"none",padding:"0",margin:"0",overflow:"hidden",flex:"1"}),o.append(s,a);const l=()=>{const t=i.value.trim();if(!t)return;const n=e.state.todos||[];e.state.todos=[...n,{id:Date.now(),text:t,done:!1}],i.value="",u.emit("node:updated",e)};r.onclick=l,i.onkeydown=e=>{"Enter"===e.key&&l(),e.stopPropagation()},i.onmousedown=e=>e.stopPropagation(),t._refs={list:a}},update(e,t,{selected:n}){t.style.borderColor=n?"#6cf":"#333";const{list:o}=t._refs,s=e.state.todos||[];o.innerHTML="",s.forEach(t=>{const n=document.createElement("li");Object.assign(n.style,{display:"flex",alignItems:"center",padding:"6px 0",borderBottom:"1px solid #2a2a31"});const s=document.createElement("input");s.type="checkbox",s.checked=t.done,s.style.marginRight="8px",s.style.pointerEvents="auto",s.onchange=()=>{t.done=s.checked,u.emit("node:updated",e)},s.onmousedown=e=>e.stopPropagation();const i=document.createElement("span");i.textContent=t.text,i.style.flex="1",i.style.textDecoration=t.done?"line-through":"none",i.style.color=t.done?"#777":"#eee";const r=document.createElement("button");r.textContent="×",Object.assign(r.style,{background:"none",border:"none",color:"#f44",cursor:"pointer",fontSize:"16px",pointerEvents:"auto"}),r.onclick=()=>{e.state.todos=e.state.todos.filter(e=>e.id!==t.id),u.emit("node:updated",e)},r.onmousedown=e=>e.stopPropagation(),n.append(s,i,r),o.appendChild(n)})}},onCreate(e){e.state.todos=[{id:1,text:"Welcome to Free Node",done:!1},{id:2,text:"Try adding a task",done:!0}]}}),p.register("math/Add",{title:"Add",size:{w:140,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"number"},{name:"b",portType:"data",datatype:"number"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"number"}],onCreate(e){e.state.a=0,e.state.b=0},onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??0,i=o+s;console.log("[Add] a:",o,"b:",s,"result:",i),n("result",i)}}),p.register("math/Subtract",{title:"Subtract",size:{w:140,h:80},inputs:[{name:"a",datatype:"number"},{name:"b",datatype:"number"}],outputs:[{name:"result",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){n("result",(t("a")??0)-(t("b")??0))}}),p.register("math/Multiply",{title:"Multiply",size:{w:140,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"number"},{name:"b",portType:"data",datatype:"number"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??0,i=o*s;console.log("[Multiply] a:",o,"b:",s,"result:",i),n("result",i)}}),p.register("math/Divide",{title:"Divide",size:{w:140,h:80},inputs:[{name:"a",datatype:"number"},{name:"b",datatype:"number"}],outputs:[{name:"result",datatype:"number"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??0,s=t("b")??1;n("result",0!==s?o/s:0)}}),p.register("logic/AND",{title:"AND",size:{w:120,h:100},inputs:[{name:"exec",portType:"exec"},{name:"a",portType:"data",datatype:"boolean"},{name:"b",portType:"data",datatype:"boolean"}],outputs:[{name:"exec",portType:"exec"},{name:"result",portType:"data",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??!1,s=t("b")??!1;console.log("[AND] Inputs - a:",o,"b:",s);const i=o&&s;console.log("[AND] Result:",i),n("result",i)}}),p.register("logic/OR",{title:"OR",size:{w:120,h:80},inputs:[{name:"a",datatype:"boolean"},{name:"b",datatype:"boolean"}],outputs:[{name:"result",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){const o=t("a")??!1,s=t("b")??!1;n("result",o||s)}}),p.register("logic/NOT",{title:"NOT",size:{w:120,h:70},inputs:[{name:"in",datatype:"boolean"}],outputs:[{name:"out",datatype:"boolean"}],onExecute(e,{getInput:t,setOutput:n}){n("out",!(t("in")??!1))}}),p.register("value/Number",{title:"Number",size:{w:140,h:60},outputs:[{name:"value",portType:"data",datatype:"number"}],onCreate(e){e.state.value=0},onExecute(e,{setOutput:t}){console.log("[Number] Outputting value:",e.state.value??0),t("value",e.state.value??0)},html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.border="1px solid #444",t.style.borderRadius="8px",n.style.backgroundColor="#2a2a31",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.textContent="Number",o.style.padding="12px",o.style.display="flex",o.style.alignItems="center",o.style.justifyContent="center";const s=document.createElement("input");s.type="number",s.value=e.state.value??0,Object.assign(s.style,{width:"100%",padding:"6px",background:"#141417",border:"1px solid #444",borderRadius:"4px",color:"#fff",fontSize:"14px",textAlign:"center",pointerEvents:"auto"}),s.addEventListener("change",t=>{e.state.value=parseFloat(t.target.value)||0}),s.addEventListener("mousedown",e=>e.stopPropagation()),s.addEventListener("keydown",e=>e.stopPropagation()),o.appendChild(s)},update(e,t,{header:n,body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#2a2a31"}},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#8f8",t.font="14px sans-serif",t.textAlign="center",t.fillText(String(e.state.value??0),o+70,s+42)}}),p.register("value/String",{title:"String",size:{w:160,h:60},outputs:[{name:"value",datatype:"string"}],onCreate(e){e.state.value="Hello"},onExecute(e,{setOutput:t}){t("value",e.state.value??"")},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#8f8",t.font="12px sans-serif",t.textAlign="center";const i=String(e.state.value??""),r=i.length>15?i.substring(0,15)+"...":i;t.fillText(r,o+80,s+42)}}),p.register("value/Boolean",{title:"Boolean",size:{w:140,h:60},outputs:[{name:"value",portType:"data",datatype:"boolean"}],onCreate(e){e.state.value=!0},onExecute(e,{setOutput:t}){console.log("[Boolean] Outputting value:",e.state.value??!1),t("value",e.state.value??!1)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle=e.state.value?"#8f8":"#f88",t.font="14px sans-serif",t.textAlign="center",t.fillText(String(e.state.value),o+70,s+42)}}),p.register("util/Print",{title:"Print",size:{w:140,h:80},inputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],onCreate(e){e.state.lastValue=null},onExecute(e,{getInput:t}){const n=t("value");n!==e.state.lastValue&&(console.log("[Print]",n),e.state.lastValue=n)}}),p.register("util/Watch",{title:"Watch",size:{w:180,h:110},inputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],outputs:[{name:"exec",portType:"exec"},{name:"value",portType:"data",datatype:"any"}],onCreate(e){e.state.displayValue="---"},onExecute(e,{getInput:t,setOutput:n}){const o=t("value");console.log("[Watch] onExecute called, value:",o),e.state.displayValue=String(o??"---"),n("value",o)},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s}=e.computed;t.fillStyle="#fa3",t.font="11px monospace",t.textAlign="left";const i=String(e.state.displayValue??"---"),r=i.length>20?i.substring(0,20)+"...":i;t.fillText(r,o+8,s+50)}}),p.register("util/Timer",{title:"Timer",size:{w:140,h:60},outputs:[{name:"time",datatype:"number"}],onCreate(e){e.state.startTime=performance.now()},onExecute(e,{setOutput:t}){t("time",((performance.now()-(e.state.startTime??0))/1e3).toFixed(2))}}),p.register("util/Trigger",{title:"Trigger",size:{w:140,h:80},outputs:[{name:"exec",portType:"exec"}],html:{init(e,t,{header:n,body:o}){t.style.backgroundColor="#1e1e24",t.style.border="1px solid #444",t.style.borderRadius="8px",n.style.backgroundColor="#2a2a31",n.style.borderBottom="1px solid #444",n.style.color="#eee",n.style.fontSize="12px",n.textContent="Trigger",o.style.padding="12px",o.style.display="flex",o.style.alignItems="center",o.style.justifyContent="center";const s=document.createElement("button");s.textContent="Fire!",Object.assign(s.style,{padding:"8px 16px",background:"#4a9eff",border:"none",borderRadius:"4px",color:"#fff",fontWeight:"bold",cursor:"pointer",pointerEvents:"auto",transition:"background 0.2s"}),s.addEventListener("mousedown",e=>{e.stopPropagation(),s.style.background="#2a7ede"}),s.addEventListener("mouseup",()=>{s.style.background="#4a9eff"}),s.addEventListener("click",t=>{if(t.stopPropagation(),e.state.triggered=!0,console.log("[Trigger] Button clicked!"),e.__runnerRef&&e.__controllerRef){console.log("[Trigger] Runner and controller found");const t=e.__runnerRef,n=e.__controllerRef,o=n.graph;console.log("[Trigger] Calling runner.runOnce with node.id:",e.id);const s=t.runOnce(e.id,0).connectedEdges,i=performance.now(),r=500,a=()=>{var t;performance.now()-i<r?(n.renderer.draw(o,{selection:n.selection,tempEdge:null,running:!0,time:performance.now(),dt:0,activeEdges:s}),null==(t=n.htmlOverlay)||t.draw(o,n.selection),requestAnimationFrame(a)):(n.render(),e.state.triggered=!1)};a()}}),o.appendChild(s)},update(e,t,{header:n,body:o,selected:s}){t.style.borderColor=s?"#6cf":"#444",n.style.backgroundColor=s?"#3a4a5a":"#2a2a31"}},onCreate(e){e.state.triggered=!1},onExecute(e,{setOutput:t}){console.log("[Trigger] Outputting triggered:",e.state.triggered),t("triggered",e.state.triggered)}}),p.register("core/Group",{title:"Group",size:{w:240,h:160},onDraw(e,{ctx:t,theme:n}){const{x:o,y:s,w:i,h:r}=e.computed,a=e.state.color||"#39424e",l=n.text||"#e9e9ef",d=(e,t)=>{const n=e.replace("#",""),o=parseInt(3===n.length?n.split("").map(e=>e+e).join(""):n,16);return`rgba(${o>>16&255},${o>>8&255},${255&o},${t})`};var c,h,u,p,g,f;t.fillStyle=d(a,.5),h=o,u=s,(p=i)<2*(f=10)&&(f=p/2),(g=r)<2*f&&(f=g/2),(c=t).beginPath(),c.moveTo(h+f,u),c.arcTo(h+p,u,h+p,u+g,f),c.arcTo(h+p,u+g,h,u+g,f),c.arcTo(h,u+g,h,u,f),c.arcTo(h,u,h+p,u,f),c.closePath(),t.fill(),t.fillStyle=d(a,.3),t.beginPath(),t.roundRect(o,s,i,24,[10,10,0,0]),t.fill(),t.fillStyle=l,t.font="600 13px system-ui",t.textBaseline="top",t.fillText(e.title,o+12,s+6)}}),function(e,{controller:t,graph:n,hooks:o}){const s=[];for(const[i,r]of n.registry.types.entries())s.push({id:`add-${i}`,label:r.title||i,action:()=>{const s=e.worldPosition||{x:100,y:100},r=n.addNode(i,{x:s.x,y:s.y});null==o||o.emit("node:updated",r),t.render()}});e.addItem("add-node","Add Node",{condition:e=>!e,submenu:s,order:5}),e.addItem("delete-node","Delete Node",{condition:e=>e&&"core/Group"!==e.type,action:e=>{const s=g(n,e);t.stack.exec(s),null==o||o.emit("node:updated",e)},order:10}),e.addItem("change-group-color","Change Color",{condition:e=>e&&"core/Group"===e.type,submenu:[{name:"Default",color:"#39424e"},{name:"Slate",color:"#4a5568"},{name:"Gray",color:"#2d3748"},{name:"Blue",color:"#1a365d"},{name:"Green",color:"#22543d"},{name:"Red",color:"#742a2a"},{name:"Purple",color:"#44337a"}].map(e=>({id:`color-${e.color}`,label:e.name,color:e.color,action:n=>{const s=n.state.color||"#39424e",i=(r=n,a=s,l=e.color,{do(){r.state.color=l},undo(){r.state.color=a}});var r,a,l;t.stack.exec(i),null==o||o.emit("node:updated",n)}})),order:20}),e.addItem("delete-group","Delete Group",{condition:e=>e&&"core/Group"===e.type,action:e=>{const s=g(n,e);t.stack.exec(s),null==o||o.emit("node:updated",e)},order:20})}(C,{controller:S,graph:f,hooks:u}),m.resize(d.clientWidth,d.clientHeight),T.resize(d.clientWidth,d.clientHeight),S.render();const M=new ResizeObserver(()=>{m.resize(d.clientWidth,d.clientHeight),T.resize(d.clientWidth,d.clientHeight),S.render()});M.observe(d);const I=S.render.bind(S);S.render=function(){I(),N&&N.render()};const O={addGroup:(e={})=>{S.graph.groupManager.addGroup(e),S.render()},graph:f,renderer:m,controller:S,runner:z,minimap:N,contextMenu:C,hooks:u,registry:p,htmlOverlay:x,propertyPanel:P,render:()=>S.render(),start:()=>z.start(),stop:()=>z.stop(),destroy:()=>{z.stop(),M.disconnect(),S.destroy(),x.destroy(),C.destroy(),P&&P.destroy(),N&&N.destroy()}};return s&&z.start(),O},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
2
2
  //# sourceMappingURL=html-overlay-node.umd.js.map