html-overlay-node 0.1.9 → 0.1.11
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.
- package/dist/example.json +9 -9
- package/dist/html-overlay-node.es.js +1000 -321
- package/dist/html-overlay-node.es.js.map +1 -1
- package/dist/html-overlay-node.umd.js +1 -1
- package/dist/html-overlay-node.umd.js.map +1 -1
- package/dist/img/favicon.svg +1 -0
- package/index.css +436 -232
- package/package.json +1 -1
- package/readme.md +143 -440
- package/src/core/Graph.js +34 -5
- package/src/core/Runner.js +188 -54
- package/src/index.js +29 -26
- package/src/interact/Controller.js +35 -6
- package/src/nodes/core.js +55 -77
- package/src/nodes/logic.js +51 -48
- package/src/nodes/math.js +23 -8
- package/src/nodes/util.js +238 -131
- package/src/nodes/value.js +87 -102
- package/src/render/CanvasRenderer.js +465 -285
- package/src/render/HtmlOverlay.js +65 -3
- package/src/render/hitTest.js +5 -2
- package/src/ui/HelpOverlay.js +158 -0
- package/src/ui/PropertyPanel.css +58 -27
- package/src/ui/PropertyPanel.js +441 -268
|
@@ -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,s)=>((e,n,s)=>n in e?t(e,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[n]=s)(e,"symbol"!=typeof n?n+"":n,s);class s{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 o(){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 s=Array.from(n,e=>e.toString(16).padStart(2,"0"));return s.slice(0,4).join("")+"-"+s.slice(4,6).join("")+"-"+s.slice(6,8).join("")+"-"+s.slice(8,10).join("")+"-"+s.slice(10,16).join("")}}catch{}const n=new Uint8Array(16);for(let o=0;o<16;o++)n[o]=Math.floor(256*Math.random());n[6]=15&n[6]|64,n[8]=63&n[8]|128;const s=Array.from(n,e=>e.toString(16).padStart(2,"0"));return s.slice(0,4).join("")+"-"+s.slice(4,6).join("")+"-"+s.slice(6,8).join("")+"-"+s.slice(8,10).join("")+"-"+s.slice(10,16).join("")}class i{constructor({id:e,type:t,title:n,x:s=0,y:i=0,width:r=160,height:a=60}){if(!t)throw new Error("Node type is required");this.id=e??o(),this.type=t,this.title=n??t,this.pos={x:s,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}}_updateMinSize(){const e=38+24*this.inputs.length+10,t=38+24*this.outputs.length+10,n=Math.max(e,t,60);this.size.height<n&&(this.size.height=n)}addInput(e,t="any",n="data"){if("string"!=typeof e||"data"===n&&!e)throw new Error("Input port name must be a string (non-empty for data ports)");const s={id:o(),name:e,datatype:t,portType:n,dir:"in"};return this.inputs.push(s),this._updateMinSize(),s}addOutput(e,t="any",n="data"){if("string"!=typeof e||"data"===n&&!e)throw new Error("Output port name must be a string (non-empty for data ports)");const s={id:o(),name:e,datatype:t,portType:n,dir:"out"};return this.outputs.push(s),this._updateMinSize(),s}}class r{constructor({id:e,fromNode:t,fromPort:n,toNode:s,toPort:i}){if(null==t||null==n||null==s||null==i)throw new Error("Edge requires fromNode, fromPort, toNode, and toPort (null/undefined not allowed)");this.id=e??o(),this.fromNode=t,this.fromPort=n,this.toNode=s,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:s=240,height:o=160,color:i="#39424e",members:r=[]}={}){var a;(s<100||o<60)&&(console.warn("Group size too small, using minimum size"),s=Math.max(100,s),o=Math.max(60,o));const d=this.graph.addNode("core/Group",{title:e,x:t,y:n,width:s,height:o});d.state.color=i;for(const l of r){const e=this.graph.getNodeById(l);if(e){if("core/Group"===e.type){console.warn(`Cannot add group ${l} as member of another group`);continue}this.graph.reparent(e,d)}else console.warn(`Member node ${l} not found, skipping`)}return this._groups.push(d),null==(a=this.hooks)||a.emit("group:change"),d}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 s=[...n.children];for(const o of s)this.graph.reparent(o,n.parent);this.graph.removeNode(e),null==(t=this.hooks)||t.emit("group:change")}resizeGroup(e,t,n){var s;const o=this.graph.getNodeById(e);if(!o||"core/Group"!==o.type)return;o.size.width=Math.max(100,o.size.width+t),o.size.height=Math.max(60,o.size.height+n),this.graph.updateWorldTransforms(),null==(s=this.hooks)||s.emit("group:change")}hitTestResizeHandle(e,t){const n=[...this.graph.nodes.values()].reverse();for(const s of n){if("core/Group"!==s.type)continue;const{x:n,y:o,w:i,h:r}=s.computed;if(e>=n+i-10&&e<=n+i&&t>=o+r-10&&t<=o+r)return{group:s,handle:"se"}}return null}}class d{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,s,o,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 d=new i({type:e,title:a.title,width:null==(n=a.size)?void 0:n.w,height:null==(s=a.size)?void 0:s.h,...t});for(const i of a.inputs||[])d.addInput(i.name,i.datatype,i.portType||"data");for(const i of a.outputs||[])d.addOutput(i.name,i.datatype,i.portType||"data");return null==(o=a.onCreate)||o.call(a,d),this.nodes.set(d.id,d),null==(r=this.hooks)||r.emit("node:create",d),d}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,s){var o;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:s});return this.edges.set(i.id,i),null==(o=this.hooks)||o.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:s}=t.pop();e.computed.x=n+e.pos.x,e.computed.y=s+e.pos.y,e.computed.w=e.size.width,e.computed.h=e.size.height;for(const o of e.children)t.push({node:o,px:e.computed.x,py:e.computed.y})}}reparent(e,t){if(e.parent===t)return;const n=e.computed.x,s=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=s-t.computed.y):(e.pos.x=n,e.pos.y=s),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 s=`${e}:${t}`;this._nextBuf().set(s,n)}getInput(e,t){for(const n of this.edges.values())if(n.toNode===e&&n.toPort===t){const s=`${n.fromNode}:${n.fromPort}`,o=this._curBuf().get(s);return console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, reading from ${n.fromNode}:${n.fromPort}, value:`,o),o}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,s;this.clear();for(const o of e.nodes){const e=new i({id:o.id,type:o.type,title:o.title,x:o.x,y:o.y,width:o.w,height:o.h}),s=null==(n=null==(t=this.registry)?void 0:t.types)?void 0:n.get(o.type);(null==s?void 0:s.onCreate)&&s.onCreate(e),e.inputs=o.inputs,e.outputs=o.outputs,e.state={...e.state,...o.state||{}},this.nodes.set(e.id,e)}for(const o of e.nodes)if(o.parentId){const e=this.nodes.get(o.id),t=this.nodes.get(o.parentId);e&&t&&(e.parent=t,t.children.add(e))}for(const o of e.edges)this.edges.set(o.id,new r(o));return this.updateWorldTransforms(),null==(s=this.hooks)||s.emit("graph:deserialize",e),this}}function l(e,t,n,s){const{x:o,y:i,w:r,h:a}=e.computed||{x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height},d=i+28+10+24*n;return"in"===s?{x:o-6,y:d-6,w:12,h:12}:"out"===s?{x:o+r-6,y:d-6,w:12,h:12}:void 0}const h=class e{constructor(e,{theme:t={},registry:n,edgeStyle:s="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=s,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}={}){var s;this.scale=Math.min(this.maxScale,Math.max(this.minScale,e)),this.offsetX=t,this.offsetY=n,null==(s=this._onTransformChange)||s.call(this)}setTransformChangeCallback(e){this._onTransformChange=e}panBy(e,t){var n;this.offsetX+=e,this.offsetY+=t,null==(n=this._onTransformChange)||n.call(this)}zoomAt(e,t,n){var s;const o=this.scale,i=Math.min(this.maxScale,Math.max(this.minScale,o*e));if(i===o)return;const r=(t-this.offsetX)/o,a=(n-this.offsetY)/o;this.offsetX=t-r*i,this.offsetY=n-a*i,this.scale=i,null==(s=this._onTransformChange)||s.call(this)}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(1,0,0,1,0,0),e.translate(this.offsetX,this.offsetY),e.scale(this.scale,this.scale)}_resetTransform(){this.ctx.setTransform(1,0,0,1,0,0)}_drawArrowhead(e,t,n,s,o=10){const{ctx:i}=this,r=o/this.scale,a=Math.atan2(s-t,n-e);i.beginPath(),i.moveTo(n,s),i.lineTo(n-r*Math.cos(a-Math.PI/6),s-r*Math.sin(a-Math.PI/6)),i.lineTo(n-r*Math.cos(a+Math.PI/6),s-r*Math.sin(a+Math.PI/6)),i.closePath(),i.fill()}_drawScreenText(e,t,n,{fontPx:s=12,color:o=this.theme.text,align:i="left",baseline:r="alphabetic",dpr:a=1}={}){const{ctx:d}=this,{x:l,y:h}=this.worldToScreen(t,n);d.save(),this._resetTransform();const c=Math.round(l)+.5,u=Math.round(h)+.5;d.font=s*this.scale+"px system-ui",d.fillStyle=o,d.textAlign=i,d.textBaseline=r,d.fillText(e,c,u),d.restore()}drawGrid(){const{ctx:e,canvas:t,theme:n,scale:s,offsetX:o,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/s;const r=20,a=-o/s,d=-i/s,l=(t.width-o)/s,h=(t.height-i)/s,c=Math.floor(a/r)*r,u=Math.floor(d/r)*r;e.beginPath();for(let p=c;p<=l;p+=r)e.moveTo(p,d),e.lineTo(p,h);for(let p=u;p<=h;p+=r)e.moveTo(a,p),e.lineTo(l,p);e.stroke(),this._resetTransform()}draw(t,{selection:n=new Set,tempEdge:s=null,running:o=!1,time:i=performance.now(),dt:r=0,groups:a=null,activeEdges:d=new Set,drawEdges:l=!0}={}){var h,c,u,p,f,g;t.updateWorldTransforms(),this.drawGrid();const{ctx:m,theme:y}=this;this._applyTransform(),m.save();for(const e of t.nodes.values())if("core/Group"===e.type){const t=n.has(e.id),s=null==(c=null==(h=this.registry)?void 0:h.types)?void 0:c.get(e.type);(null==s?void 0:s.onDraw)?s.onDraw(e,{ctx:m,theme:y,renderer:this}):this._drawNode(e,t)}if(l){m.lineWidth=1.5/this.scale;let n=null,s=0;if(o){const t=i/1e3*120/this.scale%e.FONT_SIZE;n=[6/this.scale,6/this.scale],s=-t}for(const e of t.edges.values()){const i=d&&d.size>0&&d.has(e.id);o&&i&&n?(m.setLineDash(n),m.lineDashOffset=s):(m.setLineDash([]),m.lineDashOffset=0);d&&d.has(e.id)?(m.strokeStyle="#00ffff",m.lineWidth=3/this.scale):(m.strokeStyle=y.edge,m.lineWidth=1.5/this.scale),this._drawEdge(t,e)}}if(s){const e=this.screenToWorld(s.x1,s.y1),t=this.screenToWorld(s.x2,s.y2),n=this.ctx.getLineDash();this.ctx.setLineDash([6/this.scale,6/this.scale]);let o=null;if("line"===this.edgeStyle?(this._drawLine(e.x,e.y,t.x,t.y),o=[{x:e.x,y:e.y},{x:t.x,y:t.y}]):"orthogonal"===this.edgeStyle?o=this._drawOrthogonal(e.x,e.y,t.x,t.y):(this._drawCurve(e.x,e.y,t.x,t.y),o=[{x:e.x,y:e.y},{x:t.x,y:t.y}]),this.ctx.setLineDash(n),o&&o.length>=2){const e=o[o.length-2],t=o[o.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),s=null==(p=null==(u=this.registry)?void 0:u.types)?void 0:p.get(e.type);!!(null==s?void 0:s.html)||(this._drawNode(e,t,!0),(null==s?void 0:s.onDraw)&&s.onDraw(e,{ctx:m,theme:y,renderer:this}))}for(const e of t.nodes.values())if("core/Group"!==e.type){const t=null==(g=null==(f=this.registry)?void 0:f.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("#",""),s=parseInt(3===n.length?n.split("").map(e=>e+e).join(""):n,16);return`rgba(${s>>16&255},${s>>8&255},${255&s},${t})`}_drawNode(t,n,s=!1){const{ctx:o,theme:i}=this,{x:r,y:a,w:d,h:h}=t.computed;n||(o.save(),o.shadowColor="rgba(0, 0, 0, 0.3)",o.shadowBlur=8/this.scale,o.shadowOffsetY=2/this.scale,o.fillStyle="rgba(0, 0, 0, 0.2)",u(o,r,a,d,h,8),o.fill(),o.restore()),o.fillStyle=i.node,o.strokeStyle=n?i.accentBright:i.nodeBorder,o.lineWidth=(n?1.5:1)/this.scale,u(o,r,a,d,h,8),o.fill(),o.stroke(),o.fillStyle=i.title,u(o,r,a,d,24,{tl:8,tr:8,br:0,bl:0}),o.fill(),o.strokeStyle=n?i.accentBright:i.nodeBorder,o.lineWidth=(n?1.5:1)/this.scale,o.beginPath(),o.moveTo(r+8,a),o.lineTo(r+d-8,a),o.quadraticCurveTo(r+d,a,r+d,a+8),o.lineTo(r+d,a+24),o.moveTo(r,a+24),o.lineTo(r,a+8),o.quadraticCurveTo(r,a,r+8,a),o.stroke(),this._drawScreenText(t.title,r+8,a+e.FONT_SIZE,{fontPx:e.FONT_SIZE,color:i.text,baseline:"middle",align:"left"}),s||(t.inputs.forEach((e,n)=>{const s=l(t,0,n,"in"),r=s.x+s.w/2,a=s.y+s.h/2;if("exec"===e.portType){const e=8;o.fillStyle=i.portExec,o.strokeStyle="rgba(16, 185, 129, 0.3)",o.lineWidth=2/this.scale,o.beginPath(),o.roundRect(r-e/2,a-e/2,e,e,2),o.fill(),o.stroke()}else o.fillStyle=i.port,o.strokeStyle="rgba(99, 102, 241, 0.3)",o.lineWidth=2/this.scale,o.beginPath(),o.arc(r,a,5,0,2*Math.PI),o.fill(),o.stroke()}),t.outputs.forEach((e,n)=>{const s=l(t,0,n,"out"),r=s.x+s.w/2,a=s.y+s.h/2;if("exec"===e.portType){const e=8;o.fillStyle=i.portExec,o.strokeStyle="rgba(16, 185, 129, 0.3)",o.lineWidth=2/this.scale,o.beginPath(),o.roundRect(r-e/2,a-e/2,e,e,2),o.fill(),o.stroke()}else o.fillStyle=i.port,o.strokeStyle="rgba(99, 102, 241, 0.3)",o.lineWidth=2/this.scale,o.beginPath(),o.arc(r,a,5,0,2*Math.PI),o.fill(),o.stroke()}))}_drawPorts(e){const{ctx:t,theme:n}=this;e.inputs.forEach((s,o)=>{const i=l(e,0,o,"in"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===s.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((s,o)=>{const i=l(e,0,o,"out"),r=i.x+i.w/2,a=i.y+i.h/2;if("exec"===s.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),s=e.nodes.get(t.toNode);if(!n||!s)return;const o=n.outputs.findIndex(e=>e.id===t.fromPort),i=s.inputs.findIndex(e=>e.id===t.toPort),r=l(n,0,o,"out"),a=l(s,0,i,"in"),d=r.x+r.w/2,h=r.y+r.h/2,c=a.x+a.w/2,u=a.y+a.h/2;"line"===this.edgeStyle?this._drawLine(d,h,c,u):"orthogonal"===this.edgeStyle?this._drawOrthogonal(d,h,c,u):this._drawCurve(d,h,c,u)}_drawLine(e,t,n,s){const{ctx:o}=this;o.beginPath(),o.moveTo(e,t),o.lineTo(n,s),o.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,s){const o=(e+n)/2;let i;i=[{x:e,y:t},{x:o,y:t},{x:o,y:s},{x:n,y:s}];const{ctx:r}=this,a=r.lineJoin,d=r.lineCap;return r.lineJoin="round",r.lineCap="round",this._drawPolyline(i),r.lineJoin=a,r.lineCap=d,i}_drawCurve(e,t,n,s){const{ctx:o}=this,i=Math.max(40,.4*Math.abs(n-e));o.beginPath(),o.moveTo(e,t),o.bezierCurveTo(e+i,t,n-i,s,n,s),o.stroke()}drawEdgesOnly(e,{activeEdges:t=new Set,running:n=!1,time:s=performance.now(),tempEdge:o=null}={}){this._resetTransform(),this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this._applyTransform();const{ctx:i,theme:r}=this;let a=null,d=0;if(n||t.size>0){const e=s/1e3*120/this.scale%12;a=[6/this.scale,6/this.scale],d=-e}i.lineWidth=1.5/this.scale,i.strokeStyle=r.edge;for(const l of e.edges.values()){t&&t.has(l.id)&&a?(i.setLineDash(a),i.lineDashOffset=d,i.strokeStyle="#00ffff",i.lineWidth=3/this.scale):(i.setLineDash([]),i.strokeStyle=r.edge,i.lineWidth=1.5/this.scale),this._drawEdge(e,l)}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)}}this._resetTransform()}};n(h,"FONT_SIZE",12),n(h,"SELECTED_NODE_COLOR","#6cf");let c=h;function u(e,t,n,s,o,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+s-i.tr,n),e.quadraticCurveTo(t+s,n,t+s,n+i.tr),e.lineTo(t+s,n+o-i.br),e.quadraticCurveTo(t+s,n+o,t+s-i.br,n+o),e.lineTo(t+i.bl,n+o),e.quadraticCurveTo(t,n+o,t,n+o-i.bl),e.lineTo(t,n+i.tl),e.quadraticCurveTo(t,n,t+i.tl,n),e.closePath()}function p(e,t,n,s,o){for(const[i,r]of e.edges)if(r.fromNode===t&&r.fromPort===n&&r.toNode===s&&r.toPort===o)return i;return null}function f(e,t){let n=null,s=[];return{do(){n=t,s=e.edges?[...e.edges.values()].filter(e=>e.fromNode===t.id||e.toNode===t.id):[];for(const t of s)e.edges.delete(t.id);e.nodes.delete(t.id)},undo(){n&&e.nodes.set(n.id,n);for(const t of s)e.edges.set(t.id,t)}}}class g{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:s,contextMenu:o,edgeRenderer:i,portRenderer:r}){this.graph=e,this.renderer=t,this.hooks=n,this.htmlOverlay=s,this.contextMenu=o,this.edgeRenderer=i,this.portRenderer=r,this.stack=new g,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(f(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 s of n){const{x:n,y:o,w:i,h:r}=s.computed;if(e>=n&&e<=n+i&&t>=o&&t<=o+r){if("core/Group"===s.type){const n=this._findChildNodeAtWorld(s,e,t);if(n)return n}return s}}return null}_findChildNodeAtWorld(e,t,n){const s=[];for(const o of this.graph.nodes.values())o.parent===e&&s.push(o);for(let o=s.length-1;o>=0;o--){const e=s[o],{x:i,y:r,w:a,h:d}=e.computed;if(t>=i&&t<=i+a&&n>=r&&n<=r+d){if("core/Group"===e.type){const s=this._findChildNodeAtWorld(e,t,n);if(s)return s}return e}}return null}_findPortAtWorld(e,t){for(const n of this.graph.nodes.values()){for(let s=0;s<n.inputs.length;s++){if(v(l(n,n.inputs[s],s,"in"),e,t))return{node:n,port:n.inputs[s],dir:"in",idx:s}}for(let s=0;s<n.outputs.length;s++){if(v(l(n,n.outputs[s],s,"out"),e,t))return{node:n,port:n.outputs[s],dir:"out",idx:s}}}return null}_findIncomingEdge(e,t){for(const[n,s]of this.graph.edges)if(s.toNode===e&&s.toPort===t)return{id:n,edge:s};return null}_onWheel(e){e.preventDefault();const{x:t,y:n}=this._posScreen(e),s=Math.pow(1.0015,-e.deltaY);this.renderer.zoomAt(s,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),s=this._findNodeAtWorld(n.x,n.y);s&&(null==(t=this.hooks)||t.emit("node:dblclick",s))}_resizeHandleRect(e){const{x:t,y:n,w:s,h:o}=e.computed;return{x:t+s-10,y:n+o-10,w:10,h:10}}_hitResizeHandle(e,t,n){const s=this._resizeHandleRect(e);return t>=s.x&&t<=s.x+s.w&&n>=s.y&&n<=s.y+s.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 s=this._findNodeAtWorld(n.x,n.y);if(0===e.button&&s&&this._hitResizeHandle(s,n.x,n.y))return this.resizing={nodeId:s.id,startW:s.size.width,startH:s.size.height,startX:n.x,startY:n.y},e.shiftKey||this.selection.clear(),this.selection.add(s.id),this._setCursor("se-resize"),void this.render();const o=this._findPortAtWorld(n.x,n.y);if(0===e.button&&o&&"in"===o.dir){const e=this._findIncomingEdge(o.node.id,o.port.id);if(e)return this.stack.exec(function(e,t){const n=e.edges.get(t);if(!n)return null;const{fromNode:s,fromPort:o,toNode:i,toPort:r}=n;return{do(){e.edges.delete(t)},undo(){e.addEdge(s,o,i,r)}}}(this.graph,e.id)),void this.render()}if(0===e.button&&o&&"out"===o.dir){const e=l(o.node,o.port,o.idx,"out"),t=this.renderer.worldToScreen(e.x,e.y+7);return void(this.connecting={fromNode:o.node.id,fromPort:o.port.id,x:t.x,y:t.y})}if(0!==e.button||!s)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(s.id),this.dragging={nodeId:s.id,offsetX:n.x-s.computed.x,offsetY:n.y-s.computed.y,startPos:{...s.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"===s.type){this.dragging.childrenWorldPos=[];for(const e of this.graph.nodes.values())e.parent===s&&this.dragging.childrenWorldPos.push({node:e,worldX:e.computed.x,worldY:e.computed.y})}this.render()}_onMove(t){var n,s;this.isAlt=t.altKey,this.isShift=t.shiftKey,this.isCtrl=t.ctrlKey;const o=this._posScreen(t),i=this.renderer.screenToWorld(o.x,o.y);if(this.resizing){const t=this.graph.nodes.get(this.resizing.nodeId),s=i.x-this.resizing.startX,o=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+s),t.size.height=Math.max(a,this.resizing.startH+o),null==(n=this.hooks)||n.emit("node:resize",t),this._setCursor("se-resize"),void this.render()}if(this.panning){const e=o.x-this.panning.x,t=o.y-this.panning.y;return this.panning={x:o.x,y:o.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 o=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:s,startWorldX:i,startWorldY:a}of this.dragging.selectedNodes){if(this.isShift&&"core/Group"===s.type)continue;const e=i+o,t=a+r;let n=0,d=0;s.parent&&(n=s.parent.computed.x,d=s.parent.computed.y),s.pos.x=e-n,s.pos.y=t-d}if(this.isAlt&&"core/Group"===e.type&&this.dragging.childrenWorldPos){this.graph.updateWorldTransforms();for(const t of this.dragging.childrenWorldPos){const n=t.node,s=e.computed.x,o=e.computed.y;n.pos.x=t.worldX-s,n.pos.y=t.worldY-o}}return null==(s=this.hooks)||s.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=o.x,this.connecting.y=o.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,s,o){let i=null;return{do(){e.addEdge(t,n,s,o),i=p(e,t,n,s,o)},undo(){const r=i??p(e,t,n,s,o);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,s=t,o=i,{do(){n.size.width=o.w,n.size.height=o.h},undo(){n.size.width=s.w,n.size.height=s.h}})),this.resizing=null,this._setCursor("default")}var n,s,o;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 s=e.computed.x,o=e.computed.y;n.pos.x=t.worldX-s,n.pos.y=t.worldY-o}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:s}=this.boxSelecting,o=Math.min(e,n),i=Math.max(e,n),r=Math.min(t,s),a=Math.max(t,s);for(const d of this.graph.nodes.values()){const{x:e,y:t,w:n,h:s}=d.computed;e+n>=o&&e<=i&&t+s>=r&&t<=a&&this.selection.add(d.id)}this.boxSelecting=null,this.render()}}}_autoParentNodesInGroup(e){const{x:t,y:n,w:s,h:o}=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:d,h:l}=i.computed,h=r+d/2,c=a+l/2;h>=t&&h<=t+s&&c>=n&&c<=n+o&&this.graph.reparent(i,e)}}_findPotentialParent(e,t,n){const s=[...this.graph.nodes.values()].reverse();for(const o of s){if("core/Group"!==o.type)continue;if(o===n)continue;let s=o.parent,i=!1;for(;s;){if(s===n){i=!0;break}s=s.parent}if(i)continue;const{x:r,y:a,w:d,h:l}=o.computed;if(e>=r&&e<=r+d&&t>=a&&t<=a+l)return o}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,s=-1/0,o=-1/0;for(const l of e){const{x:e,y:i,w:r,h:a}=l.computed;t=Math.min(t,e),n=Math.min(n,i),s=Math.max(s,e+r),o=Math.max(o,i+a)}const i=t-20,r=n-20,a=s-t+40,d=o-n+40;this.graph.groupManager&&(this.graph.groupManager.addGroup({title:"Group",x:i,y:r,width:a,height:d,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;const t=this.renderTempEdge();if(this.renderer.draw(this.graph,{selection:this.selection,tempEdge:null,boxSelecting:this.boxSelecting,activeEdges:this.activeEdges||new Set,drawEdges:!this.edgeRenderer}),null==(e=this.htmlOverlay)||e.draw(this.graph,this.selection),this.edgeRenderer){this.edgeRenderer.ctx.clearRect(0,0,this.edgeRenderer.canvas.width,this.edgeRenderer.canvas.height),this.edgeRenderer._applyTransform(),this.edgeRenderer.drawEdgesOnly(this.graph,{activeEdges:this.activeEdges||new Set,running:!1,time:performance.now(),tempEdge:t}),this.edgeRenderer._resetTransform()}if(this.boxSelecting){const{startX:e,startY:t,currentX:n,currentY:s}=this.boxSelecting,o=Math.min(e,n),i=Math.min(t,s),r=Math.abs(n-e),a=Math.abs(s-t),d=this.renderer.worldToScreen(o,i),l=this.renderer.worldToScreen(o+r,i+a),h=this.edgeRenderer?this.edgeRenderer.ctx:this.renderer.ctx;h.save(),this.edgeRenderer?this.edgeRenderer._resetTransform():this.renderer._resetTransform(),h.strokeStyle="#6cf",h.fillStyle="rgba(102, 204, 255, 0.1)",h.lineWidth=2,h.strokeRect(d.x,d.y,l.x-d.x,l.y-d.y),h.fillRect(d.x,d.y,l.x-d.x,l.y-d.y),h.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())"core/Group"!==e.type&&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),s=n.outputs.findIndex(e=>e.id===t),o=l(n,0,s,"out");return this.renderer.worldToScreen(o.x+o.w/2,o.y+o.h/2)}};n(m,"MIN_NODE_WIDTH",80),n(m,"MIN_NODE_HEIGHT",60);let y=m;function v(e,t,n){return t>=e.x&&t<=e.x+e.w&&n>=e.y&&n<=e.y+e.h}class x{constructor({graph:e,hooks:t,renderer:n,commandStack:s}){this.graph=e,this.hooks=t,this.renderer=n,this.commandStack=s,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:s,submenu:o,condition:i,order:r=100}=n;s||o?(this.removeItem(e),this.items.push({id:e,label:t,action:s,submenu:o,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,s=null){this.target=e,this.position={x:t,y:n},this.worldPosition=s,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(),s=window.innerWidth,o=window.innerHeight;let i=t,r=n;e.right>s&&(i=s-e.width-5),e.bottom>o&&(r=o-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 s=document.createElement("span");if(s.textContent=e.label,n.appendChild(s),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",()=>{if(t.style.backgroundColor="#3a3a3e",t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null),e.submenu){const n="function"==typeof e.submenu?e.submenu():e.submenu;this._showSubmenu(n,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 s=document.createElement("div");if(Object.assign(s.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"}),s.appendChild(t)}const o=document.createElement("span");o.textContent=e.label,s.appendChild(o),t.appendChild(s),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(),s=n.getBoundingClientRect();let o=e.right+2,i=e.top;o+s.width>window.innerWidth&&(o=e.left-s.width-2),i+s.height>window.innerHeight&&(i=window.innerHeight-s.height-5),n.style.left=`${o}px`,n.style.top=`${i}px`})}_hideSubmenu(e){e._submenuElement&&(e._submenuElement.remove(),e._submenuElement=null)}}class w{constructor({graph:e,registry:t,hooks:n,cyclesPerFrame:s=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|s)}isRunning(){return this.running}setCyclesPerFrame(e){this.cyclesPerFrame=Math.max(1,0|e)}step(e=1,t=0){var n,s;const o=Math.max(1,0|e);for(let r=0;r<o;r++){for(const e of this.graph.nodes.values()){const o=this.registry.types.get(e.type);if(null==o?void 0:o.onExecute)try{o.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 s=e.outputs.find(e=>e.name===t)||e.outputs[0];s&&this.graph.setOutput(e.id,s.id,n)}})}catch(i){null==(s=null==(n=this.hooks)?void 0:n.emit)||s.call(n,"error",i)}}this.graph.swapBuffers()}}runOnce(e,t=0){console.log("[Runner.runOnce] Starting exec flow from node:",e);const n=[],s=new Set,o=[e],i=new Set;for(;o.length>0;){const e=o.shift();if(i.has(e))continue;i.add(e);const r=this.graph.nodes.get(e);if(!r){console.warn(`[Runner.runOnce] Node not found: ${e}`);continue}n.push(e),s.add(e),console.log(`[Runner.runOnce] Executing: ${r.title} (${r.type})`);for(const n of r.inputs)if("data"===n.portType)for(const o of this.graph.edges.values())if(o.toNode===e&&o.toPort===n.id){this.graph.nodes.get(o.fromNode)&&!s.has(o.fromNode)&&(s.add(o.fromNode),this.executeNode(o.fromNode,t))}this.executeNode(e,t);const a=this.findAllNextExecNodes(e);o.push(...a)}console.log("[Runner.runOnce] Executed nodes:",n.length);const r=new Set;for(const a of this.graph.edges.values())s.has(a.fromNode)&&s.has(a.toNode)&&r.add(a.id);return console.log("[Runner.runOnce] Connected edges count:",r.size),{connectedNodes:s,connectedEdges:r}}findAllNextExecNodes(e){const t=this.graph.nodes.get(e);if(!t)return[];const n=t.outputs.filter(e=>"exec"===e.portType);if(0===n.length)return[];const s=[];for(const o of n)for(const t of this.graph.edges.values())t.fromNode===e&&t.fromPort===o.id&&s.push(t.toNode);return s}executeNode(e,t){var n,s;const o=this.graph.nodes.get(e);if(!o)return;const i=this.registry.types.get(o.type);if(null==i?void 0:i.onExecute)try{i.onExecute(o,{dt:t,graph:this.graph,getInput:e=>{const t=o.inputs.find(t=>t.name===e)||o.inputs[0];return t?this.graph.getInput(o.id,t.id):void 0},setOutput:(e,t)=>{const n=o.outputs.find(t=>t.name===e)||o.outputs[0];if(n){const e=`${o.id}:${n.id}`;this.graph._curBuf().set(e,t)}}})}catch(r){null==(s=null==(n=this.hooks)?void 0:n.emit)||s.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,s;if(!this.running)return;const o=this._last?e-this._last:0;this._last=e;const i=o/1e3;this.step(this.cyclesPerFrame,i),null==(s=null==(t=this.hooks)?void 0:t.emit)||s.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 b{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 s=document.createElement("div");return s.className="node-body",Object.assign(s.style,{flex:"1",position:"relative",overflow:"hidden",pointerEvents:"none"}),t.appendChild(n),t.appendChild(s),t._domParts={header:n,body:s},t}_ensureNodeElement(e,t,n){var s;let o=this.nodes.get(e.id);if(!o){if(null==(s=t.html)?void 0:s.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,graph:n})}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:s,offsetY:o}=this.renderer;this.container.style.transform=`translate(${s}px, ${o}px) scale(${n})`,this.container.style.transformOrigin="0 0";const i=new Set;for(const r of e.nodes.values()){const n=this.registry.types.get(r.type);if(!!!(null==n?void 0:n.html))continue;const s=this._ensureNodeElement(r,n,e);if(s){if(s.style.left=`${r.computed.x}px`,s.style.top=`${r.computed.y}px`,s.style.width=`${r.computed.w}px`,s.style.height=`${r.computed.h}px`,n.html.update){const e=s._domParts||{};n.html.update(r,s,{selected:t.has(r.id),header:e.header,body:e.body})}i.add(r.id)}}for(const[r,a]of this.nodes)i.has(r)||(a.remove(),this.nodes.delete(r))}syncTransform(){const{scale:e,offsetX:t,offsetY:n}=this.renderer;this.container.style.transform=`translate(${t}px, ${n}px) scale(${e})`,this.container.style.transformOrigin="0 0"}clear(){for(const[,e]of this.nodes)e.remove();this.nodes.clear()}destroy(){this.clear(),this.container.remove()}}class _{constructor(e,{graph:t,renderer:n,width:s=200,height:o=150}={}){this.graph=t,this.renderer=n,this.width=s,this.height=o,this.canvas=document.createElement("canvas"),this.canvas.id="minimap",this.canvas.width=s,this.canvas.height=o,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:s,height:o}=this;if(n.fillStyle="#141417",n.fillRect(0,0,s,o),0===e.nodes.size)return;let i=1/0,r=1/0,a=-1/0,d=-1/0;for(const x of e.nodes.values()){const{x:e,y:t,w:n,h:s}=x.computed;i=Math.min(i,e),r=Math.min(r,t),a=Math.max(a,e+n),d=Math.max(d,t+s)}const l=100,h=Math.max(300,a-i+200),c=Math.max(200,d-r+200);i-=l,r-=l;const u=Math.min((s-20)/h,(o-20)/c),p=(s-h*u)/2,f=(o-c*u)/2;n.strokeStyle="rgba(127, 140, 255, 0.5)",n.lineWidth=1;for(const x of e.edges.values()){const t=e.nodes.get(x.fromNode),s=e.nodes.get(x.toNode);if(!t||!s)continue;const o=(t.computed.x+t.computed.w/2-i)*u+p,a=(t.computed.y+t.computed.h/2-r)*u+f,d=(s.computed.x+s.computed.w/2-i)*u+p,l=(s.computed.y+s.computed.h/2-r)*u+f;n.beginPath(),n.moveTo(o,a),n.lineTo(d,l),n.stroke()}n.fillStyle="#6cf";for(const x of e.nodes.values()){const{x:e,y:t,w:s,h:o}=x.computed,a=(e-i)*u+p,d=(t-r)*u+f,l=s*u,h=o*u;"core/Group"===x.type?(n.fillStyle="rgba(102, 204, 255, 0.2)",n.strokeStyle="#6cf",n.lineWidth=1,n.fillRect(a,d,l,h),n.strokeRect(a,d,l,h)):(n.fillStyle="#6cf",n.fillRect(a,d,Math.max(2,l),Math.max(2,h)))}const g=(-t.offsetX/t.scale-i)*u+p,m=(-t.offsetY/t.scale-r)*u+f,y=t.canvas.width/t.scale*u,v=t.canvas.height/t.scale*u;n.strokeStyle="#ff6b6b",n.lineWidth=2,n.strokeRect(g,m,y,v)}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}}class E{constructor(e,{graph:t,hooks:n,registry:s,render:o}){this.container=e,this.graph=t,this.hooks=n,this.registry=s,this.render=o,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 s=this.currentNode;if(s){switch(e){case"title":s.title=t;break;case"x":s.pos.x=parseFloat(t),this.graph.updateWorldTransforms();break;case"y":s.pos.y=parseFloat(t),this.graph.updateWorldTransforms();break;case"width":s.size.width=parseFloat(t);break;case"height":s.size.height=parseFloat(t);break;default:if(e.startsWith("state.")){const n=e.substring(6);if(s.state){const e=s.state[n];s.state[n]="number"==typeof e?parseFloat(t):t}}}null==(n=this.hooks)||n.emit("node:updated",s),this.render&&this.render()}}destroy(){this.panel&&this.panel.remove()}}e.createGraphEditor=function(e,{theme:t,hooks:n,autorun:o=!0,showMinimap:i=!0,enablePropertyPanel:r=!0,propertyPanelContainer:a=null,setupDefaultContextMenu:l=!0,setupContextMenu:h=null,plugins:u=[]}={}){var p;let g,m;if("string"==typeof e&&(e=document.querySelector(e)),!e)throw new Error("createGraphEditor: target element not found");e instanceof HTMLCanvasElement?(g=e,m=g.parentElement):(m=e,g=m.querySelector("canvas"),g||(g=document.createElement("canvas"),g.style.display="block",g.style.width="100%",g.style.height="100%",m.appendChild(g))),"static"===getComputedStyle(m).position&&(m.style.position="relative");const v=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 s of t[e])s(...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"]),S=new s,k=new d({hooks:v,registry:S}),T=new c(g,{theme:t,registry:S}),C=new b(g.parentElement,T,S);T.setTransformChangeCallback(()=>{C.syncTransform()});const N=document.createElement("canvas");N.id="edge-canvas",Object.assign(N.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"15"}),g.parentElement.appendChild(N);const P=new c(N,{theme:t,registry:S});Object.defineProperty(P,"scale",{get:()=>T.scale,set(e){T.scale=e}}),Object.defineProperty(P,"offsetX",{get:()=>T.offsetX,set(e){T.offsetX=e}}),Object.defineProperty(P,"offsetY",{get:()=>T.offsetY,set(e){T.offsetY=e}});const M=document.createElement("canvas");M.id="port-canvas",Object.assign(M.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"20"}),g.parentElement.appendChild(M);const z=new c(M,{theme:t,registry:S});z.setTransform=T.setTransform.bind(T),z.scale=T.scale,z.offsetX=T.offsetX,z.offsetY=T.offsetY;const I=new y({graph:k,renderer:T,hooks:v,htmlOverlay:C,edgeRenderer:P,portRenderer:z}),W=new x({graph:k,hooks:v,renderer:T,commandStack:I.stack});I.contextMenu=W;let $=null;i&&($=new _(m,{graph:k,renderer:T}));let R=null;r&&(R=new E(a||m,{graph:k,hooks:v,registry:S,render:()=>I.render()}),v.on("node:dblclick",e=>{R.open(e)}));const O=new w({graph:k,registry:S,hooks:v});if(k.runner=O,k.controller=I,v.on("runner:tick",({time:e,dt:t})=>{T.draw(k,{selection:I.selection,tempEdge:I.connecting?I.renderTempEdge():null,running:!0,time:e,dt:t}),C.draw(k,I.selection)}),v.on("runner:start",()=>{T.draw(k,{selection:I.selection,tempEdge:I.connecting?I.renderTempEdge():null,running:!0,time:performance.now(),dt:0}),C.draw(k,I.selection)}),v.on("runner:stop",()=>{T.draw(k,{selection:I.selection,tempEdge:I.connecting?I.renderTempEdge():null,running:!1,time:performance.now(),dt:0}),C.draw(k,I.selection)}),v.on("node:updated",()=>{I.render()}),l&&function(e,{controller:t,graph:n,hooks:s}){e.addItem("add-node","Add Node",{condition:e=>!e,submenu:()=>{const o=[];for(const[i,r]of n.registry.types.entries())o.push({id:`add-${i}`,label:r.title||i,action:()=>{const o=e.worldPosition||{x:100,y:100},r=n.addNode(i,{x:o.x,y:o.y});null==s||s.emit("node:updated",r),t.render()}});return o},order:5}),e.addItem("delete-node","Delete Node",{condition:e=>e&&"core/Group"!==e.type,action:e=>{const o=f(n,e);t.stack.exec(o),null==s||s.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 o=n.state.color||"#39424e",i=(r=n,a=o,d=e.color,{do(){r.state.color=d},undo(){r.state.color=a}});var r,a,d;t.stack.exec(i),null==s||s.emit("node:updated",n)}})),order:20}),e.addItem("delete-group","Delete Group",{condition:e=>e&&"core/Group"===e.type,action:e=>{const o=f(n,e);t.stack.exec(o),null==s||s.emit("node:updated",e)},order:20})}(W,{controller:I,graph:k,hooks:v}),h&&h(W,{controller:I,graph:k,hooks:v}),u&&u.length>0)for(const s of u)if("function"==typeof s.install)try{s.install({graph:k,registry:S,hooks:v,runner:O,controller:I,contextMenu:W},s.options||{})}catch(D){console.error(`[createGraphEditor] Failed to install plugin "${s.name||"unknown"}":`,D),null==(p=null==v?void 0:v.emit)||p.call(v,"error",D)}else console.warn(`[createGraphEditor] Plugin "${s.name||"unknown"}" does not have an install() method`);T.resize(g.clientWidth,g.clientHeight),P.resize(g.clientWidth,g.clientHeight),z.resize(g.clientWidth,g.clientHeight),I.render();const L=new ResizeObserver(()=>{T.resize(g.clientWidth,g.clientHeight),P.resize(g.clientWidth,g.clientHeight),z.resize(g.clientWidth,g.clientHeight),I.render()});L.observe(g);const G=I.render.bind(I);I.render=function(){G(),$&&$.render()};const A={addGroup:(e={})=>{I.graph.groupManager.addGroup(e),I.render()},graph:k,renderer:T,controller:I,runner:O,minimap:$,contextMenu:W,hooks:v,registry:S,htmlOverlay:C,propertyPanel:R,render:()=>I.render(),start:()=>O.start(),stop:()=>O.stop(),destroy:()=>{O.stop(),L.disconnect(),I.destroy(),C.destroy(),W.destroy(),R&&R.destroy(),$&&$.destroy()}};return o&&O.start(),A},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,s=(e,s,n)=>((e,s,n)=>s in e?t(e,s,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[s]=n)(e,"symbol"!=typeof s?s+"":s,n);class n{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 i(){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 s=Array.from(e,e=>e.toString(16).padStart(2,"0"));return s.slice(0,4).join("")+"-"+s.slice(4,6).join("")+"-"+s.slice(6,8).join("")+"-"+s.slice(8,10).join("")+"-"+s.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 s=t.randomBytes(16);s[6]=15&s[6]|64,s[8]=63&s[8]|128;const n=Array.from(s,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("")}}catch{}const s=new Uint8Array(16);for(let i=0;i<16;i++)s[i]=Math.floor(256*Math.random());s[6]=15&s[6]|64,s[8]=63&s[8]|128;const n=Array.from(s,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("")}class o{constructor({id:e,type:t,title:s,x:n=0,y:o=0,width:r=160,height:a=60}){if(!t)throw new Error("Node type is required");this.id=e??i(),this.type=t,this.title=s??t,this.pos={x:n,y:o},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}}_updateMinSize(){const e=38+24*this.inputs.length+10,t=38+24*this.outputs.length+10,s=Math.max(e,t,60);this.size.height<s&&(this.size.height=s)}addInput(e,t="any",s="data"){if("string"!=typeof e||"data"===s&&!e)throw new Error("Input port name must be a string (non-empty for data ports)");const n={id:i(),name:e,datatype:t,portType:s,dir:"in"};return this.inputs.push(n),this._updateMinSize(),n}addOutput(e,t="any",s="data"){if("string"!=typeof e||"data"===s&&!e)throw new Error("Output port name must be a string (non-empty for data ports)");const n={id:i(),name:e,datatype:t,portType:s,dir:"out"};return this.outputs.push(n),this._updateMinSize(),n}}class r{constructor({id:e,fromNode:t,fromPort:s,toNode:n,toPort:o}){if(null==t||null==s||null==n||null==o)throw new Error("Edge requires fromNode, fromPort, toNode, and toPort (null/undefined not allowed)");this.id=e??i(),this.fromNode=t,this.fromPort=s,this.toNode=n,this.toPort=o}}class a{constructor({graph:e,hooks:t}){this.graph=e,this.hooks=t,this._groups=[]}addGroup({title:e="Group",x:t=0,y:s=0,width:n=240,height:i=160,color:o="#39424e",members:r=[]}={}){var a;(n<100||i<60)&&(console.warn("Group size too small, using minimum size"),n=Math.max(100,n),i=Math.max(60,i));const l=this.graph.addNode("core/Group",{title:e,x:t,y:s,width:n,height:i});l.state.color=o;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 s=this.graph.getNodeById(e);if(!s||"core/Group"!==s.type)return;const n=[...s.children];for(const i of n)this.graph.reparent(i,s.parent);this.graph.removeNode(e),null==(t=this.hooks)||t.emit("group:change")}resizeGroup(e,t,s){var n;const i=this.graph.getNodeById(e);if(!i||"core/Group"!==i.type)return;i.size.width=Math.max(100,i.size.width+t),i.size.height=Math.max(60,i.size.height+s),this.graph.updateWorldTransforms(),null==(n=this.hooks)||n.emit("group:change")}hitTestResizeHandle(e,t){const s=[...this.graph.nodes.values()].reverse();for(const n of s){if("core/Group"!==n.type)continue;const{x:s,y:i,w:o,h:r}=n.computed;if(e>=s+o-10&&e<=s+o&&t>=i+r-10&&t<=i+r)return{group:n,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 s,n,i,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=t.height||(null==(s=a.size)?void 0:s.h)||this._calculateDefaultNodeHeight(a),d=new o({type:e,title:a.title,width:t.width||(null==(n=a.size)?void 0:n.w)||140,height:l,...t});for(const o of a.inputs||[])d.addInput(o.name,o.datatype,o.portType||"data");for(const o of a.outputs||[])d.addOutput(o.name,o.datatype,o.portType||"data");return null==(i=a.onCreate)||i.call(a,d),this.nodes.set(d.id,d),null==(r=this.hooks)||r.emit("node:create",d),d}removeNode(e){for(const[t,s]of this.edges)s.fromNode!==e&&s.toNode!==e||this.edges.delete(t);this.nodes.delete(e)}addEdge(e,t,s,n){var i;if(!this.nodes.has(e))throw new Error(`Cannot create edge: source node "${e}" not found`);if(!this.nodes.has(s))throw new Error(`Cannot create edge: target node "${s}" not found`);const o=new r({fromNode:e,fromPort:t,toNode:s,toPort:n});return this.edges.set(o.id,o),null==(i=this.hooks)||i.emit("edge:create",o),o}clear(){this.nodes.clear(),this.edges.clear()}updateWorldTransforms(){const e=[];for(const s of this.nodes.values())s.parent||e.push(s);const t=e.map(e=>({node:e,px:0,py:0}));for(;t.length>0;){const{node:e,px:s,py:n}=t.pop();e.computed.x=s+e.pos.x,e.computed.y=n+e.pos.y,e.computed.w=e.size.width,e.computed.h=e.size.height;for(const i of e.children)t.push({node:i,px:e.computed.x,py:e.computed.y})}}reparent(e,t){if(e.parent===t)return;const s=e.computed.x,n=e.computed.y;e.parent&&e.parent.children.delete(e),e.parent=t,t?(t.children.add(e),e.pos.x=s-t.computed.x,e.pos.y=n-t.computed.y):(e.pos.x=s,e.pos.y=n),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,s){console.log(`[Graph.setOutput] nodeId: ${e}, portId: ${t}, value:`,s);const n=`${e}:${t}`;this._nextBuf().set(n,s)}getInput(e,t){for(const s of this.edges.values())if(s.toNode===e&&s.toPort===t){const n=`${s.fromNode}:${s.fromPort}`,i=this._curBuf().get(n);return console.log(`[Graph.getInput] nodeId: ${e}, portId: ${t}, reading from ${s.fromNode}:${s.fromPort}, value:`,i),i}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,s,n;this.nodes.clear(),this.edges.clear();for(const i of e.nodes){const e=null==(s=null==(t=this.registry)?void 0:t.types)?void 0:s.get(i.type),n=e?this._calculateDefaultNodeHeight(e):60,r=void 0!==i.h?i.h:n,a=new o({id:i.id,type:i.type,title:i.title,x:i.x,y:i.y,width:i.w,height:r});(null==e?void 0:e.onCreate)&&e.onCreate(a),a.inputs=i.inputs,a.outputs=i.outputs,a.state={...a.state,...i.state||{}},this.nodes.set(a.id,a)}for(const i of e.nodes)if(i.parentId){const e=this.nodes.get(i.id),t=this.nodes.get(i.parentId);e&&t&&(e.parent=t,t.children.add(e))}for(const i of e.edges)this.edges.set(i.id,new r(i));return this.updateWorldTransforms(),null==(n=this.hooks)||n.emit("graph:deserialize",e),this}_calculateDefaultNodeHeight(e){var t,s;const n=(null==(t=e.inputs)?void 0:t.length)||0,i=(null==(s=e.outputs)?void 0:s.length)||0,o=Math.max(n,i);if(e.html){const e=o>0?50+20*(o-1):26;return Math.max(e+50,90)}let r=34+20*o+8;return Math.max(r,40)}}function d(e,t,s,n){const{x:i,y:o,w:r,h:a}=e.computed||{x:e.pos.x,y:e.pos.y,w:e.size.width,h:e.size.height},l=o+26+8+20*s+10;return"in"===n?{x:i-6,y:l-6,w:12,h:12}:"out"===n?{x:i+r-6,y:l-6,w:12,h:12}:void 0}const h=class e{constructor(e,{theme:t={},registry:s,edgeStyle:n="orthogonal"}={}){this.canvas=e,this.ctx=e.getContext("2d"),this.registry=s,this.scale=1,this.minScale=.25,this.maxScale=3,this.offsetX=0,this.offsetY=0,this.edgeStyle=n,this.theme=Object.assign({bg:"#0e0e16",grid:"#1c1c2c",node:"rgba(22, 22, 34, 0.9)",nodeBorder:"rgba(255, 255, 255, 0.08)",title:"rgba(28, 28, 42, 0.95)",text:"#f5f5f7",textMuted:"#8e8eaf",port:"#4f46e5",portExec:"#10b981",edge:"rgba(255, 255, 255, 0.12)",edgeActive:"#34c38f",accent:"#6366f1",accentBright:"#818cf8",accentGlow:"rgba(99, 102, 241, 0.25)"},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:s=this.offsetY}={}){var n;this.scale=Math.min(this.maxScale,Math.max(this.minScale,e)),this.offsetX=t,this.offsetY=s,null==(n=this._onTransformChange)||n.call(this)}setTransformChangeCallback(e){this._onTransformChange=e}panBy(e,t){var s;this.offsetX+=e,this.offsetY+=t,null==(s=this._onTransformChange)||s.call(this)}zoomAt(e,t,s){var n;const i=this.scale,o=Math.min(this.maxScale,Math.max(this.minScale,i*e));if(o===i)return;const r=(t-this.offsetX)/i,a=(s-this.offsetY)/i;this.offsetX=t-r*o,this.offsetY=s-a*o,this.scale=o,null==(n=this._onTransformChange)||n.call(this)}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(1,0,0,1,0,0),e.translate(this.offsetX,this.offsetY),e.scale(this.scale,this.scale)}_resetTransform(){this.ctx.setTransform(1,0,0,1,0,0)}_drawArrowhead(e,t,s,n,i=8){const{ctx:o}=this,r=i/this.scale,a=Math.atan2(n-t,s-e);o.beginPath(),o.moveTo(s,n),o.lineTo(s-r*Math.cos(a-Math.PI/6),n-r*Math.sin(a-Math.PI/6)),o.lineTo(s-r*Math.cos(a+Math.PI/6),n-r*Math.sin(a+Math.PI/6)),o.closePath(),o.fill()}_drawScreenText(e,t,s,{fontPx:n=11,color:i=this.theme.text,align:o="left",baseline:r="alphabetic"}={}){const{ctx:a}=this,{x:l,y:d}=this.worldToScreen(t,s);a.save(),this._resetTransform();const h=Math.round(l)+.5,c=Math.round(d)+.5;a.font=n*this.scale+'px "Inter", system-ui, sans-serif',a.fillStyle=i,a.textAlign=o,a.textBaseline=r,a.fillText(e,h,c),a.restore()}drawGrid(){const{ctx:e,canvas:t,theme:s,scale:n,offsetX:i,offsetY:o}=this;this._resetTransform(),e.fillStyle=s.bg,e.fillRect(0,0,t.width,t.height),this._applyTransform();const r=-i/n,a=-o/n,l=(t.width-i)/n,d=(t.height-o)/n,h=24,c=120,u=1/n,p=1.5/n,g=Math.floor(r/h)*h,f=Math.floor(a/h)*h;e.fillStyle=this._rgba(s.grid,.7);for(let v=g;v<=l;v+=h)for(let t=f;t<=d;t+=h){const s=Math.round(v/c)*c===Math.round(v),n=Math.round(t/c)*c===Math.round(t);s&&n||(e.beginPath(),e.arc(v,t,u,0,2*Math.PI),e.fill())}const m=Math.floor(r/c)*c,y=Math.floor(a/c)*c;e.fillStyle=this._rgba(s.grid,1);for(let v=m;v<=l;v+=c)for(let t=y;t<=d;t+=c)e.beginPath(),e.arc(v,t,p,0,2*Math.PI),e.fill();this._resetTransform()}draw(e,{selection:t=new Set,tempEdge:s=null,time:n=performance.now(),activeNodes:i=new Set,activeEdges:o=new Set,activeEdgeTimes:r=new Map,drawEdges:a=!0,loopActiveEdges:l=!1}={}){var d,h,c,u;e.updateWorldTransforms(),this.drawGrid();const{ctx:p,theme:g}=this;this._applyTransform(),p.save();for(const f of e.nodes.values())if("core/Group"===f.type){const e=t.has(f.id),s=null==(h=null==(d=this.registry)?void 0:d.types)?void 0:h.get(f.type);(null==s?void 0:s.onDraw)?s.onDraw(f,{ctx:p,theme:g,renderer:this}):this._drawNode(f,e)}if(a){p.lineWidth=2.5/this.scale;for(const t of e.edges.values()){if(o&&o.has(t.id)){p.save(),p.shadowColor=this.theme.edgeActive,p.shadowBlur=8/this.scale,p.strokeStyle=this.theme.edgeActive,p.lineWidth=3/this.scale,p.setLineDash([]),this._drawEdge(e,t),p.restore(),null==r||r.get(t.id);const s=this.theme.flowSpeed||150;edgeLen;const i=l?n/1e3*(s/edgeLen)%1:n/1e3*1.2%1,o=this._getEdgeDotPosition(e,t,i);o&&(p.save(),p.fillStyle="#ffffff",p.shadowColor=this.theme.edgeActive,p.shadowBlur=10/this.scale,p.beginPath(),p.arc(o.x,o.y,3/this.scale,0,2*Math.PI),p.fill(),p.restore())}else p.setLineDash([]),p.strokeStyle=g.edge,p.lineWidth=2.5/this.scale,this._drawEdge(e,t)}}if(s){const e=this.screenToWorld(s.x1,s.y1),t=this.screenToWorld(s.x2,s.y2),n=this.ctx.getLineDash();this.ctx.setLineDash([5/this.scale,5/this.scale]),this.ctx.strokeStyle=this._rgba(this.theme.accentBright,.7),this.ctx.lineWidth=2.5/this.scale;let i=null;if("line"===this.edgeStyle?(this._drawLine(e.x,e.y,t.x,t.y),i=[{x:e.x,y:e.y},{x:t.x,y:t.y}]):"orthogonal"===this.edgeStyle?i=this._drawOrthogonal(e.x,e.y,t.x,t.y):(this._drawCurve(e.x,e.y,t.x,t.y),i=[{x:e.x,y:e.y},{x:t.x,y:t.y}]),this.ctx.setLineDash(n),i&&i.length>=2){const e=i[i.length-2],t=i[i.length-1];this.ctx.fillStyle=this.theme.accentBright,this._drawArrowhead(e.x,e.y,t.x,t.y,10)}}for(const f of e.nodes.values())if("core/Group"!==f.type){const e=t.has(f.id),s=null==(u=null==(c=this.registry)?void 0:c.types)?void 0:u.get(f.type),n=!!(null==s?void 0:s.html);this._drawNode(f,e,!n),(null==s?void 0:s.onDraw)&&s.onDraw(f,{ctx:p,theme:g,renderer:this}),n&&this._drawPorts(f)}if(i.size>0)for(const f of i){const t=e.nodes.get(f);t&&this._drawActiveNodeBorder(t,n)}this._resetTransform()}_rgba(e,t){const s=e.replace("#",""),n=parseInt(3===s.length?s.split("").map(e=>e+e).join(""):s,16);return`rgba(${n>>16&255},${n>>8&255},${255&n},${t})`}_drawNode(t,s,n=!1){var i,o;const{ctx:r,theme:a}=this,{x:l,y:h,w:c,h:p}=t.computed,g=null==(o=null==(i=this.registry)?void 0:i.types)?void 0:o.get(t.type),f=t.color||(null==g?void 0:g.color)||a.accent;if(s){r.save(),r.shadowColor="rgba(255,255,255,0.3)",r.shadowBlur=8/this.scale,r.strokeStyle="#ffffff",r.lineWidth=1.5/this.scale;const e=8/this.scale;u(r,l-e,h-e,c+2*e,p+2*e,2+e),r.stroke(),r.restore()}r.save(),r.shadowColor="rgba(0,0,0,0.7)",r.shadowBlur=20/this.scale,r.shadowOffsetY=6/this.scale,r.fillStyle=a.node,u(r,l,h,c,p,2),r.fill(),r.restore(),r.fillStyle=a.node,r.strokeStyle=s?"rgba(255,255,255,0.4)":a.nodeBorder,r.lineWidth=1/this.scale,u(r,l,h,c,p,2),r.fill(),r.stroke(),r.fillStyle=a.title,u(r,l,h,c,26,{tl:2,tr:2,br:0,bl:0}),r.fill(),r.save(),r.globalCompositeOperation="source-atop",r.fillStyle=f,r.globalAlpha=.25,r.fillRect(l,h,c,26),r.restore(),r.strokeStyle=s?"rgba(255,255,255,0.2)":this._rgba(a.nodeBorder,.6),r.lineWidth=1/this.scale,r.beginPath(),r.moveTo(l,h+26),r.lineTo(l+c,h+26),r.stroke(),r.fillStyle=f,r.beginPath(),r.roundRect(l,h,c,2.5/this.scale,{tl:2,tr:2,br:0,bl:0}),r.fill(),this._drawScreenText(t.title,l+10,h+13,{fontPx:e.FONT_SIZE,color:a.text,baseline:"middle",align:"left"}),n||(t.inputs.forEach((e,s)=>{const n=d(t,0,s,"in"),i=n.x+n.w/2,o=n.y+n.h/2;this._drawPortShape(i,o,e.portType),e.name&&this._drawScreenText(e.name,i+10,o,{fontPx:10,color:a.textMuted,baseline:"middle",align:"left"})}),t.outputs.forEach((e,s)=>{const n=d(t,0,s,"out"),i=n.x+n.w/2,o=n.y+n.h/2;this._drawPortShape(i,o,e.portType),e.name&&this._drawScreenText(e.name,i-10,o,{fontPx:10,color:a.textMuted,baseline:"middle",align:"right"})}))}_drawActiveNodeBorder(e,t){const{ctx:s,theme:n}=this,{x:i,y:o,w:r,h:a}=e.computed,l=e.radius||12;s.save(),s.shadowColor=n.accentBright||"#7080d8",s.shadowBlur=(12+4*Math.sin(t/200))/this.scale,s.strokeStyle=n.accentBright||"#7080d8",s.lineWidth=3/this.scale,s.setLineDash([6/this.scale,6/this.scale]),s.lineDashOffset=-t/1e3*30/this.scale,s.beginPath();const d=2/this.scale;this._roundRect(s,i-d,o-d,r+2*d,a+2*d,l+d),s.stroke(),s.restore()}_roundRect(e,t,s,n,i,o){"number"==typeof o&&(o={tl:o,tr:o,br:o,bl:o}),e.beginPath(),e.moveTo(t+o.tl,s),e.lineTo(t+n-o.tr,s),e.quadraticCurveTo(t+n,s,t+n,s+o.tr),e.lineTo(t+n,s+i-o.br),e.quadraticCurveTo(t+n,s+i,t+n-o.br,s+i),e.lineTo(t+o.bl,s+i),e.quadraticCurveTo(t,s+i,t,s+i-o.bl),e.lineTo(t,s+o.tl),e.quadraticCurveTo(t,s,t+o.tl,s),e.closePath()}_drawPortShape(e,t,s){const{ctx:n,theme:i}=this;if("exec"===s){const s=5/this.scale;n.save(),n.fillStyle=i.portExec,n.strokeStyle=this._rgba(i.portExec,.4),n.lineWidth=2/this.scale,n.beginPath(),n.moveTo(e,t-s),n.lineTo(e+s,t),n.lineTo(e,t+s),n.lineTo(e-s,t),n.closePath(),n.fill(),n.stroke(),n.restore()}else n.save(),n.strokeStyle=this._rgba(i.port,.35),n.lineWidth=3/this.scale,n.beginPath(),n.arc(e,t,5/this.scale,0,2*Math.PI),n.stroke(),n.fillStyle=i.port,n.beginPath(),n.arc(e,t,3.5/this.scale,0,2*Math.PI),n.fill(),n.restore()}_drawPorts(e){e.inputs.forEach((t,s)=>{const n=d(e,0,s,"in"),i=n.x+n.w/2,o=n.y+n.h/2;this._drawPortShape(i,o,t.portType)}),e.outputs.forEach((t,s)=>{const n=d(e,0,s,"out"),i=n.x+n.w/2,o=n.y+n.h/2;this._drawPortShape(i,o,t.portType)})}_drawHtmlSelectionBorder(e){const{ctx:t}=this,{x:s,y:n,w:i,h:o}=e.computed,r=2.5/this.scale;t.save(),t.shadowColor="rgba(255,255,255,0.3)",t.shadowBlur=8/this.scale,t.strokeStyle="#ffffff",t.lineWidth=1.5/this.scale,u(t,s-r,n-r,i+2*r,o+2*r,2),t.stroke(),t.restore()}_drawActiveNodeBorder(e,t){const{ctx:s}=this,{x:n,y:i,w:o,h:r}=e.computed,a=8/this.scale,l=8/this.scale,d=6/this.scale,h=-t/1e3*(50/this.scale);s.save(),s.setLineDash([l,d]),s.lineDashOffset=h,s.strokeStyle="rgba(74,176,217,0.9)",s.lineWidth=2/this.scale,s.shadowColor="#4ab0d9",s.shadowBlur=6/this.scale,u(s,n-a,i-a,o+2*a,r+2*a,2+a),s.stroke(),s.restore()}_getEdgeLength(e,t){const s=e.nodes.get(t.fromNode),n=e.nodes.get(t.toNode);if(!s||!n)return 200;const i=s.outputs.findIndex(e=>e.id===t.fromPort),o=n.inputs.findIndex(e=>e.id===t.toPort),r=d(s,0,i,"out"),a=d(n,0,o,"in"),l=r.x+r.w/2,h=r.y+r.h/2,c=a.x+a.w/2,u=a.y+a.h/2;if("orthogonal"===this.edgeStyle)return Math.abs(c-l)+Math.abs(u-h);const p=Math.sqrt((c-l)**2+(u-h)**2);return"bezier"===this.edgeStyle?1.4*p:p}_drawEdge(e,t){const s=e.nodes.get(t.fromNode),n=e.nodes.get(t.toNode);if(!s||!n)return;const i=s.outputs.findIndex(e=>e.id===t.fromPort),o=n.inputs.findIndex(e=>e.id===t.toPort),r=d(s,0,i,"out"),a=d(n,0,o,"in"),l=r.x+r.w/2,h=r.y+r.h/2,c=a.x+a.w/2,u=a.y+a.h/2;"line"===this.edgeStyle?this._drawLine(l,h,c,u):"orthogonal"===this.edgeStyle?this._drawOrthogonal(l,h,c,u):this._drawCurve(l,h,c,u)}_getEdgeDotPosition(e,t,s){const n=e.nodes.get(t.fromNode),i=e.nodes.get(t.toNode);if(!n||!i)return null;const o=n.outputs.findIndex(e=>e.id===t.fromPort),r=i.inputs.findIndex(e=>e.id===t.toPort),a=d(n,0,o,"out"),l=d(i,0,r,"in"),h=a.x+a.w/2,c=a.y+a.h/2,u=l.x+l.w/2,p=l.y+l.h/2;if("bezier"===this.edgeStyle){const e=Math.max(40,.4*Math.abs(u-h));return function(e,t,s,n,i,o,r,a,l){const d=1-l;return{x:d*d*d*e+3*d*d*l*s+3*d*l*l*i+l*l*l*r,y:d*d*d*t+3*d*d*l*n+3*d*l*l*o+l*l*l*a}}(h,c,h+e,c,u-e,p,u,p,s)}if("orthogonal"===this.edgeStyle){const e=(h+u)/2;return function(e,t){let s=0;const n=[];for(let r=0;r<e.length-1;r++){const t=e[r+1].x-e[r].x,i=e[r+1].y-e[r].y,o=Math.sqrt(t*t+i*i);n.push(o),s+=o}if(0===s)return e[0];let i=t*s,o=0;for(let r=0;r<n.length;r++){if(o+n[r]>=i){const t=n[r]>0?(i-o)/n[r]:0;return{x:e[r].x+(e[r+1].x-e[r].x)*t,y:e[r].y+(e[r+1].y-e[r].y)*t}}o+=n[r]}return e[e.length-1]}([{x:h,y:c},{x:e,y:c},{x:e,y:p},{x:u,y:p}],s)}return{x:h+(u-h)*s,y:c+(p-c)*s}}_drawLine(e,t,s,n){const{ctx:i}=this;i.beginPath(),i.moveTo(e,t),i.lineTo(s,n),i.stroke()}_drawPolyline(e){const{ctx:t}=this;t.beginPath(),t.moveTo(e[0].x,e[0].y);for(let s=1;s<e.length;s++)t.lineTo(e[s].x,e[s].y);t.stroke()}_drawOrthogonal(e,t,s,n){const i=(e+s)/2,o=[{x:e,y:t},{x:i,y:t},{x:i,y:n},{x:s,y:n}],{ctx:r}=this,a=r.lineJoin,l=r.lineCap;return r.lineJoin="round",r.lineCap="round",this._drawPolyline(o),r.lineJoin=a,r.lineCap=l,o}_drawCurve(e,t,s,n){const{ctx:i}=this,o=Math.max(40,.4*Math.abs(s-e));i.beginPath(),i.moveTo(e,t),i.bezierCurveTo(e+o,t,s-o,n,s,n),i.stroke()}drawEdgesOnly(e,{activeEdges:t=new Set,activeEdgeTimes:s=new Map,activeNodes:n=new Set,selection:i=new Set,time:o=performance.now(),tempEdge:r=null,loopActiveEdges:a=!1}={}){var l,d;this._resetTransform(),this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this._applyTransform();const{ctx:h,theme:c}=this;for(const u of e.edges.values()){if(t.has(u.id)){h.save(),h.shadowColor=c.edgeActive,h.shadowBlur=6/this.scale,h.strokeStyle=c.edgeActive,h.lineWidth=3/this.scale,h.setLineDash([]),this._drawEdge(e,u),h.restore();const t=this.theme.flowSpeed||150,n=s.get(u.id)??o,i=Math.max(50,this._getEdgeLength(e,u)),r=(o-n)/(i/t*1e3),l=a?o/1e3*(t/i)%1:Math.min(1,r),d=this._getEdgeDotPosition(e,u,l);d&&(h.save(),h.fillStyle=this._rgba(c.edgeActive,.9),h.shadowColor=c.edgeActive,h.shadowBlur=8/this.scale,h.beginPath(),h.arc(d.x,d.y,2.5/this.scale,0,2*Math.PI),h.fill(),h.restore())}else h.setLineDash([]),h.strokeStyle=c.edge,h.lineWidth=2.5/this.scale,this._drawEdge(e,u)}for(const u of i){const t=e.nodes.get(u);if(!t)continue;const s=null==(d=null==(l=this.registry)?void 0:l.types)?void 0:d.get(t.type);(null==s?void 0:s.html)&&this._drawHtmlSelectionBorder(t)}if(n.size>0)for(const u of n){const t=e.nodes.get(u);t&&this._drawActiveNodeBorder(t,o)}if(r){const e=this.screenToWorld(r.x1,r.y1),t=this.screenToWorld(r.x2,r.y2),s=this.ctx.getLineDash();this.ctx.setLineDash([5/this.scale,5/this.scale]),this.ctx.strokeStyle=this._rgba(this.theme.accentBright,.7),this.ctx.lineWidth=2.5/this.scale;let n=null;if("line"===this.edgeStyle?(this._drawLine(e.x,e.y,t.x,t.y),n=[{x:e.x,y:e.y},{x:t.x,y:t.y}]):"orthogonal"===this.edgeStyle?n=this._drawOrthogonal(e.x,e.y,t.x,t.y):(this._drawCurve(e.x,e.y,t.x,t.y),n=[{x:e.x,y:e.y},{x:t.x,y:t.y}]),this.ctx.setLineDash(s),n&&n.length>=2){const e=n[n.length-2],t=n[n.length-1];this.ctx.fillStyle=this.theme.accentBright,this._drawArrowhead(e.x,e.y,t.x,t.y,10)}}this._resetTransform()}};s(h,"FONT_SIZE",11),s(h,"SELECTED_NODE_COLOR","#6cf");let c=h;function u(e,t,s,n,i,o=6){"number"==typeof o&&(o={tl:o,tr:o,br:o,bl:o}),e.beginPath(),e.moveTo(t+o.tl,s),e.lineTo(t+n-o.tr,s),e.quadraticCurveTo(t+n,s,t+n,s+o.tr),e.lineTo(t+n,s+i-o.br),e.quadraticCurveTo(t+n,s+i,t+n-o.br,s+i),e.lineTo(t+o.bl,s+i),e.quadraticCurveTo(t,s+i,t,s+i-o.bl),e.lineTo(t,s+o.tl),e.quadraticCurveTo(t,s,t+o.tl,s),e.closePath()}function p(e,t,s,n,i){for(const[o,r]of e.edges)if(r.fromNode===t&&r.fromPort===s&&r.toNode===n&&r.toPort===i)return o;return null}function g(e,t){let s=null,n=[];return{do(){s=t,n=e.edges?[...e.edges.values()].filter(e=>e.fromNode===t.id||e.toNode===t.id):[];for(const t of n)e.edges.delete(t.id);e.nodes.delete(t.id)},undo(){s&&e.nodes.set(s.id,s);for(const t of n)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:s,htmlOverlay:n,contextMenu:i,edgeRenderer:o,portRenderer:r}){this.graph=e,this.renderer=t,this.hooks=s,this.htmlOverlay=n,this.contextMenu=i,this.edgeRenderer=o,this.portRenderer=r,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.activeEdges=new Set,this.activeEdgeTimes=new Map,this.activeNodes=new Set,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(),this.hooks.on("runner:step-updated",({activeNodeId:e,activeEdgeIds:t=[]})=>{this.activeNodes=e?new Set([e]):new Set,this.activeEdges=new Set(t),this.activeEdgeTimes.clear();const s=performance.now();for(const n of t)this.activeEdgeTimes.set(n,s);this.render()})}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 s=[...this.graph.nodes.values()].reverse();for(const n of s){const{x:s,y:i,w:o,h:r}=n.computed;if(e>=s&&e<=s+o&&t>=i&&t<=i+r){if("core/Group"===n.type){const s=this._findChildNodeAtWorld(n,e,t);if(s)return s}return n}}return null}_findChildNodeAtWorld(e,t,s){const n=[];for(const i of this.graph.nodes.values())i.parent===e&&n.push(i);for(let i=n.length-1;i>=0;i--){const e=n[i],{x:o,y:r,w:a,h:l}=e.computed;if(t>=o&&t<=o+a&&s>=r&&s<=r+l){if("core/Group"===e.type){const n=this._findChildNodeAtWorld(e,t,s);if(n)return n}return e}}return null}_findPortAtWorld(e,t){for(const s of this.graph.nodes.values()){for(let n=0;n<s.inputs.length;n++){if(v(d(s,s.inputs[n],n,"in"),e,t))return{node:s,port:s.inputs[n],dir:"in",idx:n}}for(let n=0;n<s.outputs.length;n++){if(v(d(s,s.outputs[n],n,"out"),e,t))return{node:s,port:s.outputs[n],dir:"out",idx:n}}}return null}_findIncomingEdge(e,t){for(const[s,n]of this.graph.edges)if(n.toNode===e&&n.toPort===t)return{id:s,edge:n};return null}_onWheel(e){e.preventDefault();const{x:t,y:s}=this._posScreen(e),n=Math.pow(1.0015,-e.deltaY);this.renderer.zoomAt(n,t,s),this.render()}_onContextMenu(e){if(e.preventDefault(),!this.contextMenu)return;const t=this._posWorld(e),s=this._findNodeAtWorld(t.x,t.y);this.contextMenu.show(s,e.clientX,e.clientY,t)}_onDblClick(e){var t;const s=this._posWorld(e),n=this._findNodeAtWorld(s.x,s.y);n&&(null==(t=this.hooks)||t.emit("node:dblclick",n))}_resizeHandleRect(e){const{x:t,y:s,w:n,h:i}=e.computed;return{x:t+n-10,y:s+i-10,w:10,h:10}}_hitResizeHandle(e,t,s){const n=this._resizeHandleRect(e);return t>=n.x&&t<=n.x+n.w&&s>=n.y&&s<=n.y+n.h}_onDown(e){const t=this._posScreen(e),s=this._posWorld(e);if(1===e.button)return void(this.panning={x:t.x,y:t.y});const n=this._findNodeAtWorld(s.x,s.y);if(0===e.button&&n&&this._hitResizeHandle(n,s.x,s.y))return this.resizing={nodeId:n.id,startW:n.size.width,startH:n.size.height,startX:s.x,startY:s.y},e.shiftKey||this.selection.clear(),this.selection.add(n.id),this._setCursor("se-resize"),void this.render();const i=this._findPortAtWorld(s.x,s.y);if(0===e.button&&i&&"in"===i.dir){const e=this._findIncomingEdge(i.node.id,i.port.id);if(e)return this.stack.exec(function(e,t){const s=e.edges.get(t);if(!s)return null;const{fromNode:n,fromPort:i,toNode:o,toPort:r}=s;return{do(){e.edges.delete(t)},undo(){e.addEdge(n,i,o,r)}}}(this.graph,e.id)),void this.render()}if(0===e.button&&i&&"out"===i.dir){const e=d(i.node,i.port,i.idx,"out"),t=this.renderer.worldToScreen(e.x,e.y+7);return void(this.connecting={fromNode:i.node.id,fromPort:i.port.id,x:t.x,y:t.y})}if(0!==e.button||!n)return 0===e.button?(this.selection.size&&this.selection.clear(),e.ctrlKey||e.metaKey?this.boxSelecting={startX:s.x,startY:s.y,currentX:s.x,currentY:s.y}:this.panning={x:t.x,y:t.y},void this.render()):void 0;e.shiftKey||this.selection.clear(),this.selection.add(n.id),this.dragging={nodeId:n.id,offsetX:s.x-n.computed.x,offsetY:s.y-n.computed.y,startPos:{...n.pos},selectedNodes:[]};for(const o of this.selection){const e=this.graph.nodes.get(o);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"===n.type){this.dragging.childrenWorldPos=[];for(const e of this.graph.nodes.values())e.parent===n&&this.dragging.childrenWorldPos.push({node:e,worldX:e.computed.x,worldY:e.computed.y})}this.render()}_onMove(t){var s,n;this.isAlt=t.altKey,this.isShift=t.shiftKey,this.isCtrl=t.ctrlKey;const i=this._posScreen(t),o=this.renderer.screenToWorld(i.x,i.y);if(this.resizing){const t=this.graph.nodes.get(this.resizing.nodeId),n=o.x-this.resizing.startX,i=o.y-this.resizing.startY,r=e.MIN_NODE_WIDTH,a=Math.max(t.inputs.length,t.outputs.length),l=a>0?Math.max(e.MIN_NODE_HEIGHT,42+20*a):e.MIN_NODE_HEIGHT;return t.size.width=Math.max(r,this.resizing.startW+n),t.size.height=Math.max(l,this.resizing.startH+i),null==(s=this.hooks)||s.emit("node:resize",t),this._setCursor("se-resize"),void this.render()}if(this.panning){const e=i.x-this.panning.x,t=i.y-this.panning.y;return this.panning={x:i.x,y:i.y},this.renderer.panBy(e,t),void this.render()}if(this.dragging){const e=this.graph.nodes.get(this.dragging.nodeId);let t=o.x-this.dragging.offsetX,s=this.isShift?o.y-0:o.y-this.dragging.offsetY;this.snapToGrid&&(t=this._snapToGrid(t),s=this._snapToGrid(s));const i=t-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldX,r=s-this.dragging.selectedNodes.find(t=>t.node.id===e.id).startWorldY;this.graph.updateWorldTransforms();for(const{node:n,startWorldX:o,startWorldY:a}of this.dragging.selectedNodes){if(this.isShift&&"core/Group"===n.type)continue;const e=o+i,t=a+r;let s=0,l=0;n.parent&&(s=n.parent.computed.x,l=n.parent.computed.y),n.pos.x=e-s,n.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 s=t.node,n=e.computed.x,i=e.computed.y;s.pos.x=t.worldX-n,s.pos.y=t.worldY-i}}return null==(n=this.hooks)||n.emit("node:move",e),void this.render()}if(this.boxSelecting)return this.boxSelecting.currentX=o.x,this.boxSelecting.currentY=o.y,void this.render();this.connecting&&(this.connecting.x=i.x,this.connecting.y=i.y,this.render());const r=this._findPortAtWorld(o.x,o.y),a=this._findNodeAtWorld(o.x,o.y);a&&this._hitResizeHandle(a,o.x,o.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,s=this._findPortAtWorld(t.x,t.y);s&&"in"===s.dir&&this.stack.exec(function(e,t,s,n,i){let o=null;return{do(){e.addEdge(t,s,n,i),o=p(e,t,s,n,i)},undo(){const r=o??p(e,t,s,n,i);null!=r&&e.edges.delete(r)}}}(this.graph,e.fromNode,e.fromPort,s.node.id,s.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},o={w:e.size.width,h:e.size.height};t.w===o.w&&t.h===o.h||this.stack.exec((s=e,n=t,i=o,{do(){s.size.width=i.w,s.size.height=i.h},undo(){s.size.width=n.w,s.size.height=n.h}})),this.resizing=null,this._setCursor("default")}var s,n,i;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 s=t.node;this.graph.updateWorldTransforms();const n=e.computed.x,i=e.computed.y;s.pos.x=t.worldX-n,s.pos.y=t.worldY-i}else if("core/Group"!==e.type||this.isAlt){if("core/Group"!==e.type){const s=this._findPotentialParent(t.x,t.y,e);s&&s!==e.parent?this.graph.reparent(e,s):!s&&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:s,currentY:n}=this.boxSelecting,i=Math.min(e,s),o=Math.max(e,s),r=Math.min(t,n),a=Math.max(t,n);for(const l of this.graph.nodes.values()){const{x:e,y:t,w:s,h:n}=l.computed;e+s>=i&&e<=o&&t+n>=r&&t<=a&&this.selection.add(l.id)}this.boxSelecting=null,this.render()}}}_autoParentNodesInGroup(e){const{x:t,y:s,w:n,h:i}=e.computed;for(const o of this.graph.nodes.values()){if(o===e)continue;if(o.parent===e)continue;if("core/Group"===o.type)continue;const{x:r,y:a,w:l,h:d}=o.computed,h=r+l/2,c=a+d/2;h>=t&&h<=t+n&&c>=s&&c<=s+i&&this.graph.reparent(o,e)}}_findPotentialParent(e,t,s){const n=[...this.graph.nodes.values()].reverse();for(const i of n){if("core/Group"!==i.type)continue;if(i===s)continue;let n=i.parent,o=!1;for(;n;){if(n===s){o=!0;break}n=n.parent}if(o)continue;const{x:r,y:a,w:l,h:d}=i.computed;if(e>=r&&e<=r+l&&t>=a&&t<=a+d)return i}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,s=1/0,n=-1/0,i=-1/0;for(const d of e){const{x:e,y:o,w:r,h:a}=d.computed;t=Math.min(t,e),s=Math.min(s,o),n=Math.max(n,e+r),i=Math.max(i,o+a)}const o=t-20,r=s-20,a=n-t+40,l=i-s+40;this.graph.groupManager&&(this.graph.groupManager.addGroup({title:"Group",x:o,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 s of e){const e=s.parent?s.parent.computed.y:0;s.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 s of e){const e=s.parent?s.parent.computed.x:0;s.pos.x=t-e}this.graph.updateWorldTransforms(),this.render()}render(e=performance.now()){var t;const s=this.renderTempEdge(),n=this.graph.runner,i=!!n&&"step"===n.executionMode;if(this.renderer.draw(this.graph,{selection:this.selection,tempEdge:null,boxSelecting:this.boxSelecting,activeEdges:this.activeEdges||new Set,activeEdgeTimes:this.activeEdgeTimes,drawEdges:!this.edgeRenderer,time:e,loopActiveEdges:i}),null==(t=this.htmlOverlay)||t.draw(this.graph,this.selection),this.edgeRenderer){this.edgeRenderer.ctx.clearRect(0,0,this.edgeRenderer.canvas.width,this.edgeRenderer.canvas.height),this.edgeRenderer._applyTransform(),this.edgeRenderer.drawEdgesOnly(this.graph,{activeEdges:this.activeEdges,activeEdgeTimes:this.activeEdgeTimes,activeNodes:this.activeNodes,selection:this.selection,time:e,tempEdge:s,loopActiveEdges:i}),this.edgeRenderer._resetTransform()}if(this.boxSelecting){const{startX:e,startY:t,currentX:s,currentY:n}=this.boxSelecting,i=Math.min(e,s),o=Math.min(t,n),r=Math.abs(s-e),a=Math.abs(n-t),l=this.renderer.worldToScreen(i,o),d=this.renderer.worldToScreen(i+r,o+a),h=this.edgeRenderer?this.edgeRenderer.ctx:this.renderer.ctx;h.save(),this.edgeRenderer?this.edgeRenderer._resetTransform():this.renderer._resetTransform(),h.strokeStyle="#6cf",h.fillStyle="rgba(102, 204, 255, 0.1)",h.lineWidth=2,h.strokeRect(l.x,l.y,d.x-l.x,d.y-l.y),h.fillRect(l.x,l.y,d.x-l.x,d.y-l.y),h.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())"core/Group"!==e.type&&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 s=this.graph.nodes.get(e),n=s.outputs.findIndex(e=>e.id===t),i=d(s,0,n,"out");return this.renderer.worldToScreen(i.x+i.w/2,i.y+i.h/2)}};s(m,"MIN_NODE_WIDTH",80),s(m,"MIN_NODE_HEIGHT",60);let y=m;function v(e,t,s){return t>=e.x&&t<=e.x+e.w&&s>=e.y&&s<=e.y+e.h}class x{constructor({graph:e,hooks:t,renderer:s,commandStack:n}){this.graph=e,this.hooks=t,this.renderer=s,this.commandStack=n,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,s={}){const{action:n,submenu:i,condition:o,order:r=100}=s;n||i?(this.removeItem(e),this.items.push({id:e,label:t,action:n,submenu:i,condition:o,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,s,n=null){this.target=e,this.position={x:t,y:s},this.worldPosition=n,this.visible=!0,this._renderItems(),this.menuElement.style.left=`${t}px`,this.menuElement.style.top=`${s}px`,this.menuElement.style.display="block",requestAnimationFrame(()=>{const e=this.menuElement.getBoundingClientRect(),n=window.innerWidth,i=window.innerHeight;let o=t,r=s;e.right>n&&(o=n-e.width-5),e.bottom>i&&(r=i-e.height-5),this.menuElement.style.left=`${o}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 s=document.createElement("div");Object.assign(s.style,{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"});const n=document.createElement("span");if(n.textContent=e.label,s.appendChild(n),e.submenu){const e=document.createElement("span");e.textContent="▶",e.style.marginLeft="12px",e.style.fontSize="10px",e.style.opacity="0.7",s.appendChild(e)}t.appendChild(s),Object.assign(t.style,{padding:"4px 8px",cursor:"pointer",transition:"background-color 0.15s ease",userSelect:"none",position:"relative"}),t.addEventListener("mouseenter",()=>{if(t.style.backgroundColor="#3a3a3e",t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null),e.submenu){const s="function"==typeof e.submenu?e.submenu():e.submenu;this._showSubmenu(s,t)}}),t.addEventListener("mouseleave",s=>{if(t.style.backgroundColor="transparent",e.submenu){const e=t._submenuElement;e&&(t._hideTimeout=setTimeout(()=>{e.contains(document.elementFromPoint(s.clientX,s.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 s=document.createElement("div");s.className="context-submenu",Object.assign(s.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 n=document.createElement("div");if(Object.assign(n.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"}),n.appendChild(t)}const i=document.createElement("span");i.textContent=e.label,n.appendChild(i),t.appendChild(n),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()}),s.appendChild(t)}),s.addEventListener("mouseenter",()=>{t._hideTimeout&&(clearTimeout(t._hideTimeout),t._hideTimeout=null)}),s.addEventListener("mouseleave",e=>{t.contains(e.relatedTarget)||this._hideSubmenu(t)}),document.body.appendChild(s),t._submenuElement=s,requestAnimationFrame(()=>{const e=t.getBoundingClientRect(),n=s.getBoundingClientRect();let i=e.right+2,o=e.top;i+n.width>window.innerWidth&&(i=e.left-n.width-2),o+n.height>window.innerHeight&&(o=window.innerHeight-n.height-5),s.style.left=`${i}px`,s.style.top=`${o}px`})}_hideSubmenu(e){e._submenuElement&&(e._submenuElement.remove(),e._submenuElement=null)}}class b{constructor({graph:e,registry:t,hooks:s,cyclesPerFrame:n=1}){this.graph=e,this.registry=t,this.hooks=s,this.running=!1,this._raf=null,this._last=0,this.cyclesPerFrame=Math.max(1,0|n),this.executionMode="run",this.activePlan=null,this.activeStepIndex=-1,this.stepCache=new Map}isRunning(){return this.running}setCyclesPerFrame(e){this.cyclesPerFrame=Math.max(1,0|e)}step(e=1,t=0){var s,n;const i=Math.max(1,0|e);for(let r=0;r<i;r++){for(const e of this.graph.nodes.values()){const i=this.registry.types.get(e.type);if(null==i?void 0:i.onExecute)try{i.onExecute(e,{dt:t,graph:this.graph,getInput:t=>{const s=e.inputs.find(e=>e.name===t)||e.inputs[0];return s?this.graph.getInput(e.id,s.id):void 0},setOutput:(t,s)=>{const n=e.outputs.find(e=>e.name===t)||e.outputs[0];n&&this.graph.setOutput(e.id,n.id,s)}})}catch(o){null==(n=null==(s=this.hooks)?void 0:s.emit)||n.call(s,"error",o)}}this.graph.swapBuffers()}}runOnce(e,t=0){const s=new Set,n=[],i=new Map,o=[{nodeId:e,fromEdgeId:null}],r=new Set;for(;o.length>0;){const{nodeId:e,fromEdgeId:a}=o.shift();if(r.has(e))continue;r.add(e),a&&n.push(a);const l=this.graph.nodes.get(e);if(!l)continue;s.add(e);for(const n of l.inputs)if("data"===n.portType)for(const o of this.graph.edges.values())if(o.toNode===e&&o.toPort===n.id){this.graph.nodes.get(o.fromNode)&&!s.has(o.fromNode)&&(s.add(o.fromNode),this._executeNodeWithCache(o.fromNode,t,i))}this._executeNodeWithCache(e,t,i);const d=l.outputs.filter(e=>"exec"===e.portType);for(const t of d)for(const s of this.graph.edges.values())s.fromNode===e&&s.fromPort===t.id&&o.push({nodeId:s.toNode,fromEdgeId:s.id})}const a=new Set;for(const l of this.graph.edges.values())s.has(l.fromNode)&&s.has(l.toNode)&&a.add(l.id);return{connectedNodes:s,connectedEdges:a,execEdgeOrder:n}}setExecutionMode(e){this.executionMode=e,"run"===e&&this.resetStepping()}resetStepping(){var e,t;this.activePlan=null,this.activeStepIndex=-1,this.stepCache.clear(),null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:step-updated",{activeNodeId:null})}buildPlan(e){const t=[],s=new Set,n=[{nodeId:e,fromEdgeId:null}],i=new Set;for(;n.length>0;){const{nodeId:e,fromEdgeId:o}=n.shift();if(i.has(e))continue;i.add(e),s.add(e);const r=this.graph.nodes.get(e);if(!r)continue;const a=[];for(const t of r.inputs)if("data"===t.portType)for(const n of this.graph.edges.values())if(n.toNode===e&&n.toPort===t.id){const e=n.fromNode;s.has(e)||(s.add(e),a.push(e))}const l=[];for(const t of this.graph.edges.values())t.toNode===e&&l.push(t.id);t.push({nodeId:e,fromEdgeId:o,incomingEdges:l,dataDeps:a});const d=r.outputs.filter(e=>"exec"===e.portType);for(const t of d)for(const s of this.graph.edges.values())s.fromNode===e&&s.fromPort===t.id&&n.push({nodeId:s.toNode,fromEdgeId:s.id})}return t}startStepping(e){var t,s;this.stepCache.clear(),this.activePlan=this.buildPlan(e),this.activeStepIndex=0;const n=this.activePlan[0];null==(s=null==(t=this.hooks)?void 0:t.emit)||s.call(t,"runner:step-updated",{activeNodeId:null==n?void 0:n.nodeId,activeEdgeIds:(null==n?void 0:n.incomingEdges)||[]}),this.start()}executeNextStep(){var e,t,s,n;if(!this.activePlan||this.activeStepIndex<0||this.activeStepIndex>=this.activePlan.length)return this.resetStepping(),null;const i=this.activePlan[this.activeStepIndex];for(const o of i.dataDeps)this._executeNodeWithCache(o,0,this.stepCache);if(this._executeNodeWithCache(i.nodeId,0,this.stepCache),this.activeStepIndex++,this.activeStepIndex<this.activePlan.length){const s=this.activePlan[this.activeStepIndex];null==(t=null==(e=this.hooks)?void 0:e.emit)||t.call(e,"runner:step-updated",{activeNodeId:s.nodeId,activeEdgeIds:s.incomingEdges||[]})}else null==(n=null==(s=this.hooks)?void 0:s.emit)||n.call(s,"runner:step-updated",{activeNodeId:null}),this.resetStepping();return i.nodeId}_executeNodeWithCache(e,t,s){var n,i;const o=this.graph.nodes.get(e);if(!o)return;const r=this.registry.types.get(o.type);if(null==r?void 0:r.onExecute)try{r.onExecute(o,{dt:t,graph:this.graph,getInput:t=>{const n=o.inputs.find(e=>e.name===t)||o.inputs[0];if(n)for(const i of this.graph.edges.values())if(i.toNode===e&&i.toPort===n.id){const e=`${i.fromNode}:${i.fromPort}`;return s.has(e)?s.get(e):this.graph._curBuf().get(e)}},setOutput:(e,t)=>{const n=o.outputs.find(t=>t.name===e)||o.outputs[0];n&&s.set(`${o.id}:${n.id}`,t)}})}catch(a){null==(i=null==(n=this.hooks)?void 0:n.emit)||i.call(n,"error",a)}}findAllNextExecNodes(e){const t=this.graph.nodes.get(e);if(!t)return[];const s=t.outputs.filter(e=>"exec"===e.portType);if(0===s.length)return[];const n=[];for(const i of s)for(const t of this.graph.edges.values())t.fromNode===e&&t.fromPort===i.id&&n.push(t.toNode);return n}executeNode(e,t){var s,n;const i=this.graph.nodes.get(e);if(!i)return;const o=this.registry.types.get(i.type);if(null==o?void 0:o.onExecute)try{o.onExecute(i,{dt:t,graph:this.graph,getInput:e=>{const t=i.inputs.find(t=>t.name===e)||i.inputs[0];return t?this.graph.getInput(i.id,t.id):void 0},setOutput:(e,t)=>{const s=i.outputs.find(t=>t.name===e)||i.outputs[0];if(s){const e=`${i.id}:${s.id}`;this.graph._curBuf().set(e,t)}}})}catch(r){null==(n=null==(s=this.hooks)?void 0:s.emit)||n.call(s,"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 s=e=>{var t,n;if(!this.running)return;const i=this._last?e-this._last:0;this._last=e;const o=i/1e3;"run"===this.executionMode&&this.step(this.cyclesPerFrame,o),null==(n=null==(t=this.hooks)?void 0:t.emit)||n.call(t,"runner:tick",{time:e,dt:o,running:!0,cps:this.cyclesPerFrame}),this._raf=requestAnimationFrame(s)};this._raf=requestAnimationFrame(s)}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,s){this.host=e,this.renderer=t,this.registry=s,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 s=document.createElement("div");s.className="node-header",Object.assign(s.style,{height:"26px",flexShrink:"0",display:"flex",alignItems:"center",padding:"0 8px",cursor:"grab",userSelect:"none",pointerEvents:"none"});const n=document.createElement("div");return n.className="node-body",Object.assign(n.style,{flex:"1",position:"relative",overflow:"hidden",pointerEvents:"none"}),t.appendChild(s),t.appendChild(n),t._domParts={header:s,body:n},t}_ensureNodeElement(e,t,s){var n;let i=this.nodes.get(e.id);if(!i){if(null==(n=t.html)?void 0:n.render)i=t.html.render(e);else{if(!t.html)return null;i=this._createDefaultNodeLayout(e),t.html.init&&t.html.init(e,i,{...i._domParts,graph:s})}if(!i)return null;i.style.position="absolute",i.style.pointerEvents="none",this.container.appendChild(i),this.nodes.set(e.id,i)}return i}draw(e,t=new Set){const{scale:s,offsetX:n,offsetY:i}=this.renderer;this.container.style.transform=`translate(${n}px, ${i}px) scale(${s})`,this.container.style.transformOrigin="0 0";const o=new Set;for(const r of e.nodes.values()){const s=this.registry.types.get(r.type);if(!!!(null==s?void 0:s.html))continue;const n=this._ensureNodeElement(r,s,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`,s.html.update){const e=n._domParts||{};s.html.update(r,n,{selected:t.has(r.id),header:e.header,body:e.body})}o.add(r.id)}}this._drawStepOverlay(e);for(const[r,a]of this.nodes)o.has(r)||(a.remove(),this.nodes.delete(r))}_drawStepOverlay(e){const t=e.runner;if(!t||"step"!==t.executionMode||!t.activePlan)return void(this._stepBtn&&(this._stepBtn.style.display="none"));const s=t.activePlan[t.activeStepIndex];if(!s)return void(this._stepBtn&&(this._stepBtn.style.display="none"));const n=e.nodes.get(s.nodeId);n&&(this._stepBtn||(this._stepBtn=document.createElement("button"),this._stepBtn.className="step-play-button",this._stepBtn.innerHTML="▶",Object.assign(this._stepBtn.style,{position:"absolute",zIndex:"100",width:"20px",height:"20px",borderRadius:"4px",border:"none",background:"transparent",color:"white",fontSize:"12px",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",pointerEvents:"auto",transition:"transform 0.1s, background 0.2s"}),this._stepBtn.addEventListener("mouseover",()=>{this._stepBtn.style.transform="scale(1)"}),this._stepBtn.addEventListener("mouseout",()=>{this._stepBtn.style.transform="scale(1)"}),this._stepBtn.addEventListener("click",e=>{e.stopPropagation(),t.executeNextStep()}),this.container.appendChild(this._stepBtn)),this._stepBtn.style.display="flex",this._stepBtn.style.left=n.computed.x+n.computed.w-26+"px",this._stepBtn.style.top=`${n.computed.y+2}px`)}syncTransform(){const{scale:e,offsetX:t,offsetY:s}=this.renderer;this.container.style.transform=`translate(${t}px, ${s}px) scale(${e})`,this.container.style.transformOrigin="0 0"}clear(){for(const[,e]of this.nodes)e.remove();this.nodes.clear()}destroy(){this.clear(),this.container.remove()}}class _{constructor(e,{graph:t,renderer:s,width:n=200,height:i=150}={}){this.graph=t,this.renderer=s,this.width=n,this.height=i,this.canvas=document.createElement("canvas"),this.canvas.id="minimap",this.canvas.width=n,this.canvas.height=i,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:s,width:n,height:i}=this;if(s.fillStyle="#141417",s.fillRect(0,0,n,i),0===e.nodes.size)return;let o=1/0,r=1/0,a=-1/0,l=-1/0;for(const x of e.nodes.values()){const{x:e,y:t,w:s,h:n}=x.computed;o=Math.min(o,e),r=Math.min(r,t),a=Math.max(a,e+s),l=Math.max(l,t+n)}const d=100,h=Math.max(300,a-o+200),c=Math.max(200,l-r+200);o-=d,r-=d;const u=Math.min((n-20)/h,(i-20)/c),p=(n-h*u)/2,g=(i-c*u)/2;s.strokeStyle="rgba(127, 140, 255, 0.5)",s.lineWidth=1;for(const x of e.edges.values()){const t=e.nodes.get(x.fromNode),n=e.nodes.get(x.toNode);if(!t||!n)continue;const i=(t.computed.x+t.computed.w/2-o)*u+p,a=(t.computed.y+t.computed.h/2-r)*u+g,l=(n.computed.x+n.computed.w/2-o)*u+p,d=(n.computed.y+n.computed.h/2-r)*u+g;s.beginPath(),s.moveTo(i,a),s.lineTo(l,d),s.stroke()}s.fillStyle="#6cf";for(const x of e.nodes.values()){const{x:e,y:t,w:n,h:i}=x.computed,a=(e-o)*u+p,l=(t-r)*u+g,d=n*u,h=i*u;"core/Group"===x.type?(s.fillStyle="rgba(102, 204, 255, 0.2)",s.strokeStyle="#6cf",s.lineWidth=1,s.fillRect(a,l,d,h),s.strokeRect(a,l,d,h)):(s.fillStyle="#6cf",s.fillRect(a,l,Math.max(2,d),Math.max(2,h)))}const f=(-t.offsetX/t.scale-o)*u+p,m=(-t.offsetY/t.scale-r)*u+g,y=t.canvas.width/t.scale*u,v=t.canvas.height/t.scale*u;s.strokeStyle="#ff6b6b",s.lineWidth=2,s.strokeRect(f,m,y,v)}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}}class E{constructor(e,{graph:t,hooks:s,registry:n,render:i}){this.container=e,this.graph=t,this.hooks=s,this.registry=n,this.render=i,this._def=null,this.panel=null,this.currentNode=null,this.isVisible=!1,this._selfUpdating=!1,this._createPanel(),this._bindHooks()}_bindHooks(){var e,t,s,n,i,o;null==(e=this.hooks)||e.on("edge:create",()=>{this._canRefresh()&&this._renderContent()}),null==(t=this.hooks)||t.on("edge:delete",()=>{this._canRefresh()&&this._renderContent()}),null==(s=this.hooks)||s.on("node:updated",e=>{var t;this._canRefresh()&&(null==(t=this.currentNode)?void 0:t.id)===(null==e?void 0:e.id)&&!this._selfUpdating&&this._renderContent()}),null==(n=this.hooks)||n.on("node:move",e=>{var t;this._canRefresh()&&(null==(t=this.currentNode)?void 0:t.id)===(null==e?void 0:e.id)&&this._updatePositionFields()}),null==(i=this.hooks)||i.on("runner:tick",()=>{this._canRefresh()&&this._updateLiveValues()}),null==(o=this.hooks)||o.on("runner:stop",()=>{this._canRefresh()&&this._updateLiveValues()})}_canRefresh(){return!(!this.isVisible||!this.currentNode)&&!this.panel.querySelector("[data-field]:focus")}_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){var t,s;e&&(this.currentNode=e,this._def=(null==(s=null==(t=this.registry)?void 0:t.types)?void 0:s.get(e.type))||null,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._renderConnections(e)}\n ${this._renderPorts(e)}\n ${this._renderLiveValues(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()}_renderConnections(e){const t=[...this.graph.edges.values()],s=t.filter(t=>t.toNode===e.id),n=t.filter(t=>t.fromNode===e.id);if(!s.length&&!n.length)return"";const i=(e,t)=>{const s="in"===t?e.fromNode:e.toNode,n=this.graph.nodes.get(s);return`<div class="port-item">\n <span class="port-icon data"></span>\n <span class="port-name" style="font-size:10px;color:#5a5a78;">${(null==n?void 0:n.title)??s}</span>\n </div>`};return`\n <div class="section">\n <div class="section-title">Connections</div>\n <div class="section-body">\n ${s.length?`\n <div class="port-group">\n <div class="port-group-title">Incoming (${s.length})</div>\n ${s.map(e=>i(e,"in")).join("")}\n </div>`:""}\n ${n.length?`\n <div class="port-group">\n <div class="port-group-title">Outgoing (${n.length})</div>\n ${n.map(e=>i(e,"out")).join("")}\n </div>`:""}\n </div>\n </div>\n `}_renderLiveValues(e){var t,s;const n=null==(s=null==(t=this.graph)?void 0:t._curBuf)?void 0:s.call(t);if(!n)return"";const i=[];for(const o of e.inputs){e.id,o.id;for(const t of this.graph.edges.values())if(t.toNode===e.id&&t.toPort===o.id){const e=`${t.fromNode}:${t.fromPort}`,s=n.get(e);void 0!==s&&i.push(`<div class="port-item">\n <span class="port-icon data"></span>\n <span class="port-name">↳ ${o.name}</span>\n <span class="port-type" style="color:var(--color-primary);background:rgba(99,102,241,0.1);">${JSON.stringify(s)}</span>\n </div>`);break}}for(const o of e.outputs){const t=`${e.id}:${o.id}`,s=n.get(t);void 0!==s&&i.push(`<div class="port-item">\n <span class="port-icon exec" style="background:#10b981;"></span>\n <span class="port-name">↳ ${o.name}</span>\n <span class="port-type" style="color:#10b981;background:rgba(16,185,129,0.1);">${JSON.stringify(s)}</span>\n </div>`)}return i.length?`\n <div class="section">\n <div class="section-title">Live Values</div>\n <div class="section-body">\n ${i.join("")}\n </div>\n </div>\n `:""}_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 ${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){if(!e.state)return"";const t=Object.entries(e.state).filter(([e,t])=>{if(e.startsWith("_"))return!1;const s=typeof t;return"string"===s||"number"===s||"boolean"===s});if(!t.length)return"";return`\n <div class="section">\n <div class="section-title">State</div>\n <div class="section-body">\n ${t.map(([e,t])=>"boolean"==typeof t?`\n <div class="field">\n <label>${e}</label>\n <select data-field="state.${e}">\n <option value="true"${t?" selected":""}>true</option>\n <option value="false"${t?"":" selected"}>false</option>\n </select>\n </div>`:`\n <div class="field">\n <label>${e}</label>\n <input type="${"number"==typeof t?"number":"text"}"\n data-field="state.${e}"\n value="${t}" />\n </div>`).join("")}\n </div>\n </div>\n `}_attachInputListeners(){var e;this.panel.querySelectorAll("[data-field]").forEach(e=>{e.addEventListener("change",()=>{this._selfUpdating=!0,this._handleFieldChange(e.dataset.field,e.value),this._selfUpdating=!1})}),null==(e=this.panel.querySelector(".panel-close-btn"))||e.addEventListener("click",()=>{this.close()})}_handleFieldChange(e,t){var s,n;const i=this.currentNode;if(i){switch(e){case"title":i.title=t;break;case"x":i.pos.x=parseFloat(t),this.graph.updateWorldTransforms();break;case"y":i.pos.y=parseFloat(t),this.graph.updateWorldTransforms();break;case"width":i.size.width=parseFloat(t);break;case"height":i.size.height=parseFloat(t);break;default:if(e.startsWith("state.")){const s=e.substring(6);if(i.state&&s in i.state){const e=i.state[s];i.state[s]="boolean"==typeof e?"true"===t:"number"==typeof e?parseFloat(t):t}}}null==(s=this.hooks)||s.emit("node:updated",i),null==(n=this.render)||n.call(this)}}_updatePositionFields(){const e=this.currentNode;if(!e)return;const t=this.panel.querySelector('[data-field="x"]'),s=this.panel.querySelector('[data-field="y"]');t&&(t.value=Math.round(e.computed.x)),s&&(s.value=Math.round(e.computed.y))}_updateLiveValues(){var e,t;const s=this.currentNode;if(!s)return;if(!(null==(t=null==(e=this.graph)?void 0:e._curBuf)?void 0:t.call(e)))return;let n=this.panel.querySelector(".live-values-section");const i=this._renderLiveValues(s);if(!i)return void(n&&n.remove());const o=document.createElement("div");o.innerHTML=i;const r=o.firstElementChild;if(r.classList.add("live-values-section"),n)n.replaceWith(r);else{this.panel.querySelectorAll(".section");const e=this.panel.querySelector(".panel-actions");e?e.before(r):this.panel.querySelector(".panel-content").appendChild(r)}}destroy(){var e;null==(e=this.panel)||e.remove()}}class S{constructor(e,t={}){this.container=e,this.options={shortcuts:t.shortcuts||this._getDefaultShortcuts(),onToggle:t.onToggle||null},this.isVisible=!1,this.overlay=null,this.toggleBtn=null,this._createElements(),this._bindEvents()}_getDefaultShortcuts(){return[{group:"Selection",items:[{label:"Select node",key:"Click"},{label:"Multi-select",key:"Shift+Click"},{label:"Box select",key:"Ctrl+Drag"}]},{group:"Edit",items:[{label:"Delete",key:"Del"},{label:"Undo",key:"Ctrl+Z"},{label:"Redo",key:"Ctrl+Y"}]},{group:"Group & Align",items:[{label:"Create group",key:"Ctrl+G"},{label:"Align horizontal",key:"A"},{label:"Align vertical",key:"Shift+A"}]},{group:"View",items:[{label:"Toggle snap",key:"G"},{label:"Pan",key:"Mid+Drag"},{label:"Zoom",key:"Scroll"},{label:"Context menu",key:"RClick"}]}]}_createElements(){this.toggleBtn=document.createElement("div"),this.toggleBtn.id="helpToggle",this.toggleBtn.title="단축키 (?)",this.toggleBtn.textContent="?",this.container.appendChild(this.toggleBtn),this.overlay=document.createElement("div"),this.overlay.id="helpOverlay";const e=this.options.shortcuts.map(e=>`\n <h4>${e.group}</h4>\n ${e.items.map(e=>`\n <div class="shortcut-item">\n <span>${e.label}</span>\n <span class="shortcut-key">${e.key}</span>\n </div>\n `).join("")}\n `).join("");this.overlay.innerHTML=`\n <h3>\n <span>Keyboard Shortcuts</span>\n <button class="close-btn" id="helpClose" title="Close">×</button>\n </h3>\n ${e}\n `,this.container.appendChild(this.overlay)}_bindEvents(){this.toggleBtn.addEventListener("click",()=>this.toggle());const e=this.overlay.querySelector("#helpClose");e&&e.addEventListener("click",e=>{e.stopPropagation(),this.close()}),document.addEventListener("mousedown",e=>{this.isVisible&&(this.overlay.contains(e.target)||this.toggleBtn.contains(e.target)||this.close())}),window.addEventListener("keydown",e=>{("?"===e.key||e.shiftKey&&"/"===e.key)&&(["INPUT","TEXTAREA","SELECT"].includes(document.activeElement.tagName)||(e.preventDefault(),this.toggle())),"Escape"===e.key&&this.isVisible&&this.close()})}toggle(){this.isVisible?this.close():this.open()}open(){this.isVisible=!0,this.overlay.classList.add("visible"),this.toggleBtn.classList.add("active"),this.options.onToggle&&this.options.onToggle(!0)}close(){this.isVisible=!1,this.overlay.classList.remove("visible"),this.toggleBtn.classList.remove("active"),this.options.onToggle&&this.options.onToggle(!1)}destroy(){var e,t;null==(e=this.toggleBtn)||e.remove(),null==(t=this.overlay)||t.remove()}}e.createGraphEditor=function(e,{theme:t,hooks:s,autorun:i=!0,showMinimap:o=!0,enablePropertyPanel:r=!0,propertyPanelContainer:a=null,enableHelp:d=!0,helpShortcuts:h=null,setupDefaultContextMenu:u=!0,setupContextMenu:p=null,plugins:f=[]}={}){var m;let v,k;if("string"==typeof e&&(e=document.querySelector(e)),!e)throw new Error("createGraphEditor: target element not found");e instanceof HTMLCanvasElement?(v=e,k=v.parentElement):(k=e,v=k.querySelector("canvas"),v||(v=document.createElement("canvas"),v.style.display="block",v.style.width="100%",v.style.height="100%",k.appendChild(v))),"static"===getComputedStyle(k).position&&(k.style.position="relative");const T=s??function(e){const t=Object.fromEntries(e.map(e=>[e,new Set]));return{on:(e,s)=>(t[e]||(t[e]=new Set),t[e].add(s),()=>t[e].delete(s)),off(e,s){t[e]&&t[e].delete(s)},emit(e,...s){if(t[e])for(const n of t[e])n(...s)}}}(["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"]),N=new n,C=new l({hooks:T,registry:N}),P=new c(v,{theme:t,registry:N}),M=new w(v.parentElement,P,N);P.setTransformChangeCallback(()=>{M.syncTransform()});const I=document.createElement("canvas");I.id="edge-canvas",Object.assign(I.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"15"}),v.parentElement.appendChild(I);const $=new c(I,{theme:t,registry:N});Object.defineProperty($,"scale",{get:()=>P.scale,set(e){P.scale=e}}),Object.defineProperty($,"offsetX",{get:()=>P.offsetX,set(e){P.offsetX=e}}),Object.defineProperty($,"offsetY",{get:()=>P.offsetY,set(e){P.offsetY=e}});const z=document.createElement("canvas");z.id="port-canvas",Object.assign(z.style,{position:"absolute",top:"0",left:"0",pointerEvents:"none",zIndex:"20"}),v.parentElement.appendChild(z);const W=new c(z,{theme:t,registry:N});W.setTransform=P.setTransform.bind(P),W.scale=P.scale,W.offsetX=P.offsetX,W.offsetY=P.offsetY;const L=new y({graph:C,renderer:P,hooks:T,htmlOverlay:M,edgeRenderer:$,portRenderer:W}),B=new x({graph:C,hooks:T,renderer:P,commandStack:L.stack});L.contextMenu=B;let A=null;o&&(A=new _(k,{graph:C,renderer:P}));let R=null;r&&(R=new E(a||k,{graph:C,hooks:T,registry:N,render:()=>L.render()}),T.on("node:dblclick",e=>{R.open(e)}));let D=null;d&&(D=new S(k,{shortcuts:h}));const O=new b({graph:C,registry:N,hooks:T});if(C.runner=O,C.controller=L,T.on("runner:tick",({time:e,dt:t})=>{L.render(e)}),T.on("runner:start",()=>{L.render(performance.now())}),T.on("runner:stop",()=>{L.render(performance.now())}),T.on("node:updated",()=>{L.render()}),T.on("graph:deserialize",()=>{P.setTransform({scale:1,offsetX:0,offsetY:0}),L.render()}),u&&function(e,{controller:t,graph:s,hooks:n}){e.addItem("add-node","Add Node",{condition:e=>!e,submenu:()=>{const i=[];for(const[o,r]of s.registry.types.entries())i.push({id:`add-${o}`,label:r.title||o,action:()=>{const i=e.worldPosition||{x:100,y:100},r=s.addNode(o,{x:i.x,y:i.y});null==n||n.emit("node:updated",r),t.render()}});return i},order:5}),e.addItem("delete-node","Delete Node",{condition:e=>e&&"core/Group"!==e.type,action:e=>{const i=g(s,e);t.stack.exec(i),null==n||n.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:s=>{const i=s.state.color||"#39424e",o=(r=s,a=i,l=e.color,{do(){r.state.color=l},undo(){r.state.color=a}});var r,a,l;t.stack.exec(o),null==n||n.emit("node:updated",s)}})),order:20}),e.addItem("delete-group","Delete Group",{condition:e=>e&&"core/Group"===e.type,action:e=>{const i=g(s,e);t.stack.exec(i),null==n||n.emit("node:updated",e)},order:20})}(B,{controller:L,graph:C,hooks:T}),p&&p(B,{controller:L,graph:C,hooks:T}),f&&f.length>0)for(const n of f)if("function"==typeof n.install)try{n.install({graph:C,registry:N,hooks:T,runner:O,controller:L,contextMenu:B},n.options||{})}catch(H){console.error(`[createGraphEditor] Failed to install plugin "${n.name||"unknown"}":`,H),null==(m=null==T?void 0:T.emit)||m.call(T,"error",H)}else console.warn(`[createGraphEditor] Plugin "${n.name||"unknown"}" does not have an install() method`);P.resize(v.clientWidth,v.clientHeight),$.resize(v.clientWidth,v.clientHeight),W.resize(v.clientWidth,v.clientHeight),L.render();const G=new ResizeObserver(()=>{P.resize(v.clientWidth,v.clientHeight),$.resize(v.clientWidth,v.clientHeight),W.resize(v.clientWidth,v.clientHeight),L.render()});G.observe(v);const j=L.render.bind(L);L.render=function(){j(),A&&A.render()};const Y={addGroup:(e={})=>{L.graph.groupManager.addGroup(e),L.render()},graph:C,renderer:P,edgeRenderer:$,controller:L,runner:O,minimap:A,contextMenu:B,hooks:T,registry:N,htmlOverlay:M,propertyPanel:R,render:()=>L.render(),start:()=>O.start(),stop:()=>O.stop(),setEdgeStyle:e=>{P.setEdgeStyle(e),$.setEdgeStyle(e)},setExecutionMode:e=>{O.setExecutionMode(e),L.render()},destroy:()=>{O.stop(),G.disconnect(),L.destroy(),M.destroy(),B.destroy(),R&&R.destroy(),A&&A.destroy(),D&&D.destroy()}};return i&&O.start(),Y},Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=html-overlay-node.umd.js.map
|