vortx-gl 1.0.2 → 1.0.4
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/README.md +485 -35
- package/dist/engine/events.d.ts +32 -0
- package/dist/engine/interaction.d.ts +26 -0
- package/dist/engine/overlay.d.ts +80 -0
- package/dist/engine/picking.d.ts +40 -0
- package/dist/engine/renderer.d.ts +469 -0
- package/dist/engine/shaders.d.ts +4 -0
- package/dist/engine/types.d.ts +225 -0
- package/dist/index.d.ts +8 -1
- package/dist/vortx-gl.css +1 -1
- package/dist/vortx-gl.es.js +1 -1
- package/dist/vortx-gl.umd.js +1 -1
- package/examples/vue-example.vue +80 -0
- package/package.json +18 -25
- package/dist/webgl-graph-library.css +0 -1
- package/dist/webgl-graph.es.js +0 -1
- package/dist/webgl-graph.umd.js +0 -1
package/dist/vortx-gl.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("gl-matrix"),require("vue")):"function"==typeof define&&define.amd?define(["exports","gl-matrix","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VortxGL={},e.glMatrix,e.Vue)}(this,function(e,t,n){"use strict";var a="undefined"!=typeof document?document.currentScript:null;class o{gl;constructor(e){this.gl=e}pickNode(e){const t=this.gl,{mouseX:n,mouseY:a,canvas:o,program:i,pickingFramebuffer:s,vao:l,viewMatrix:r,pointSize:c,nodeCount:d}=e;t.bindFramebuffer(t.FRAMEBUFFER,s),t.viewport(0,0,o.width,o.height),t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(i),t.uniformMatrix3fv(t.getUniformLocation(i,"u_matrix"),!1,r),t.uniform1f(t.getUniformLocation(i,"u_pointSize"),c),t.uniform1i(t.getUniformLocation(i,"u_pickingMode"),1),t.bindVertexArray(l),t.drawArrays(t.POINTS,0,d);const h=new Uint8Array(4),u=window.devicePixelRatio;if(t.readPixels(Math.floor(n*u),Math.floor(o.height-a*u),1,1,t.RGBA,t.UNSIGNED_BYTE,h),t.bindFramebuffer(t.FRAMEBUFFER,null),0===h[3])return null;return(h[0]<<16|h[1]<<8|h[2])-1}pickEdge(e){const{mouseX:n,mouseY:a,edgeIndices:o,positions:i,nodeCount:s,viewMatrix:l,canvasW:r,canvasH:c}=e;if(!o||!i)return null;const d=window.devicePixelRatio,h=n*d,u=a*d,m=5*d,v=t.mat3.invert(t.mat3.create(),l);if(!v)return null;const g=t.vec3.fromValues(h/r*2-1,-(u/c*2-1),1);t.vec3.transformMat3(g,g,v);const p=g[0],f=g[1],M=2*m/Math.abs(l[0]+l[4]);for(let k=0;k<o.length/2;k++){const e=o[2*k],n=o[2*k+1];if(e>=s||n>=s)continue;const a=i[2*e],d=i[2*e+1],v=i[2*n],g=i[2*n+1];if(p<(a<v?a:v)-M||p>(a>v?a:v)+M||f<(d<g?d:g)-M||f>(d>g?d:g)+M)continue;const w=this.project(a,d,l,r,c),y=this.project(v,g,l,r,c);if(this.distToSegment(t.vec2.fromValues(h,u),w,y)<m)return`${e}-${n}`}return null}pickCombo(e){const{mouseX:t,mouseY:n,combos:a,positions:o,nodeCount:i,viewMatrix:s,canvasW:l,canvasH:r}=e;if(!o)return null;const c=window.devicePixelRatio,d=t*c,h=n*c;for(const u of a.values()){let e=1/0,t=1/0,n=-1/0,a=-1/0,m=!1;for(const c of u.nodeIds){if(c>=i)continue;const d=this.project(o[2*c],o[2*c+1],s,l,r);e=Math.min(e,d[0]),t=Math.min(t,d[1]),n=Math.max(n,d[0]),a=Math.max(a,d[1]),m=!0}if(m){const o=30*c;if(d>=e-o&&d<=n+o&&h>=t-o&&h<=a+o)return u.id}}return null}distToSegment(e,n,a){const o=t.vec2.dist(n,a)**2;if(0===o)return t.vec2.dist(e,n);let i=((e[0]-n[0])*(a[0]-n[0])+(e[1]-n[1])*(a[1]-n[1]))/o;i=Math.max(0,Math.min(1,i));const s=t.vec2.fromValues(n[0]+i*(a[0]-n[0]),n[1]+i*(a[1]-n[1]));return t.vec2.dist(e,s)}project(e,n,a,o,i){const s=t.vec3.fromValues(e,n,1);return t.vec3.transformMat3(s,s,a),t.vec2.fromValues((.5*s[0]+.5)*o,(.5*-s[1]+.5)*i)}}class i{canvas;ctx;constructor(e,t){this.canvas=e,this.ctx=t}_posScratch=t.vec3.create();_spScratch=t.vec2.create();_s1Scratch=t.vec2.create();_s2Scratch=t.vec2.create();project(e,n,a,o){return t.vec3.set(this._posScratch,e,n,1),t.vec3.transformMat3(this._posScratch,this._posScratch,a),o[0]=(.5*this._posScratch[0]+.5)*this.canvas.width,o[1]=(.5*-this._posScratch[1]+.5)*this.canvas.height,o}render(e){const{ctx:n,canvas:a}=this,{positions:o,nodeCount:i,edgeCount:s,edgeIndices:l,metadata:r,combos:c,theme:d,zoom:h,viewMatrix:u,selectedNodeIds:m,selectedEdgeId:v,selectedComboId:g,hoveredNodeId:p,isBoxSelecting:f,selectionStart:M,selectionEnd:k,isFlowAnimating:w}=e;if(!o)return;const y=t.mat3.invert(t.mat3.create(),u);if(!y)return;const b=t.vec3.fromValues(-1.1,1.1,1),E=t.vec3.fromValues(1.1,-1.1,1);t.vec3.transformMat3(b,b,y),t.vec3.transformMat3(E,E,y);const x=Math.min(b[0],E[0]),B=Math.max(b[0],E[0]),N=Math.min(b[1],E[1]),C=Math.max(b[1],E[1]);n.clearRect(0,0,a.width,a.height);const V=window.devicePixelRatio,S=40*h*V;f&&this.drawMarquee(M,k,V),this.drawCombos(c,o,i,u,g,d,V),this.drawNodeHighlights(m,i,o,u,S,V),this.drawEdgeHighlight(v,l,o,u,V);const I=h<.1;w&&!(h<.3)&&s<5e4&&this.drawFlow(l,s,o,u,d,V,x,B,N,C),null!==p&&l&&this.drawHoverHighlight(p,l,s,o,u,d,V),I||this.drawNodeLabels(i,r,o,u,d,V,S,h,x,B,N,C)}drawMarquee(e,t,n){const a=Math.min(e[0],t[0]),o=Math.min(e[1],t[1]),i=Math.abs(e[0]-t[0]),s=Math.abs(e[1]-t[1]);this.ctx.strokeStyle="rgba(0, 255, 204, 0.8)",this.ctx.fillStyle="rgba(0, 255, 204, 0.1)",this.ctx.lineWidth=1*n,this.ctx.strokeRect(a,o,i,s),this.ctx.fillRect(a,o,i,s)}drawCombos(e,t,n,a,o,i,s){const l=this.ctx,r=this._spScratch;for(const c of e.values()){let e=1/0,d=1/0,h=-1/0,u=-1/0,m=!1;for(const o of c.nodeIds){if(o>=n)continue;const i=this.project(t[2*o],t[2*o+1],a,r);e=Math.min(e,i[0]),d=Math.min(d,i[1]),h=Math.max(h,i[0]),u=Math.max(u,i[1]),m=!0}if(m){const t=30*s,n=e-t,a=d-t,r=h-e+2*t,m=u-d+2*t;if(n+r<0||n>this.canvas.width||a+m<0||a>this.canvas.height)continue;const v=o===c.id;l.save(),l.beginPath();const g=16*s;l.moveTo(n+g,a),l.arcTo(n+r,a,n+r,a+m,g),l.arcTo(n+r,a+m,n,a+m,g),l.arcTo(n,a+m,n,a,g),l.arcTo(n,a,n+r,a,g),l.closePath(),l.fillStyle=v?"rgba(0, 255, 204, 0.15)":"dark"===i?"rgba(0, 255, 204, 0.05)":"rgba(0, 170, 136, 0.05)",l.strokeStyle=v?"rgba(0, 255, 204, 0.8)":"dark"===i?"rgba(0, 255, 204, 0.2)":"rgba(0, 170, 136, 0.2)",l.lineWidth=(v?3:2)*s,l.setLineDash(v?[]:[5*s,5*s]),l.fill(),l.stroke(),l.setLineDash([]),l.fillStyle=v||"dark"===i?"#00ffcc":"#00aa88",l.font=`bold ${14*s}px Inter, sans-serif`,l.fillText(c.label.toUpperCase(),n+10*s,a-10*s),l.restore()}}}drawNodeHighlights(e,t,n,a,o,i){const s=this.ctx,l=this._spScratch;s.strokeStyle="#00ffcc",s.lineWidth=2*i;for(const r of e)r>=t||(this.project(n[2*r],n[2*r+1],a,l),l[0]<-50||l[0]>this.canvas.width+50||l[1]<-50||l[1]>this.canvas.height+50||(s.beginPath(),s.arc(l[0],l[1],.7*o,0,2*Math.PI),s.stroke()))}drawEdgeHighlight(e,t,n,a,o){if(!e||!t)return;const i=this.ctx,s=e.split("-"),l=Number(s[0]),r=Number(s[1]);if(isNaN(l)||isNaN(r))return;const c=this._s1Scratch,d=this._s2Scratch;this.project(n[2*l],n[2*l+1],a,c),this.project(n[2*r],n[2*r+1],a,d);const h=Math.min(c[0],d[0]),u=Math.max(c[0],d[0]),m=Math.min(c[1],d[1]),v=Math.max(c[1],d[1]);u<-20||h>this.canvas.width+20||v<-20||m>this.canvas.height+20||(i.strokeStyle="#ff00ff",i.lineWidth=3*o,i.beginPath(),i.moveTo(c[0],c[1]),i.lineTo(d[0],d[1]),i.stroke())}drawFlow(e,t,n,a,o,i,s,l,r,c){if(!e)return;const d=this.ctx,h=.002*performance.now(),u=this._s1Scratch,m=this._s2Scratch,v=Math.max(1,Math.floor(t/15e3));for(let g=0;g<t/2;g+=v){const t=e[2*g],v=e[2*g+1],p=n[2*t],f=n[2*t+1],M=n[2*v],k=n[2*v+1];if((p>M?p:M)<s||(p<M?p:M)>l||(f>k?f:k)<r||(f<k?f:k)>c)continue;this.project(p,f,a,u),this.project(M,k,a,m);const w=Math.min(u[0],m[0]),y=Math.max(u[0],m[0]),b=Math.min(u[1],m[1]),E=Math.max(u[1],m[1]);if(y<-20||w>this.canvas.width+20||E<-20||b>this.canvas.height+20)continue;const x=(h+.1*g%1)%1,B=u[0]+(m[0]-u[0])*x,N=u[1]+(m[1]-u[1])*x;d.beginPath(),d.arc(B,N,2*i,0,2*Math.PI),d.fillStyle="dark"===o?"#00ffcc":"#00aa88",d.shadowBlur=4*i,d.shadowColor=d.fillStyle,d.fill(),d.shadowBlur=0}}drawHoverHighlight(e,t,n,a,o,i,s){const l=this.ctx;l.lineWidth=2*s,l.strokeStyle="dark"===i?"rgba(0, 255, 204, 0.6)":"rgba(0, 170, 136, 0.5)";const r=this._s1Scratch,c=this._s2Scratch;for(let d=0;d<n/2;d++){const n=t[2*d],i=t[2*d+1];n!==e&&i!==e||(this.project(a[2*n],a[2*n+1],o,r),this.project(a[2*i],a[2*i+1],o,c),l.beginPath(),l.moveTo(r[0],r[1]),l.lineTo(c[0],c[1]),l.stroke())}}drawNodeLabels(e,t,n,a,o,i,s,l,r,c,d,h){const u=this.ctx;u.textAlign="center",u.textBaseline="top";const m=this._spScratch;let v=0;for(let g=0;g<e&&!(v>200);g++){const e=n[2*g],p=n[2*g+1];if(e<r||e>c||p<d||p>h)continue;const f=t.get(g);if(!f)continue;this.project(e,p,a,m);const M=m[0],k=m[1];if(M<-50||M>this.canvas.width+50||k<-50||k>this.canvas.height+50)continue;v++;const w=.5*s,y=l>.6;if(s>12*i&&(u.beginPath(),u.arc(M,k,w,0,2*Math.PI),u.strokeStyle="dark"===o?"rgba(255, 255, 255, 0.8)":"rgba(0, 0, 0, 0.8)",u.lineWidth=1*i,u.stroke()),y)if(f.processedImageElement){const e=s*.7;u.drawImage(f.processedImageElement,M-e/2,k-e/2,e,e)}else if(f.imageElement&&f.imageElement.complete&&0!==f.imageElement.naturalWidth){const e=s*.7;u.save(),u.beginPath(),u.arc(M,k,.9*w,0,2*Math.PI),u.clip(),u.filter="grayscale(100%) brightness(1000%)",u.drawImage(f.imageElement,M-e/2,k-e/2,e,e),u.restore()}else f.icon&&(u.font=.6*s+"px serif",u.fillStyle="dark"===o?"white":"black",u.fillText(f.icon,M,k-.2*s));if(!(l<.5)&&f.label){const e=w+8*i;u.font=12*i+"px Inter, sans-serif",u.fillStyle="dark"===o?"white":"black",u.fillText(f.label,M,k+e)}}}}class s{lastMouse=t.vec2.create();isPanning=!1;engine;canvas;lastTouchDistance=0;lastTouchMidpoint=t.vec2.create();isTouching=!1;touchStartTime=0;touchStartPos=t.vec2.create();longPressTimer=null;isLongPress=!1;constructor(e,t){this.engine=e,this.canvas=t,this.setupListeners()}setupListeners(){this.canvas.addEventListener("mousedown",e=>this.handleMouseDown(e)),window.addEventListener("mousemove",e=>this.handleMouseMove(e)),window.addEventListener("mouseup",()=>this.handleMouseUp()),this.canvas.addEventListener("wheel",e=>this.handleWheel(e),{passive:!1}),this.canvas.addEventListener("touchstart",e=>this.handleTouchStart(e),{passive:!1}),this.canvas.addEventListener("touchmove",e=>this.handleTouchMove(e),{passive:!1}),this.canvas.addEventListener("touchend",e=>this.handleTouchEnd(e)),this.canvas.addEventListener("touchcancel",e=>this.handleTouchEnd(e))}handleMouseDown(e){const n=this.canvas.getBoundingClientRect(),a=e.clientX-n.left,o=e.clientY-n.top,i=this.engine.getState();if(e.shiftKey)return this.engine.updateInteractionState({isBoxSelecting:!0,selectionStart:t.vec2.fromValues(a,o),selectionEnd:t.vec2.fromValues(a,o)}),void this.engine.render();const s=this.engine.pick(a,o);if(null!==s){const t=i.selectedNodeIds.has(s);e.ctrlKey||e.metaKey||t||i.selectedNodeIds.clear(),i.selectedNodeIds.add(s),this.engine.updateInteractionState({isMultiDragging:!0,draggedNodeId:null}),this.engine.syncDragOffsets(a,o),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:s})),this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:Array.from(i.selectedNodeIds)}))}else{i.selectedNodeIds.clear();const e=this.engine.getEdgeAt(a,o),t=this.engine.pickCombo(a,o);this.engine.updateInteractionState({selectedEdgeId:e,selectedComboId:t}),this.canvas.dispatchEvent(new CustomEvent("edge-selected",{detail:e})),this.canvas.dispatchEvent(new CustomEvent("combo-selected",{detail:t})),this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:[]})),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:null})),e||t||(this.isPanning=!0)}t.vec2.set(this.lastMouse,e.clientX,e.clientY),this.engine.render()}handleMouseMove(e){const n=this.canvas.getBoundingClientRect(),a=e.clientX-n.left,o=e.clientY-n.top,i=this.engine.getState();if(i.isBoxSelecting)return this.engine.updateInteractionState({selectionEnd:t.vec2.fromValues(a,o)}),void this.engine.render();if(i.isMultiDragging||null!==i.draggedNodeId)this.engine.handleDragging(a,o);else if(this.isPanning){const n=(e.clientX-this.lastMouse[0])/(this.canvas.width/2),a=-(e.clientY-this.lastMouse[1])/(this.canvas.height/2);this.engine.pan(n,a),t.vec2.set(this.lastMouse,e.clientX,e.clientY)}else{const e=i.hoveredNodeId,t=this.engine.pick(a,o);t!==e&&(this.engine.updateInteractionState({hoveredNodeId:t}),this.engine.render(),this.canvas.dispatchEvent(new CustomEvent("node-hovered",{detail:t})))}}handleMouseUp(){this.engine.getState().isBoxSelecting&&this.engine.performBoxSelection(),this.isPanning=!1,this.engine.updateInteractionState({isBoxSelecting:!1,isMultiDragging:!1,draggedNodeId:null}),this.engine.render()}handleWheel(e){e.preventDefault();const t=this.canvas.getBoundingClientRect(),n=e.clientX-t.left,a=e.clientY-t.top,o=.001*-e.deltaY;this.engine.zoomAt(n,a,o)}handleTouchStart(e){if(e.preventDefault(),this.isTouching=!0,this.touchStartTime=Date.now(),this.isLongPress=!1,1===e.touches.length){const n=e.touches[0],a=this.canvas.getBoundingClientRect(),o=n.clientX-a.left,i=n.clientY-a.top;t.vec2.set(this.touchStartPos,o,i),t.vec2.set(this.lastMouse,n.clientX,n.clientY),this.longPressTimer=window.setTimeout(()=>{this.isLongPress=!0,this.triggerContextMenu(n.clientX,n.clientY)},500);const s=this.engine.pick(o,i),l=this.engine.getState();null!==s&&(l.selectedNodeIds.has(s)||(l.selectedNodeIds.clear(),l.selectedNodeIds.add(s)),this.engine.updateInteractionState({isMultiDragging:!0,draggedNodeId:null}),this.engine.syncDragOffsets(o,i),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:s})))}else if(2===e.touches.length){this.clearLongPress();const n=e.touches[0],a=e.touches[1];this.lastTouchDistance=t.vec2.distance(t.vec2.fromValues(n.clientX,n.clientY),t.vec2.fromValues(a.clientX,a.clientY)),t.vec2.set(this.lastTouchMidpoint,(n.clientX+a.clientX)/2,(n.clientY+a.clientY)/2)}}handleTouchMove(e){if(e.preventDefault(),this.isTouching){if(1===e.touches.length){const n=e.touches[0],a=this.canvas.getBoundingClientRect(),o=n.clientX-a.left,i=n.clientY-a.top;if(t.vec2.distance(this.touchStartPos,t.vec2.fromValues(o,i))>10&&this.clearLongPress(),this.isLongPress)return;if(this.engine.getState().isMultiDragging)this.engine.handleDragging(o,i);else{const e=(n.clientX-this.lastMouse[0])/(this.canvas.width/2),t=-(n.clientY-this.lastMouse[1])/(this.canvas.height/2);this.engine.pan(e,t)}t.vec2.set(this.lastMouse,n.clientX,n.clientY)}else if(2===e.touches.length){this.clearLongPress();const n=e.touches[0],a=e.touches[1],o=t.vec2.distance(t.vec2.fromValues(n.clientX,n.clientY),t.vec2.fromValues(a.clientX,a.clientY)),i=t.vec2.fromValues((n.clientX+a.clientX)/2,(n.clientY+a.clientY)/2),s=o/this.lastTouchDistance,l=this.canvas.getBoundingClientRect(),r=i[0]-l.left,c=i[1]-l.top;this.engine.zoomAt(r,c,s-1);const d=(i[0]-this.lastTouchMidpoint[0])/(this.canvas.width/2),h=-(i[1]-this.lastTouchMidpoint[1])/(this.canvas.height/2);this.engine.pan(d,h),this.lastTouchDistance=o,t.vec2.copy(this.lastTouchMidpoint,i)}this.engine.render()}}handleTouchEnd(e){if(this.isTouching=!1,this.clearLongPress(),0===e.touches.length&&!this.isLongPress){const n=Date.now()-this.touchStartTime,a=e.changedTouches[0],o=this.canvas.getBoundingClientRect(),i=a.clientX-o.left,s=a.clientY-o.top,l=t.vec2.distance(this.touchStartPos,t.vec2.fromValues(i,s));if(n<250&&l<10){const e=this.engine.pick(i,s);this.engine.selectNode(e)}}this.engine.updateInteractionState({isMultiDragging:!1}),this.engine.render()}clearLongPress(){null!==this.longPressTimer&&(window.clearTimeout(this.longPressTimer),this.longPressTimer=null)}triggerContextMenu(e,t){const n=new MouseEvent("contextmenu",{clientX:e,clientY:t,bubbles:!0,cancelable:!0});this.canvas.dispatchEvent(n)}}class l{gl;pickingEngine;overlayManager;program=null;edgeProgram=null;viewMatrix=t.mat3.create();vao=null;edgeVao=null;positionBuffer=null;colorBuffer=null;idBuffer=null;edgeIndexBuffer=null;pickingFramebuffer=null;pickingTexture=null;uMatrixLoc=null;uPointSizeLoc=null;uPickingModeLoc=null;uEdgeMatrixLoc=null;uEdgeColorLoc=null;nodeCount=0;edgeCount=0;positions=null;metadata=new Map;edgeIndices=null;edgeMetadata=new Map;combos=new Map;theme="dark";static MAX_ZOOM=100;static MIN_ZOOM=1e-4;state={zoom:1,offset:t.vec2.fromValues(0,0),selectedNodeIds:new Set,selectedEdgeId:null,selectedComboId:null,hoveredNodeId:null,isBoxSelecting:!1,selectionStart:t.vec2.create(),selectionEnd:t.vec2.create(),isMultiDragging:!1,draggedNodeId:null};dragOffsets=new Map;isFlowAnimating=!1;canvas;overlayCanvas;constructor(e,t){this.canvas=e,this.overlayCanvas=t;const n=e.getContext("webgl2",{antialias:!0,stencil:!0});if(!n)throw new Error("WebGL2 not supported");this.gl=n;const a=t.getContext("2d");if(!a)throw new Error("Canvas 2D not supported");this.pickingEngine=new o(n),this.overlayManager=new i(t,a),this.initShaders(),this.initBuffers(),this.initPicking(),this.updateMatrix(),new s(this,e),this.handleResize(),window.addEventListener("resize",()=>this.handleResize())}setData(e,t,n,a,o,i){this.positions=e,this.nodeCount=n.length,this.metadata=a;const s=this.gl;s.bindBuffer(s.ARRAY_BUFFER,this.positionBuffer),s.bufferData(s.ARRAY_BUFFER,e,s.DYNAMIC_DRAW),s.bindBuffer(s.ARRAY_BUFFER,this.colorBuffer),s.bufferData(s.ARRAY_BUFFER,t,s.STATIC_DRAW),s.bindBuffer(s.ARRAY_BUFFER,this.idBuffer),s.bufferData(s.ARRAY_BUFFER,n,s.STATIC_DRAW),o&&(this.edgeIndices=o,this.edgeCount=o.length,s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,this.edgeIndexBuffer),s.bufferData(s.ELEMENT_ARRAY_BUFFER,o,s.STATIC_DRAW)),i&&(this.edgeMetadata=i),this.render()}updatePositions(e){this.positions=e;const t=this.gl;t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,e),this.render()}setNodePosition(e,t,n){if(!this.positions||e>=this.nodeCount)return;this.positions[2*e]=t,this.positions[2*e+1]=n;const a=this.gl;a.bindBuffer(a.ARRAY_BUFFER,this.positionBuffer),a.bufferSubData(a.ARRAY_BUFFER,2*e*4,this.positions.subarray(2*e,2*e+2)),this.render()}getNodePosition(e){return!this.positions||e>=this.nodeCount?null:t.vec2.fromValues(this.positions[2*e],this.positions[2*e+1])}setFlowAnimation(e){if(this.isFlowAnimating=e,e){const e=()=>{this.isFlowAnimating&&(this.render(),requestAnimationFrame(e))};requestAnimationFrame(e)}else this.render()}setTheme(e){this.theme=e,this.render()}createCombo(e,t,n){this.combos.set(e,{id:e,label:t,nodeIds:new Set(n)}),this.render()}removeCombo(e){this.combos.delete(e),this.render()}updateCombo(e,t,n){const a=this.combos.get(e);a&&(a.label=t,a.nodeIds=new Set(n),this.render())}getCombo(e){return this.combos.get(e)}getSelectedNodeIds(){return Array.from(this.state.selectedNodeIds)}getNodeMetadata(e){return this.metadata.get(e)}getEdgeMetadata(e){return this.edgeMetadata.get(e)}addNodeToCombo(e,t){const n=this.combos.get(e);n&&(n.nodeIds.add(t),this.render())}removeNodeFromCombo(e,t){const n=this.combos.get(e);n&&(n.nodeIds.delete(t),this.render())}render(){const e=this.gl;if(!this.program)return;this.updateMatrix();const t="dark"===this.theme?.05:.95;e.clearColor(t,t,t,1),e.clear(e.COLOR_BUFFER_BIT),this.edgeCount>0&&this.edgeProgram&&(e.useProgram(this.edgeProgram),e.uniformMatrix3fv(this.uEdgeMatrixLoc,!1,this.viewMatrix),e.uniform4fv(this.uEdgeColorLoc,"dark"===this.theme?[1,1,1,.15]:[0,0,0,.1]),e.bindVertexArray(this.edgeVao),e.drawElements(e.LINES,this.edgeCount,e.UNSIGNED_INT,0)),e.useProgram(this.program),e.uniformMatrix3fv(this.uMatrixLoc,!1,this.viewMatrix),e.uniform1f(this.uPointSizeLoc,40*this.state.zoom*window.devicePixelRatio),e.uniform1i(this.uPickingModeLoc,0),e.bindVertexArray(this.vao),e.drawArrays(e.POINTS,0,this.nodeCount),this.renderOverlay()}renderOverlay(){this.overlayManager.render({...this.state,positions:this.positions,nodeCount:this.nodeCount,edgeCount:this.edgeCount,edgeIndices:this.edgeIndices,metadata:this.metadata,edgeMetadata:this.edgeMetadata,combos:this.combos,theme:this.theme,viewMatrix:this.viewMatrix,isFlowAnimating:this.isFlowAnimating})}getState(){return this.state}updateInteractionState(e){Object.assign(this.state,e)}pick(e,t){return this.program&&this.pickingFramebuffer&&this.vao?this.pickingEngine.pickNode({mouseX:e,mouseY:t,canvas:this.canvas,program:this.program,pickingFramebuffer:this.pickingFramebuffer,vao:this.vao,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,pointSize:40*this.state.zoom*window.devicePixelRatio}):null}getEdgeAt(e,t){return this.pickingEngine.pickEdge({mouseX:e,mouseY:t,edgeIndices:this.edgeIndices,positions:this.positions,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,canvasW:this.canvas.width,canvasH:this.canvas.height})}pickCombo(e,t){return this.pickingEngine.pickCombo({mouseX:e,mouseY:t,combos:this.combos,positions:this.positions,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,canvasW:this.canvas.width,canvasH:this.canvas.height})}fitToView(){if(!this.positions||0===this.nodeCount)return this.state.zoom=1,this.state.offset=t.vec2.fromValues(0,0),void this.render();let e=1/0,n=1/0,a=-1/0,o=-1/0;for(let t=0;t<this.nodeCount;t++){const i=this.positions[2*t],s=this.positions[2*t+1];i<e&&(e=i),i>a&&(a=i),s<n&&(n=s),s>o&&(o=s)}const i=a-e,s=o-n,l=(e+a)/2,r=(n+o)/2,c=2*(this.canvas.width/this.canvas.height)*.9/Math.max(i,.01),d=1.8/Math.max(s,.01);this.state.zoom=Math.min(c,d,100),this.state.offset=t.vec2.fromValues(-l,-r),this.render()}zoomIn(){this.state.zoom=Math.min(l.MAX_ZOOM,1.2*this.state.zoom),this.render()}zoomOut(){this.state.zoom=Math.max(l.MIN_ZOOM,this.state.zoom/1.2),this.render()}setCameraCenter(e,n){this.state.offset=t.vec2.fromValues(-e,-n),this.render()}setCameraZoom(e){this.state.zoom=Math.max(l.MIN_ZOOM,Math.min(l.MAX_ZOOM,e)),this.render()}selectNode(e){this.state.selectedNodeIds.clear(),null!==e&&this.state.selectedNodeIds.add(e),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:e})),this.render()}zoomAt(e,n,a){const o=window.devicePixelRatio,i=this.unproject(e*o,n*o);this.state.zoom=Math.max(l.MIN_ZOOM,Math.min(l.MAX_ZOOM,this.state.zoom*(1+a))),this.updateMatrix();const s=this.unproject(e*o,n*o);t.vec2.add(this.state.offset,this.state.offset,t.vec2.sub(t.vec2.create(),s,i)),this.render()}pan(e,n){t.vec2.add(this.state.offset,this.state.offset,t.vec2.fromValues(e/this.state.zoom,n/this.state.zoom)),this.render()}syncDragOffsets(e,n){const a=window.devicePixelRatio,o=this.unproject(e*a,n*a);this.dragOffsets.clear();for(const i of this.state.selectedNodeIds){const e=this.getNodePosition(i);e&&this.dragOffsets.set(i,t.vec2.sub(t.vec2.create(),e,o))}}handleDragging(e,n){if(!this.positions)return;const a=window.devicePixelRatio,o=this.unproject(e*a,n*a),i=this.gl;i.bindBuffer(i.ARRAY_BUFFER,this.positionBuffer);for(const s of this.state.selectedNodeIds){const e=this.dragOffsets.get(s);if(e){const n=t.vec2.add(t.vec2.create(),o,e);this.positions[2*s]=n[0],this.positions[2*s+1]=n[1],i.bufferSubData(i.ARRAY_BUFFER,2*s*4,this.positions.subarray(2*s,2*s+2)),this.canvas.dispatchEvent(new CustomEvent("node-moved",{detail:{id:s,x:n[0],y:n[1]}}))}}this.render()}performBoxSelection(){if(!this.positions)return;const e=window.devicePixelRatio,t=this.unproject(this.state.selectionStart[0]*e,this.state.selectionStart[1]*e),n=this.unproject(this.state.selectionEnd[0]*e,this.state.selectionEnd[1]*e),a=Math.min(t[0],n[0]),o=Math.max(t[0],n[0]),i=Math.min(t[1],n[1]),s=Math.max(t[1],n[1]);this.state.selectedNodeIds.clear();for(let l=0;l<this.nodeCount;l++){const e=this.positions[2*l],t=this.positions[2*l+1];e>=a&&e<=o&&t>=i&&t<=s&&this.state.selectedNodeIds.add(l)}this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:Array.from(this.state.selectedNodeIds)}))}unproject(e,n){const a=e/this.canvas.width*2-1,o=-(n/this.canvas.height*2-1),i=t.mat3.invert(t.mat3.create(),this.viewMatrix);if(!i)return t.vec2.create();const s=t.vec3.fromValues(a,o,1);return t.vec3.transformMat3(s,s,i),t.vec2.fromValues(s[0],s[1])}initShaders(){const e=this.gl;this.program=this.createProgram("#version 300 es\nlayout(location = 0) in vec2 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in float a_id;\n\nuniform mat3 u_matrix;\nuniform float u_pointSize;\nuniform bool u_pickingMode;\n\nout vec4 v_color;\nflat out uint v_id;\n\nvoid main() {\n vec3 position = u_matrix * vec3(a_position, 1.0);\n gl_Position = vec4(position.xy, 0, 1);\n gl_PointSize = u_pointSize;\n v_color = a_color;\n v_id = uint(a_id);\n}\n","#version 300 es\nprecision highp float;\n\nin vec4 v_color;\nflat in uint v_id;\n\nuniform bool u_pickingMode;\nout vec4 outColor;\n\nvoid main() {\n float dist = distance(gl_PointCoord, vec2(0.5));\n if (dist > 0.5) discard;\n\n if (u_pickingMode) {\n uint id = v_id + 1u;\n float r = float((id >> 16) & 0xFFu) / 255.0;\n float g = float((id >> 8) & 0xFFu) / 255.0;\n float b = float(id & 0xFFu) / 255.0;\n outColor = vec4(r, g, b, 1.0);\n } else {\n outColor = v_color;\n }\n}\n"),this.uMatrixLoc=e.getUniformLocation(this.program,"u_matrix"),this.uPointSizeLoc=e.getUniformLocation(this.program,"u_pointSize"),this.uPickingModeLoc=e.getUniformLocation(this.program,"u_pickingMode"),this.edgeProgram=this.createProgram("#version 300 es\nlayout(location = 0) in vec2 a_position;\n\nuniform mat3 u_matrix;\n\nvoid main() {\n vec3 position = u_matrix * vec3(a_position, 1.0);\n gl_Position = vec4(position.xy, 0, 1);\n}\n","#version 300 es\nprecision highp float;\nuniform vec4 u_color;\nout vec4 outColor;\n\nvoid main() {\n outColor = u_color;\n}\n"),this.uEdgeMatrixLoc=e.getUniformLocation(this.edgeProgram,"u_matrix"),this.uEdgeColorLoc=e.getUniformLocation(this.edgeProgram,"u_color")}initBuffers(){const e=this.gl;this.vao=e.createVertexArray(),e.bindVertexArray(this.vao),this.positionBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,2,e.FLOAT,!1,0,0),this.colorBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.colorBuffer),e.enableVertexAttribArray(1),e.vertexAttribPointer(1,4,e.FLOAT,!1,0,0),this.idBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.idBuffer),e.enableVertexAttribArray(2),e.vertexAttribPointer(2,1,e.FLOAT,!1,0,0),this.edgeVao=e.createVertexArray(),e.bindVertexArray(this.edgeVao),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,2,e.FLOAT,!1,0,0),this.edgeIndexBuffer=e.createBuffer(),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.edgeIndexBuffer),e.bindVertexArray(null)}initPicking(){const e=this.gl;this.pickingFramebuffer=e.createFramebuffer(),this.pickingTexture=e.createTexture(),e.bindTexture(e.TEXTURE_2D,this.pickingTexture),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,this.canvas.width,this.canvas.height,0,e.RGBA,e.UNSIGNED_BYTE,null),e.bindFramebuffer(e.FRAMEBUFFER,this.pickingFramebuffer),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,this.pickingTexture,0),e.bindFramebuffer(e.FRAMEBUFFER,null)}handleResize(){const e=window.devicePixelRatio;this.canvas.width=this.canvas.clientWidth*e,this.canvas.height=this.canvas.clientHeight*e,this.overlayCanvas.width=this.overlayCanvas.clientWidth*e,this.overlayCanvas.height=this.overlayCanvas.clientHeight*e,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.initPicking(),this.render()}updateMatrix(){const e=this.canvas.width/this.canvas.height;t.mat3.identity(this.viewMatrix),t.mat3.scale(this.viewMatrix,this.viewMatrix,[this.state.zoom/e,this.state.zoom]),t.mat3.translate(this.viewMatrix,this.viewMatrix,this.state.offset)}createProgram(e,t){const n=this.gl,a=n.createShader(n.VERTEX_SHADER);n.shaderSource(a,e),n.compileShader(a);const o=n.createShader(n.FRAGMENT_SHADER);n.shaderSource(o,t),n.compileShader(o);const i=n.createProgram();return n.attachShader(i,a),n.attachShader(i,o),n.linkProgram(i),i}}const r=["width","height","stroke-width"],c=["d"],d=["cx","cy","r"],h=["x1","y1","x2","y2"],u=["x","y","width","height","rx","ry"],m=(e,t)=>{const n=e.__vccOpts||e;for(const[a,o]of t)n[a]=o;return n},v=m(n.defineComponent({__name:"LucideIcon",props:{name:{},size:{default:24},strokeWidth:{default:2}},setup(e){const t=e,a={sun:{paths:["M12 1v2","M12 21v2","M4.22 4.22l1.42 1.42","M18.36 18.36l1.42 1.42","M1 12h2","M21 12h2","M4.22 19.78l1.42-1.42","M18.36 5.64l1.42-1.42"],circles:[{cx:12,cy:12,r:4}]},moon:{paths:["M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"]},maximize:{paths:["M8 3H5a2 2 0 0 0-2 2v3","M21 8V5a2 2 0 0 0-2-2h-3","M3 16v3a2 2 0 0 0 2 2h3","M16 21h3a2 2 0 0 0 2-2v-3"]},plus:{paths:["M12 5v14","M5 12h14"]},minus:{paths:["M5 12h14"]},rocket:{paths:["M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z","m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z","M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0","M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"]},shrink:{paths:["M14 10l7-7","M3 21l7-7","M21 10h-7V3","M3 14h7v7"]},expand:{paths:["M15 3h6v6","M9 21H3v-6","M21 3l-7 7","M3 21l7-7"]},"layout-grid":{rects:[{x:3,y:3,width:7,height:7,rx:1},{x:14,y:3,width:7,height:7,rx:1},{x:14,y:14,width:7,height:7,rx:1},{x:3,y:14,width:7,height:7,rx:1}]},circle:{circles:[{cx:12,cy:12,r:10}]},dices:{rects:[{x:2,y:11,width:11,height:11,rx:2}],paths:["M7 15h.01","M7 19h.01","M11 19h.01","M22 5V2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2","M22 5h-4a1 1 0 0 0-1 1v4","M22 5c0-1.1-.9-2-2-2","M14 7h.01"]},spiral:{paths:["M12 12a1 1 0 1 0 1 1","M12 7a5 5 0 1 0 5 5","M12 2a10 10 0 1 0 10 10","M12 12a1 1 0 0 0-1-1"]},target:{circles:[{cx:12,cy:12,r:10},{cx:12,cy:12,r:6},{cx:12,cy:12,r:2}]},atom:{circles:[{cx:12,cy:12,r:1}],paths:["M12 2v2","M12 20v2","m4.93 4.93 1.41 1.41","m17.66 17.66 1.41 1.41","M2 12h2","M20 12h2","m6.34 17.66-1.41-1.41","m19.07 4.93-1.41 1.41"]},"git-branch":{circles:[{cx:6,cy:3,r:3},{cx:6,cy:21,r:3},{cx:18,cy:9,r:3}],paths:["M6 6v12","M18 12a6 6 0 0 1-6 6H6"]},sparkles:{paths:["m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z","M5 3L4 5.5 1.5 6.5 4 7.5 5 10l1-2.5 2.5-1L6 5.5 5 3Z","M19 19l-1 2.5-2.5 1L18 23.5l1 2.5 1-2.5 2.5-1-2.5-1-1-2.5Z"]},search:{circles:[{cx:11,cy:11,r:8}],lines:[{x1:21,y1:21,x2:16.65,y2:16.65}]},info:{circles:[{cx:12,cy:12,r:10}],paths:["M12 16v-4","M12 8h.01"]},"trash-2":{paths:["M3 6h18","M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6","M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2","M10 11v6","M14 11v6"]},palette:{circles:[{cx:13.5,cy:6.5,r:.5},{cx:17.5,cy:10.5,r:.5},{cx:8.5,cy:7.5,r:.5},{cx:6.5,cy:12.5,r:.5}],paths:["M12 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10a3 3 0 0 0 3-3c0-.8-.34-1.56-.92-2.1l-.1-.08c-.53-.45-.88-1.1-.88-1.82 0-1.38 1.12-2.5 2.5-2.5H19a3 3 0 0 0 2-5c0-4.42-4.03-8-9-8Z"]},pin:{paths:["M15 4.5l-4 4L7 10l-1.5 1.5 7 7L14 17l1.5-4 4-4L15 4.5z","m9 21 3-3","M14 4.5L19.5 10"]},box:{paths:["M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z","m3.3 7 8.7 5 8.7-5","M12 22V12"]},eye:{paths:["M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"],circles:[{cx:12,cy:12,r:3}]},"eye-off":{paths:["M9.88 9.88a3 3 0 1 0 4.24 4.24","M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68","M6.61 6.61A13.52 13.52 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61","m2 2 20 20"]},"refresh-cw":{paths:["M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8","M21 3v5h-5","M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16","M3 21v-5h5"]},scale:{paths:["m16 16 3-8 3 8c-.87.62-1.92 1-3 1s-2.13-.38-3-1Z","m2 16 3-8 3 8c-.87.62-1.92 1-3 1s-2.13-.38-3-1Z","M7 21h10","M12 3v18","M3 7h18"]},zap:{paths:["M13 2 3 14h9l-1 8 10-12h-9l1-8z"]},x:{paths:["M18 6L6 18","M6 6l12 12"]}},o=n.computed(()=>a[t.name]||a.search),i=n.computed(()=>o.value.paths||[]),s=n.computed(()=>o.value.circles||[]),l=n.computed(()=>o.value.lines||[]),m=n.computed(()=>o.value.rects||[]);return(t,a)=>(n.openBlock(),n.createElementBlock("svg",{xmlns:"http://www.w3.org/2000/svg",width:e.size,height:e.size,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":e.strokeWidth,"stroke-linecap":"round","stroke-linejoin":"round",class:"lucide-icon"},[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(i.value,(e,t)=>(n.openBlock(),n.createElementBlock("path",{key:t,d:e},null,8,c))),128)),(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(s.value,(e,t)=>(n.openBlock(),n.createElementBlock("circle",{key:t,cx:e.cx,cy:e.cy,r:e.r},null,8,d))),128)),(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(l.value,(e,t)=>(n.openBlock(),n.createElementBlock("line",{key:t,x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2},null,8,h))),128)),(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(m.value,(e,t)=>(n.openBlock(),n.createElementBlock("rect",{key:t,x:e.x,y:e.y,width:e.width,height:e.height,rx:e.rx,ry:e.ry},null,8,u))),128))],8,r))}}),[["__scopeId","data-v-6aa14fad"]]),g={class:"radial-center glass shadow-glow"},p=["onClick"],f={class:"radial-icon"},M={class:"radial-label"},k=m(n.defineComponent({__name:"RadialMenu",props:{show:{type:Boolean},x:{},y:{},type:{},items:{}},emits:["action"],setup(e){const t=(e,t)=>{const n=e/t*Math.PI*2-Math.PI/2,a=window.innerWidth<768?100:85;return{transform:`translate(-50%, -50%) translate(${Math.cos(n)*a}px, ${Math.sin(n)*a}px)`,transitionDelay:.05*e+"s"}};return(a,o)=>e.show?(n.openBlock(),n.createElementBlock("div",{key:0,class:"radial-menu",style:n.normalizeStyle({left:e.x+"px",top:e.y+"px"}),onMousedown:o[0]||(o[0]=n.withModifiers(()=>{},["stop"])),onTouchstart:o[1]||(o[1]=n.withModifiers(()=>{},["stop"]))},[n.createElementVNode("div",g,[n.renderSlot(a.$slots,"center",{},()=>["node"===e.type?(n.openBlock(),n.createBlock(v,{key:0,name:"target",size:"24"})):"edge"===e.type?(n.openBlock(),n.createBlock(v,{key:1,name:"git-branch",size:"24"})):(n.openBlock(),n.createBlock(v,{key:2,name:"search",size:"24"}))],!0)]),(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.items,(o,i)=>(n.openBlock(),n.createElementBlock("div",{key:i,class:"radial-item",style:n.normalizeStyle(t(i,e.items.length))},[n.createElementVNode("button",{class:"radial-item-inner glass-interactive",onClick:e=>a.$emit("action",o.action)},[n.createElementVNode("span",f,[n.createVNode(v,{name:o.icon,size:"20"},null,8,["name"])]),n.createElementVNode("span",M,n.toDisplayString(o.label),1)],8,p)],4))),128))],36)):n.createCommentVNode("",!0)}}),[["__scopeId","data-v-9d75fdc7"]]),w={class:"top-right-controls glass"},y=["title"],b=m(n.defineComponent({__name:"ViewControls",props:{theme:{}},emits:["toggleTheme","fit","zoomIn","zoomOut","stress"],setup(e){const t=n.ref(!1),a=()=>{document.fullscreenElement?(document.exitFullscreen(),t.value=!1):(document.documentElement.requestFullscreen(),t.value=!0)};return(o,i)=>(n.openBlock(),n.createElementBlock("div",w,[n.createElementVNode("button",{class:"control-btn glass-interactive",onClick:i[0]||(i[0]=e=>o.$emit("toggleTheme")),title:"dark"===e.theme?"Switch to Light":"Switch to Dark"},[n.createVNode(v,{name:"dark"===e.theme?"moon":"sun",size:"20"},null,8,["name"])],8,y),i[5]||(i[5]=n.createElementVNode("div",{class:"divider"},null,-1)),n.createElementVNode("button",{class:"control-btn glass-interactive",onClick:i[1]||(i[1]=e=>o.$emit("fit")),title:"Fit View"},[n.createVNode(v,{name:"maximize",size:"20"})]),n.createElementVNode("button",{class:"control-btn glass-interactive",onClick:i[2]||(i[2]=e=>o.$emit("zoomIn")),title:"Zoom In"},[n.createVNode(v,{name:"plus",size:"20"})]),n.createElementVNode("button",{class:"control-btn glass-interactive",onClick:i[3]||(i[3]=e=>o.$emit("zoomOut")),title:"Zoom Out"},[n.createVNode(v,{name:"minus",size:"20"})]),i[6]||(i[6]=n.createElementVNode("div",{class:"divider"},null,-1)),n.createElementVNode("button",{class:"control-btn glass-interactive stress-btn",onClick:i[4]||(i[4]=e=>o.$emit("stress")),title:"Stress Test (100k Nodes)"},[n.createVNode(v,{name:"rocket",size:"20"})]),i[7]||(i[7]=n.createElementVNode("div",{class:"divider"},null,-1)),n.createElementVNode("button",{class:"control-btn glass-interactive",onClick:a,title:"Toggle Fullscreen"},[n.createVNode(v,{name:t.value?"shrink":"expand",size:"20"},null,8,["name"])])]))}}),[["__scopeId","data-v-f90ff709"]]),E={class:"layout-controls glass"},x={class:"scroll-container"},B=["onClick"],N={class:"btn-icon"},C={class:"btn-name"},V=m(n.defineComponent({__name:"LayoutControls",props:{layouts:{},currentLayout:{},flowEnabled:{type:Boolean}},emits:["runLayout","toggleFlow"],setup:e=>(t,a)=>(n.openBlock(),n.createElementBlock("div",E,[n.createElementVNode("div",x,[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(e.layouts,a=>(n.openBlock(),n.createElementBlock("button",{key:a.id,class:n.normalizeClass(["layout-btn glass-interactive",{active:e.currentLayout===a.id}]),onClick:e=>t.$emit("runLayout",a.id)},[n.createElementVNode("span",N,[n.createVNode(v,{name:a.icon,size:"16"},null,8,["name"])]),n.createElementVNode("span",C,n.toDisplayString(a.name),1)],10,B))),128))])]))}),[["__scopeId","data-v-2650a643"]]),S={key:0,class:"property-panel glass node-panel"},I={class:"panel-header"},_={class:"panel-content"},F={key:0,class:"val-container"},A=["src"],R={key:1},L={key:1,class:"json-prop"},T={key:1,class:"property-panel glass edge-panel"},P={class:"panel-header"},z={class:"panel-content"},D={class:"prop-group"},U={class:"id-badge"},Y={key:0,class:"val-container"},O=["src"],X={key:1},j={key:1,class:"json-prop"},W={key:2,class:"property-panel glass combo-panel"},Z={class:"panel-header"},H={class:"panel-content"},$={class:"prop-group shadow-sm"},G=["value"],q={class:"prop-group"},K={class:"member-list"},J=["onClick"],Q={key:0,class:"more-hint"},ee=m(n.defineComponent({__name:"PropertyPanel",props:{nodeId:{},nodeMetadata:{},nodeX:{},nodeY:{},edgeId:{},edgeMetadata:{},combo:{}},emits:["update:nodeX","update:nodeY","update:nodeId","update:edgeId","update:combo","update:comboLabel","removeFromCombo"],setup(e,{emit:t}){const a=e=>"string"==typeof e&&e.startsWith("data:image/");return(t,o)=>null!==e.nodeId?(n.openBlock(),n.createElementBlock("div",S,[n.createElementVNode("div",I,[o[4]||(o[4]=n.createElementVNode("h3",null,"Node Properties",-1)),n.createElementVNode("button",{class:"close-btn",onClick:o[0]||(o[0]=e=>t.$emit("update:nodeId",null))},[n.createVNode(v,{name:"x",size:"18"})])]),n.createElementVNode("div",_,[e.nodeMetadata&&e.nodeMetadata.data?(n.openBlock(!0),n.createElementBlock(n.Fragment,{key:0},n.renderList(e.nodeMetadata.data,(t,o)=>(n.openBlock(),n.createElementBlock("div",{key:o,class:"prop-group"},[n.createElementVNode("label",null,n.toDisplayString(o),1),"object"!=typeof t?(n.openBlock(),n.createElementBlock("div",F,[a(t)?(n.openBlock(),n.createElementBlock("img",{key:0,src:t,class:"prop-image",style:n.normalizeStyle("backgroundImage"===o?{backgroundColor:e.nodeMetadata.data.color||"var(--primary)"}:{})},null,12,A)):(n.openBlock(),n.createElementBlock("span",R,n.toDisplayString(t),1))])):(n.openBlock(),n.createElementBlock("pre",L,n.toDisplayString(JSON.stringify(t,null,2)),1))]))),128)):n.createCommentVNode("",!0)])])):null!==e.edgeId?(n.openBlock(),n.createElementBlock("div",T,[n.createElementVNode("div",P,[o[5]||(o[5]=n.createElementVNode("h3",null,"Edge Properties",-1)),n.createElementVNode("button",{class:"close-btn",onClick:o[1]||(o[1]=e=>t.$emit("update:edgeId",null))},[n.createVNode(v,{name:"x",size:"18"})])]),n.createElementVNode("div",z,[n.createElementVNode("div",D,[o[6]||(o[6]=n.createElementVNode("label",null,"ID",-1)),n.createElementVNode("span",U,n.toDisplayString(e.edgeId),1)]),e.edgeMetadata&&e.edgeMetadata.data?(n.openBlock(!0),n.createElementBlock(n.Fragment,{key:0},n.renderList(e.edgeMetadata.data,(t,o)=>(n.openBlock(),n.createElementBlock("div",{key:o,class:"prop-group"},[n.createElementVNode("label",null,n.toDisplayString(o),1),"object"!=typeof t?(n.openBlock(),n.createElementBlock("div",Y,[a(t)?(n.openBlock(),n.createElementBlock("img",{key:0,src:t,class:"prop-image",style:n.normalizeStyle("backgroundImage"===o?{backgroundColor:e.edgeMetadata.data.color||"var(--secondary)"}:{})},null,12,O)):(n.openBlock(),n.createElementBlock("span",X,n.toDisplayString(t),1))])):(n.openBlock(),n.createElementBlock("pre",j,n.toDisplayString(JSON.stringify(t,null,2)),1))]))),128)):n.createCommentVNode("",!0)])])):e.combo?(n.openBlock(),n.createElementBlock("div",W,[n.createElementVNode("div",Z,[o[7]||(o[7]=n.createElementVNode("h3",null,"Group Properties",-1)),n.createElementVNode("button",{class:"close-btn",onClick:o[2]||(o[2]=e=>t.$emit("update:combo",null))},[n.createVNode(v,{name:"x",size:"18"})])]),n.createElementVNode("div",H,[n.createElementVNode("div",$,[o[8]||(o[8]=n.createElementVNode("label",null,"Label",-1)),n.createElementVNode("input",{type:"text",value:e.combo.label,class:"full-width",onInput:o[3]||(o[3]=e=>t.$emit("update:comboLabel",e.target.value))},null,40,G)]),n.createElementVNode("div",q,[n.createElementVNode("label",null,"Members ("+n.toDisplayString(e.combo.nodeIds.size)+")",1),n.createElementVNode("div",K,[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(Array.from(e.combo.nodeIds).slice(0,50),e=>(n.openBlock(),n.createElementBlock("div",{key:e,class:"member-item"},[n.createElementVNode("span",null,"Node "+n.toDisplayString(e),1),n.createElementVNode("button",{class:"remove-btn",onClick:n=>t.$emit("removeFromCombo",e)},[n.createVNode(v,{name:"trash-2",size:"12"})],8,J)]))),128)),e.combo.nodeIds.size>50?(n.openBlock(),n.createElementBlock("div",Q," + "+n.toDisplayString(e.combo.nodeIds.size-50)+" more ",1)):n.createCommentVNode("",!0)])])])])):n.createCommentVNode("",!0)}}),[["__scopeId","data-v-205cc0b7"]]),te={class:"search-icon"},ne={key:0,class:"results-panel"},ae=["onMousedown","onTouchstart"],oe={class:"node-icon"},ie={class:"node-info"},se={class:"node-label"},le={class:"node-id"},re={key:1,class:"no-results"},ce=m(n.defineComponent({__name:"SearchBar",props:{nodes:{}},emits:["select"],setup(e,{emit:t}){const a=e,o=t,i=n.ref(""),s=n.ref(!1),l=n.ref(null),r=n.ref(window.innerWidth<768),c=()=>{r.value=window.innerWidth<768};n.onMounted(()=>{window.addEventListener("resize",c)}),n.onUnmounted(()=>{window.removeEventListener("resize",c)});const d=n.computed(()=>{if(!i.value)return[];const e=i.value.toLowerCase();return a.nodes.filter(t=>t.label?.toLowerCase().includes(e)||t.id.toString().includes(e)).slice(0,10)}),h=e=>{o("select",e),i.value="",s.value=!1},u=()=>{r.value&&!s.value&&(s.value=!0,setTimeout(()=>l.value?.focus(),100))},m=()=>{i.value="",s.value=!1,l.value?.blur()},g=()=>{r.value||setTimeout(()=>{s.value=!1},200)};return(e,t)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["search-wrapper",{"mobile-expanded":s.value&&r.value,"is-mobile":r.value}])},[n.createElementVNode("div",{class:n.normalizeClass(["search-container glass",{expanded:s.value,"has-query":i.value.length>0}])},[n.createElementVNode("div",{class:"search-input-wrapper",onClick:u},[n.createElementVNode("span",te,[n.createVNode(v,{name:"search",size:r.value?20:14},null,8,["size"])]),n.withDirectives(n.createElementVNode("input",{ref_key:"inputRef",ref:l,"onUpdate:modelValue":t[0]||(t[0]=e=>i.value=e),placeholder:"Search nodes...",onFocus:t[1]||(t[1]=e=>s.value=!0),onBlur:g,onKeydown:t[2]||(t[2]=n.withKeys(e=>s.value=!1,["esc"]))},null,544),[[n.vModelText,i.value]]),i.value||r.value&&s.value?(n.openBlock(),n.createElementBlock("div",{key:0,class:"clear-btn",onClick:n.withModifiers(m,["stop"])},[n.createVNode(v,{name:i.value?"x":"chevron-down",size:r.value?20:12},null,8,["name","size"])])):n.createCommentVNode("",!0)]),s.value&&d.value.length>0?(n.openBlock(),n.createElementBlock("div",ne,[(n.openBlock(!0),n.createElementBlock(n.Fragment,null,n.renderList(d.value,e=>(n.openBlock(),n.createElementBlock("div",{key:e.id,class:"result-item",onMousedown:t=>h(e.id),onTouchstart:t=>h(e.id)},[n.createElementVNode("span",oe,[n.createVNode(v,{name:"circle",size:"8"})]),n.createElementVNode("div",ie,[n.createElementVNode("div",se,n.toDisplayString(e.label),1),n.createElementVNode("div",le,"ID: "+n.toDisplayString(e.id),1)])],40,ae))),128))])):s.value&&i.value?(n.openBlock(),n.createElementBlock("div",re," No nodes found ")):n.createCommentVNode("",!0)],2),r.value&&s.value?(n.openBlock(),n.createElementBlock("div",{key:0,class:"mobile-backdrop",onClick:m})):n.createCommentVNode("",!0)],2))}}),[["__scopeId","data-v-283de02c"]]),de={key:0,class:"mobile-menu glass shadow-lg"},he={class:"drawer-header"},ue={key:0,class:"drawer-content"},me={key:1,class:"drawer-content"},ve={class:"help-header"},ge={class:"help-content"},pe={class:"key-badge"},fe={class:"action-desc"},Me=m(n.defineComponent({__name:"GraphCanvas",props:{data:{}},setup(e){const t=e,o=n.ref(null),i=n.ref(null),s=n.ref(1e4),r=n.ref(null),c=n.ref(null),d=n.ref(null),h=n.ref(null),u=n.ref(0),m=n.ref("dark"),g=n.ref(null),p=n.ref(0),f=n.ref(0),M=n.ref(null),w=n.ref(null),y=n.ref("random"),E=n.ref(!1),x=n.ref(0),B=n.ref([]),N=n.ref(!1),C=n.ref(window.innerWidth<768),S=n.ref(null),I=n.ref(!1),_=n.ref(null),F=e=>{S.value===e?S.value=null:S.value=e},A=e=>{Q(e),S.value=null},R=()=>{I.value=!0,setTimeout(()=>{_.value?.handleWrapperClick?.()},50)},L=()=>{C.value=window.innerWidth<768},T=[{key:"Shift + Drag",action:"Box Selection"},{key:"Ctrl + Click",action:"Multi Selection"},{key:"Right Click",action:"Radial Menu"},{key:"Scroll",action:"Zoom In/Out"},{key:"Drag",action:"Pan View"}],P=[{id:"random",name:"Random",icon:"dices"},{id:"grid",name:"Grid",icon:"layout-grid"},{id:"circle",name:"Circle",icon:"circle"},{id:"spiral",name:"Spiral",icon:"spiral"},{id:"concentric",name:"Concentric",icon:"target"},{id:"radial",name:"Radial",icon:"atom"},{id:"hierarchy",name:"Hierarchy",icon:"git-branch"},{id:"force",name:"Force",icon:"sparkles"}];let z,D,U,Y=null;const O=()=>{m.value="dark"===m.value?"light":"dark",z&&z.setTheme(m.value),document.documentElement.classList.toggle("light","light"===m.value)},X=()=>{null!==c.value&&z&&z.setNodePosition(c.value,p.value,f.value)};n.watch(c,()=>(()=>{if(null!==c.value&&z){const e=z.getNodePosition(c.value);e&&(p.value=Number(e[0].toFixed(3)),f.value=Number(e[1].toFixed(3))),M.value=z.getNodeMetadata(c.value)}else M.value=null})()),n.watch(p,()=>X()),n.watch(f,()=>X()),n.watch(h,e=>{g.value=e&&z?z.getCombo(e):null}),n.watch(g,e=>{null===e&&(h.value=null)});const j=e=>{if(h.value&&z){const t=z.getCombo(h.value);t&&(z.updateCombo(t.id,e,Array.from(t.nodeIds)),g.value={...t,label:e})}},W=e=>{if(h.value&&z){z.removeNodeFromCombo(h.value,e);const t=z.getCombo(h.value);t&&(g.value={...t,nodeIds:new Set(t.nodeIds)})}},Z=()=>{E.value=!E.value,z&&z.setFlowAnimation(E.value)},H=e=>{if(z){z.selectNode(e);const t=z.getNodePosition(e);t&&(z.setCameraCenter(t[0],t[1]),z.setCameraZoom(2))}I.value=!1},$=n.ref({show:!1,x:0,y:0,type:"none",target:null}),G=n.ref([]),q=e=>{const t=o.value?.getBoundingClientRect();if(!t||!z)return;const n=e.clientX-t.left,a=e.clientY-t.top,i=z.pick(n,a);if(null!==i)$.value={show:!0,x:e.clientX,y:e.clientY,type:"node",target:i},G.value=[{label:"Delete",icon:"trash-2",action:"delete"},{label:"Color",icon:"palette",action:"color"},{label:"Pin",icon:"pin",action:"pin"},{label:"Info",icon:"info",action:"info"}],u.value>1&&G.value.push({label:"Group",icon:"box",action:"create-combo"});else{const t=z.getEdgeAt(n,a);if(t)$.value={show:!0,x:e.clientX,y:e.clientY,type:"edge",target:t},G.value=[{label:"Hide",icon:"eye-off",action:"hide"},{label:"Weight",icon:"scale",action:"weight"},{label:"Reverse",icon:"refresh-cw",action:"reverse"}];else{const t=z.pickCombo(n,a);t?($.value={show:!0,x:e.clientX,y:e.clientY,type:"none",target:t},G.value=[{label:"Dissolve",icon:"zap",action:"dissolve-combo"},{label:"Add Selected",icon:"plus",action:"add-to-group"}]):$.value.show=!1}}},K=e=>{if(z)if("create-combo"===e){const e=z.getSelectedNodeIds(),t=`combo-${Date.now()}`;z.createCombo(t,`Group ${t.split("-")[1]}`,e),x.value++}else if("dissolve-combo"===e)$.value.target&&(z.removeCombo($.value.target),x.value--);else if("add-to-group"===e){const e=z.getSelectedNodeIds();if($.value.target)for(const t of e)z.addNodeToCombo($.value.target,t)}$.value.show=!1},J=e=>{if(!z)return;s.value=e;const t=new Float32Array(2*e),n=new Float32Array(4*e),a=new Float32Array(e);D=new Float32Array(e);const o=new Map;for(let s=0;s<e;s++)t[2*s]=50*(Math.random()-.5),t[2*s+1]=50*(Math.random()-.5),n[4*s]=Math.random(),n[4*s+1]=Math.random(),n[4*s+2]=Math.random(),n[4*s+3]=1,a[s]=s,D[s]=.05,o.set(s,{label:`S-${s}`,width:50,height:50});const i=[];for(let s=0;s<5e4;s++)i.push(Math.floor(Math.random()*e)),i.push(Math.floor(Math.random()*e));U=new Uint32Array(i),z.setData(t,n,a,o,U,new Map),Y&&Y.postMessage({type:"init",data:{nodeCount:e,positions:t,nodeRadii:D,edgeIndices:U}}),z.fitToView()},Q=e=>{y.value=e,Y&&Y.postMessage({type:"layout",data:{layoutType:e,nodeRadii:D,edgeIndices:U}})};return n.onMounted(()=>{if(window.addEventListener("resize",L),o.value&&i.value){z=new l(o.value,i.value);const e=t.data.nodes||[],n=t.data.edges||[],m=e.length;s.value=m;const v=new Float32Array(2*m),g=new Float32Array(4*m),M=new Float32Array(m);D=new Float32Array(m);const k=new Map,y=new Map,b=e=>{const t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?[parseInt(t[1],16)/255,parseInt(t[2],16)/255,parseInt(t[3],16)/255]:[Math.random(),Math.random(),Math.random()]};e.forEach((e,t)=>{const n=e.data,a=e.position||{x:0,y:0};y.set(n.id,t),v[2*t]=((a.x??2500)-2500)/500,v[2*t+1]=((a.y??2500)-2500)/500;const o=n.color||"#000000",i=b(o);g[4*t]=i[0],g[4*t+1]=i[1],g[4*t+2]=i[2],g[4*t+3]=1,M[t]=t;const s=n.width||50,l=n.height||50;D[t]=Math.max(s,l)/1e3;const r={label:n.label||n.code||`Node ${n.id}`,image:n.backgroundImage,width:s,height:l,data:n};if(n.backgroundImage){const e=new Image;e.src=n.backgroundImage,e.onload=()=>{const t=document.createElement("canvas"),n=128;t.width=n,t.height=n;const a=t.getContext("2d");a&&(a.beginPath(),a.arc(64,64,64,0,2*Math.PI),a.clip(),a.filter="grayscale(100%) brightness(1000%)",a.drawImage(e,0,0,n,n),r.processedImageElement=t)},r.imageElement=e}k.set(t,r)}),B.value=Array.from(k.entries()).map(([e,t])=>({id:e,label:t.label}));const E=[],x=new Map;n.forEach(e=>{const t=e.data.source,n=e.data.target,a=y.get(t),o=y.get(n);if(void 0!==a&&void 0!==o){E.push(a),E.push(o);const t=`${a}-${o}`;x.set(t,{label:e.data.label,data:e.data})}}),U=new Uint32Array(E),z.setData(v,g,M,k,U,x),Y=new Worker(new URL("/assets/layout.worker-CBxeMMC3.js","undefined"==typeof document&&"undefined"==typeof location?require("url").pathToFileURL(__filename).href:"undefined"==typeof document?location.href:a&&"SCRIPT"===a.tagName.toUpperCase()&&a.src||new URL("vortx-gl.umd.js",document.baseURI).href),{type:"module"}),Y.postMessage({type:"init",data:{nodeCount:m,positions:v,nodeRadii:D,edgeIndices:U}}),Y.onmessage=e=>{"layout-complete"===e.data.type&&(e=>{if(!z)return;const t=s.value,n=new Float32Array(2*t);for(let s=0;s<t;s++){const e=z.getNodePosition(s);e&&(n[2*s]=e[0],n[2*s+1]=e[1])}const a=performance.now(),o=new Float32Array(2*t),i=s=>{const l=s-a,r=Math.min(l/1e3,1),c=r<.5?4*r**3:1-Math.pow(-2*r+2,3)/2;if(n&&e)for(let a=0;a<2*t;a++)o[a]=n[a]+(e[a]-n[a])*c;z.updatePositions(o),r<1?requestAnimationFrame(i):z.fitToView()};requestAnimationFrame(i)})(e.data.positions)},window.addEventListener("mousedown",()=>{$.value.show=!1}),o.value.addEventListener("node-picked",e=>{r.value=e.detail}),o.value.addEventListener("node-selected",e=>{c.value=e.detail,null!==e.detail&&(d.value=null,h.value=null)}),o.value.addEventListener("edge-selected",e=>{d.value=e.detail,null!==e.detail?(c.value=null,h.value=null,w.value=z.getEdgeMetadata(e.detail)):w.value=null}),o.value.addEventListener("combo-selected",e=>{h.value=e.detail,null!==e.detail&&(c.value=null,d.value=null)}),o.value.addEventListener("nodes-selected",e=>{u.value=e.detail.length}),o.value.addEventListener("node-moved",e=>{e.detail.id===c.value&&(p.value=Number(e.detail.x.toFixed(3)),f.value=Number(e.detail.y.toFixed(3)))})}}),n.onUnmounted(()=>{window.removeEventListener("resize",L),Y&&Y.terminate()}),(e,t)=>(n.openBlock(),n.createElementBlock("div",{class:n.normalizeClass(["container vortx-gl-root",m.value])},[n.createElementVNode("canvas",{ref_key:"canvasRef",ref:o,class:"main-canvas",onContextmenu:n.withModifiers(q,["prevent"])},null,544),n.createElementVNode("canvas",{ref_key:"overlayCanvasRef",ref:i,class:"overlay-canvas"},null,512),C.value?(n.openBlock(),n.createElementBlock("div",de,[n.createElementVNode("button",{class:n.normalizeClass(["menu-btn",{active:"layouts"===S.value}]),onClick:t[0]||(t[0]=e=>F("layouts"))},[n.createVNode(v,{name:"layout",size:"20"}),t[22]||(t[22]=n.createElementVNode("span",null,"Layout",-1))],2),n.createElementVNode("button",{class:n.normalizeClass(["menu-btn",{active:"view"===S.value}]),onClick:t[1]||(t[1]=e=>F("view"))},[n.createVNode(v,{name:"eye",size:"20"}),t[23]||(t[23]=n.createElementVNode("span",null,"View",-1))],2),t[25]||(t[25]=n.createElementVNode("div",{class:"menu-divider"},null,-1)),n.createElementVNode("button",{class:"menu-btn search-trigger",onClick:R},[n.createVNode(v,{name:"search",size:"20"}),t[24]||(t[24]=n.createElementVNode("span",null,"Search",-1))])])):n.createCommentVNode("",!0),C.value&&S.value?(n.openBlock(),n.createElementBlock("div",{key:1,class:"drawer-overlay",onClick:t[8]||(t[8]=e=>S.value=null)},[n.createElementVNode("div",{class:"mobile-drawer glass shadow-2xl",onClick:t[7]||(t[7]=n.withModifiers(()=>{},["stop"]))},[t[26]||(t[26]=n.createElementVNode("div",{class:"drawer-handle"},null,-1)),n.createElementVNode("div",he,[n.createElementVNode("h3",null,n.toDisplayString("layouts"===S.value?"Graph Layouts":"View Controls"),1),n.createElementVNode("button",{class:"drawer-close",onClick:t[2]||(t[2]=e=>S.value=null)},[n.createVNode(v,{name:"x",size:"20"})])]),"layouts"===S.value?(n.openBlock(),n.createElementBlock("div",ue,[n.createVNode(V,{layouts:P,currentLayout:y.value,flowEnabled:E.value,onRunLayout:A,onToggleFlow:Z},null,8,["currentLayout","flowEnabled"])])):n.createCommentVNode("",!0),"view"===S.value?(n.openBlock(),n.createElementBlock("div",me,[n.createVNode(b,{theme:m.value,onToggleTheme:O,onFit:t[3]||(t[3]=e=>n.unref(z)?.fitToView()),onZoomIn:t[4]||(t[4]=e=>n.unref(z)?.zoomIn()),onZoomOut:t[5]||(t[5]=e=>n.unref(z)?.zoomOut()),onStress:t[6]||(t[6]=e=>J(1e5))},null,8,["theme"])])):n.createCommentVNode("",!0)])])):n.createCommentVNode("",!0),n.withDirectives(n.createVNode(ce,{ref_key:"searchBarRef",ref:_,nodes:B.value,onSelect:H},null,8,["nodes"]),[[n.vShow,!C.value||I.value]]),C.value?n.createCommentVNode("",!0):(n.openBlock(),n.createBlock(b,{key:2,theme:m.value,onToggleTheme:O,onFit:t[9]||(t[9]=e=>n.unref(z)?.fitToView()),onZoomIn:t[10]||(t[10]=e=>n.unref(z)?.zoomIn()),onZoomOut:t[11]||(t[11]=e=>n.unref(z)?.zoomOut()),onStress:t[12]||(t[12]=e=>J(1e5))},null,8,["theme"])),C.value?n.createCommentVNode("",!0):(n.openBlock(),n.createBlock(V,{key:3,layouts:P,currentLayout:y.value,flowEnabled:E.value,onRunLayout:Q,onToggleFlow:Z},null,8,["currentLayout","flowEnabled"])),n.createVNode(k,{show:$.value.show,x:$.value.x,y:$.value.y,type:$.value.type,items:G.value,onAction:K},null,8,["show","x","y","type","items"]),n.createVNode(ee,{nodeId:c.value,"onUpdate:nodeId":t[13]||(t[13]=e=>c.value=e),nodeMetadata:M.value,nodeX:p.value,"onUpdate:nodeX":t[14]||(t[14]=e=>p.value=e),nodeY:f.value,"onUpdate:nodeY":t[15]||(t[15]=e=>f.value=e),edgeId:d.value,"onUpdate:edgeId":t[16]||(t[16]=e=>d.value=e),edgeMetadata:w.value,combo:g.value,"onUpdate:combo":t[17]||(t[17]=e=>g.value=e),"onUpdate:comboLabel":j,onRemoveFromCombo:W},null,8,["nodeId","nodeMetadata","nodeX","nodeY","edgeId","edgeMetadata","combo"]),C.value?n.createCommentVNode("",!0):(n.openBlock(),n.createElementBlock("div",{key:4,class:"help-trigger glass-interactive",onClick:t[18]||(t[18]=e=>N.value=!0),title:"Keyboard Shortcuts"},[n.createVNode(v,{name:"info",size:"18"})])),N.value?(n.openBlock(),n.createElementBlock("div",{key:5,class:"help-modal-overlay",onClick:t[21]||(t[21]=e=>N.value=!1)},[n.createElementVNode("div",{class:"help-modal glass",onClick:t[20]||(t[20]=n.withModifiers(()=>{},["stop"]))},[n.createElementVNode("div",ve,[t[27]||(t[27]=n.createElementVNode("h3",null,"Keyboard Shortcuts",-1)),n.createElementVNode("button",{class:"close-btn",onClick:t[19]||(t[19]=e=>N.value=!1)},[n.createVNode(v,{name:"x",size:"20"})])]),n.createElementVNode("div",ge,[(n.openBlock(),n.createElementBlock(n.Fragment,null,n.renderList(T,e=>n.createElementVNode("div",{key:e.key,class:"shortcut-row"},[n.createElementVNode("span",pe,n.toDisplayString(e.key),1),n.createElementVNode("span",fe,n.toDisplayString(e.action),1)])),64))])])])):n.createCommentVNode("",!0)],2))}}),[["__scopeId","data-v-d6f5f92c"]]);e.GraphCanvas=Me,e.GraphEngine=l,e.createGraphData=(e,t)=>({nodes:e,edges:t}),Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("gl-matrix"),require("vue")):"function"==typeof define&&define.amd?define(["exports","gl-matrix","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VortxGL={},e.glMatrix,e.Vue)}(this,function(e,t,o){"use strict";var s="undefined"!=typeof document?document.currentScript:null;class i{gl;constructor(e){this.gl=e}pickNode(e){const t=this.gl,{mouseX:o,mouseY:s,canvas:i,program:a,pickingFramebuffer:n,vao:r,viewMatrix:l,pointSize:d,nodeCount:c}=e;t.bindFramebuffer(t.FRAMEBUFFER,n),t.viewport(0,0,i.width,i.height),t.clearColor(0,0,0,0),t.clear(t.COLOR_BUFFER_BIT),t.useProgram(a),t.uniformMatrix3fv(t.getUniformLocation(a,"u_matrix"),!1,l),t.uniform1f(t.getUniformLocation(a,"u_pointSize"),d),t.uniform1i(t.getUniformLocation(a,"u_pickingMode"),1),t.bindVertexArray(r),t.drawArrays(t.POINTS,0,c);const h=new Uint8Array(4),u=window.devicePixelRatio;if(t.readPixels(Math.floor(o*u),Math.floor(i.height-s*u),1,1,t.RGBA,t.UNSIGNED_BYTE,h),t.bindFramebuffer(t.FRAMEBUFFER,null),0===h[3])return null;return(h[0]<<16|h[1]<<8|h[2])-1}pickEdge(e){const{mouseX:o,mouseY:s,edgeIndices:i,positions:a,nodeCount:n,viewMatrix:r,canvasW:l,canvasH:d}=e;if(!i||!a)return null;const c=window.devicePixelRatio,h=o*c,u=s*c,m=5*c,g=t.mat3.invert(t.mat3.create(),r);if(!g)return null;const f=t.vec3.fromValues(h/l*2-1,-(u/d*2-1),1);t.vec3.transformMat3(f,f,g);const v=f[0],p=f[1],b=2*m/Math.abs(r[0]+r[4]);for(let M=0;M<i.length/2;M++){const e=i[2*M],o=i[2*M+1];if(e>=n||o>=n)continue;const s=a[2*e],c=a[2*e+1],g=a[2*o],f=a[2*o+1];if(v<(s<g?s:g)-b||v>(s>g?s:g)+b||p<(c<f?c:f)-b||p>(c>f?c:f)+b)continue;const E=this.project(s,c,r,l,d),w=this.project(g,f,r,l,d);if(this.distToSegment(t.vec2.fromValues(h,u),E,w)<m)return`${e}-${o}`}return null}pickCombo(e){const{mouseX:t,mouseY:o,combos:s,positions:i,nodeCount:a,viewMatrix:n,zoom:r,canvasW:l,canvasH:d}=e;if(!i)return null;const c=window.devicePixelRatio,h=t*c,u=o*c;for(const m of s.values()){let e=1/0,t=1/0,o=-1/0,s=-1/0,g=!1;for(const r of m.nodeIds){if(r>=a)continue;const c=this.project(i[2*r],i[2*r+1],n,l,d);e=Math.min(e,c[0]),t=Math.min(t,c[1]),o=Math.max(o,c[0]),s=Math.max(s,c[1]),g=!0}if(g){const i=30*r*c;if(h>=e-i&&h<=o+i&&u>=t-i&&u<=s+i)return m.id}}return null}distToSegment(e,o,s){const i=t.vec2.dist(o,s)**2;if(0===i)return t.vec2.dist(e,o);let a=((e[0]-o[0])*(s[0]-o[0])+(e[1]-o[1])*(s[1]-o[1]))/i;a=Math.max(0,Math.min(1,a));const n=t.vec2.fromValues(o[0]+a*(s[0]-o[0]),o[1]+a*(s[1]-o[1]));return t.vec2.dist(e,n)}project(e,o,s,i,a){const n=t.vec3.fromValues(e,o,1);return t.vec3.transformMat3(n,n,s),t.vec2.fromValues((.5*n[0]+.5)*i,(.5*-n[1]+.5)*a)}}class a{canvas;ctx;hudRects=new Map;projectedCache=new Float32Array(0);constructor(e,t){this.canvas=e,this.ctx=t}_posScratch=t.vec3.create();_spScratch=t.vec2.create();_s1Scratch=t.vec2.create();_s2Scratch=t.vec2.create();project(e,o,s,i){return t.vec3.set(this._posScratch,e,o,1),t.vec3.transformMat3(this._posScratch,this._posScratch,s),i[0]=(.5*this._posScratch[0]+.5)*this.canvas.width,i[1]=(.5*-this._posScratch[1]+.5)*this.canvas.height,i}getThemeColor(e,t){return{dark:{primary:"#00ffcc",secondary:"rgba(0, 255, 204, 0.6)",danger:"#ff6b6b",edge:"rgba(255,255,255,0.15)"},light:{primary:"#00aa88",secondary:"rgba(0, 170, 136, 0.5)",danger:"#d00000",edge:"rgba(0,0,0,0.1)"}}[e][t]}render(e){const{ctx:o,canvas:s}=this,{positions:i,nodeCount:a,hiddenNodes:n,edgeCount:r,edgeIndices:l,metadata:d,edgeMetadata:c,combos:h,theme:u,zoom:m,viewMatrix:g,selectedNodeIds:f,selectedEdgeId:v,selectedComboId:p,hoveredNodeId:b,lockedNodes:M,isBoxSelecting:E,selectionStart:w,selectionEnd:y,phantomEdge:I,edgeIndexToUserIdMap:N}=e;if(!i)return;const x=t.mat3.invert(t.mat3.create(),g);if(!x)return;const k=t.vec3.fromValues(-1.1,1.1,1),C=t.vec3.fromValues(1.1,-1.1,1);t.vec3.transformMat3(k,k,x),t.vec3.transformMat3(C,C,x);const B=Math.min(k[0],C[0]),R=Math.max(k[0],C[0]),A=Math.min(k[1],C[1]),F=Math.max(k[1],C[1]);this.projectedCache.length<2*a&&(this.projectedCache=new Float32Array(2*a));const V=this._s1Scratch,S=.2*(R-B),_=.2*(F-A),D=B-S,L=R+S,T=A-_,P=F+_;for(let t=0;t<a;t++){const e=i[2*t],o=i[2*t+1];n.has(t)||e<D||e>L||o<T||o>P?this.projectedCache[2*t]=NaN:(this.project(e,o,g,V),this.projectedCache[2*t]=V[0],this.projectedCache[2*t+1]=V[1])}o.clearRect(0,0,s.width,s.height);const z=window.devicePixelRatio,U=40*m*z;E&&this.drawMarquee(w,y,z),this.drawCombos(h,i,a,g,m,p,u,z),this.drawNodeHighlights(f,a,i,g,U,z),this.drawEdgeHighlight(v,l,i,g,z),l&&N&&this.drawCustomEdges(l,r,c,N,u,z),e.flow.enabled&&l&&this.drawFlow(l,r,m,z,u,e.flow,c,e.selectedNodes),I&&this.drawPhantomEdge(I,i,g,z),null!==b&&l&&this.drawHoverHighlight(b,l,r,u,z),m>.1&&l&&N&&this.drawEdgeLabels(l,r,c,N,u,z,m),m>.1&&this.drawNodeLabels(a,d,i,u,z,U,m,{vMinX:B,vMaxX:R,vMinY:A,vMaxY:F},M)}drawMarquee(e,t,o){const s=Math.min(e[0],t[0])*o,i=Math.min(e[1],t[1])*o,a=Math.abs(e[0]-t[0])*o,n=Math.abs(e[1]-t[1])*o;this.ctx.strokeStyle="rgba(0, 255, 204, 0.8)",this.ctx.fillStyle="rgba(0, 255, 204, 0.1)",this.ctx.lineWidth=1*o,this.ctx.strokeRect(s,i,a,n),this.ctx.fillRect(s,i,a,n)}drawCombos(e,t,o,s,i,a,n,r){const l=this.ctx,d=this._spScratch;for(const c of e.values()){let e=1/0,h=1/0,u=-1/0,m=-1/0,g=!1;const f=Array.from(c.nodeIds);for(const i of f){if(i>=o)continue;const a=this.project(t[2*i],t[2*i+1],s,d);e=Math.min(e,a[0]),h=Math.min(h,a[1]),u=Math.max(u,a[0]),m=Math.max(m,a[1]),g=!0}if(g){const o=30*r,g=e-o,v=h-o,p=m-h+2*o;if(g+(u-e+2*o)<0||g>this.canvas.width||v+p<0||v>this.canvas.height)continue;const b=a===c.id;if(l.save(),c.collapsed){if(0===f.length)continue;const e=f[0];if(void 0===e)continue;const o=this.project(t[2*e],t[2*e+1],s,d),a=25*i*r;l.beginPath(),l.arc(o[0],o[1],a,0,2*Math.PI),l.fillStyle=b?"rgba(0, 255, 204, 0.4)":"rgba(0, 255, 204, 0.2)",l.strokeStyle="#00ffcc",l.lineWidth=3*r,l.fill(),l.stroke(),l.fillStyle="dark"===n?"#fff":"#000",l.font=`bold ${14*i*r}px Inter, sans-serif`,l.textAlign="center",l.fillText(c.label,o[0],o[1]+a+20*i*r);const h=o[0]-a,u=o[1]-a-35*r;this.drawGroupHUD(l,c,h,u,r)}else{const t=40*i*r,o=e-t,s=h-t,a=u-e+2*t,d=m-h+2*t;l.beginPath();const g=16*i*r;l.moveTo(o+g,s),l.arcTo(o+a,s,o+a,s+d,g),l.arcTo(o+a,s+d,o,s+d,g),l.arcTo(o,s+d,o,s,g),l.arcTo(o,s,o+a,s,g),l.closePath(),l.fillStyle=b?"rgba(0, 255, 204, 0.15)":"dark"===n?"rgba(0, 255, 204, 0.05)":"rgba(0, 170, 136, 0.05)",l.strokeStyle=b?"rgba(0, 255, 204, 0.8)":"dark"===n?"rgba(0, 255, 204, 0.2)":"rgba(0, 170, 136, 0.2)",l.lineWidth=(b?3:2)*i*r,l.setLineDash(b?[]:[5*r,5*r]),l.fill(),l.stroke(),l.setLineDash([]);const f=o,v=s-30*r;this.drawGroupHUD(l,c,f,v,r)}l.restore()}}}drawGroupHUD(e,t,o,s,i){const a=12*i,n=(t.label||"Group").toUpperCase();e.font=`bold ${11*i}px Inter, sans-serif`;const r=2*a+e.measureText(n).width+24*i,l=32*i;e.fillStyle="rgba(18, 18, 22, 0.9)",e.strokeStyle="rgba(255, 255, 255, 0.1)",e.lineWidth=1*i,e.beginPath(),e.roundRect(o-6*i,s-6*i,r,l,[12*i]),e.fill(),e.stroke(),e.beginPath(),e.arc(o+a,s+10*i,a,0,2*Math.PI),e.fillStyle=t.collapsed?"rgba(0, 255, 204, 1)":"rgba(255,255,255,0.1)",e.fill(),t.collapsed||(e.strokeStyle="rgba(0, 255, 204, 0.5)",e.stroke()),e.fillStyle=t.collapsed?"#000":"#00ffcc",e.font=`800 ${14*i}px Inter, sans-serif`,e.textAlign="center",e.textBaseline="middle",e.fillText(t.collapsed?"+":"−",o+a,s+10*i),e.textAlign="left",e.fillStyle="#ffffff",e.font=`bold ${11*i}px Inter, sans-serif`,e.fillText(n,o+2*a+8*i,s+10*i),this.hudRects.set(t.id,{x:o-6*i,y:s-6*i,w:r,h:l})}pickHUDAction(e,t){const o=window.devicePixelRatio,s=e*o,i=t*o;for(const[a,n]of this.hudRects)if(s>=n.x&&s<=n.x+n.w&&i>=n.y&&i<=n.y+n.h)return{comboId:a,action:"expand"};return null}drawNodeHighlights(e,t,o,s,i,a){const n=this.ctx,r=this._spScratch;n.strokeStyle="#00ffcc",n.lineWidth=2*a;for(const l of e)l>=t||(this.project(o[2*l],o[2*l+1],s,r),r[0]<-50||r[0]>this.canvas.width+50||r[1]<-50||r[1]>this.canvas.height+50||(n.beginPath(),n.arc(r[0],r[1],.7*i,0,2*Math.PI),n.stroke()))}drawEdgeHighlight(e,t,o,s,i){if(!e||!t)return;const a=this.ctx,n=e.split("-"),r=Number(n[0]),l=Number(n[1]);if(isNaN(r)||isNaN(l))return;const d=this._s1Scratch,c=this._s2Scratch;this.project(o[2*r],o[2*r+1],s,d),this.project(o[2*l],o[2*l+1],s,c);const h=Math.min(d[0],c[0]),u=Math.max(d[0],c[0]),m=Math.min(d[1],c[1]),g=Math.max(d[1],c[1]);if(u<-20||h>this.canvas.width+20||g<-20||m>this.canvas.height+20)return;const f=this.projectedCache[2*r],v=this.projectedCache[2*r+1],p=this.projectedCache[2*l],b=this.projectedCache[2*l+1];isNaN(f)||isNaN(p)||(a.strokeStyle="#ff00ff",a.lineWidth=3*i,a.beginPath(),a.moveTo(f,v),a.lineTo(p,b),a.stroke())}drawPhantomEdge(e,t,o,s){const i=this.ctx,a=this._s1Scratch,n=this._s2Scratch,r=e.sourceNodeId;this.project(t[2*r],t[2*r+1],o,a),this.project(e.targetPos[0],e.targetPos[1],o,n),i.save(),i.strokeStyle="rgba(0, 255, 204, 0.6)",i.lineWidth=2*s,i.setLineDash([5*s,5*s]),i.beginPath(),i.moveTo(a[0],a[1]),i.lineTo(n[0],n[1]),i.stroke(),i.restore()}drawFlow(e,t,o,s,i,a,n,r){const{velocity:l,size:d,color:c,filterProp:h,filterValue:u}=a,m=this.ctx,g=performance.now()*l,f=Math.max(1,Math.floor(t/4e4)),v=d*Math.min(3,Math.pow(1/o,.25))*s;m.save();const p=a.opacity??1,b=a.pulse?.6+.4*Math.sin(.005*performance.now()):1;m.globalAlpha=p*b,m.globalCompositeOperation="dark"===i?"lighter":"source-over",m.beginPath(),m.fillStyle=c||("dark"===i?"#00ffcc":"#00aa88");for(let M=0;M<t/2;M+=f){const t=e[2*M],o=e[2*M+1];if(a.selectedOnly&&!r.has(t)&&!r.has(o))continue;if(h){const e=`${t}-${o}`,s=n.get(e);if(!s||!s.data||s.data[h]!==u)continue}const s=this.projectedCache[2*t],i=this.projectedCache[2*t+1],l=this.projectedCache[2*o],d=this.projectedCache[2*o+1];if(isNaN(s)||isNaN(l))continue;const c=(g+.1*M%1)%1,f=s+(l-s)*c,p=i+(d-i)*c;m.moveTo(f+v,p),m.arc(f,p,v,0,2*Math.PI)}m.fill(),m.restore()}drawHoverHighlight(e,t,o,s,i){const a=this.ctx;a.lineWidth=2*i,a.strokeStyle=this.getThemeColor(s,"secondary");for(let n=0;n<o/2;n++){const o=t[2*n],s=t[2*n+1];if(o===e||s===e){const e=this.projectedCache[2*o],t=this.projectedCache[2*o+1],i=this.projectedCache[2*s],n=this.projectedCache[2*s+1];if(isNaN(e)||isNaN(i))continue;a.beginPath(),a.moveTo(e,t),a.lineTo(i,n),a.stroke()}}}drawCustomEdges(e,t,o,s,i,a){const n=this.ctx;n.save();for(let r=0;r<t/2;r++){const t=e[2*r],l=e[2*r+1],d=s.get(`${t}-${l}`);if(!d)continue;const c=o.get(d);if(c&&c.data&&(c.data.customColor||c.data.customSize)){const e=this.projectedCache[2*t],o=this.projectedCache[2*t+1],s=this.projectedCache[2*l],r=this.projectedCache[2*l+1];if(isNaN(e)||isNaN(s))continue;n.beginPath(),n.strokeStyle=c.data.customColor||("dark"===i?"rgba(255,255,255,0.4)":"rgba(0,0,0,0.2)"),n.lineWidth=(c.data.customSize||1.5)*a,n.lineCap="round",n.moveTo(e,o),n.lineTo(s,r),n.stroke()}}n.restore()}drawEdgeLabels(e,t,o,s,i,a,n){if(n<.3)return;const r=this.ctx;r.font=`bold ${10*a}px Inter, sans-serif`,r.textAlign="center",r.textBaseline="middle";for(let l=0;l<t/2;l++){const t=e[2*l],n=e[2*l+1],d=s.get(`${t}-${n}`);if(!d)continue;const c=o.get(d);if(c&&c.label){const e=this.projectedCache[2*t],o=this.projectedCache[2*t+1],s=this.projectedCache[2*n],l=this.projectedCache[2*n+1];if(isNaN(e)||isNaN(s))continue;const d=(e+s)/2,h=(o+l)/2;if(d<0||d>this.canvas.width||h<0||h>this.canvas.height)continue;const u=r.measureText(c.label).width;r.fillStyle="dark"===i?"rgba(0,0,0,0.8)":"rgba(255,255,255,0.8)",r.fillRect(d-u/2-4*a,h-8*a,u+8*a,16*a),r.fillStyle="dark"===i?"#fff":"#000",r.fillText(c.label,d,h)}}}drawNodeLabels(e,t,o,s,i,a,n,r,l){const d=this.ctx;d.textAlign="center",d.textBaseline="top";let c=0;const h=60*i,u=new Set,m=Math.min(1,Math.max(0,15*(n-.02)));if(m<=0)return;d.globalAlpha=m;const g=Array.from({length:e},(e,t)=>t);e<1e4&&g.sort((e,t)=>(l.has(t)?1:0)-(l.has(e)?1:0));for(const f of g){const e=o[2*f],g=o[2*f+1];if(e<r.vMinX||e>r.vMaxX||g<r.vMinY||g>r.vMaxY)continue;const v=t.get(f);if(!v)continue;const p=this.projectedCache[2*f],b=this.projectedCache[2*f+1];if(isNaN(p))continue;if(p<-50||p>this.canvas.width+50||b<-50||b>this.canvas.height+50)continue;const M=.5*a,E=n>.04,w=a>6*i;if(w&&(d.beginPath(),d.arc(p,b,M,0,2*Math.PI),d.strokeStyle="dark"===s?"rgba(255, 255, 255, 0.8)":"rgba(0, 0, 0, 0.8)",d.lineWidth=1*i,d.stroke()),E&&(this.drawNodeIcon(d,v,p,b,a,M,s,i),c++),l.has(f)&&w&&this.drawLockIcon(d,p,b,M,s,i),c>800)break;const y=l.has(f)?.05:.12;if(n<y)continue;const I=`${Math.floor(p/h)},${Math.floor(b/h)}`;if(u.has(I))continue;u.add(I);const N=Math.min(m,Math.max(0,12*(n-y)));if(d.globalAlpha=N,v.label){const e=M+8*i,t=Math.max(8*i,Math.min(13*i,10*n*i*4));d.font=`${t}px Inter, sans-serif`,d.shadowBlur=4*i,d.shadowColor="dark"===s?"rgba(0,0,0,1)":"rgba(255,255,255,1)",d.fillStyle="dark"===s?"rgba(255,255,255,1)":"rgba(0,0,0,1)",d.fillText(v.label,p,b+e),d.shadowBlur=0}}d.globalAlpha=1}drawNodeIcon(e,t,o,s,i,a,n,r){if(t.processedImageElement){const a=.7*i;e.drawImage(t.processedImageElement,o-a/2,s-a/2,a,a)}else if(t.imageElement?.complete){const n=.7*i;e.save(),e.beginPath(),e.arc(o,s,.9*a,0,2*Math.PI),e.clip(),e.filter="grayscale(100%) brightness(1000%)",e.drawImage(t.imageElement,o-n/2,s-n/2,n,n),e.restore()}else if(t.icon){const a=Math.max(12*r,.6*i);e.font=`${a}px serif`,e.fillStyle="dark"===n?"white":"black",e.fillText(t.icon,o,s-.2*i)}}drawLockIcon(e,t,o,s,i,a){const n=Math.max(8*a,Math.min(12*a,.4*s));e.font=`${n}px serif`,e.fillStyle="dark"===i?"#ff6b6b":"#d00000",e.shadowBlur=2*a,e.shadowColor="rgba(0,0,0,0.5)",e.fillText("🔒",t+.7*s,o-.7*s)}}class n{lastMouse=t.vec2.create();isPanning=!1;engine;canvas;lastTouchDistance=0;lastTouchMidpoint=t.vec2.create();isTouching=!1;touchStartTime=0;touchStartPos=t.vec2.create();longPressTimer=null;isLongPress=!1;isDraggingCombo=!1;constructor(e,t){this.engine=e,this.canvas=t,this.setupListeners()}setupListeners(){this.canvas.addEventListener("mousedown",e=>this.handleMouseDown(e)),window.addEventListener("mousemove",e=>this.handleMouseMove(e)),window.addEventListener("mouseup",()=>this.handleMouseUp()),this.canvas.addEventListener("wheel",e=>this.handleWheel(e),{passive:!1}),this.canvas.addEventListener("touchstart",e=>this.handleTouchStart(e),{passive:!1}),this.canvas.addEventListener("touchmove",e=>this.handleTouchMove(e),{passive:!1}),this.canvas.addEventListener("touchend",e=>this.handleTouchEnd(e)),this.canvas.addEventListener("touchcancel",e=>this.handleTouchEnd(e))}handleMouseDown(e){const o=this.canvas.getBoundingClientRect(),s=e.clientX-o.left,i=e.clientY-o.top,a=this.engine.getState(),n=this.engine.pickHUD(s,i);if(n)return void("expand"===n.action&&n.comboId&&this.engine.toggleComboCollapse(n.comboId));if(e.shiftKey)return this.engine.updateInteractionState({isBoxSelecting:!0,selectionStart:t.vec2.fromValues(s,i),selectionEnd:t.vec2.fromValues(s,i)}),void this.engine.render();const r=this.engine.pick(s,i);if(null!==r){const t=a.selectedNodeIds.has(r);e.ctrlKey||e.metaKey||t||a.selectedNodeIds.clear(),a.selectedNodeIds.add(r),this.engine.updateInteractionState({isMultiDragging:!0,draggedNodeId:null}),this.engine.syncDragOffsets(s,i),this.engine.emitEvent("node:mousedown",{id:this.engine.getNodeId(r)??r,index:r}),this.engine.emitEvent("node:click",{id:this.engine.getNodeId(r)??r,index:r}),this.engine.emitEvent("nodes:select",{ids:Array.from(a.selectedNodeIds).map(e=>this.engine.getNodeId(e)??e),indices:Array.from(a.selectedNodeIds)}),this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:Array.from(a.selectedNodeIds)})),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:r}))}else{a.selectedNodeIds.clear();const e=this.engine.getEdgeAt(s,i),t=this.engine.pickCombo(s,i);this.engine.updateInteractionState({selectedEdgeId:e,selectedComboId:t}),e&&(this.engine.emitEvent("edge:mousedown",{id:e}),this.engine.emitEvent("edge:click",{id:e}),this.engine.emitEvent("edge:select",{id:e}),this.canvas.dispatchEvent(new CustomEvent("edge-selected",{detail:e}))),t&&(this.isDraggingCombo=!0,this.engine.emitEvent("combo:mousedown",{id:t}),this.engine.emitEvent("combo:click",{id:t}),this.engine.emitEvent("combo:select",{id:t}),this.canvas.dispatchEvent(new CustomEvent("combo-selected",{detail:t}))),e||t||(this.isPanning=!0,this.engine.emitEvent("graph:click",{x:s,y:i}),this.engine.emitEvent("graph:mousedown",{x:s,y:i}),this.canvas.dispatchEvent(new CustomEvent("graph:click",{detail:{x:s,y:i}})))}t.vec2.set(this.lastMouse,s,i),this.engine.render()}handleMouseMove(e){const o=this.canvas.getBoundingClientRect(),s=e.clientX-o.left,i=e.clientY-o.top,a=this.engine.getState();if(a.isBoxSelecting)return this.engine.updateInteractionState({selectionEnd:t.vec2.fromValues(s,i)}),void this.engine.render();if(a.isMultiDragging||null!==a.draggedNodeId)this.engine.handleDragging(s,i);else{if(this.isDraggingCombo&&a.selectedComboId){const e=window.devicePixelRatio,o=this.engine.unproject(s*e,i*e),n=this.engine.unproject(this.lastMouse[0]*e,this.lastMouse[1]*e),r=o[0]-n[0],l=o[1]-n[1];return this.engine.moveGroup(a.selectedComboId,r,l),void t.vec2.set(this.lastMouse,s,i)}if(this.isPanning){const e=window.devicePixelRatio,o=(s-this.lastMouse[0])*e/(this.canvas.width/2),a=-(i-this.lastMouse[1])*e/(this.canvas.height/2);this.engine.pan(o,a),t.vec2.set(this.lastMouse,s,i)}else{const e=a.hoveredNodeId,t=this.engine.pick(s,i);if(t!==e&&(this.engine.updateInteractionState({hoveredNodeId:t}),this.engine.render(),null!==t?this.engine.emitEvent("node:hover",{id:this.engine.getNodeId(t)??t,index:t}):this.engine.emitEvent("node:hoverout",{prevIndex:e})),null===t){const e=this.engine.getEdgeAt(s,i),t=this.engine.pickCombo(s,i);e&&this.engine.emitEvent("edge:hover",{id:e}),t&&this.engine.emitEvent("combo:hover",{id:t})}}}}handleMouseUp(){const e=this.engine.getState();this.isPanning&&this.engine.emitEvent("graph:mouseup"),this.isPanning=!1,this.isDraggingCombo=!1,e.isBoxSelecting&&(this.engine.performBoxSelection(),this.engine.emitEvent("boxselect",{indices:Array.from(e.selectedNodeIds),ids:Array.from(e.selectedNodeIds).map(e=>this.engine.getNodeId(e)??e)}),this.engine.emitEvent("boxend")),this.engine.updateInteractionState({isBoxSelecting:!1,isMultiDragging:!1,draggedNodeId:null}),this.engine.render()}handleWheel(e){e.preventDefault();const t=this.canvas.getBoundingClientRect(),o=e.clientX-t.left,s=e.clientY-t.top,i=.001*-e.deltaY;this.engine.zoomAt(o,s,i)}handleTouchStart(e){if(e.preventDefault(),this.isTouching=!0,this.touchStartTime=Date.now(),this.isLongPress=!1,1===e.touches.length){const o=e.touches[0],s=this.canvas.getBoundingClientRect(),i=o.clientX-s.left,a=o.clientY-s.top;t.vec2.set(this.touchStartPos,i,a),t.vec2.set(this.lastMouse,i,a),this.longPressTimer=window.setTimeout(()=>{this.isLongPress=!0,this.triggerContextMenu(o.clientX,o.clientY)},500);const n=this.engine.pick(i,a),r=this.engine.getState();null!==n&&(r.selectedNodeIds.has(n)||(r.selectedNodeIds.clear(),r.selectedNodeIds.add(n)),this.engine.updateInteractionState({isMultiDragging:!0,draggedNodeId:null}),this.engine.syncDragOffsets(i,a),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:n})))}else if(2===e.touches.length){this.clearLongPress();const o=e.touches[0],s=e.touches[1];this.lastTouchDistance=t.vec2.distance(t.vec2.fromValues(o.clientX,o.clientY),t.vec2.fromValues(s.clientX,s.clientY)),t.vec2.set(this.lastTouchMidpoint,(o.clientX+s.clientX)/2,(o.clientY+s.clientY)/2)}}handleTouchMove(e){if(e.preventDefault(),this.isTouching){if(1===e.touches.length){const o=e.touches[0],s=this.canvas.getBoundingClientRect(),i=o.clientX-s.left,a=o.clientY-s.top;if(t.vec2.distance(this.touchStartPos,t.vec2.fromValues(i,a))>10&&this.clearLongPress(),this.isLongPress)return;if(this.engine.getState().isMultiDragging)this.engine.handleDragging(i,a);else{const e=window.devicePixelRatio,t=(i-this.lastMouse[0])*e/(this.canvas.width/2),o=-(a-this.lastMouse[1])*e/(this.canvas.height/2);this.engine.pan(t,o)}t.vec2.set(this.lastMouse,i,a)}else if(2===e.touches.length){this.clearLongPress();const o=e.touches[0],s=e.touches[1],i=t.vec2.distance(t.vec2.fromValues(o.clientX,o.clientY),t.vec2.fromValues(s.clientX,s.clientY)),a=t.vec2.fromValues((o.clientX+s.clientX)/2,(o.clientY+s.clientY)/2),n=i/this.lastTouchDistance,r=this.canvas.getBoundingClientRect(),l=a[0]-r.left,d=a[1]-r.top;this.engine.zoomAt(l,d,n-1);const c=window.devicePixelRatio,h=(a[0]-this.lastTouchMidpoint[0])*c/(this.canvas.width/2),u=-(a[1]-this.lastTouchMidpoint[1])*c/(this.canvas.height/2);this.engine.pan(h,u),this.lastTouchDistance=i,t.vec2.copy(this.lastTouchMidpoint,a)}this.engine.render()}}handleTouchEnd(e){if(this.isTouching=!1,this.clearLongPress(),0===e.touches.length&&!this.isLongPress){const o=Date.now()-this.touchStartTime,s=e.changedTouches[0],i=this.canvas.getBoundingClientRect(),a=s.clientX-i.left,n=s.clientY-i.top,r=t.vec2.distance(this.touchStartPos,t.vec2.fromValues(a,n));if(o<250&&r<10){const e=this.engine.pick(a,n);this.engine.selectNode(e)}}this.engine.updateInteractionState({isMultiDragging:!1}),this.engine.render()}clearLongPress(){null!==this.longPressTimer&&(window.clearTimeout(this.longPressTimer),this.longPressTimer=null)}triggerContextMenu(e,t){const o=new MouseEvent("contextmenu",{clientX:e,clientY:t,bubbles:!0,cancelable:!0});this.canvas.dispatchEvent(o)}}class r{listeners=new Map;on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){this.listeners.get(e)?.forEach(e=>{try{e(t)}catch(o){}})}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}}class l{gl;pickingEngine;overlayManager;emitter=new r;program=null;edgeProgram=null;viewMatrix=t.mat3.create();vao=null;edgeVao=null;positionBuffer=null;colorBuffer=null;idBuffer=null;edgeIndexBuffer=null;pickingFramebuffer=null;pickingTexture=null;uMatrixLoc=null;uPointSizeLoc=null;uPickingModeLoc=null;uEdgeMatrixLoc=null;uEdgeColorLoc=null;nodeCount=0;edgeCount=0;positions=null;metadata=new Map;edgeIndices=null;edgeMetadata=new Map;combos=new Map;theme="dark";static MAX_ZOOM=100;static MIN_ZOOM=1e-4;colorData=new Float32Array(0);idData=new Float32Array(0);capacity=0;autoNodeId=0;autoEdgeId=0;nodeIdMap=new Map;reverseIdMap=new Map;edgeList=[];edgeIdMap=new Map;edgeIndexToUserIdMap=new Map;stylesheet={};lockedNodes=new Set;hiddenNodes=new Set;hiddenEdges=new Set;state={zoom:1,offset:t.vec2.fromValues(0,0),selectedNodeIds:new Set,selectedEdgeId:null,selectedComboId:null,hoveredNodeId:null,isBoxSelecting:!1,selectionStart:t.vec2.create(),selectionEnd:t.vec2.create(),isMultiDragging:!1,draggedNodeId:null,flow:{enabled:!1,velocity:.002,size:1.5,opacity:1,pulse:!1},phantomEdge:null};dragOffsets=new Map;canvas;overlayCanvas;setPhantomEdge(e,o,s){this.state.phantomEdge={sourceNodeId:e,targetPos:t.vec2.fromValues(o,s)},this.render()}clearPhantomEdge(){this.state.phantomEdge=null,this.render()}constructor(e,t){this.canvas=e,this.overlayCanvas=t;const o=e.getContext("webgl2",{antialias:!0,stencil:!0});if(!o)throw new Error("WebGL2 not supported");this.gl=o,o.enable(o.BLEND),o.blendFunc(o.SRC_ALPHA,o.ONE_MINUS_SRC_ALPHA);const s=t.getContext("2d");if(!s)throw new Error("Canvas 2D not supported");this.pickingEngine=new i(o),this.overlayManager=new a(t,s),this.initShaders(),this.initBuffers(),this.initPicking(),this.updateMatrix(),new n(this,e),this.handleResize(),window.addEventListener("resize",()=>this.handleResize())}on(e,t){return this.emitter.on(e,t)}off(e,t){this.emitter.off(e,t)}emitEvent(e,t){this.emitter.emit(e,t)}setStylesheet(e){this.stylesheet=e,this.render()}resolveStyle(e){if(!e||0===e.length)return{};const t={};for(const o of e){const e=this.stylesheet[o];e&&Object.assign(t,e)}return t}addNode(e){const t=e.id??"vortx-node-"+this.autoNodeId++;if(this.nodeIdMap.has(t))throw new Error(`[VortxGL] Node '${t}' already exists. Use a unique ID.`);const o=this.nodeCount;this.ensureNodeCapacity(o+1);const s=this.resolveStyle(e.classes),i=e.position?.x??0,a=e.position?.y??0;this.positions[2*o]=i,this.positions[2*o+1]=a;const n=e.color||s.color,[r,l,d,c]=this.parseColor(n);this.colorData[4*o]=r,this.colorData[4*o+1]=l,this.colorData[4*o+2]=d,this.colorData[4*o+3]=c,this.idData[o]=o,this.nodeIdMap.set(t,o),this.reverseIdMap.set(o,t);const h={label:e.label||s.label,icon:e.icon||s.icon,image:e.image||s.image,data:e.data,classes:e.classes,width:e.size||s.size||50,height:e.size||s.size||50};this.metadata.set(o,h),e.image&&this.loadNodeImage(o,e.image),this.nodeCount++;const u=this.gl;return u.bindBuffer(u.ARRAY_BUFFER,this.positionBuffer),u.bufferSubData(u.ARRAY_BUFFER,8*o,this.positions.subarray(2*o,2*o+2)),u.bindBuffer(u.ARRAY_BUFFER,this.colorBuffer),u.bufferSubData(u.ARRAY_BUFFER,16*o,this.colorData.subarray(4*o,4*o+4)),u.bindBuffer(u.ARRAY_BUFFER,this.idBuffer),u.bufferSubData(u.ARRAY_BUFFER,4*o,this.idData.subarray(o,o+1)),this.emitter.emit("node:add",{id:t,index:o,position:{x:i,y:a}}),this.render(),t}updateNode(e){if(void 0===e.id)throw new Error("[VortxGL] Must provide node ID for update.");const t=this.nodeIdMap.get(e.id);if(void 0===t)throw new Error(`[VortxGL] Node '${e.id}' not found for update.`);const o=this.resolveStyle(e.classes||this.metadata.get(t)?.classes);e.position&&(this.positions[2*t]=e.position.x,this.positions[2*t+1]=e.position.y);const s=e.color||(e.classes?o.color:void 0);if(s){const[e,o,i,a]=this.parseColor(s);this.colorData[4*t]=e,this.colorData[4*t+1]=o,this.colorData[4*t+2]=i,this.colorData[4*t+3]=a}const i=this.metadata.get(t);if(i){e.classes&&(i.classes=e.classes),(void 0!==e.label||o.label)&&(i.label=e.label||o.label),(void 0!==e.icon||o.icon)&&(i.icon=e.icon||o.icon),(void 0!==e.image||o.image)&&(i.image=e.image||o.image,i.image&&this.loadNodeImage(t,i.image)),void 0!==e.data&&(i.data=e.data);const s=e.size||(e.classes?o.size:void 0);void 0!==s&&(i.width=s,i.height=s)}const a=this.gl;e.position&&(a.bindBuffer(a.ARRAY_BUFFER,this.positionBuffer),a.bufferSubData(a.ARRAY_BUFFER,8*t,this.positions.subarray(2*t,2*t+2))),e.color&&(a.bindBuffer(a.ARRAY_BUFFER,this.colorBuffer),a.bufferSubData(a.ARRAY_BUFFER,16*t,this.colorData.subarray(4*t,4*t+4))),this.emitter.emit("node:update",{id:e.id,index:t}),this.render()}removeNode(e){const t=this.nodeIdMap.get(e);if(void 0===t)return;const o=this.edgeList.filter(e=>e.u===t||e.v===t).map(e=>e.id);for(const i of o)this._removeEdgeInternal(i);const s=this.nodeCount-1;if(t!==s){this.positions[2*t]=this.positions[2*s],this.positions[2*t+1]=this.positions[2*s+1],this.colorData[4*t]=this.colorData[4*s],this.colorData[4*t+1]=this.colorData[4*s+1],this.colorData[4*t+2]=this.colorData[4*s+2],this.colorData[4*t+3]=this.colorData[4*s+3],this.idData[t]=t;const e=this.metadata.get(s);e&&this.metadata.set(t,e),this.metadata.delete(s);const o=this.reverseIdMap.get(s);this.nodeIdMap.set(o,t),this.reverseIdMap.set(t,o);for(const a of this.edgeList)a.u===s&&(a.u=t),a.v===s&&(a.v=t);this.rebuildEdgeIndexToUserIdMap(),this.lockedNodes.has(s)&&(this.lockedNodes.delete(s),this.lockedNodes.add(t)),this.hiddenNodes.has(s)&&(this.hiddenNodes.delete(s),this.hiddenNodes.add(t)),this.state.selectedNodeIds.has(s)&&(this.state.selectedNodeIds.delete(s),this.state.selectedNodeIds.add(t)),this.state.hoveredNodeId===s&&(this.state.hoveredNodeId=t);const i=this.gl;i.bindBuffer(i.ARRAY_BUFFER,this.positionBuffer),i.bufferSubData(i.ARRAY_BUFFER,8*t,this.positions.subarray(2*t,2*t+2)),i.bindBuffer(i.ARRAY_BUFFER,this.colorBuffer),i.bufferSubData(i.ARRAY_BUFFER,16*t,this.colorData.subarray(4*t,4*t+4)),i.bindBuffer(i.ARRAY_BUFFER,this.idBuffer),i.bufferSubData(i.ARRAY_BUFFER,4*t,this.idData.subarray(t,t+1))}else this.metadata.delete(s);this.nodeIdMap.delete(e),this.reverseIdMap.delete(s),this.lockedNodes.delete(s),this.hiddenNodes.delete(s),this.state.selectedNodeIds.delete(s),this.nodeCount--,this.rebuildEdgeBuffer(),this.emitter.emit("node:remove",{id:e}),this.render()}addEdge(e){const t=this.nodeIdMap.get(e.source),o=this.nodeIdMap.get(e.target);if(void 0===t)throw new Error(`[VortxGL] Source node '${e.source}' not found.`);if(void 0===o)throw new Error(`[VortxGL] Target node '${e.target}' not found.`);const s=e.id??"vortx-edge-"+this.autoEdgeId++;if(this.edgeIdMap.has(s))throw new Error(`[VortxGL] Edge '${s}' already exists.`);const i=this.resolveStyle(e.classes),a={label:e.label||i.label,data:e.data,classes:e.classes};return this.edgeList.push({id:s,u:t,v:o,metadata:a}),this.edgeIdMap.set(s,this.edgeList.length-1),this.edgeMetadata.set(s,a),this.edgeIndexToUserIdMap.set(`${t}-${o}`,s),this.edgeIndexToUserIdMap.set(`${o}-${t}`,s),this.rebuildEdgeBuffer(),this.emitter.emit("edge:add",{id:s,source:e.source,target:e.target}),this.render(),s}updateEdge(e){if(!e.id)throw new Error("[VortxGL] Must provide edge ID for update.");const t=this.edgeIdMap.get(e.id);if(void 0===t)throw new Error(`[VortxGL] Edge '${e.id}' not found.`);const o=this.edgeList[t];void 0!==e.label&&(o.metadata.label=e.label),void 0!==e.data&&(o.metadata.data=e.data),this.emitter.emit("edge:update",{id:e.id}),this.render()}removeEdge(e){this._removeEdgeInternal(e),this.rebuildEdgeBuffer(),this.emitter.emit("edge:remove",{id:e}),this.render()}clear(){this.nodeCount=0,this.edgeCount=0,this.edgeList=[],this.edgeIdMap.clear(),this.edgeIndexToUserIdMap.clear(),this.nodeIdMap.clear(),this.reverseIdMap.clear(),this.metadata.clear(),this.edgeMetadata.clear(),this.edgeIndices=null,this.combos.clear(),this.lockedNodes.clear(),this.hiddenNodes.clear(),this.hiddenEdges.clear(),this.state.selectedNodeIds.clear(),this.state.selectedEdgeId=null,this.state.hoveredNodeId=null,this.emitter.emit("graph:clear",{}),this.render()}getAllNodes(){const e=[];for(const[t,o]of this.nodeIdMap){const s=this.metadata.get(o);e.push({id:t,_index:o,position:{x:this.positions?this.positions[2*o]:0,y:this.positions?this.positions[2*o+1]:0},label:s?.label,icon:s?.icon,image:s?.image,data:s?.data,size:s?.width})}return e}getAllEdges(){return this.edgeList.map(e=>({id:e.id,source:this.reverseIdMap.get(e.u)??e.u,target:this.reverseIdMap.get(e.v)??e.v,label:e.metadata.label,data:e.metadata.data}))}getNodesByClass(e){return this.getAllNodes().filter(t=>{const o=this.metadata.get(t._index);return o?.classes?.includes(e)})}getEdgesByClass(e){return this.getAllEdges().filter(t=>{const o=this.edgeMetadata.get(t.id);return o?.classes?.includes(e)})}getNode(e){const t=this.resolveNodeIndex(e);if(null===t)return null;const o=this.metadata.get(t);return{id:e,_index:t,position:{x:this.positions[2*t],y:this.positions[2*t+1]},label:o?.label,icon:o?.icon,data:o?.data,classes:o?.classes}}getInternalIndex(e){return this.nodeIdMap.get(e)??null}getNodeId(e){return this.reverseIdMap.get(e)??null}getNodeMetadata(e){const t=this.resolveNodeIndex(e);return null===t?null:this.metadata.get(t)??null}getEdgeMetadata(e){return this.edgeMetadata.get(e)??null}lockNode(e){const t=this.resolveNodeIndex(e);null!==t&&(this.lockedNodes.add(t),this.render())}unlockNode(e){const t=this.resolveNodeIndex(e);null!==t&&(this.lockedNodes.delete(t),this.render())}isNodeLocked(e){return this.lockedNodes.has(e)}hideNode(e){const t=this.resolveNodeIndex(e);if(null===t)return;this.hiddenNodes.add(t),this.colorData[4*t+3]=0;const o=this.gl;o.bindBuffer(o.ARRAY_BUFFER,this.colorBuffer),o.bufferSubData(o.ARRAY_BUFFER,16*t+12,this.colorData.subarray(4*t+3,4*t+4)),this.render()}showNode(e){const t=this.resolveNodeIndex(e);if(null===t)return;this.hiddenNodes.delete(t),this.colorData[4*t+3]=1;const o=this.gl;o.bindBuffer(o.ARRAY_BUFFER,this.colorBuffer),o.bufferSubData(o.ARRAY_BUFFER,16*t+12,this.colorData.subarray(4*t+3,4*t+4)),this.render()}isNodeHidden(e){return this.hiddenNodes.has(e)}setNodeColor(e,t,o,s,i=1){const a=this.resolveNodeIndex(e);if(null===a)return;this.colorData[4*a]=t,this.colorData[4*a+1]=o,this.colorData[4*a+2]=s,this.hiddenNodes.has(a)||(this.colorData[4*a+3]=i);const n=this.gl;n.bindBuffer(n.ARRAY_BUFFER,this.colorBuffer),n.bufferSubData(n.ARRAY_BUFFER,16*a,this.colorData.subarray(4*a,4*a+4)),this.render()}setNodeLabel(e,t){const o=this.resolveNodeIndex(e);if(null===o)return;let s=this.metadata.get(o);s?s.label=t:(s={label:t,width:50,height:50},this.metadata.set(o,s)),this.render()}setNodeData(e,t){const o=this.resolveNodeIndex(e);if(null===o)return;let s=this.metadata.get(o);s?s.data={...s.data||{},...t}:(s={data:t,width:50,height:50},this.metadata.set(o,s))}hideEdge(e){this.hiddenEdges.add(e),this.rebuildEdgeBuffer(),this.render()}showEdge(e){this.hiddenEdges.delete(e),this.rebuildEdgeBuffer(),this.render()}reverseEdge(e){const t=this.edgeMetadata.get(e),o=e.split("-");if(o.length<2)return;const s=this.resolveNodeIndex(o[0]),i=this.resolveNodeIndex(o[1]);if(null!==s&&null!==i&&this.edgeIndices){for(let e=0;e<this.edgeIndices.length;e+=2)if(this.edgeIndices[e]===s&&this.edgeIndices[e+1]===i){this.edgeIndices[e]=i,this.edgeIndices[e+1]=s;break}t&&(this.edgeMetadata.delete(e),this.edgeMetadata.set(`${o[1]}-${o[0]}`,t)),this.render()}}setEdgeLabel(e,t){const o=this.edgeMetadata.get(e);o&&(o.label=t,this.render())}setEdgeData(e,t){const o=this.edgeMetadata.get(e);o&&(o.data={...o.data,...t})}deselectAll(){this.state.selectedNodeIds.clear(),this.state.selectedEdgeId=null,this.state.selectedComboId=null,this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:null})),this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:[]})),this.emitter.emit("nodes:select",{ids:[],indices:[]}),this.render()}setData(e,t,o,s,i,a,n){if(this.positions=e,this.nodeCount=o.length,this.metadata=s,this.capacity=o.length,this.colorData=new Float32Array(t),this.idData=new Float32Array(o),this.nodeIdMap.clear(),this.reverseIdMap.clear(),n){this.nodeIdMap=new Map(n);for(const[e,t]of n)this.reverseIdMap.set(t,e)}else for(let l=0;l<this.nodeCount;l++)this.nodeIdMap.set(l,l),this.reverseIdMap.set(l,l);const r=this.gl;if(r.bindBuffer(r.ARRAY_BUFFER,this.positionBuffer),r.bufferData(r.ARRAY_BUFFER,e,r.DYNAMIC_DRAW),r.bindBuffer(r.ARRAY_BUFFER,this.colorBuffer),r.bufferData(r.ARRAY_BUFFER,t,r.DYNAMIC_DRAW),r.bindBuffer(r.ARRAY_BUFFER,this.idBuffer),r.bufferData(r.ARRAY_BUFFER,o,r.DYNAMIC_DRAW),i){this.edgeIndices=i,this.edgeCount=i.length,this.edgeList=[],this.edgeIdMap.clear(),this.edgeIndexToUserIdMap.clear();for(let e=0;e<i.length/2;e++){const t=i[2*e],o=i[2*e+1],s=`${t}-${o}`,n=a?.get(s)??{};this.edgeList.push({id:s,u:t,v:o,metadata:n}),this.edgeIdMap.set(s,e),this.edgeIndexToUserIdMap.set(`${t}-${o}`,s),this.edgeIndexToUserIdMap.set(`${o}-${t}`,s)}r.bindVertexArray(this.edgeVao),r.bindBuffer(r.ELEMENT_ARRAY_BUFFER,this.edgeIndexBuffer),r.bufferData(r.ELEMENT_ARRAY_BUFFER,i,r.DYNAMIC_DRAW),r.bindVertexArray(null)}a&&(this.edgeMetadata=a),this.render()}updatePositions(e){this.positions=e;for(const o of this.combos.values())if(o.collapsed){const t=o.representativeId??Array.from(o.nodeIds)[0];if(void 0===t||!this.positions)continue;const s=this.positions[2*t],i=this.positions[2*t+1];let a=0,n=0,r=0;for(const l of o.nodeIds)l<this.nodeCount&&(a+=e[2*l],n+=e[2*l+1],r++);if(r>0){a/=r,n/=r,o.originalPositions||(o.originalPositions=new Map);for(const t of o.nodeIds)if(t<this.nodeCount){const r=e[2*t]-a,l=e[2*t+1]-n;o.originalPositions.set(t,{x:s+r,y:i+l}),e[2*t]=s,e[2*t+1]=i}}}const t=this.gl;t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,e),this.render()}setNodePosition(e,t,o){if(!this.positions||e>=this.nodeCount)return;this.positions[2*e]=t,this.positions[2*e+1]=o;const s=this.gl;s.bindBuffer(s.ARRAY_BUFFER,this.positionBuffer),s.bufferSubData(s.ARRAY_BUFFER,2*e*4,this.positions.subarray(2*e,2*e+2)),this.render()}getNodePosition(e){return!this.positions||e>=this.nodeCount?null:t.vec2.fromValues(this.positions[2*e],this.positions[2*e+1])}setFlowAnimation(e){if(this.state.flow.enabled=e,e){const e=()=>{this.state.flow.enabled&&(this.render(),requestAnimationFrame(e))};requestAnimationFrame(e)}else this.render()}setTheme(e){this.theme=e,this.render()}setFlowVelocity(e){this.state.flow.velocity=e,this.render()}setFlowSize(e){this.state.flow.size=e,this.render()}setFlowColor(e){this.state.flow.color=e,this.render()}setFlowFilter(e,t=null){this.state.flow.filterProp=e,this.state.flow.filterValue=t,this.render()}createGroup(e,t,o){const s=new Set;for(const i of o){const e=this.resolveNodeIndex(i);null!==e&&s.add(e)}this.combos.set(e,{id:e,label:t,nodeIds:s}),this.emitter.emit("combo:add",{id:e,label:t,nodeIds:o}),this.render()}removeGroup(e){this.combos.delete(e)&&(this.emitter.emit("combo:remove",{id:e}),this.render())}addToGroup(e,t){const o=this.combos.get(e),s=this.resolveNodeIndex(t);o&&null!==s&&(o.nodeIds.add(s),this.emitter.emit("combo:update",{id:e,nodeId:t,action:"add"}),this.render())}removeFromGroup(e,t){const o=this.combos.get(e),s=this.resolveNodeIndex(t);o&&null!==s&&(o.nodeIds.delete(s),this.emitter.emit("combo:update",{id:e,nodeId:t,action:"remove"}),this.render())}toggleComboCollapse(e){const t=this.combos.get(e);if(!t||0===t.nodeIds.size)return;if(t.collapsed=!t.collapsed,t.collapsed){let e=0,o=0;t.nodeIds.forEach(t=>{e+=this.positions[2*t],o+=this.positions[2*t+1]}),e/=t.nodeIds.size,o/=t.nodeIds.size,t.originalPositions=new Map;const s=Array.from(t.nodeIds);t.representativeId=s[0];for(const i of s)t.originalPositions.set(i,{x:this.positions[2*i],y:this.positions[2*i+1]}),this.positions[2*i]=e,this.positions[2*i+1]=o,i!==t.representativeId?(this.hiddenNodes.add(i),this.colorData[4*i+3]=0):(this.hiddenNodes.delete(i),this.colorData[4*i+3]=1);for(const i of this.edgeList)t.nodeIds.has(i.u)&&t.nodeIds.has(i.v)&&this.hiddenEdges.add(i.id)}else{if(t.originalPositions)for(const[e,o]of t.originalPositions)this.positions[2*e]=o.x,this.positions[2*e+1]=o.y,this.hiddenNodes.delete(e),this.colorData[4*e+3]=1;for(const e of this.edgeList)t.nodeIds.has(e.u)&&t.nodeIds.has(e.v)&&this.hiddenEdges.delete(e.id);t.originalPositions=void 0,t.representativeId=void 0}const o=this.gl;o.bindBuffer(o.ARRAY_BUFFER,this.positionBuffer),o.bufferData(o.ARRAY_BUFFER,this.positions,o.DYNAMIC_DRAW),o.bindBuffer(o.ARRAY_BUFFER,this.colorBuffer),o.bufferData(o.ARRAY_BUFFER,this.colorData,o.DYNAMIC_DRAW),this.rebuildEdgeBuffer(),this.emitter.emit("combo:update",{id:e,action:"collapse",state:t.collapsed}),this.render()}moveGroup(e,t,o,s=!1){const i=this.combos.get(e);if(i){for(const e of i.nodeIds)if(this.positions&&!(e>=this.nodeCount)&&(this.positions[2*e]+=t,this.positions[2*e+1]+=o,i.collapsed&&i.originalPositions)){const s=i.originalPositions.get(e);s&&(s.x+=t,s.y+=o)}if(!s){const e=this.gl;e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.bufferData(e.ARRAY_BUFFER,this.positions,e.DYNAMIC_DRAW),this.render()}}}getAllGroups(){return Array.from(this.combos.values()).map(e=>({id:e.id,label:e.label,nodeIds:Array.from(e.nodeIds).map(e=>this.reverseIdMap.get(e)??e)}))}createCombo(e,t,o){this.createGroup(e,t,o)}removeCombo(e){this.removeGroup(e)}updateCombo(e,t,o){this.createGroup(e,t,o)}getCombo(e){return this.combos.get(e)}addNodeToCombo(e,t){this.addToGroup(e,t)}removeNodeFromCombo(e,t){this.removeFromGroup(e,t)}render(){const e=this.gl;if(!this.program)return;this.updateMatrix();const t="dark"===this.theme?.05:.95;e.clearColor(t,t,t,1),e.clear(e.COLOR_BUFFER_BIT),this.edgeCount>0&&this.edgeProgram&&(e.useProgram(this.edgeProgram),e.uniformMatrix3fv(this.uEdgeMatrixLoc,!1,this.viewMatrix),e.uniform4fv(this.uEdgeColorLoc,"dark"===this.theme?[1,1,1,.15]:[0,0,0,.1]),e.bindVertexArray(this.edgeVao),e.drawElements(e.LINES,this.edgeCount,e.UNSIGNED_INT,0)),e.useProgram(this.program),e.uniformMatrix3fv(this.uMatrixLoc,!1,this.viewMatrix),e.uniform1f(this.uPointSizeLoc,40*this.state.zoom*window.devicePixelRatio),e.uniform1i(this.uPickingModeLoc,0),e.bindVertexArray(this.vao),e.drawArrays(e.POINTS,0,this.nodeCount),this.renderOverlay()}renderOverlay(){this.overlayManager.render({...this.state,selectedNodes:this.state.selectedNodeIds,lockedNodes:this.lockedNodes,hiddenNodes:this.hiddenNodes,positions:this.positions,nodeCount:this.nodeCount,edgeCount:this.edgeCount,edgeIndices:this.edgeIndices,metadata:this.metadata,edgeMetadata:this.edgeMetadata,combos:this.combos,theme:this.theme,viewMatrix:this.viewMatrix,edgeIndexToUserIdMap:this.edgeIndexToUserIdMap})}getState(){return this.state}updateInteractionState(e){Object.assign(this.state,e)}setFlowConfig(e){this.state.flow={...this.state.flow,...e},this.render()}pick(e,t){if(!this.program||!this.pickingFramebuffer||!this.vao)return null;const o=this.pickingEngine.pickNode({mouseX:e,mouseY:t,canvas:this.canvas,program:this.program,pickingFramebuffer:this.pickingFramebuffer,vao:this.vao,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,pointSize:40*this.state.zoom*window.devicePixelRatio});return null!==o&&this.hiddenNodes.has(o)?null:o}getEdgeAt(e,t){const o=this.pickingEngine.pickEdge({mouseX:e,mouseY:t,edgeIndices:this.edgeIndices,positions:this.positions,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,canvasW:this.canvas.width,canvasH:this.canvas.height});if(!o)return null;const s=this.edgeIndexToUserIdMap.get(o);return s&&this.hiddenEdges.has(s)?null:s??o}pickCombo(e,t){return this.pickingEngine.pickCombo({mouseX:e,mouseY:t,combos:this.combos,positions:this.positions,nodeCount:this.nodeCount,viewMatrix:this.viewMatrix,zoom:this.state.zoom,canvasW:this.canvas.width,canvasH:this.canvas.height})}pickHUD(e,t){return this.overlayManager.pickHUDAction(e,t)}fitToView(){if(!this.positions||0===this.nodeCount)return this.state.zoom=1,this.state.offset=t.vec2.fromValues(0,0),void this.render();let e=1/0,o=1/0,s=-1/0,i=-1/0;for(let t=0;t<this.nodeCount;t++){if(this.hiddenNodes.has(t))continue;const a=this.positions[2*t],n=this.positions[2*t+1];a<e&&(e=a),a>s&&(s=a),n<o&&(o=n),n>i&&(i=n)}const a=s-e,n=i-o,r=(e+s)/2,l=(o+i)/2,d=2*(this.canvas.width/this.canvas.height)*.9/Math.max(a,.01),c=1.8/Math.max(n,.01);this.state.zoom=Math.min(d,c,100),this.state.offset=t.vec2.fromValues(-r,-l),this.render()}zoomIn(){this.state.zoom=Math.min(l.MAX_ZOOM,1.2*this.state.zoom),this.render()}zoomOut(){this.state.zoom=Math.max(l.MIN_ZOOM,this.state.zoom/1.2),this.render()}setCameraCenter(e,o){this.state.offset=t.vec2.fromValues(-e,-o),this.render()}setCameraZoom(e){this.state.zoom=Math.max(l.MIN_ZOOM,Math.min(l.MAX_ZOOM,e)),this.render()}getSelectedNodeIds(){return Array.from(this.state.selectedNodeIds)}getSelectedUserIds(){return Array.from(this.state.selectedNodeIds).map(e=>this.reverseIdMap.get(e)??e)}selectNode(e){this.state.selectedNodeIds.clear(),null!==e&&this.state.selectedNodeIds.add(e),this.canvas.dispatchEvent(new CustomEvent("node-selected",{detail:e})),this.emitter.emit("node:select",{id:null!==e?this.reverseIdMap.get(e)??e:null,index:e}),this.render()}zoomAt(e,o,s){const i=window.devicePixelRatio,a=this.unproject(e*i,o*i);this.state.zoom=Math.max(l.MIN_ZOOM,Math.min(l.MAX_ZOOM,this.state.zoom*(1+s))),this.updateMatrix();const n=this.unproject(e*i,o*i);t.vec2.add(this.state.offset,this.state.offset,t.vec2.sub(t.vec2.create(),n,a)),this.emitter.emit("graph:zoom",{zoom:this.state.zoom}),this.render()}runLayout(e,t){if(!this.positions)return;const o=t?.selectedOnly?Array.from(this.state.selectedNodeIds):Array.from({length:this.nodeCount},(e,t)=>t);if(0===o.length)return;const s=this.nodeCount,i=new Float32Array(this.positions),a=new Float32Array(this.positions);if("grid"===e){const e=Math.ceil(Math.sqrt(o.length)),t=150;o.forEach((o,s)=>{const i=Math.floor(s/e),n=s%e;a[2*o]=n*t-e*t/2,a[2*o+1]=i*t-e*t/2})}else if("circle"===e){const e=Math.max(200,15*o.length),t=2*Math.PI/o.length;o.forEach((o,s)=>{a[2*o]=Math.cos(s*t)*e,a[2*o+1]=Math.sin(s*t)*e})}else if("random"===e){const e=Math.max(500,100*Math.sqrt(o.length));o.forEach(t=>{a[2*t]=(Math.random()-.5)*e,a[2*t+1]=(Math.random()-.5)*e})}const n=performance.now(),r=e=>{const t=e-n,o=Math.min(t/600,1),l=o<.5?2*o*o:1-Math.pow(-2*o+2,2)/2;for(let n=0;n<2*s;n++)this.positions[n]=i[n]+(a[n]-i[n])*l;this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,this.positions,this.gl.DYNAMIC_DRAW),this.render(),o<1&&requestAnimationFrame(r)};requestAnimationFrame(r)}radialMenuConfigs={node:[{label:"Connect",icon:"link",action:"start-connect"},{label:"Lock",icon:"lock",action:"pin"},{label:"Color",icon:"palette",action:"color"},{label:"Delete",icon:"trash-2",action:"delete"},{label:"Group",icon:"box",action:"create-combo"}],edge:[{label:"Reverse",icon:"repeat",action:"reverse"},{label:"Hide",icon:"eye-off",action:"hide"},{label:"Delete",icon:"trash-2",action:"delete-edge"}],combo:[{label:"Toggle",icon:"maximize",action:"toggle-collapse"},{label:"Rename",icon:"edit-3",action:"rename-group"},{label:"Ungroup",icon:"zap",action:"dissolve-combo"}]};setRadialMenu(e,t){this.radialMenuConfigs[e]=t}getRadialMenu(e){return this.radialMenuConfigs[e]||[]}pan(e,o){t.vec2.add(this.state.offset,this.state.offset,t.vec2.fromValues(e/this.state.zoom,o/this.state.zoom)),this.emitter.emit("graph:pan",{dx:e,dy:o,offset:{x:this.state.offset[0],y:this.state.offset[1]}}),this.emitter.emit("viewport",{zoom:this.state.zoom,offset:this.state.offset}),this.render()}syncDragOffsets(e,o){const s=window.devicePixelRatio,i=this.unproject(e*s,o*s);this.dragOffsets.clear();for(const a of this.state.selectedNodeIds){const e=this.getNodePosition(a);e&&this.dragOffsets.set(a,t.vec2.sub(t.vec2.create(),e,i))}}handleDragging(e,o){if(!this.positions)return;const s=window.devicePixelRatio,i=this.unproject(e*s,o*s),a=this.gl;a.bindBuffer(a.ARRAY_BUFFER,this.positionBuffer);const n=new Set;for(const r of this.state.selectedNodeIds){if(this.lockedNodes.has(r))continue;const e=this.dragOffsets.get(r);if(e&&this.positions){const o=t.vec2.add(t.vec2.create(),i,e);let s=null;for(const e of this.combos.values())if(e.collapsed&&e.representativeId===r){s=e.id;break}if(s&&!n.has(s)){const e=o[0]-this.positions[2*r],t=o[1]-this.positions[2*r+1];this.moveGroup(s,e,t,!0),n.add(s)}else s||(this.positions[2*r]=o[0],this.positions[2*r+1]=o[1]);this.canvas.dispatchEvent(new CustomEvent("node-moved",{detail:{id:r,x:o[0],y:o[1]}})),this.emitter.emit("node:drag",{id:this.reverseIdMap.get(r)??r,index:r,x:o[0],y:o[1]})}}this.positions&&(a.bindBuffer(a.ARRAY_BUFFER,this.positionBuffer),a.bufferData(a.ARRAY_BUFFER,this.positions,a.DYNAMIC_DRAW)),this.render()}performBoxSelection(){if(!this.positions)return;const e=window.devicePixelRatio,t=this.unproject(this.state.selectionStart[0]*e,this.state.selectionStart[1]*e),o=this.unproject(this.state.selectionEnd[0]*e,this.state.selectionEnd[1]*e),s=Math.min(t[0],o[0]),i=Math.max(t[0],o[0]),a=Math.min(t[1],o[1]),n=Math.max(t[1],o[1]);this.state.selectedNodeIds.clear();for(let r=0;r<this.nodeCount;r++){if(this.hiddenNodes.has(r))continue;const e=this.positions[2*r],t=this.positions[2*r+1];e>=s&&e<=i&&t>=a&&t<=n&&this.state.selectedNodeIds.add(r)}this.canvas.dispatchEvent(new CustomEvent("nodes-selected",{detail:Array.from(this.state.selectedNodeIds)})),this.emitter.emit("nodes:select",{ids:Array.from(this.state.selectedNodeIds).map(e=>this.reverseIdMap.get(e)??e),indices:Array.from(this.state.selectedNodeIds)})}unproject(e,o){const s=e/this.canvas.width*2-1,i=-(o/this.canvas.height*2-1),a=t.mat3.invert(t.mat3.create(),this.viewMatrix);if(!a)return t.vec2.create();const n=t.vec3.fromValues(s,i,1);return t.vec3.transformMat3(n,n,a),t.vec2.fromValues(n[0],n[1])}initShaders(){const e=this.gl;this.program=this.createProgram("#version 300 es\nlayout(location = 0) in vec2 a_position;\nlayout(location = 1) in vec4 a_color;\nlayout(location = 2) in float a_id;\n\nuniform mat3 u_matrix;\nuniform float u_pointSize;\nuniform bool u_pickingMode;\n\nout vec4 v_color;\nflat out uint v_id;\n\nvoid main() {\n vec3 position = u_matrix * vec3(a_position, 1.0);\n gl_Position = vec4(position.xy, 0, 1);\n gl_PointSize = u_pointSize;\n v_color = a_color;\n v_id = uint(a_id);\n}\n","#version 300 es\nprecision highp float;\n\nin vec4 v_color;\nflat in uint v_id;\n\nuniform bool u_pickingMode;\nout vec4 outColor;\n\nvoid main() {\n float dist = distance(gl_PointCoord, vec2(0.5));\n if (dist > 0.5) discard;\n if (v_color.a <= 0.0) discard;\n\n if (u_pickingMode) {\n uint id = v_id + 1u;\n float r = float((id >> 16) & 0xFFu) / 255.0;\n float g = float((id >> 8) & 0xFFu) / 255.0;\n float b = float(id & 0xFFu) / 255.0;\n outColor = vec4(r, g, b, 1.0);\n } else {\n outColor = v_color;\n }\n}\n"),this.uMatrixLoc=e.getUniformLocation(this.program,"u_matrix"),this.uPointSizeLoc=e.getUniformLocation(this.program,"u_pointSize"),this.uPickingModeLoc=e.getUniformLocation(this.program,"u_pickingMode"),this.edgeProgram=this.createProgram("#version 300 es\nlayout(location = 0) in vec2 a_position;\n\nuniform mat3 u_matrix;\n\nvoid main() {\n vec3 position = u_matrix * vec3(a_position, 1.0);\n gl_Position = vec4(position.xy, 0, 1);\n}\n","#version 300 es\nprecision highp float;\nuniform vec4 u_color;\nout vec4 outColor;\n\nvoid main() {\n outColor = u_color;\n}\n"),this.uEdgeMatrixLoc=e.getUniformLocation(this.edgeProgram,"u_matrix"),this.uEdgeColorLoc=e.getUniformLocation(this.edgeProgram,"u_color")}initBuffers(){const e=this.gl;this.vao=e.createVertexArray(),e.bindVertexArray(this.vao),this.positionBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,2,e.FLOAT,!1,0,0),this.colorBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.colorBuffer),e.enableVertexAttribArray(1),e.vertexAttribPointer(1,4,e.FLOAT,!1,0,0),this.idBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.idBuffer),e.enableVertexAttribArray(2),e.vertexAttribPointer(2,1,e.FLOAT,!1,0,0),this.edgeVao=e.createVertexArray(),e.bindVertexArray(this.edgeVao),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,2,e.FLOAT,!1,0,0),this.edgeIndexBuffer=e.createBuffer(),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.edgeIndexBuffer),e.bindVertexArray(null)}initPicking(){const e=this.gl;this.pickingFramebuffer=e.createFramebuffer(),this.pickingTexture=e.createTexture(),e.bindTexture(e.TEXTURE_2D,this.pickingTexture),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,this.canvas.width,this.canvas.height,0,e.RGBA,e.UNSIGNED_BYTE,null),e.bindFramebuffer(e.FRAMEBUFFER,this.pickingFramebuffer),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,this.pickingTexture,0),e.bindFramebuffer(e.FRAMEBUFFER,null)}handleResize(){const e=window.devicePixelRatio;this.canvas.width=this.canvas.clientWidth*e,this.canvas.height=this.canvas.clientHeight*e,this.overlayCanvas.width=this.overlayCanvas.clientWidth*e,this.overlayCanvas.height=this.overlayCanvas.clientHeight*e,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.initPicking(),this.render()}updateMatrix(){const e=this.canvas.width/this.canvas.height;t.mat3.identity(this.viewMatrix),t.mat3.scale(this.viewMatrix,this.viewMatrix,[this.state.zoom/e,this.state.zoom]),t.mat3.translate(this.viewMatrix,this.viewMatrix,this.state.offset)}createProgram(e,t){const o=this.gl,s=o.createShader(o.VERTEX_SHADER);o.shaderSource(s,e),o.compileShader(s);const i=o.createShader(o.FRAGMENT_SHADER);o.shaderSource(i,t),o.compileShader(i);const a=o.createProgram();return o.attachShader(a,s),o.attachShader(a,i),o.linkProgram(a),a}resolveNodeIndex(e){let t=this.nodeIdMap.get(e);if(void 0!==t)return t;if(t=this.nodeIdMap.get(e.toString()),void 0!==t)return t;const o="number"==typeof e?e:Number(e);return!isNaN(o)&&o>=0&&o<this.nodeCount?o:null}parseColor(e){if(!e)return[Math.random(),Math.random(),Math.random(),1];if(Array.isArray(e))return[e[0]??0,e[1]??0,e[2]??0,e[3]??1];if("string"==typeof e){const t=e.replace("#","");if(t.length>=6)return[parseInt(t.slice(0,2),16)/255,parseInt(t.slice(2,4),16)/255,parseInt(t.slice(4,6),16)/255,1]}return[Math.random(),Math.random(),Math.random(),1]}ensureNodeCapacity(e){if(e<=this.capacity)return;const t=Math.max(2*this.capacity,e,64),o=new Float32Array(2*t),s=new Float32Array(4*t),i=new Float32Array(t);this.positions&&o.set(this.positions.subarray(0,2*this.nodeCount)),this.colorData.length>0&&s.set(this.colorData.subarray(0,4*this.nodeCount)),this.idData.length>0&&i.set(this.idData.subarray(0,this.nodeCount)),this.positions=o,this.colorData=s,this.idData=i,this.capacity=t;const a=this.gl;a.bindBuffer(a.ARRAY_BUFFER,this.positionBuffer),a.bufferData(a.ARRAY_BUFFER,this.positions,a.DYNAMIC_DRAW),a.bindBuffer(a.ARRAY_BUFFER,this.colorBuffer),a.bufferData(a.ARRAY_BUFFER,this.colorData,a.DYNAMIC_DRAW),a.bindBuffer(a.ARRAY_BUFFER,this.idBuffer),a.bufferData(a.ARRAY_BUFFER,this.idData,a.DYNAMIC_DRAW)}rebuildEdgeBuffer(){const e=this.gl,t=this.edgeList.filter(e=>!this.hiddenEdges.has(e.id));if(0===t.length)return this.edgeIndices=null,void(this.edgeCount=0);this.edgeIndices=new Uint32Array(2*t.length);for(let o=0;o<t.length;o++)this.edgeIndices[2*o]=t[o].u,this.edgeIndices[2*o+1]=t[o].v;this.edgeCount=this.edgeIndices.length,e.bindVertexArray(this.edgeVao),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.edgeIndexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,this.edgeIndices,e.DYNAMIC_DRAW),e.bindVertexArray(null)}rebuildEdgeIndexToUserIdMap(){this.edgeIndexToUserIdMap.clear();for(const e of this.edgeList)this.edgeIndexToUserIdMap.set(`${e.u}-${e.v}`,e.id),this.edgeIndexToUserIdMap.set(`${e.v}-${e.u}`,e.id)}_removeEdgeInternal(e){const t=this.edgeIdMap.get(e);if(void 0===t)return;const o=this.edgeList.length-1;if(t!==o){const e=this.edgeList[o];this.edgeList[t]=e,this.edgeIdMap.set(e.id,t)}this.edgeList.pop(),this.edgeIdMap.delete(e),this.edgeMetadata.delete(e),this.hiddenEdges.delete(e),this.rebuildEdgeIndexToUserIdMap()}loadNodeImage(e,t){const o=this.metadata.get(e);if(!o)return;const s=new Image;s.src=t,s.onload=()=>{const e=document.createElement("canvas"),t=128;e.width=t,e.height=t;const i=e.getContext("2d");i&&(i.beginPath(),i.arc(64,64,64,0,2*Math.PI),i.clip(),i.filter="grayscale(100%) brightness(1000%)",i.drawImage(s,0,0,t,t),o.processedImageElement=e),o.imageElement=s,this.render()},o.imageElement=s}}function d(){const e=o.ref([]);return{graphNodes:e,processGraphData:t=>{const o=t.nodes||[],s=t.edges||[],i=o.length,a=new Float32Array(2*i),n=new Float32Array(4*i),r=new Float32Array(i),l=new Float32Array(i),d=new Map,c=new Map;o.forEach((e,t)=>{const o=e.data,s=e.position||{x:0,y:0};c.set(o.id,t),a[2*t]=((s.x??2500)-2500)/500,a[2*t+1]=((s.y??2500)-2500)/500;const i=(e=>{const t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?[parseInt(t[1],16)/255,parseInt(t[2],16)/255,parseInt(t[3],16)/255]:[Math.random(),Math.random(),Math.random()]})(o.color||"#000000");n[4*t]=i[0],n[4*t+1]=i[1],n[4*t+2]=i[2],n[4*t+3]=1,r[t]=t;const h=o.width||50,u=o.height||50;l[t]=Math.max(h,u)/1e3;const m={label:o.label||o.code||`Node ${o.id}`,image:o.backgroundImage,width:h,height:u,data:o};if(o.backgroundImage){const e=new Image;e.src=o.backgroundImage,e.onload=()=>{const t=document.createElement("canvas"),o=128;t.width=o,t.height=o;const s=t.getContext("2d");s&&(s.beginPath(),s.arc(64,64,64,0,2*Math.PI),s.clip(),s.filter="grayscale(100%) brightness(1000%)",s.drawImage(e,0,0,o,o),m.processedImageElement=t)},m.imageElement=e}d.set(t,m)}),e.value=Array.from(d.entries()).map(([e,t])=>({id:e,_index:e,label:t.label,position:{x:a[2*e],y:a[2*e+1]}}));const h=[],u=new Map;return s.forEach(e=>{const t=e.data.source,o=e.data.target,s=c.get(t),i=c.get(o);if(void 0!==s&&void 0!==i){h.push(s),h.push(i);const t=`${s}-${i}`;u.set(t,{label:e.data.label,data:e.data})}}),{positions:a,colors:n,ids:r,nodeRadii:l,metadata:d,edgeIndices:new Uint32Array(h),edgeMetadata:u,idMap:c,count:i}}}}const c=["width","height","stroke-width"],h=["d"],u=["cx","cy","r"],m=["x1","y1","x2","y2"],g=["x","y","width","height","rx","ry"],f=(e,t)=>{const o=e.__vccOpts||e;for(const[s,i]of t)o[s]=i;return o},v=f(o.defineComponent({__name:"LucideIcon",props:{name:{},size:{default:24},strokeWidth:{default:2}},setup(e){const t=e,s={sun:{paths:["M12 1v2","M12 21v2","M4.22 4.22l1.42 1.42","M18.36 18.36l1.42 1.42","M1 12h2","M21 12h2","M4.22 19.78l1.42-1.42","M18.36 5.64l1.42-1.42"],circles:[{cx:12,cy:12,r:4}]},moon:{paths:["M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"]},maximize:{paths:["M8 3H5a2 2 0 0 0-2 2v3","M21 8V5a2 2 0 0 0-2-2h-3","M3 16v3a2 2 0 0 0 2 2h3","M16 21h3a2 2 0 0 0 2-2v-3"]},plus:{paths:["M12 5v14","M5 12h14"]},minus:{paths:["M5 12h14"]},rocket:{paths:["M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z","m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z","M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0","M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"]},shrink:{paths:["M14 10l7-7","M3 21l7-7","M21 10h-7V3","M3 14h7v7"]},expand:{paths:["M15 3h6v6","M9 21H3v-6","M21 3l-7 7","M3 21l7-7"]},"layout-grid":{rects:[{x:3,y:3,width:7,height:7,rx:1},{x:14,y:3,width:7,height:7,rx:1},{x:14,y:14,width:7,height:7,rx:1},{x:3,y:14,width:7,height:7,rx:1}]},circle:{circles:[{cx:12,cy:12,r:10}]},dices:{rects:[{x:2,y:11,width:11,height:11,rx:2}],paths:["M7 15h.01","M7 19h.01","M11 19h.01","M22 5V2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2","M22 5h-4a1 1 0 0 0-1 1v4","M22 5c0-1.1-.9-2-2-2","M14 7h.01"]},spiral:{paths:["M12 12a1 1 0 1 0 1 1","M12 7a5 5 0 1 0 5 5","M12 2a10 10 0 1 0 10 10","M12 12a1 1 0 0 0-1-1"]},target:{circles:[{cx:12,cy:12,r:10},{cx:12,cy:12,r:6},{cx:12,cy:12,r:2}]},atom:{circles:[{cx:12,cy:12,r:1}],paths:["M12 2v2","M12 20v2","m4.93 4.93 1.41 1.41","m17.66 17.66 1.41 1.41","M2 12h2","M20 12h2","m6.34 17.66-1.41-1.41","m19.07 4.93-1.41 1.41"]},"git-branch":{circles:[{cx:6,cy:3,r:3},{cx:6,cy:21,r:3},{cx:18,cy:9,r:3}],paths:["M6 6v12","M18 12a6 6 0 0 1-6 6H6"]},sparkles:{paths:["m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z","M5 3L4 5.5 1.5 6.5 4 7.5 5 10l1-2.5 2.5-1L6 5.5 5 3Z","M19 19l-1 2.5-2.5 1L18 23.5l1 2.5 1-2.5 2.5-1-2.5-1-1-2.5Z"]},search:{circles:[{cx:11,cy:11,r:8}],lines:[{x1:21,y1:21,x2:16.65,y2:16.65}]},info:{circles:[{cx:12,cy:12,r:10}],paths:["M12 16v-4","M12 8h.01"]},"trash-2":{paths:["M3 6h18","M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6","M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2","M10 11v6","M14 11v6"]},palette:{circles:[{cx:13.5,cy:6.5,r:.5},{cx:17.5,cy:10.5,r:.5},{cx:8.5,cy:7.5,r:.5},{cx:6.5,cy:12.5,r:.5}],paths:["M12 2C6.47 2 2 6.47 2 12s4.47 10 9.99 10a3 3 0 0 0 3-3c0-.8-.34-1.56-.92-2.1l-.1-.08c-.53-.45-.88-1.1-.88-1.82 0-1.38 1.12-2.5 2.5-2.5H19a3 3 0 0 0 2-5c0-4.42-4.03-8-9-8Z"]},pin:{paths:["M15 4.5l-4 4L7 10l-1.5 1.5 7 7L14 17l1.5-4 4-4L15 4.5z","m9 21 3-3","M14 4.5L19.5 10"]},box:{paths:["M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z","m3.3 7 8.7 5 8.7-5","M12 22V12"]},eye:{paths:["M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"],circles:[{cx:12,cy:12,r:3}]},"eye-off":{paths:["M9.88 9.88a3 3 0 1 0 4.24 4.24","M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68","M6.61 6.61A13.52 13.52 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61","m2 2 20 20"]},"refresh-cw":{paths:["M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8","M21 3v5h-5","M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16","M3 21v-5h5"]},scale:{paths:["m16 16 3-8 3 8c-.87.62-1.92 1-3 1s-2.13-.38-3-1Z","m2 16 3-8 3 8c-.87.62-1.92 1-3 1s-2.13-.38-3-1Z","M7 21h10","M12 3v18","M3 7h18"]},zap:{paths:["M13 2 3 14h9l-1 8 10-12h-9l1-8z"]},x:{paths:["M18 6L6 18","M6 6l12 12"]}},i=o.computed(()=>s[t.name]||s.search),a=o.computed(()=>i.value.paths||[]),n=o.computed(()=>i.value.circles||[]),r=o.computed(()=>i.value.lines||[]),l=o.computed(()=>i.value.rects||[]);return(t,s)=>(o.openBlock(),o.createElementBlock("svg",{xmlns:"http://www.w3.org/2000/svg",width:e.size,height:e.size,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":e.strokeWidth,"stroke-linecap":"round","stroke-linejoin":"round",class:"lucide-icon"},[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(a.value,(e,t)=>(o.openBlock(),o.createElementBlock("path",{key:t,d:e},null,8,h))),128)),(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(n.value,(e,t)=>(o.openBlock(),o.createElementBlock("circle",{key:t,cx:e.cx,cy:e.cy,r:e.r},null,8,u))),128)),(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(r.value,(e,t)=>(o.openBlock(),o.createElementBlock("line",{key:t,x1:e.x1,y1:e.y1,x2:e.x2,y2:e.y2},null,8,m))),128)),(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(l.value,(e,t)=>(o.openBlock(),o.createElementBlock("rect",{key:t,x:e.x,y:e.y,width:e.width,height:e.height,rx:e.rx,ry:e.ry},null,8,g))),128))],8,c))}}),[["__scopeId","data-v-6aa14fad"]]),p={class:"radial-center glass shadow-glow"},b=["onClick"],M={class:"radial-icon"},E={class:"radial-label"},w=f(o.defineComponent({__name:"RadialMenu",props:{show:{type:Boolean},x:{},y:{},type:{},items:{}},emits:["action"],setup(e){const t=(e,t)=>{const o=e/t*Math.PI*2-Math.PI/2,s=window.innerWidth<768?96:76;return{transform:`translate(-50%, -50%) translate(${Math.cos(o)*s}px, ${Math.sin(o)*s}px)`,transitionDelay:.04*e+"s"}};return(s,i)=>e.show?(o.openBlock(),o.createElementBlock("div",{key:0,class:"radial-menu",style:o.normalizeStyle({left:e.x+"px",top:e.y+"px"}),onMousedown:i[0]||(i[0]=o.withModifiers(()=>{},["stop"])),onTouchstart:i[1]||(i[1]=o.withModifiers(()=>{},["stop"]))},[o.createElementVNode("div",p,[o.renderSlot(s.$slots,"center",{},()=>["node"===e.type?(o.openBlock(),o.createBlock(v,{key:0,name:"target",size:"24"})):"edge"===e.type?(o.openBlock(),o.createBlock(v,{key:1,name:"git-branch",size:"24"})):(o.openBlock(),o.createBlock(v,{key:2,name:"search",size:"24"}))],!0)]),(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(e.items,(i,a)=>(o.openBlock(),o.createElementBlock("div",{key:a,class:"radial-item",style:o.normalizeStyle(t(a,e.items.length))},[o.createElementVNode("button",{class:"radial-item-inner glass-interactive",onClick:e=>s.$emit("action",i.action)},[o.createElementVNode("span",M,[o.createVNode(v,{name:i.icon,size:"20"},null,8,["name"])]),o.createElementVNode("span",E,o.toDisplayString(i.label),1)],8,b)],4))),128))],36)):o.createCommentVNode("",!0)}}),[["__scopeId","data-v-31b4eac1"]]),y={class:"top-right-controls glass"},I=["title"],N=["title"],x=f(o.defineComponent({__name:"ViewControls",props:{theme:{},flowEnabled:{type:Boolean}},emits:["toggleTheme","fit","zoomIn","zoomOut","stress","toggleFlow"],setup(e){const t=o.ref(!1),s=()=>{document.fullscreenElement?(document.exitFullscreen(),t.value=!1):(document.documentElement.requestFullscreen(),t.value=!0)};return(i,a)=>(o.openBlock(),o.createElementBlock("div",y,[o.createElementVNode("button",{class:"control-btn glass-interactive",onClick:a[0]||(a[0]=e=>i.$emit("toggleTheme")),title:"dark"===e.theme?"Switch to Light":"Switch to Dark"},[o.createVNode(v,{name:"dark"===e.theme?"moon":"sun",size:"20"},null,8,["name"])],8,I),a[6]||(a[6]=o.createElementVNode("div",{class:"divider"},null,-1)),o.createElementVNode("button",{class:"control-btn glass-interactive",onClick:a[1]||(a[1]=e=>i.$emit("fit")),title:"Fit View"},[o.createVNode(v,{name:"maximize",size:"20"})]),o.createElementVNode("button",{class:"control-btn glass-interactive",onClick:a[2]||(a[2]=e=>i.$emit("zoomIn")),title:"Zoom In"},[o.createVNode(v,{name:"plus",size:"20"})]),o.createElementVNode("button",{class:"control-btn glass-interactive",onClick:a[3]||(a[3]=e=>i.$emit("zoomOut")),title:"Zoom Out"},[o.createVNode(v,{name:"minus",size:"20"})]),a[7]||(a[7]=o.createElementVNode("div",{class:"divider"},null,-1)),o.createElementVNode("button",{class:"control-btn glass-interactive stress-btn",onClick:a[4]||(a[4]=e=>i.$emit("stress")),title:"Stress Test (100k Nodes)"},[o.createVNode(v,{name:"rocket",size:"20"})]),a[8]||(a[8]=o.createElementVNode("div",{class:"divider"},null,-1)),o.createElementVNode("button",{class:o.normalizeClass(["control-btn glass-interactive",{"flow-active":e.flowEnabled}]),onClick:a[5]||(a[5]=e=>i.$emit("toggleFlow")),title:e.flowEnabled?"Disable Energy Flow":"Enable Energy Flow"},[o.createVNode(v,{name:"waves",size:"20"})],10,N),a[9]||(a[9]=o.createElementVNode("div",{class:"divider"},null,-1)),o.createElementVNode("button",{class:"control-btn glass-interactive",onClick:s,title:"Toggle Fullscreen"},[o.createVNode(v,{name:t.value?"shrink":"expand",size:"20"},null,8,["name"])])]))}}),[["__scopeId","data-v-08b2ffb9"]]),k={class:"layout-controls glass"},C={class:"scroll-container no-scrollbar"},B=["onClick","title"],R={class:"btn-icon"},A={key:0,class:"btn-name"},F=f(o.defineComponent({__name:"LayoutControls",props:{layouts:{},currentLayout:{},flowEnabled:{type:Boolean}},emits:["runLayout","toggleFlow"],setup:e=>(t,s)=>(o.openBlock(),o.createElementBlock("div",k,[o.createElementVNode("div",C,[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(e.layouts,s=>(o.openBlock(),o.createElementBlock("button",{key:s.id,class:o.normalizeClass(["layout-btn glass-interactive",{active:e.currentLayout===s.id}]),onClick:e=>t.$emit("runLayout",s.id),title:s.name},[o.createElementVNode("span",R,[o.createVNode(v,{name:s.icon,size:"18"},null,8,["name"])]),e.currentLayout===s.id?(o.openBlock(),o.createElementBlock("span",A,o.toDisplayString(s.name),1)):o.createCommentVNode("",!0)],10,B))),128))])]))}),[["__scopeId","data-v-503d31c7"]]),V={key:0,class:"property-panel glass node-panel"},S={class:"panel-header"},_={class:"panel-content"},D={key:0,class:"val-container"},L=["src"],T={key:1},P={key:1,class:"json-prop"},z={key:1,class:"property-panel glass edge-panel"},U={class:"panel-header"},Y={class:"panel-content"},j={class:"prop-group"},$={class:"id-badge"},O={key:0,class:"val-container"},G=["src"],W={key:1},X={key:1,class:"json-prop"},H={key:2,class:"property-panel glass combo-panel"},Z={class:"panel-header"},q={class:"panel-content"},K={class:"prop-group shadow-sm"},J=["value"],Q={class:"prop-group"},ee={class:"member-list"},te=["onClick"],oe={key:0,class:"more-hint"},se=f(o.defineComponent({__name:"PropertyPanel",props:{nodeId:{},nodeMetadata:{},nodeX:{},nodeY:{},edgeId:{},edgeMetadata:{},combo:{}},emits:["update:nodeX","update:nodeY","update:nodeId","update:edgeId","update:combo","update:comboLabel","removeFromCombo"],setup(e,{emit:t}){const s=e=>"string"==typeof e&&e.startsWith("data:image/");return(t,i)=>null!==e.nodeId?(o.openBlock(),o.createElementBlock("div",V,[o.createElementVNode("div",S,[i[4]||(i[4]=o.createElementVNode("h3",null,"Node Properties",-1)),o.createElementVNode("button",{class:"close-btn",onClick:i[0]||(i[0]=e=>t.$emit("update:nodeId",null))},[o.createVNode(v,{name:"x",size:"18"})])]),o.createElementVNode("div",_,[e.nodeMetadata&&e.nodeMetadata.data?(o.openBlock(!0),o.createElementBlock(o.Fragment,{key:0},o.renderList(e.nodeMetadata.data,(t,i)=>(o.openBlock(),o.createElementBlock("div",{key:i,class:"prop-group"},[o.createElementVNode("label",null,o.toDisplayString(i),1),"object"!=typeof t?(o.openBlock(),o.createElementBlock("div",D,[s(t)?(o.openBlock(),o.createElementBlock("img",{key:0,src:t,class:"prop-image",style:o.normalizeStyle("backgroundImage"===i?{backgroundColor:e.nodeMetadata.data.color||"var(--primary)"}:{})},null,12,L)):(o.openBlock(),o.createElementBlock("span",T,o.toDisplayString(t),1))])):(o.openBlock(),o.createElementBlock("pre",P,o.toDisplayString(JSON.stringify(t,null,2)),1))]))),128)):o.createCommentVNode("",!0)])])):null!==e.edgeId?(o.openBlock(),o.createElementBlock("div",z,[o.createElementVNode("div",U,[i[5]||(i[5]=o.createElementVNode("h3",null,"Edge Properties",-1)),o.createElementVNode("button",{class:"close-btn",onClick:i[1]||(i[1]=e=>t.$emit("update:edgeId",null))},[o.createVNode(v,{name:"x",size:"18"})])]),o.createElementVNode("div",Y,[o.createElementVNode("div",j,[i[6]||(i[6]=o.createElementVNode("label",null,"ID",-1)),o.createElementVNode("span",$,o.toDisplayString(e.edgeId),1)]),e.edgeMetadata&&e.edgeMetadata.data?(o.openBlock(!0),o.createElementBlock(o.Fragment,{key:0},o.renderList(e.edgeMetadata.data,(t,i)=>(o.openBlock(),o.createElementBlock("div",{key:i,class:"prop-group"},[o.createElementVNode("label",null,o.toDisplayString(i),1),"object"!=typeof t?(o.openBlock(),o.createElementBlock("div",O,[s(t)?(o.openBlock(),o.createElementBlock("img",{key:0,src:t,class:"prop-image",style:o.normalizeStyle("backgroundImage"===i?{backgroundColor:e.edgeMetadata.data.color||"var(--secondary)"}:{})},null,12,G)):(o.openBlock(),o.createElementBlock("span",W,o.toDisplayString(t),1))])):(o.openBlock(),o.createElementBlock("pre",X,o.toDisplayString(JSON.stringify(t,null,2)),1))]))),128)):o.createCommentVNode("",!0)])])):e.combo?(o.openBlock(),o.createElementBlock("div",H,[o.createElementVNode("div",Z,[i[7]||(i[7]=o.createElementVNode("h3",null,"Group Properties",-1)),o.createElementVNode("button",{class:"close-btn",onClick:i[2]||(i[2]=e=>t.$emit("update:combo",null))},[o.createVNode(v,{name:"x",size:"18"})])]),o.createElementVNode("div",q,[o.createElementVNode("div",K,[i[8]||(i[8]=o.createElementVNode("label",null,"Label",-1)),o.createElementVNode("input",{type:"text",value:e.combo.label,class:"full-width",onInput:i[3]||(i[3]=e=>t.$emit("update:comboLabel",e.target.value))},null,40,J)]),o.createElementVNode("div",Q,[o.createElementVNode("label",null,"Members ("+o.toDisplayString(e.combo.nodeIds.size)+")",1),o.createElementVNode("div",ee,[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(Array.from(e.combo.nodeIds).slice(0,50),e=>(o.openBlock(),o.createElementBlock("div",{key:e,class:"member-item"},[o.createElementVNode("span",null,"Node "+o.toDisplayString(e),1),o.createElementVNode("button",{class:"remove-btn",onClick:o=>t.$emit("removeFromCombo",e)},[o.createVNode(v,{name:"trash-2",size:"12"})],8,te)]))),128)),e.combo.nodeIds.size>50?(o.openBlock(),o.createElementBlock("div",oe," + "+o.toDisplayString(e.combo.nodeIds.size-50)+" more ",1)):o.createCommentVNode("",!0)])])])])):o.createCommentVNode("",!0)}}),[["__scopeId","data-v-acd191f9"]]),ie={class:"search-icon"},ae={key:0,class:"results-panel"},ne=["onMousedown","onTouchstart"],re={class:"node-icon"},le={class:"node-info"},de={class:"node-label"},ce={class:"node-id"},he={key:1,class:"no-results"},ue=f(o.defineComponent({__name:"SearchBar",props:{nodes:{}},emits:["select"],setup(e,{emit:t}){const s=e,i=t,a=o.ref(""),n=o.ref(!1),r=o.ref(null),l=o.ref(window.innerWidth<768),d=()=>{l.value=window.innerWidth<768};o.onMounted(()=>{window.addEventListener("resize",d)}),o.onUnmounted(()=>{window.removeEventListener("resize",d)});const c=o.computed(()=>{if(!a.value)return[];const e=a.value.toLowerCase();return s.nodes.filter(t=>t.label?.toLowerCase().includes(e)||t.id.toString().includes(e)).slice(0,10)}),h=e=>{i("select",e),a.value="",n.value=!1},u=()=>{l.value&&!n.value&&(n.value=!0,setTimeout(()=>r.value?.focus(),100))},m=()=>{a.value="",n.value=!1,r.value?.blur()},g=()=>{l.value||setTimeout(()=>{n.value=!1},200)};return(e,t)=>(o.openBlock(),o.createElementBlock("div",{class:o.normalizeClass(["search-wrapper",{"mobile-expanded":n.value&&l.value,"is-mobile":l.value}])},[o.createElementVNode("div",{class:o.normalizeClass(["search-container glass",{expanded:n.value,"has-query":a.value.length>0}])},[o.createElementVNode("div",{class:"search-input-wrapper",onClick:u},[o.createElementVNode("span",ie,[o.createVNode(v,{name:"search",size:l.value?20:14},null,8,["size"])]),o.withDirectives(o.createElementVNode("input",{ref_key:"inputRef",ref:r,"onUpdate:modelValue":t[0]||(t[0]=e=>a.value=e),placeholder:"Search nodes...",onFocus:t[1]||(t[1]=e=>n.value=!0),onBlur:g,onKeydown:t[2]||(t[2]=o.withKeys(e=>n.value=!1,["esc"]))},null,544),[[o.vModelText,a.value]]),a.value||l.value&&n.value?(o.openBlock(),o.createElementBlock("div",{key:0,class:"clear-btn",onClick:o.withModifiers(m,["stop"])},[o.createVNode(v,{name:a.value?"x":"chevron-down",size:l.value?20:12},null,8,["name","size"])])):o.createCommentVNode("",!0)]),n.value&&c.value.length>0?(o.openBlock(),o.createElementBlock("div",ae,[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(c.value,e=>(o.openBlock(),o.createElementBlock("div",{key:e.id,class:"result-item",onMousedown:t=>h(e.id),onTouchstart:t=>h(e.id)},[o.createElementVNode("span",re,[o.createVNode(v,{name:"circle",size:"8"})]),o.createElementVNode("div",le,[o.createElementVNode("div",de,o.toDisplayString(e.label),1),o.createElementVNode("div",ce,"ID: "+o.toDisplayString(e.id),1)])],40,ne))),128))])):n.value&&a.value?(o.openBlock(),o.createElementBlock("div",he," No nodes found ")):o.createCommentVNode("",!0)],2),l.value&&n.value?(o.openBlock(),o.createElementBlock("div",{key:0,class:"mobile-backdrop",onClick:m})):o.createCommentVNode("",!0)],2))}}),[["__scopeId","data-v-283de02c"]]),me={key:0,class:"mobile-menu glass shadow-lg"},ge={class:"drawer-header"},fe={key:0,class:"drawer-content"},ve={key:1,class:"drawer-content"},pe={key:2,class:"top-right-controls"},be={class:"help-header"},Me={class:"help-content no-scrollbar"},Ee={class:"key-badge"},we={class:"action-desc"},ye={key:6,class:"connecting-toast glass shadow-glow"},Ie={class:"toast-content"},Ne={class:"edge-config-group"},xe={class:"color-presets"},ke=["onClick"],Ce={class:"size-config"},Be=f(o.defineComponent({__name:"GraphCanvas",props:{data:{}},setup(e){const t=e,i=o.ref(null),a=o.ref(null),n=o.shallowRef(null),{theme:r,isMobile:c,activeMobileDrawer:h,showMobileSearch:u,showHelp:m,apiPanelOpen:g,toggleTheme:f,toggleMobileDrawer:p,handleMobileSearch:b}=function(e){const t=o.ref("dark"),s=o.ref(window.innerWidth<768),i=o.ref(null),a=o.ref(!1),n=o.ref(!1),r=o.ref(!1),l=o.ref(null),d=()=>{s.value=window.innerWidth<768};return o.onMounted(()=>{window.addEventListener("resize",d)}),o.onUnmounted(()=>{window.removeEventListener("resize",d)}),{theme:t,isMobile:s,activeMobileDrawer:i,showMobileSearch:a,showHelp:n,apiPanelOpen:r,searchBarRef:l,toggleTheme:()=>{t.value="dark"===t.value?"light":"dark",e.value&&e.value.setTheme(t.value),document.documentElement.classList.toggle("light","light"===t.value)},toggleMobileDrawer:e=>{i.value===e?i.value=null:i.value=e},handleMobileSearch:()=>{a.value=!0,setTimeout(()=>{l.value?.handleWrapperClick?.()},50)},updateMobileStatus:d}}(n),{graphNodes:M,processGraphData:E}=d(),{selectedId:y,selectedEdgeId:I,selectedComboId:N,selectedCount:k,nodeX:C,nodeY:B,selectedNodeMetadata:R,selectedEdgeMetadata:A,currentCombo:V,updateComboLabel:S,removeFromCombo:_,syncEdgeMetadata:D}=function(e){const t=o.ref(null),s=o.ref(null),i=o.ref(null),a=o.ref(0),n=o.ref(0),r=o.ref(0),l=o.ref(null),d=o.ref(null),c=o.ref(null),h=()=>{if(null!==t.value&&e.value){const o=e.value.getNodePosition(t.value);o&&(n.value=Number(o[0].toFixed(3)),r.value=Number(o[1].toFixed(3))),l.value=e.value.getNodeMetadata(t.value)}else l.value=null},u=()=>{null!==s.value&&e.value?d.value=e.value.getEdgeMetadata(s.value):d.value=null},m=()=>{null!==t.value&&e.value&&e.value.setNodePosition(t.value,n.value,r.value)};return o.watch(t,h),o.watch(s,u),o.watch(n,m),o.watch(r,m),o.watch(i,t=>{t&&e.value?c.value=e.value.getCombo(t)||null:c.value=null}),{selectedId:t,selectedEdgeId:s,selectedComboId:i,selectedCount:a,nodeX:n,nodeY:r,selectedNodeMetadata:l,selectedEdgeMetadata:d,currentCombo:c,updateComboLabel:t=>{if(i.value&&e.value){const o=e.value.getCombo(i.value);o&&(e.value.updateCombo(o.id,t,Array.from(o.nodeIds)),c.value={...o,label:t})}},removeFromCombo:t=>{if(i.value&&e.value){e.value.removeNodeFromCombo(i.value,t);const o=e.value.getCombo(i.value);o&&(c.value={...o,nodeIds:new Set(o.nodeIds)})}},syncNodeMetadata:h,syncEdgeMetadata:u}}(n),{currentLayout:L,runLayout:T,runStressTest:P,initWorker:z}=function(e){const t=o.ref("random"),i=o.ref(0);let a,n,r=null;const l=(e,t,o,l)=>{i.value=e,a=o,n=l,r&&r.terminate(),r=new Worker(new URL("/assets/layout.worker-CBxeMMC3.js","undefined"==typeof document&&"undefined"==typeof location?require("url").pathToFileURL(__filename).href:"undefined"==typeof document?location.href:s&&"SCRIPT"===s.tagName.toUpperCase()&&s.src||new URL("vortx-gl.umd.js",document.baseURI).href),{type:"module"}),r.postMessage({type:"init",data:{nodeCount:e,positions:t,nodeRadii:a,edgeIndices:n}}),r.onmessage=e=>{"layout-complete"===e.data.type&&d(e.data.positions)}},d=t=>{if(!e.value)return;const o=i.value,s=new Float32Array(2*o);for(let i=0;i<o;i++){const t=e.value.getNodePosition(i);t&&(s[2*i]=t[0],s[2*i+1]=t[1])}const a=performance.now(),n=new Float32Array(2*o),r=i=>{const l=i-a,d=Math.min(l/1e3,1),c=d<.5?4*d**3:1-Math.pow(-2*d+2,3)/2;if(s&&t)for(let e=0;e<2*o;e++)n[e]=s[e]+(t[e]-s[e])*c;e.value?.updatePositions(n),d<1?requestAnimationFrame(r):e.value?.fitToView()};requestAnimationFrame(r)};return o.onUnmounted(()=>{r&&r.terminate()}),{currentLayout:t,nodeCount:i,runLayout:e=>{t.value=e,r&&r.postMessage({type:"layout",data:{layoutType:e,nodeRadii:a,edgeIndices:n}})},runStressTest:t=>{if(!e.value)return;i.value=t;const o=new Float32Array(2*t),s=new Float32Array(4*t),a=new Float32Array(t),n=new Float32Array(t),r=new Map;for(let e=0;e<t;e++)o[2*e]=50*(Math.random()-.5),o[2*e+1]=50*(Math.random()-.5),s[4*e]=Math.random(),s[4*e+1]=Math.random(),s[4*e+2]=Math.random(),s[4*e+3]=1,a[e]=e,n[e]=.05,r.set(e,{label:`S-${e}`,width:50,height:50});const d=[];for(let e=0;e<5e4;e++)d.push(Math.floor(Math.random()*t)),d.push(Math.floor(Math.random()*t));const c=new Uint32Array(d);e.value.setData(o,s,a,r,c,new Map),l(t,o,n,c),e.value.fitToView()},initWorker:l,animateToPositions:d}}(n),{contextMenu:U,menuItems:Y,handleContextMenu:j,handleMenuAction:$,connectingSourceId:O}=function(e,t,s,i,a){const n=o.ref({show:!1,x:0,y:0,type:"none",target:null}),r=o.ref(null),l=o.computed(()=>{const t=n.value.type;return e.value&&"none"!==t?e.value.getRadialMenu(t).map(o=>{if("node"===t&&"pin"===o.action){const t=null!==n.value.target&&e.value?.isNodeLocked(n.value.target);return{...o,label:t?"Unlock":"Lock",icon:t?"lock-open":"lock"}}if("combo"===t&&"toggle-collapse"===o.action){const t=e.value?.getCombo(n.value.target)?.collapsed;return{...o,label:t?"Expand":"Collapse",icon:t?"maximize":"minimize"}}return o}):[]});return{contextMenu:n,menuItems:l,handleContextMenu:o=>{const i=t.value?.getBoundingClientRect();if(!i||!e.value)return;const r=o.clientX-i.left,l=o.clientY-i.top,d=140;let c=o.clientX,h=o.clientY;const u=(a.value?380:0)+d,m=(null!==s.value?window.innerWidth-340:window.innerWidth)-d;c=Math.min(Math.max(c,u),m);const g=window.innerHeight-d;h=Math.min(Math.max(h,140),g);const f=e.value.pick(r,l);if(null!==f)return void(n.value={show:!0,x:c,y:h,type:"node",target:f});const v=e.value.getEdgeAt(r,l);if(v)return void(n.value={show:!0,x:c,y:h,type:"edge",target:v});const p=e.value.pickCombo(r,l);p?n.value={show:!0,x:c,y:h,type:"combo",target:p}:n.value.show=!1},handleMenuAction:(t,o)=>{if(!e.value)return;const a={"start-connect":()=>{r.value=n.value.target},delete:()=>{(i.value>1?e.value.getSelectedUserIds():[e.value.getNodeId(n.value.target)??n.value.target]).forEach(t=>e.value.removeNode(t)),s.value=null},pin:()=>{const t=e.value.getNodeId(n.value.target)??n.value.target;e.value.isNodeLocked(n.value.target)?e.value.unlockNode(t):e.value.lockNode(t)},color:()=>{const t=i.value>1?e.value.getSelectedUserIds():[e.value.getNodeId(n.value.target)??n.value.target],o=["#ff6b6b","#4ecdc4","#45b7d1","#f7dc6f","#bb8fce","#6bcb77"],s=o[Math.floor(Math.random()*o.length)],a=parseInt(s.slice(1,3),16)/255,r=parseInt(s.slice(3,5),16)/255,l=parseInt(s.slice(5,7),16)/255;t.forEach(t=>e.value.setNodeColor(t,a,r,l))},hide:()=>{"edge"===n.value.type&&n.value.target&&e.value.hideEdge(n.value.target)},reverse:()=>{"edge"===n.value.type&&n.value.target&&e.value.reverseEdge(n.value.target)},weight:()=>{alert("Weight adjustment coming soon!")},"delete-edge":()=>{n.value.target&&e.value.removeEdge(n.value.target)},"create-combo":()=>{const t=e.value.getSelectedUserIds();if(t.length<2){const o=e.value.getNodeId(n.value.target)??n.value.target;null!=o&&t.push(o)}if(0===t.length)return;const s=`group-${Date.now()}`;e.value.createGroup(s,`Group ${s.split("-")[1]}`,t),o.value++},"dissolve-combo":()=>{n.value.target&&(e.value.removeGroup(n.value.target),o.value--)},"toggle-collapse":()=>{n.value.target&&e.value.toggleComboCollapse(n.value.target)},"rename-group":()=>{const t=n.value.target,o=e.value.getCombo(t);if(o){const s=prompt("Enter new group name:",o.label);s&&e.value.updateCombo(t,s,Array.from(o.nodeIds).map(t=>e.value.getNodeId(t)??t))}},"add-to-group":()=>{const t=e.value.getSelectedUserIds();t.length>0&&n.value.target&&t.forEach(t=>e.value.addToGroup(n.value.target,t))}};a[t]&&a[t](),n.value.show=!1},connectingSourceId:r}}(n,i,y,k,g),G=o.ref(!1),W=o.ref(0),X=o.ref({label:"relation",color:"#00ffcc",size:1.5}),H=["#00ffcc","#ff00ff","#ffcc00","#ff4444","#ffffff","#44aaff"],Z=[{id:"random",name:"Random",icon:"dices"},{id:"grid",name:"Grid",icon:"layout-grid"},{id:"circle",name:"Circle",icon:"circle"},{id:"spiral",name:"Spiral",icon:"spiral"},{id:"concentric",name:"Concentric",icon:"target"},{id:"radial",name:"Radial",icon:"atom"},{id:"hierarchy",name:"Hierarchy",icon:"git-branch"},{id:"force",name:"Force",icon:"sparkles"}],q=[{key:"Shift + Drag",action:"Box Selection"},{key:"Ctrl + Click",action:"Multi Selection"},{key:"Right Click",action:"Radial Menu"},{key:"Scroll",action:"Zoom In/Out"},{key:"Drag",action:"Pan View"}],K=()=>{G.value=!G.value,n.value&&n.value.setFlowAnimation(G.value)},J=e=>{T(e),h.value=null},Q=e=>{$(e,W)},ee=e=>{if(n.value){n.value.selectNode(e);const t=n.value.getNodePosition(e);t&&(n.value.setCameraCenter(t[0],t[1]),n.value.setCameraZoom(2))}u.value=!1};return o.onMounted(()=>{if(i.value&&a.value){n.value=new l(i.value,a.value);const e=E(t.data);n.value.setData(e.positions,e.colors,e.ids,e.metadata,e.edgeIndices,e.edgeMetadata,e.idMap),z(e.count,e.positions,e.nodeRadii,e.edgeIndices);const s=()=>{U.value.show=!1},r=e=>{if(null!==O.value&&n.value&&i.value){const t=i.value.getBoundingClientRect(),o=e.clientX-t.left,s=e.clientY-t.top,a=window.devicePixelRatio,r=n.value.unproject(o*a,s*a);n.value.setPhantomEdge(O.value,r[0],r[1])}};window.addEventListener("mousedown",s),window.addEventListener("mousemove",r),o.watch(O,e=>{null===e&&n.value&&n.value.clearPhantomEdge()}),o.onUnmounted(()=>{window.removeEventListener("mousedown",s),window.removeEventListener("mousemove",r)}),i.value.addEventListener("graph:click",()=>{null!==O.value&&(O.value=null)}),i.value.addEventListener("node-selected",e=>{const t=e.detail;if(null!==O.value&&null!==t&&t!==O.value){const e=n.value.getNodeId(O.value),o=n.value.getNodeId(t);return e&&o&&n.value.addEdge({source:e,target:o,label:X.value.label,data:{customColor:X.value.color,customSize:X.value.size}}),O.value=null,void(X.value={label:"relation",color:"#00ffcc",size:1.5})}y.value=t,null!==y.value&&(I.value=null,N.value=null)}),i.value.addEventListener("edge-selected",e=>{const t=e.detail;I.value=t,null!==t&&(y.value=null,N.value=null,D())}),i.value.addEventListener("combo-selected",e=>{N.value=e.detail,null!==N.value&&(y.value=null,I.value=null)}),i.value.addEventListener("nodes-selected",e=>{k.value=e.detail.length}),i.value.addEventListener("node-moved",e=>{const t=e.detail;t.id===y.value&&(C.value=Number(t.x.toFixed(3)),B.value=Number(t.y.toFixed(3)))})}}),(e,t)=>(o.openBlock(),o.createElementBlock("div",{class:o.normalizeClass(["container vortx-gl-root",o.unref(r)])},[o.createElementVNode("canvas",{ref_key:"canvasRef",ref:i,class:"main-canvas",onContextmenu:t[0]||(t[0]=o.withModifiers((...e)=>o.unref(j)&&o.unref(j)(...e),["prevent"]))},null,544),o.createElementVNode("canvas",{ref_key:"overlayCanvasRef",ref:a,class:"overlay-canvas"},null,512),o.unref(c)?(o.openBlock(),o.createElementBlock("div",me,[o.createElementVNode("button",{class:o.normalizeClass(["menu-btn",{active:"layouts"===o.unref(h)}]),onClick:t[1]||(t[1]=e=>o.unref(p)("layouts"))},[o.createVNode(v,{name:"layout",size:"20"}),t[30]||(t[30]=o.createElementVNode("span",null,"Layout",-1))],2),o.createElementVNode("button",{class:o.normalizeClass(["menu-btn",{active:"view"===o.unref(h)}]),onClick:t[2]||(t[2]=e=>o.unref(p)("view"))},[o.createVNode(v,{name:"eye",size:"20"}),t[31]||(t[31]=o.createElementVNode("span",null,"View",-1))],2),t[33]||(t[33]=o.createElementVNode("div",{class:"menu-divider"},null,-1)),o.createElementVNode("button",{class:"menu-btn search-trigger",onClick:t[3]||(t[3]=(...e)=>o.unref(b)&&o.unref(b)(...e))},[o.createVNode(v,{name:"search",size:"20"}),t[32]||(t[32]=o.createElementVNode("span",null,"Search",-1))])])):o.createCommentVNode("",!0),o.unref(c)&&o.unref(h)?(o.openBlock(),o.createElementBlock("div",{key:1,class:"drawer-overlay",onClick:t[10]||(t[10]=e=>h.value=null)},[o.createElementVNode("div",{class:"mobile-drawer glass shadow-2xl",onClick:t[9]||(t[9]=o.withModifiers(()=>{},["stop"]))},[t[34]||(t[34]=o.createElementVNode("div",{class:"drawer-handle"},null,-1)),o.createElementVNode("div",ge,[o.createElementVNode("h3",null,o.toDisplayString("layouts"===o.unref(h)?"Graph Layouts":"View Controls"),1),o.createElementVNode("button",{class:"drawer-close",onClick:t[4]||(t[4]=e=>h.value=null)},[o.createVNode(v,{name:"x",size:"20"})])]),"layouts"===o.unref(h)?(o.openBlock(),o.createElementBlock("div",fe,[o.createVNode(F,{layouts:Z,currentLayout:o.unref(L),flowEnabled:G.value,onRunLayout:J,onToggleFlow:K},null,8,["currentLayout","flowEnabled"])])):o.createCommentVNode("",!0),"view"===o.unref(h)?(o.openBlock(),o.createElementBlock("div",ve,[o.createVNode(x,{theme:o.unref(r),flowEnabled:G.value,onToggleTheme:o.unref(f),onFit:t[5]||(t[5]=e=>n.value?.fitToView()),onZoomIn:t[6]||(t[6]=e=>n.value?.zoomIn()),onZoomOut:t[7]||(t[7]=e=>n.value?.zoomOut()),onStress:t[8]||(t[8]=e=>o.unref(P)(1e5))},null,8,["theme","flowEnabled","onToggleTheme"])])):o.createCommentVNode("",!0)])])):o.createCommentVNode("",!0),o.withDirectives(o.createVNode(ue,{ref:"searchBarRef",nodes:o.unref(M),onSelect:ee},null,8,["nodes"]),[[o.vShow,!o.unref(c)||o.unref(u)]]),o.unref(c)?o.createCommentVNode("",!0):(o.openBlock(),o.createElementBlock("div",pe,[o.createVNode(x,{theme:o.unref(r),flowEnabled:G.value,onToggleTheme:o.unref(f),onToggleFlow:K,onFit:t[11]||(t[11]=e=>n.value?.fitToView()),onZoomIn:t[12]||(t[12]=e=>n.value?.zoomIn()),onZoomOut:t[13]||(t[13]=e=>n.value?.zoomOut()),onStress:t[14]||(t[14]=e=>o.unref(P)(1e5))},null,8,["theme","flowEnabled","onToggleTheme"]),o.createElementVNode("div",{class:"help-trigger glass-interactive",onClick:t[15]||(t[15]=e=>m.value=!0),title:"Keyboard Shortcuts"},[o.createVNode(v,{name:"info",size:"18"})])])),o.createVNode(w,{show:o.unref(U).show,x:o.unref(U).x,y:o.unref(U).y,type:o.unref(U).type,items:o.unref(Y),onAction:Q},null,8,["show","x","y","type","items"]),o.createVNode(se,{nodeId:o.unref(y),"onUpdate:nodeId":t[16]||(t[16]=e=>o.isRef(y)?y.value=e:null),nodeMetadata:o.unref(R),nodeX:o.unref(C),"onUpdate:nodeX":t[17]||(t[17]=e=>o.isRef(C)?C.value=e:null),nodeY:o.unref(B),"onUpdate:nodeY":t[18]||(t[18]=e=>o.isRef(B)?B.value=e:null),edgeId:o.unref(I),"onUpdate:edgeId":t[19]||(t[19]=e=>o.isRef(I)?I.value=e:null),edgeMetadata:o.unref(A),combo:o.unref(V),"onUpdate:combo":t[20]||(t[20]=e=>o.isRef(V)?V.value=e:null),"onUpdate:comboLabel":o.unref(S),onRemoveFromCombo:o.unref(_)},null,8,["nodeId","nodeMetadata","nodeX","nodeY","edgeId","edgeMetadata","combo","onUpdate:comboLabel","onRemoveFromCombo"]),o.unref(c)?(o.openBlock(),o.createElementBlock("div",{key:3,class:"help-trigger mobile help-trigger-pos glass-interactive",onClick:t[21]||(t[21]=e=>m.value=!0)},[o.createVNode(v,{name:"info",size:"18"})])):o.createCommentVNode("",!0),o.unref(m)?(o.openBlock(),o.createElementBlock("div",{key:4,class:"help-modal-overlay",onClick:t[24]||(t[24]=e=>m.value=!1)},[o.createElementVNode("div",{class:"help-modal glass",onClick:t[23]||(t[23]=o.withModifiers(()=>{},["stop"]))},[o.createElementVNode("div",be,[t[35]||(t[35]=o.createElementVNode("h3",null,"Keyboard Shortcuts",-1)),o.createElementVNode("button",{class:"close-btn",onClick:t[22]||(t[22]=e=>m.value=!1)},[o.createVNode(v,{name:"x",size:"20"})])]),o.createElementVNode("div",Me,[(o.openBlock(),o.createElementBlock(o.Fragment,null,o.renderList(q,e=>o.createElementVNode("div",{key:e.key,class:"shortcut-row"},[o.createElementVNode("span",Ee,o.toDisplayString(e.key),1),o.createElementVNode("span",we,o.toDisplayString(e.action),1)])),64))])])])):o.createCommentVNode("",!0),o.unref(c)?o.createCommentVNode("",!0):(o.openBlock(),o.createBlock(F,{key:5,layouts:Z,currentLayout:o.unref(L),flowEnabled:G.value,onRunLayout:o.unref(T),onToggleFlow:K},null,8,["currentLayout","flowEnabled","onRunLayout"])),null!==o.unref(O)?(o.openBlock(),o.createElementBlock("div",ye,[o.createElementVNode("div",Ie,[o.createVNode(v,{name:"link-2",size:"20",class:"pulse-icon"}),o.createElementVNode("div",Ne,[o.withDirectives(o.createElementVNode("input",{"onUpdate:modelValue":t[25]||(t[25]=e=>X.value.label=e),class:"vortx-input-sm",placeholder:"Label",onMousedown:t[26]||(t[26]=o.withModifiers(()=>{},["stop"]))},null,544),[[o.vModelText,X.value.label]]),o.createElementVNode("div",xe,[(o.openBlock(),o.createElementBlock(o.Fragment,null,o.renderList(H,e=>o.createElementVNode("button",{key:e,class:"color-dot",style:o.normalizeStyle({backgroundColor:e,border:X.value.color===e?"2px solid white":"none"}),onClick:t=>X.value.color=e},null,12,ke)),64))]),o.createElementVNode("div",Ce,[o.createVNode(v,{name:"move-horizontal",size:"14",class:"dim"}),o.withDirectives(o.createElementVNode("input",{"onUpdate:modelValue":t[27]||(t[27]=e=>X.value.size=e),type:"number",class:"vortx-input-sm narrow",placeholder:"Size",onMousedown:t[28]||(t[28]=o.withModifiers(()=>{},["stop"]))},null,544),[[o.vModelText,X.value.size,void 0,{number:!0}]])])]),t[36]||(t[36]=o.createElementVNode("div",{class:"divider-v"},null,-1)),t[37]||(t[37]=o.createElementVNode("span",{class:"instruction-text"},"Click target node",-1))]),o.createElementVNode("button",{class:"toast-cancel",onClick:t[29]||(t[29]=e=>O.value=null)},[o.createVNode(v,{name:"x",size:"14"})])])):o.createCommentVNode("",!0)],2))}}),[["__scopeId","data-v-0b4073a8"]]);e.GraphCanvas=Be,e.GraphEngine=l,e.createGraphData=(e,t)=>({nodes:e,edges:t}),Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { onMounted, ref } from "vue";
|
|
3
|
+
import { GraphCanvas, createGraphData } from "webgl-graph-library";
|
|
4
|
+
import "webgl-graph-library/dist/webgl-graph-library.css";
|
|
5
|
+
|
|
6
|
+
// 1. Prepare your data (can be loaded from an API)
|
|
7
|
+
const graphData = ref({
|
|
8
|
+
nodes: [
|
|
9
|
+
{
|
|
10
|
+
data: { id: "1", label: "CEO", color: "#ff4757", width: 80, height: 80 },
|
|
11
|
+
position: { x: 2500, y: 2400 },
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
data: { id: "2", label: "CTO", color: "#2ed573", width: 60, height: 60 },
|
|
15
|
+
position: { x: 2300, y: 2600 },
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
data: { id: "3", label: "CFO", color: "#1e90ff", width: 60, height: 60 },
|
|
19
|
+
position: { x: 2700, y: 2600 },
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
edges: [
|
|
23
|
+
{ data: { id: "e1-2", source: "1", target: "2", label: "Reports to" } },
|
|
24
|
+
{ data: { id: "e1-3", source: "1", target: "3", label: "Reports to" } },
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// 2. Event Handled
|
|
29
|
+
const onNodeSelect = (nodeId) => {
|
|
30
|
+
console.log("User selected node:", nodeId);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const onEdgeSelect = (edgeId) => {
|
|
34
|
+
console.log("User selected edge:", edgeId);
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<template>
|
|
39
|
+
<div class="app-container">
|
|
40
|
+
<header>
|
|
41
|
+
<h1>Org Chart Visualizer</h1>
|
|
42
|
+
<p>Powered by VortxGL</p>
|
|
43
|
+
</header>
|
|
44
|
+
|
|
45
|
+
<main class="viewport">
|
|
46
|
+
<!-- 3. Use the Library Component -->
|
|
47
|
+
<GraphCanvas
|
|
48
|
+
:data="graphData"
|
|
49
|
+
@node-selected="onNodeSelect"
|
|
50
|
+
@edge-selected="onEdgeSelect"
|
|
51
|
+
/>
|
|
52
|
+
</main>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
/* Ensure the container has dimensions */
|
|
58
|
+
.viewport {
|
|
59
|
+
width: 100vw;
|
|
60
|
+
height: calc(100vh - 80px);
|
|
61
|
+
position: relative;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
header {
|
|
65
|
+
height: 80px;
|
|
66
|
+
background: #1a1a2e;
|
|
67
|
+
color: white;
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
align-items: center;
|
|
72
|
+
border-bottom: 1px solid #16213e;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
body {
|
|
76
|
+
margin: 0;
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
font-family: "Inter", sans-serif;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vortx-gl",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"description": "Secure High-performance
|
|
4
|
+
"version": "1.0.4",
|
|
5
|
+
"description": "Secure High-performance WebGL Graph Rendering Library",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/vortx-gl.umd.js",
|
|
8
8
|
"module": "./dist/vortx-gl.es.js",
|
|
@@ -19,33 +19,26 @@
|
|
|
19
19
|
},
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"files": [
|
|
22
|
-
"dist"
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE",
|
|
25
|
+
"examples"
|
|
23
26
|
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"dev": "vite",
|
|
26
|
-
"build": "vue-tsc -b && vite build",
|
|
27
|
-
"build:lib": "vue-tsc -b && cross-env BUILD_LIB=true vite build",
|
|
28
|
-
"preview": "vite preview"
|
|
29
|
-
},
|
|
30
27
|
"peerDependencies": {
|
|
31
28
|
"gl-matrix": "^3.0.0",
|
|
32
29
|
"vue": "^3.0.0"
|
|
33
30
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"typescript": "~5.9.3",
|
|
47
|
-
"vite": "^7.2.4",
|
|
48
|
-
"vite-plugin-dts": "^4.5.1",
|
|
49
|
-
"vue-tsc": "^3.1.4"
|
|
31
|
+
"keywords": [
|
|
32
|
+
"webgl",
|
|
33
|
+
"graph",
|
|
34
|
+
"visualization",
|
|
35
|
+
"performance",
|
|
36
|
+
"vortx-gl",
|
|
37
|
+
"network"
|
|
38
|
+
],
|
|
39
|
+
"author": "VortxGL Contributors",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/doryhokayem/vortx-gl.git"
|
|
50
43
|
}
|
|
51
44
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.lucide-icon[data-v-6aa14fad]{display:inline-block;vertical-align:middle}.radial-menu[data-v-a2a68595]{position:fixed;width:0;height:0;z-index:2000;pointer-events:none}.radial-center[data-v-a2a68595]{position:absolute;width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;transform:translate(-50%,-50%);animation:popIn-a2a68595 .4s cubic-bezier(.34,1.56,.64,1);pointer-events:auto;font-size:24px;border-color:var(--primary);box-shadow:0 0 20px #00ffd533}.radial-item[data-v-a2a68595]{position:absolute;width:64px;height:64px;pointer-events:none;animation:itemIn-a2a68595 .5s cubic-bezier(.34,1.56,.64,1) backwards;z-index:2001}.radial-item-inner[data-v-a2a68595]{width:100%;height:100%;border-radius:50%;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;pointer-events:auto;transition:all .3s cubic-bezier(.175,.885,.32,1.275);border:1px solid var(--border);color:var(--text);outline:none;padding:0}.radial-item-inner[data-v-a2a68595]:hover{background:var(--primary);color:#fff;transform:scale(1.15);border-color:#fff;box-shadow:0 0 25px #00ffd580}.radial-icon[data-v-a2a68595]{font-size:20px}.radial-label[data-v-a2a68595]{font-size:9px;text-transform:uppercase;margin-top:2px;font-weight:700;letter-spacing:.5px}@keyframes popIn-a2a68595{0%{transform:translate(-50%,-50%) scale(0);opacity:0}to{transform:translate(-50%,-50%) scale(1);opacity:1}}@keyframes itemIn-a2a68595{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}to{opacity:1}}@media(max-width:768px){.radial-item[data-v-a2a68595]{width:72px;height:72px}.radial-icon[data-v-a2a68595]{font-size:24px}.radial-label[data-v-a2a68595]{font-size:10px}}.top-right-controls[data-v-4a9f463d]{position:absolute;top:var(--space-md);right:var(--space-md);display:flex;flex-direction:column;gap:var(--space-xs);padding:var(--space-xs);border-radius:var(--radius-md);z-index:1000;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}.control-btn[data-v-4a9f463d]{width:44px;height:44px;display:flex;align-items:center;justify-content:center;cursor:pointer;border-radius:var(--radius-sm);transition:all .2s cubic-bezier(.4,0,.2,1);font-size:20px;background:transparent;border:none;color:var(--text);outline:none;position:relative;overflow:hidden}.control-btn[data-v-4a9f463d]:hover{background:var(--border);transform:translateY(-2px)}.control-btn[data-v-4a9f463d]:active{transform:scale(.9)}.stress-btn[data-v-4a9f463d]{color:var(--warning)}.divider[data-v-4a9f463d]{width:24px;height:1px;background:var(--border);align-self:center;margin:4px 0}@media(max-width:768px){.top-right-controls[data-v-4a9f463d]{flex-direction:row;top:auto;bottom:var(--space-xl);right:50%;transform:translate(50%);gap:var(--space-sm);padding:var(--space-sm)}.divider[data-v-4a9f463d]{width:1px;height:24px;margin:0 4px}.control-btn[data-v-4a9f463d]{width:48px;height:48px;font-size:22px}}.layout-controls[data-v-ff660e6a]{position:absolute;bottom:var(--space-lg);left:50%;transform:translate(-50%);width:auto;max-width:90vw;padding:6px;border-radius:var(--radius-lg);z-index:1000;overflow:hidden}.scroll-container[data-v-ff660e6a]{display:flex;gap:var(--space-xs);overflow-x:auto;padding:2px;scrollbar-width:none;-ms-overflow-style:none}.scroll-container[data-v-ff660e6a]::-webkit-scrollbar{display:none}.layout-btn[data-v-ff660e6a]{padding:10px 18px;border-radius:var(--radius-md);cursor:pointer;font-size:14px;font-weight:500;transition:all .3s cubic-bezier(.4,0,.2,1);background:transparent;border:none;display:flex;align-items:center;gap:8px;color:var(--text-dim);white-space:nowrap;outline:none}.layout-btn[data-v-ff660e6a]:hover{background:var(--border);color:var(--text);transform:translateY(-2px)}.layout-btn.active[data-v-ff660e6a]{background:var(--primary);color:#fff;box-shadow:0 4px 15px var(--primary-glow);border-color:var(--primary)}.btn-icon[data-v-ff660e6a]{font-size:18px}@media(max-width:768px){.layout-controls[data-v-ff660e6a]{bottom:calc(var(--space-xl) + 80px);max-width:95vw;border-radius:var(--radius-md)}.layout-btn[data-v-ff660e6a]{padding:12px 20px;font-size:15px}}.property-panel[data-v-7c0e24e3]{position:absolute;top:var(--space-md);left:var(--space-md);width:320px;max-height:calc(100vh - var(--space-xl) * 2);border-radius:var(--radius-lg);z-index:1100;display:flex;flex-direction:column;animation:slideIn-7c0e24e3 .4s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes slideIn-7c0e24e3{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.panel-header[data-v-7c0e24e3]{padding:var(--space-md);border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center}.panel-header h3[data-v-7c0e24e3]{margin:0;font-size:16px;font-weight:600;color:var(--primary);text-transform:uppercase;letter-spacing:.5px}.close-btn[data-v-7c0e24e3]{background:transparent;border:none;color:var(--text-dim);cursor:pointer;padding:4px;font-size:18px;display:flex;align-items:center;justify-content:center;transition:color .2s}.close-btn[data-v-7c0e24e3]:hover{color:var(--text)}.panel-content[data-v-7c0e24e3]{padding:var(--space-md);overflow-y:auto;flex:1}.prop-group[data-v-7c0e24e3]{margin-bottom:var(--space-md)}.prop-row[data-v-7c0e24e3]{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-sm)}.prop-group label[data-v-7c0e24e3]{display:block;font-size:11px;font-weight:600;color:var(--text-dim);text-transform:uppercase;margin-bottom:var(--space-xs)}.id-badge[data-v-7c0e24e3]{display:inline-block;background:var(--border);padding:4px 8px;border-radius:var(--radius-sm);font-family:monospace;font-size:13px}.val-container[data-v-7c0e24e3]{font-size:14px}input[data-v-7c0e24e3]{width:100%;background:var(--border);border:1px solid transparent;padding:8px 12px;border-radius:var(--radius-sm);color:var(--text);font-family:monospace;font-size:14px;transition:all .2s}input[data-v-7c0e24e3]:focus{outline:none;border-color:var(--primary);background:transparent}.json-prop[data-v-7c0e24e3]{font-size:11px;background:#0003;padding:8px;border-radius:var(--radius-sm);margin:0;max-height:150px;overflow:auto;white-space:pre-wrap;word-break:break-all;border:1px solid var(--border)}.member-list[data-v-7c0e24e3]{background:var(--border);border-radius:var(--radius-sm);padding:var(--space-xs);max-height:200px;overflow-y:auto;display:flex;flex-direction:column;gap:2px}.member-item[data-v-7c0e24e3]{display:flex;justify-content:space-between;align-items:center;padding:4px 8px;font-size:12px;border-radius:4px}.member-item[data-v-7c0e24e3]:hover{background:var(--border)}.remove-btn[data-v-7c0e24e3]{background:#ff00001a;border:none;color:var(--danger);width:20px;height:20px;border-radius:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:10px}.prop-image[data-v-7c0e24e3]{width:64px;height:64px;object-fit:contain;padding:8px;background:var(--surface);border-radius:50%;border:1px solid var(--border);margin-top:4px;box-shadow:0 2px 8px #0003}.edge-panel .panel-header h3[data-v-7c0e24e3]{color:var(--secondary)}.combo-panel .panel-header h3[data-v-7c0e24e3]{color:var(--success)}@media(max-width:768px){.property-panel[data-v-7c0e24e3]{inset:auto 0 0;width:100%;height:50vh;border-radius:var(--radius-lg) var(--radius-lg) 0 0;animation:slideUp-7c0e24e3 .4s cubic-bezier(.16,1,.3,1)}@keyframes slideUp-7c0e24e3{0%{transform:translateY(100%)}to{transform:translateY(0)}}}.search-wrapper[data-v-3fda8a90]{position:absolute;top:var(--space-md);left:50%;transform:translate(-50%);z-index:1200;transition:all .3s ease}.search-container[data-v-3fda8a90]{width:240px;max-width:90vw;border-radius:var(--radius-full);transition:all .3s cubic-bezier(.4,0,.2,1);padding:4px}.search-container.expanded[data-v-3fda8a90]{width:320px;border-radius:var(--radius-md)}.search-input-wrapper[data-v-3fda8a90]{display:flex;align-items:center;gap:8px;padding:4px 12px}.search-icon[data-v-3fda8a90]{opacity:.6}input[data-v-3fda8a90]{flex:1;background:transparent;border:none;border-radius:999px;padding:8px 4px;font-size:14px;color:var(--text);outline:none}.clear-btn[data-v-3fda8a90]{cursor:pointer;opacity:.5;padding:4px;display:flex;align-items:center}.results-panel[data-v-3fda8a90]{margin-top:4px;border-top:1px solid var(--border);padding:4px;max-height:250px;overflow-y:auto}.result-item[data-v-3fda8a90]{display:flex;align-items:center;gap:12px;padding:10px 12px;cursor:pointer;border-radius:var(--radius-sm);transition:background .2s}.result-item[data-v-3fda8a90]:hover{background:var(--border)}.node-icon[data-v-3fda8a90]{color:var(--primary)}.node-info[data-v-3fda8a90]{display:flex;flex-direction:column}.node-label[data-v-3fda8a90]{font-size:13px;font-weight:500}.node-id[data-v-3fda8a90]{font-size:11px;opacity:.6}.no-results[data-v-3fda8a90]{padding:12px;text-align:center;font-size:13px;color:var(--text-dim);border-top:1px solid var(--border)}.search-wrapper.is-mobile[data-v-3fda8a90]{top:auto;bottom:var(--space-md);left:var(--space-md);transform:none}.is-mobile .search-container[data-v-3fda8a90]{width:52px;height:52px;overflow:hidden;padding:0;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 20px #0000004d;border-color:var(--primary)}.is-mobile .search-input-wrapper[data-v-3fda8a90]{padding:0;width:100%;height:100%;justify-content:center}.is-mobile .search-icon[data-v-3fda8a90]{margin:0}.is-mobile input[data-v-3fda8a90]{display:none}.is-mobile .search-container.expanded[data-v-3fda8a90]{position:fixed;bottom:0;left:0;width:100vw;height:auto;max-width:none;border-radius:var(--radius-lg) var(--radius-lg) 0 0;transform:none;padding:var(--space-md);display:block}.is-mobile .search-wrapper.mobile-expanded[data-v-3fda8a90]{left:0;bottom:0;width:100%}.is-mobile .search-container.expanded .search-input-wrapper[data-v-3fda8a90]{height:auto;padding:4px 0;margin-bottom:var(--space-md);border-bottom:1px solid var(--border)}.is-mobile .search-container.expanded input[data-v-3fda8a90]{display:block;font-size:16px}.is-mobile .search-container.expanded .results-panel[data-v-3fda8a90]{max-height:60vh}.mobile-backdrop[data-v-3fda8a90]{position:fixed;inset:0;background:#00000080;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:-1}.container[data-v-87730b5c]{position:relative;width:100vw;height:100vh;transition:background-color .4s cubic-bezier(.4,0,.2,1);background:var(--bg);color:var(--text);overflow:hidden}.main-canvas[data-v-87730b5c],.overlay-canvas[data-v-87730b5c]{position:absolute;top:0;left:0;width:100%;height:100%;display:block}.overlay-canvas[data-v-87730b5c]{pointer-events:none}.mobile-menu[data-v-87730b5c]{position:fixed;bottom:var(--space-md);left:50%;transform:translate(-50%);display:flex;align-items:center;padding:6px;border-radius:var(--radius-full);z-index:1000;gap:4px}.menu-btn[data-v-87730b5c]{background:transparent;border:none;color:var(--text-dim);display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 16px;border-radius:var(--radius-full);cursor:pointer;transition:all .2s;min-width:70px}.menu-btn span[data-v-87730b5c]{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.menu-btn.active[data-v-87730b5c]{background:var(--primary);color:#fff;box-shadow:0 4px 12px var(--primary-glow)}.menu-divider[data-v-87730b5c]{width:1px;height:24px;background:var(--border);margin:0 4px}.drawer-overlay[data-v-87730b5c]{position:fixed;inset:0;background:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:1300}.mobile-drawer[data-v-87730b5c]{position:absolute;bottom:0;left:0;width:100%;border-radius:var(--radius-lg) var(--radius-lg) 0 0;padding:var(--space-md);padding-bottom:env(safe-area-inset-bottom,var(--space-lg));animation:slideUp-87730b5c .3s cubic-bezier(.16,1,.3,1)}@keyframes slideUp-87730b5c{0%{transform:translateY(100%)}to{transform:translateY(0)}}.drawer-handle[data-v-87730b5c]{width:40px;height:4px;background:var(--border);border-radius:2px;margin:0 auto var(--space-md)}.drawer-header[data-v-87730b5c]{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--space-md)}.drawer-header h3[data-v-87730b5c]{margin:0;font-size:16px;font-weight:700}.drawer-close[data-v-87730b5c]{background:transparent;border:none;color:var(--text-dim);cursor:pointer}.drawer-content[data-v-87730b5c]{padding:var(--space-sm) 0}.drawer-content[data-v-87730b5c] .layout-controls,.drawer-content[data-v-87730b5c] .top-right-controls{position:static;transform:none;width:100%;max-width:none;padding:0;background:transparent;box-shadow:none;border:none;flex-direction:row;flex-wrap:wrap;justify-content:center}.drawer-content[data-v-87730b5c] .divider{display:none}.help-trigger[data-v-87730b5c]{position:absolute;bottom:var(--space-md);right:var(--space-md);width:32px;height:32px;background:var(--surface);border:1px solid var(--border);border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;font-weight:700;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);z-index:1000;transition:all .2s;color:var(--text-dim)}.help-trigger[data-v-87730b5c]:hover{background:var(--primary);color:#fff;border-color:#fff;transform:scale(1.1)}.help-modal-overlay[data-v-87730b5c]{position:fixed;inset:0;background:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:2000;display:flex;align-items:center;justify-content:center}.help-modal[data-v-87730b5c]{width:340px;border-radius:var(--radius-lg);overflow:hidden;animation:modalPop-87730b5c .3s cubic-bezier(.34,1.56,.64,1)}@keyframes modalPop-87730b5c{0%{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}.help-header[data-v-87730b5c]{padding:var(--space-md);border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center}.help-header h3[data-v-87730b5c]{margin:0;font-size:16px}.shortcut-row[data-v-87730b5c]{display:flex;justify-content:space-between;align-items:center;padding:12px var(--space-md);border-bottom:1px solid var(--border)}.shortcut-row[data-v-87730b5c]:last-child{border-bottom:none}.key-badge[data-v-87730b5c]{background:var(--border);padding:4px 10px;border-radius:var(--radius-sm);font-family:monospace;font-size:12px}.action-desc[data-v-87730b5c]{font-size:13px;color:var(--text-dim)}.close-btn[data-v-87730b5c]{background:transparent;border:none;color:var(--text-dim);font-size:20px;cursor:pointer}@media(max-width:768px){.help-trigger[data-v-87730b5c]{display:none}}
|