drawui-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ var K={identity(){return {a:1,b:0,c:0,d:1,e:0,f:0}},translation(u,t){return {a:1,b:0,c:0,d:1,e:u,f:t}},rotation(u){let t=Math.cos(u),e=Math.sin(u);return {a:t,b:e,c:-e,d:t,e:0,f:0}},scale(u,t){return {a:u,b:0,c:0,d:t,e:0,f:0}},multiply(u,t){return {a:u.a*t.a+u.b*t.c,b:u.a*t.b+u.b*t.d,c:u.c*t.a+u.d*t.c,d:u.c*t.b+u.d*t.d,e:u.e*t.a+u.f*t.c+t.e,f:u.e*t.b+u.f*t.d+t.f}},apply(u,t){return {x:u.a*t.x+u.c*t.y+u.e,y:u.b*t.x+u.d*t.y+u.f}},invert(u){let t=u.a*u.d-u.b*u.c;if(Math.abs(t)<1e-10)return null;let e=1/t;return {a:u.d*e,b:-u.b*e,c:-u.c*e,d:u.a*e,e:(u.c*u.f-u.d*u.e)*e,f:(u.b*u.e-u.a*u.f)*e}},createCameraMatrix(u){let t=K.scale(u.zoom,u.zoom),e=K.translation(u.x,u.y);return K.multiply(e,t)}};var q=class{x=0;y=0;zoom=1;minZoom=.1;maxZoom=10;getMatrix(){return K.createCameraMatrix({x:this.x,y:this.y,zoom:this.zoom})}getInverseMatrix(){return K.invert(this.getMatrix())}screenToPage(t){let e=this.getInverseMatrix();return e?K.apply(e,t):t}pageToScreen(t){return K.apply(this.getMatrix(),t)}translate(t,e){this.x+=t,this.y+=e;}zoomAt(t,e){let i=this.screenToPage(t);this.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.zoom*e));let n=this.pageToScreen(i);this.x+=t.x-n.x,this.y+=t.y-n.y;}reset(){this.x=0,this.y=0,this.zoom=1;}getZoom(){return this.zoom}fitBounds(t,e=50){let i=window.innerWidth,n=window.innerHeight,s=(i-e*2)/t.width,r=(n-e*2)/t.height,o=Math.min(s,r,this.maxZoom);this.zoom=Math.max(this.minZoom,o),this.x=(i-t.width*this.zoom)/2-t.x*this.zoom,this.y=(n-t.height*this.zoom)/2-t.y*this.zoom;}};var G={selection:{outline:"#3b82f6",handleStroke:"#3b82f6",handleFill:"#ffffff",outlineFill:"rgba(59, 130, 246, 0.1)",shadow:"#3b82f6"},grid:"#e5e7eb",text:"#1f2937",editBorder:"#3b82f6"};function gt(){return G}var H=class{context;constructor(t){this.context=t;}getCursor(t){return null}dispose(){}};var yt={strokeColor:"#1f2937",fillColor:"transparent",strokeWidth:2,strokeDash:null},pt={rectangle:{width:120,height:60},"rounded-rectangle":{width:120,height:60,borderRadius:8},circle:{width:80,height:80},ellipse:{width:100,height:70},diamond:{width:100,height:80},triangle:{width:100,height:90},hexagon:{width:100,height:90},parallelogram:{width:120,height:60},trapezoid:{width:120,height:70},pentagon:{width:100,height:90},octagon:{width:100,height:90},star:{width:100,height:100}},$=14,L=22,Y=36,N=14,D=8,j=160,ft=["rectangle","rounded-rectangle","circle","ellipse","diamond","triangle","hexagon","parallelogram","trapezoid","pentagon","octagon","star","uml-class","uml-interface"];function rt(u){return ft.includes(u)}function lt(u,t,e){let i=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,n=pt[u]||{width:120,height:60},s=n.width,r=n.height,o=n.radius??s/2,a=n.borderRadius??8,l=e??{...yt};switch(u){case "rounded-rectangle":return {id:i,type:"rounded-rectangle",x:t.x,y:t.y,width:s,height:r,borderRadius:a,rotation:0,style:l};case "circle":return {id:i,type:"circle",x:t.x,y:t.y,radius:o,rotation:0,style:l};case "ellipse":return {id:i,type:"ellipse",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "diamond":return {id:i,type:"diamond",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "triangle":return {id:i,type:"triangle",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "hexagon":return {id:i,type:"hexagon",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "parallelogram":return {id:i,type:"parallelogram",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "trapezoid":return {id:i,type:"trapezoid",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "pentagon":return {id:i,type:"pentagon",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "octagon":return {id:i,type:"octagon",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "star":return {id:i,type:"star",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l};case "uml-class":{let h="ClassName",d=[{id:`attr_${Date.now()}_1`,visibility:"+",name:"field",type:"string"}],c=[{id:`meth_${Date.now()}_1`,visibility:"+",name:"method",params:"param",returnType:"void"}],{width:g,height:y}=ot(h,d,c);return {id:i,type:"uml-class",x:t.x,y:t.y,width:g,height:y,className:h,attributes:d,methods:c,rotation:0,style:{...l,fillColor:"#ffffff"}}}case "uml-interface":{let h="Interface",d=[{id:`meth_${Date.now()}_1`,visibility:"+",name:"method",params:"type",returnType:"type"}],c=$*.6,g=d.map(S=>`${S.visibility} ${S.name}(${S.params}): ${S.returnType}`),y=Math.max(h.length,...g.map(S=>S.length),0),p=Math.max(j,y*c+N*2),f=d.length>0?d.length*L+D*2:D*2,T=Y+f;return {id:i,type:"uml-interface",x:t.x,y:t.y,width:p,height:T,className:h,methods:d,rotation:0,style:{...l,fillColor:"#ffffff"}}}default:return {id:i,type:"rectangle",x:t.x,y:t.y,width:s,height:r,rotation:0,style:l}}}function ot(u,t,e){let i=$*.6,n=[u];for(let d of t)n.push(`${d.visibility} ${d.name}: ${d.type}`);for(let d of e)n.push(`${d.visibility} ${d.name}(${d.params}): ${d.returnType}`);let r=Math.max(...n.map(d=>d.length),0)*i+N*2,o=Math.max(j,r),a=t.length>0?t.length*L+D*2:D*2,l=e.length>0?e.length*L+D*2:D*2,h=Y+a+l;return {width:o,height:h}}var R=class extends H{type;constructor(t,e){super(t),this.type=e;}onPointerDown(t,e){let i=lt(this.type,t);this.context.commitHistory(),this.context.addShape(i),this.context.selectShape(i.id,false);}onPointerMove(t,e){}onPointerUp(t,e){this.context.setActiveTool("select");}onKeyDown(t){}onKeyUp(t){}getCursor(){return "crosshair"}};var J=class u{ctx;width=0;height=0;dpr=window.devicePixelRatio||1;showGrid=false;textLayoutCache=new Map;static TEXT_CACHE_LIMIT=500;constructor(t){let e=t.getContext("2d");if(!e)throw new Error("Failed to get Canvas 2D context");this.ctx=e;}resize(t,e){this.width=t,this.height=e;let i=Math.ceil(t*this.dpr),n=Math.ceil(e*this.dpr);(this.ctx.canvas.width!==i||this.ctx.canvas.height!==n)&&(this.ctx.canvas.width=i,this.ctx.canvas.height=n,this.ctx.canvas.style.width=`${t}px`,this.ctx.canvas.style.height=`${e}px`);}setShowGrid(t){this.showGrid=t;}render(t,e,i,n,s,r,o){let a=this.ctx,l=e.getZoom(),h=e.x,d=e.y;a.setTransform(1,0,0,1,0,0),a.clearRect(0,0,this.ctx.canvas.width,this.ctx.canvas.height);let c=this.dpr*l;a.setTransform(c,0,0,c,c*h,c*d),this.showGrid&&this.drawGrid(a,l,h,d);let g=null;for(let p of t)if(p.type==="arrow"&&(p.startBinding||p.endBinding)){g=new Map,t.forEach(f=>g.set(f.id,f));break}let y=g??new Map;t.forEach(p=>{a.save(),a.translate(p.x,p.y),a.rotate(p.rotation);let f=i.has(p.id);this.drawShape(a,p,f,l,y),a.restore();}),o&&this.drawGenerationPreview(a,o,l),n&&this.drawBrushRect(a,n,l),s&&s.length>0&&this.drawSnapGuides(a,s,l),r&&this.drawConnectingTargetHighlight(a,r,t,l),a.setTransform(1,0,0,1,0,0);}drawConnectingTargetHighlight(t,e,i,n){let s=i.find(l=>l.id===e);if(!s||s.type==="line"||s.type==="arrow"||s.type==="draw")return;let r=this.getShapeWorldBounds(s),o=[{x:r.x+r.width/2,y:r.y},{x:r.x+r.width,y:r.y+r.height/2},{x:r.x+r.width/2,y:r.y+r.height},{x:r.x,y:r.y+r.height/2}],a=6/n;t.save(),o.forEach(l=>{t.beginPath(),t.arc(l.x,l.y,a,0,Math.PI*2),t.fillStyle="#ffffff",t.fill(),t.strokeStyle="#3b82f6",t.lineWidth=2/n,t.stroke();}),t.restore();}getShapeWorldBounds(t){let e=this.getShapeBounds(t);return {x:t.x+e.x,y:t.y+e.y,width:e.width,height:e.height}}drawBrushRect(t,e,i){t.save(),t.fillStyle="rgba(59, 130, 246, 0.08)",t.strokeStyle="rgba(59, 130, 246, 0.8)",t.lineWidth=1.5/i,t.setLineDash([4/i,4/i]),t.beginPath(),t.rect(e.x,e.y,e.width,e.height),t.fill(),t.stroke(),t.restore();}drawGrid(t,e,i,n){let s=-i,r=-n,o=s+this.width/e,a=r+this.height/e,l=20;for(;l*e<10;)l*=2;t.strokeStyle=G.grid,t.lineWidth=1/e,t.beginPath();for(let h=Math.floor(s/l)*l;h<=o;h+=l)t.moveTo(h,r),t.lineTo(h,a);for(let h=Math.floor(r/l)*l;h<=a;h+=l)t.moveTo(s,h),t.lineTo(o,h);t.stroke();}drawShape(t,e,i,n,s){switch(this.applyStyle(t,e.style,n),e.type){case "rectangle":this.drawRectangle(t,e,i);break;case "rounded-rectangle":this.drawRoundedRectangle(t,e,i);break;case "circle":this.drawCircle(t,e,i);break;case "ellipse":this.drawEllipse(t,e,i);break;case "diamond":this.drawDiamond(t,e,i);break;case "triangle":this.drawTriangle(t,e,i);break;case "hexagon":this.drawHexagon(t,e,i);break;case "parallelogram":this.drawParallelogram(t,e,i);break;case "trapezoid":this.drawTrapezoid(t,e,i);break;case "pentagon":this.drawPentagon(t,e,i);break;case "octagon":this.drawOctagon(t,e,i);break;case "star":this.drawStar(t,e,i);break;case "uml-class":this.drawUmlClass(t,e,i);break;case "uml-interface":this.drawUmlInterface(t,e,i);break;case "line":this.drawLine(t,e,s,i,n);break;case "arrow":this.drawArrow(t,e,s,i,n);break;case "text":this.drawText(t,e);break;case "draw":this.drawFreehand(t,e);break}e.text&&e.type!=="text"&&e.type!=="line"&&e.type!=="arrow"&&e.type!=="draw"&&this.drawShapeText(t,e),i&&(e.type==="line"||e.type==="arrow"||e.type==="draw"||this.drawSelectionOutline(t,e,n),this.drawSelectionHandles(t,e,n));}applyStyle(t,e,i){t.strokeStyle=e.strokeColor,t.fillStyle=e.fillColor,t.lineWidth=e.strokeWidth/i,t.lineCap="round",t.lineJoin="round",e.strokeDash?t.setLineDash(e.strokeDash):t.setLineDash([]);}drawRectangle(t,e,i){t.beginPath(),t.rect(-e.width/2,-e.height/2,e.width,e.height),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawRoundedRectangle(t,e,i){let{width:n,height:s,borderRadius:r}=e;t.beginPath(),t.roundRect(-n/2,-s/2,n,s,r),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawCircle(t,e,i){t.beginPath(),t.arc(0,0,e.radius,0,Math.PI*2),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawEllipse(t,e,i){t.beginPath(),t.ellipse(0,0,e.width/2,e.height/2,0,0,Math.PI*2),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawDiamond(t,e,i){t.beginPath(),t.moveTo(0,-e.height/2),t.lineTo(e.width/2,0),t.lineTo(0,e.height/2),t.lineTo(-e.width/2,0),t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawTriangle(t,e,i){t.beginPath(),t.moveTo(0,-e.height/2),t.lineTo(e.width/2,e.height/2),t.lineTo(-e.width/2,e.height/2),t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawHexagon(t,e,i){let n=e.width/2,s=e.height/2;t.beginPath();for(let r=0;r<6;r++){let o=r*Math.PI/3-Math.PI/2,a=n*Math.cos(o),l=s*Math.sin(o);r===0?t.moveTo(a,l):t.lineTo(a,l);}t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawParallelogram(t,e,i){let{width:n,height:s}=e,r=n*.2;t.beginPath(),t.moveTo(-n/2+r,-s/2),t.lineTo(n/2,-s/2),t.lineTo(n/2-r,s/2),t.lineTo(-n/2,s/2),t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawTrapezoid(t,e,i){let{width:n,height:s}=e,r=n*.15;t.beginPath(),t.moveTo(-n/2+r,-s/2),t.lineTo(n/2-r,-s/2),t.lineTo(n/2,s/2),t.lineTo(-n/2,s/2),t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawPentagon(t,e,i){let n=e.width/2,s=e.height/2;t.beginPath();for(let r=0;r<5;r++){let o=r*2*Math.PI/5-Math.PI/2,a=n*Math.cos(o),l=s*Math.sin(o);r===0?t.moveTo(a,l):t.lineTo(a,l);}t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawOctagon(t,e,i){let n=e.width/2,s=e.height/2;t.beginPath();for(let r=0;r<8;r++){let o=r*Math.PI/4-Math.PI/8,a=n*Math.cos(o),l=s*Math.sin(o);r===0?t.moveTo(a,l):t.lineTo(a,l);}t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawStar(t,e,i){let n=e.width/2,s=e.height/2,r=5,o=.4;t.beginPath();for(let a=0;a<r*2;a++){let l=a*Math.PI/r-Math.PI/2,h=a%2===0?1:o,d=n*h*Math.cos(l),c=s*h*Math.sin(l);a===0?t.moveTo(d,c):t.lineTo(d,c);}t.closePath(),e.style.fillColor!=="transparent"&&t.fill(),t.stroke();}drawUmlClass(t,e,i){let{width:n,height:s,className:r,attributes:o,methods:a}=e,l=n/2,h=s/2,d=Y,c=o.length>0?o.length*L+D*2:D*2;t.beginPath(),t.rect(-l,-h,n,s),e.style.fillColor!=="transparent"&&(t.fillStyle=e.style.fillColor,t.fill()),t.stroke(),t.beginPath(),t.moveTo(-l,-h+d),t.lineTo(l,-h+d),t.moveTo(-l,-h+d+c),t.lineTo(l,-h+d+c),t.stroke(),t.save(),t.textBaseline="middle";let g=n-N*2,y=(S,I)=>{if(t.measureText(S).width<=g)return S;let b="\u2026",w=0,M=S.length;for(;w<M;){let C=w+M+1>>1;t.measureText(S.slice(0,C)+b).width<=g?w=C:M=C-1;}return S.slice(0,w)+b},p=(S,I,b,w,M,C,O)=>{let U=w||$,z=M||"#1f2937";t.font=`${U}px sans-serif`,t.fillStyle=z,t.textAlign=O;let A=y(S,t.font);if(t.fillText(A,I,b),C){let V=t.measureText(A).width,it=I;t.strokeStyle=z,t.beginPath(),t.moveTo(it,b),t.lineTo(it+V,b),t.stroke();}};t.font=`bold ${e.fontSize||$}px sans-serif`,t.textAlign="center",t.fillStyle=e.textColor||"#1f2937",t.fillText(y(r,t.font),0,-h+d/2);let f=-h+d+D+L/2;o.forEach((S,I)=>{let b=`${S.visibility} ${S.name}: ${S.type}`;p(b,-l+N,f+I*L,S.fontSize||e.fontSize||0,S.textColor||e.textColor||"",!!S.strikethrough,"left");});let T=-h+d+c+D+L/2;a.forEach((S,I)=>{let b=`${S.visibility} ${S.name}(${S.params}): ${S.returnType}`;p(b,-l+N,T+I*L,S.fontSize||e.fontSize||0,S.textColor||e.textColor||"",!!S.strikethrough,"left");}),t.restore();}drawUmlInterface(t,e,i){let{width:n,height:s,className:r,methods:o}=e,a=n/2,l=s/2,h=Y;t.beginPath(),t.rect(-a,-l,n,s),e.style.fillColor!=="transparent"&&(t.fillStyle=e.style.fillColor,t.fill()),t.stroke(),t.beginPath(),t.moveTo(-a,-l+h),t.lineTo(a,-l+h),t.stroke(),t.save(),t.textBaseline="middle";let d=n-N*2,c=(f,T)=>{if(t.measureText(f).width<=d)return f;let S="\u2026",I=0,b=f.length;for(;I<b;){let w=I+b+1>>1;t.measureText(f.slice(0,w)+S).width<=d?I=w:b=w-1;}return f.slice(0,I)+S},g=(f,T,S,I,b,w,M)=>{let C=I||$,O=b||"#1f2937";t.font=`${C}px sans-serif`,t.fillStyle=O,t.textAlign=M;let U=c(f,t.font);if(t.fillText(U,T,S),w){let z=t.measureText(U).width,A=T;t.strokeStyle=O,t.beginPath(),t.moveTo(A,S),t.lineTo(A+z,S),t.stroke();}};t.font=`bold ${e.fontSize||$}px sans-serif`,t.textAlign="center",t.fillStyle=e.textColor||"#1f2937";let y=`<<${r||"Interface"}>>`;t.fillText(c(y,t.font),0,-l+h/2);let p=-l+h+D+L/2;o.forEach((f,T)=>{let S=`${f.visibility} ${f.name}(${f.params}): ${f.returnType}`;g(S,-a+N,p+T*L,f.fontSize||e.fontSize||0,f.textColor||e.textColor||"",!!f.strikethrough,"left");}),t.restore();}static DEFAULT_CORNER_RADIUS=10;static ROUTE_CLEARANCE=30;static CONNECTION_LEAD=24;static CONNECTED_PAD=8;drawLine(t,e,i,n,s){if(e.points.length<2)return;let r=e.points.map(h=>({x:h.x,y:h.y}));r.length>=2&&(r[0]=this.clampToShapeOutline(r[0],i,e.id,r[r.length-1]),r[r.length-1]=this.clampToShapeOutline(r[r.length-1],i,e.id,r[0]));let o=r.map(h=>({x:h.x-e.x,y:h.y-e.y})),a=e.connectionType||"straight",l=a==="straight"?this.getAlignedPoints(o):this.buildPathPoints(o,a,this.collectRoutingObstacles(e.id,o[0],o[o.length-1],i,{x:e.x,y:e.y}),{fromN:this.getDockNormal(r[0],i,e.id),toN:this.getDockNormal(r[r.length-1],i,e.id)});if(a==="straight"){t.beginPath(),t.moveTo(o[0].x,o[0].y);for(let h=1;h<o.length;h++)t.lineTo(o[h].x,o[h].y);t.stroke(),t.setLineDash([]);}else a==="orthogonal"?this.drawSharpPolyline(t,l):this.drawRoundedPolyline(t,l,u.DEFAULT_CORNER_RADIUS),t.stroke(),t.setLineDash([]);n&&this.drawLinearHighlight(t,l,s);}drawArrow(t,e,i,n,s){if(e.points.length<2)return;let r=e.points.map(c=>({x:c.x,y:c.y}));if(e.startBinding){let c=i.get(e.startBinding.toShapeId);c&&(r[0]=this.getBindingPoint(c,e.startBinding));}if(e.endBinding){let c=i.get(e.endBinding.toShapeId);c&&(r[r.length-1]=this.getBindingPoint(c,e.endBinding));}r[0]=this.clampToShapeOutline(r[0],i,e.id,r[r.length-1]),r[r.length-1]=this.clampToShapeOutline(r[r.length-1],i,e.id,r[0]);let o=r.map(c=>({x:c.x-e.x,y:c.y-e.y})),a=e.connectionType||"straight",l=e.arrowHead??"end";!!e.dashed?t.setLineDash([8,5]):t.setLineDash([]);let d;if(a==="straight"){let c=this.getAlignedPoints(o);d=c,t.beginPath(),t.moveTo(c[0].x,c[0].y);for(let g=1;g<c.length;g++)t.lineTo(c[g].x,c[g].y);t.stroke(),this.drawArrowHeads(t,c,l);}else d=this.buildPathPoints(o,a,this.collectRoutingObstacles(e.id,o[0],o[o.length-1],i,{x:e.x,y:e.y}),{fromN:this.getDockNormal(r[0],i,e.id),toN:this.getDockNormal(r[r.length-1],i,e.id)}),a==="orthogonal"?this.drawSharpPolyline(t,d):this.drawRoundedPolyline(t,d,u.DEFAULT_CORNER_RADIUS),t.stroke(),this.drawArrowHeads(t,d,l);t.setLineDash([]),n&&this.drawLinearHighlight(t,d,s);}drawLinearHighlight(t,e,i){if(!(!e||e.length<2)){t.save(),t.strokeStyle="rgba(59, 130, 246, 0.4)",t.lineWidth=7/i,t.lineCap="round",t.lineJoin="round",t.setLineDash([]),t.beginPath(),t.moveTo(e[0].x,e[0].y);for(let n=1;n<e.length;n++)t.lineTo(e[n].x,e[n].y);t.stroke(),t.restore();}}drawGenerationPreview(t,e,i){let{from:n,to:s,type:r}=e;t.save(),t.beginPath(),t.arc(n.x,n.y,7/i,0,Math.PI*2),t.fillStyle="#3b82f6",t.fill(),t.strokeStyle="rgba(59, 130, 246, 0.55)",t.lineWidth=2/i,t.setLineDash([5/i,4/i]),t.lineCap="round",t.beginPath(),t.moveTo(n.x,n.y),t.lineTo(s.x,s.y),t.stroke(),r&&this.drawPreviewShapeOutline(t,s,r,i),t.restore();}drawPreviewShapeOutline(t,e,i,n){let l=e.x,h=e.y;switch(t.strokeStyle="rgba(59, 130, 246, 0.55)",t.lineWidth=1.5/n,t.setLineDash([6/n,4/n]),t.beginPath(),i){case "circle":case "ellipse":t.ellipse(l,h,i==="circle"?40:120/2,i==="circle"?40:60/2,0,0,Math.PI*2);break;case "diamond":t.moveTo(l,h-30),t.lineTo(l+60,h),t.lineTo(l,h+30),t.lineTo(l-60,h),t.closePath();break;case "triangle":t.moveTo(l,h-30),t.lineTo(l+60,h+30),t.lineTo(l-60,h+30),t.closePath();break;case "hexagon":for(let d=0;d<6;d++){let c=d*Math.PI/3-Math.PI/2,g=l+60*Math.cos(c),y=h+30*Math.sin(c);d===0?t.moveTo(g,y):t.lineTo(g,y);}t.closePath();break;case "parallelogram":t.moveTo(l-60+18,h-30),t.lineTo(l+60,h-30),t.lineTo(l+60-18,h+30),t.lineTo(l-60,h+30),t.closePath();break;default:t.roundRect(l-60,h-30,120,60,i==="rounded-rectangle"?8:4);break}t.stroke();}static ALIGN_TOLERANCE=8;getAlignedPoints(t){if(t.length<2)return t;let e=t[0],i=t[t.length-1],n=i.x-e.x,s=i.y-e.y,r=u.ALIGN_TOLERANCE;if(Math.abs(s)<=r){let o=(e.y+i.y)/2;return t.map(a=>({x:a.x,y:o}))}if(Math.abs(n)<=r){let o=(e.x+i.x)/2;return t.map(a=>({x:o,y:a.y}))}return t}buildPathPoints(t,e,i=[],n={}){if(t.length<2||e==="straight")return t;switch(e){case "polyline":return this.generatePolylinePoints(t,i,n);case "orthogonal":return this.generateOrthogonalPoints(t,i,n);case "curve":return this.generateCurvePoints(t);default:return t}}drawRoundedPolyline(t,e,i){if(t.beginPath(),!(e.length<2)){if(e.length===2||i<=0){t.moveTo(e[0].x,e[0].y);for(let n=1;n<e.length;n++)t.lineTo(e[n].x,e[n].y);return}t.moveTo(e[0].x,e[0].y);for(let n=1;n<e.length-1;n++){let s=e[n-1],r=e[n],o=e[n+1],a=r.x-s.x,l=r.y-s.y,h=o.x-r.x,d=o.y-r.y,c=Math.hypot(a,l),g=Math.hypot(h,d);if(c<1e-6||g<1e-6){t.lineTo(r.x,r.y);continue}let y=Math.min(i,c*.5,g*.5),p=r.x-a/c*y,f=r.y-l/c*y,T=r.x+h/g*y,S=r.y+d/g*y;t.lineTo(p,f),t.arcTo(r.x,r.y,T,S,y);}t.lineTo(e[e.length-1].x,e[e.length-1].y);}}drawSharpPolyline(t,e){if(t.beginPath(),!(e.length<2)){t.moveTo(e[0].x,e[0].y);for(let i=1;i<e.length;i++)t.lineTo(e[i].x,e[i].y);}}drawSnapGuides(t,e,i){t.save(),t.strokeStyle="rgba(59, 130, 246, 0.6)",t.lineWidth=1/i,t.setLineDash([6/i,4/i]),t.beginPath();for(let n of e)n.type==="horizontal"?(t.moveTo(n.start,n.position),t.lineTo(n.end,n.position)):(t.moveTo(n.position,n.start),t.lineTo(n.position,n.end));t.stroke(),t.restore();}generatePolylinePoints(t,e=[],i={}){if(t.length<2)return t;let n=[];n.push(t[0]);let s=t.length-1,r=u.CONNECTION_LEAD;for(let o=1;o<t.length;o++){let a=t[o-1],l=t[o],h=o===1,d=o===s,c=h&&i.fromN?{x:a.x+i.fromN.x*r,y:a.y+i.fromN.y*r}:a,g=d&&i.toN?{x:l.x+i.toN.x*r,y:l.y+i.toN.y*r}:l;h&&i.fromN&&n.push(c);let y=this.computeOrthogonalRoute(c,g,e);for(let p of y)n.push(p);d&&i.toN&&n.push(l);}return this.collapseLinePoints(n)}generateOrthogonalPoints(t,e=[],i={}){return this.generatePolylinePoints(t,e,i)}collapseLinePoints(t){let e=[];for(let i of t){let n=e.length;if(!(n>0&&Math.abs(e[n-1].x-i.x)<1e-6&&Math.abs(e[n-1].y-i.y)<1e-6)){if(n>=2){let s=e[n-2],r=e[n-1];if(Math.abs(s.y-r.y)<1e-6&&Math.abs(r.y-i.y)<1e-6||Math.abs(s.x-r.x)<1e-6&&Math.abs(r.x-i.x)<1e-6){e[n-1]=i;continue}}e.push(i);}}return e}computeOrthogonalRoute(t,e,i=[]){let n=e.x-t.x,s=e.y-t.y;if(Math.abs(n)<1||Math.abs(s)<1)return [e];let r=t.x+n/2,o=t.y+s/2,a=i.length>0,l=[[{x:r,y:t.y},{x:r,y:e.y},e],[{x:t.x,y:o},{x:e.x,y:o},e],[{x:e.x,y:t.y},e],[{x:t.x,y:e.y},e]];if(a){for(let d of l)if(!this.routeHitsObstacles(t,d,i))return d;let h=this.routeOrthogonalAvoidance(t,e,i);return h&&h.length>0?h:i.length>0&&!this.routeHitsObstacles(t,l[0],i)?l[0]:this.routeHitsObstacles(t,l[1],i)?[e]:l[1]}return Math.abs(n)>=Math.abs(s)?[{x:r,y:t.y},{x:r,y:e.y},e]:[{x:t.x,y:o},{x:e.x,y:o},e]}collectRoutingObstacles(t,e,i,n,s){if(!n||n.size===0)return [];let r=[];return n.forEach((o,a)=>{if(a===t)return;let l=this.getShapeBounds(o),h={x:o.x+l.x-s.x,y:o.y+l.y-s.y,width:l.width,height:l.height};h.pad=this.pointInRect(e,h)||this.pointInRect(i,h)?u.CONNECTED_PAD:u.ROUTE_CLEARANCE,r.push(h);}),r}pointInRect(t,e){return t.x>=e.x&&t.x<=e.x+e.width&&t.y>=e.y&&t.y<=e.y+e.height}routeHitsObstacles(t,e,i){let n=[t,...e];for(let s=1;s<n.length;s++)if(this.segmentHitsObstacle(n[s-1],n[s],i))return true;return false}segmentHitsObstacle(t,e,i){for(let n of i){let s=n.pad??u.ROUTE_CLEARANCE,r=n.y-s,o=n.y+n.height+s,a=n.x-s,l=n.x+n.width+s;if(Math.abs(t.y-e.y)<.5){if(t.y>=r&&t.y<=o){let h=Math.min(t.x,e.x);if(Math.max(t.x,e.x)>=a&&h<=l)return true}}else if(t.x>=a&&t.x<=l){let h=Math.min(t.y,e.y);if(Math.max(t.y,e.y)>=r&&h<=o)return true}}return false}routeOrthogonalAvoidance(t,e,i){let n=new Set([t.x,e.x]),s=new Set([t.y,e.y]);for(let m of i){let x=m.pad??u.ROUTE_CLEARANCE;n.add(m.x-x),n.add(m.x+m.width+x),s.add(m.y-x),s.add(m.y+m.height+x);}let r=[...n].sort((m,x)=>m-x),o=[...s].sort((m,x)=>m-x),a=r.length,l=o.length,h=m=>{for(let x=0;x<a;x++)if(Math.abs(r[x]-m)<1e-6)return x;return 0},d=m=>{for(let x=0;x<l;x++)if(Math.abs(o[x]-m)<1e-6)return x;return 0},c=h(t.x),g=d(t.y),y=h(e.x),p=d(e.y),f=[],T=[];for(let m=0;m<a;m++)f.push(new Array(l).fill(true)),T.push(new Array(l).fill(true));for(let m of i){let x=m.pad??u.ROUTE_CLEARANCE,v=m.x-x,P=m.x+m.width+x,k=m.y-x,B=m.y+m.height+x;for(let E=0;E<a-1;E++)if(!(r[E+1]<=v||r[E]>=P))for(let _=0;_<l;_++)o[_]>k&&o[_]<B&&(f[E][_]=false);for(let E=0;E<l-1;E++)if(!(o[E+1]<=k||o[E]>=B))for(let _=0;_<a;_++)r[_]>v&&r[_]<P&&(T[_][E]=false);}let S=a*l,I=1/0,b=new Float64Array(S).fill(I),w=new Int32Array(S).fill(-1),M=new Uint8Array(S),C=m=>{let x=m%a,v=m/a|0;return Math.abs(r[x]-e.x)+Math.abs(o[v]-e.y)},O=(m,x)=>m%a===x%a,U=g*a+c,z=p*a+y;b[U]=0;let A=[U],V=[C(U)],it=m=>{let x=b[m]+C(m);A.push(m),V.push(x);let v=A.length-1;for(;v>0;){let P=v-1>>1;if(V[P]<=V[v])break;[A[P],A[v]]=[A[v],A[P]],[V[P],V[v]]=[V[v],V[P]],v=P;}},ut=()=>{let m=A[0],x=A.pop(),v=V.pop();if(A.length){A[0]=x,V[0]=v;let P=0;for(;;){let k=P*2+1,B=k+1,E=P;if(k<A.length&&V[k]<V[E]&&(E=k),B<A.length&&V[B]<V[E]&&(E=B),E===P)break;[A[E],A[P]]=[A[P],A[E]],[V[E],V[P]]=[V[P],V[E]],P=E;}}return m},st=(m,x,v)=>{let P=m%a,k=x%a,B=b[m]+(Math.abs(k-P)===0?Math.abs(o[x/a|0]-o[m/a|0]):Math.abs(r[k]-r[P]))+v;B<b[x]&&(b[x]=B,w[x]=m,M[x]||it(x));};for(;A.length;){let m=ut();if(m===z||M[m]){if(m===z)break;continue}M[m]=1;let x=m%a,v=m/a|0,P=w[m],k=P>=0?O(P,m)?"v":"h":"",B=E=>{let _=O(m,E)?"v":"h";return k!==""&&k!==_?24:0};x+1<a&&f[x][v]&&st(m,v*a+x+1,B(v*a+x+1)),x-1>=0&&f[x-1][v]&&st(m,v*a+x-1,B(v*a+x-1)),v+1<l&&T[x][v]&&st(m,(v+1)*a+x,B((v+1)*a+x)),v-1>=0&&T[x][v-1]&&st(m,(v-1)*a+x,B((v-1)*a+x));}if(b[z]===I)return null;let ht=[];for(let m=z;m!==-1;m=w[m])ht.push({x:r[m%a],y:o[m/a|0]});ht.reverse();let F=[];for(let m of ht){let x=F.length;if(x>=2){let v=F[x-2],P=F[x-1];if(Math.abs(v.y-P.y)<1e-6&&Math.abs(P.y-m.y)<1e-6||Math.abs(v.x-P.x)<1e-6&&Math.abs(P.x-m.x)<1e-6){F[x-1]=m;continue}}F.push(m);}return F.slice(1)}generateCurvePoints(t){if(t.length<2)return t;let e=[],i=20,n=t[0],s=n;e.push(n);for(let r=1;r<t.length;r++){let o=t[r],a=this.computeCurveControlPoint(s,o);for(let l=1;l<=i;l++){let h=l/i,d=(1-h)*(1-h)*s.x+2*(1-h)*h*a.x+h*h*o.x,c=(1-h)*(1-h)*s.y+2*(1-h)*h*a.y+h*h*o.y;e.push({x:d,y:c});}s=o;}return e}computeCurveControlPoint(t,e){let i=e.x-t.x,n=e.y-t.y,s=Math.sqrt(i*i+n*n);if(s<1)return {x:t.x,y:t.y};let r=(t.x+e.x)/2,o=(t.y+e.y)/2,a=-n/s,l=i/s,h=Math.min(s*.2,30);return {x:r+a*h,y:o+l*h}}getDisplayLines(t,e,i,n,s,r){let o=n*1.3,a=Math.max(1,Math.floor(r/o)),l=`${e.id}\0${i}\0${n}\0${s}x${r}\0${a}`,h=this.textLayoutCache.get(l);if(h)return h;let d=i.split(`
2
+ `),c=[];for(let y of d){let p=this.wrapLine(t,y,s);for(let f of p)c.push(f);}let g=c.slice(0,a);return c.length>a&&g.length>0&&(g[g.length-1]=this.truncateLine(t,g[g.length-1],s)),this.textLayoutCache.size>=u.TEXT_CACHE_LIMIT&&this.textLayoutCache.clear(),this.textLayoutCache.set(l,g),g}drawShapeText(t,e){let i=e.text||"";if(!i)return;let n=e.fontSize||16;t.font=`${n}px sans-serif`,t.fillStyle=e.textColor||"#1f2937";let s=this.getTextBounds(e),r=n*1.3,o=this.getDisplayLines(t,e,i,n,s.width,s.height),l=-(o.length*r)/2+r/2;t.textAlign="center",t.textBaseline="middle",o.forEach((h,d)=>{let c=l+d*r;t.fillText(h,0,c);});}getTextBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return {width:t.width*.85,height:t.height*.85};case "circle":return {width:t.radius*2*.85,height:t.radius*2*.85};default:return {width:100,height:40}}}wrapLine(t,e,i){if(!e)return [""];let n=[],s="";for(let r of e){let o=s+r;t.measureText(o).width>i&&s?(n.push(s),s=r):s=o;}return s&&n.push(s),n}truncateLine(t,e,i){if(!e)return "...";let n="...",s=e;for(;s.length>0&&t.measureText(s+n).width>i;)s=s.slice(0,-1);return s+n}getBindingPoint(t,e){if(e.isPrecise&&e.normalizedAnchor){let i=this.getShapePageBounds(t),n={x:i.x+i.width*e.normalizedAnchor.x,y:i.y+i.height*e.normalizedAnchor.y},s={x:t.x,y:t.y};return this.nearestPointOnShapeOutline(t,s,n)}if(e.handle){let i=this.getShapeBounds(t);switch(e.handle){case "top":return {x:t.x,y:t.y+i.y};case "bottom":return {x:t.x,y:t.y+i.y+i.height};case "left":return {x:t.x+i.x,y:t.y};case "right":return {x:t.x+i.x+i.width,y:t.y}}}return {x:t.x,y:t.y}}getDockNormal(t,e,i){if(!e||e.size===0)return;let n=null,s=1/0,r=.5;if(e.forEach((h,d)=>{if(d===i)return;let c=this.getShapePageBounds(h);if(t.x>=c.x-r&&t.x<=c.x+c.width+r&&t.y>=c.y-r&&t.y<=c.y+c.height+r){let g=(t.x-h.x)**2+(t.y-h.y)**2;g<s&&(s=g,n=h);}}),!n)return;let o=n,a=t.x-o.x,l=t.y-o.y;return Math.abs(l)>Math.abs(a)?{x:0,y:l<0?-1:1}:{x:a<0?-1:1,y:0}}getShapePageBounds(t){let e=this.getShapeBounds(t);return {x:t.x+e.x,y:t.y+e.y,width:e.width,height:e.height}}clampToShapeOutline(t,e,i,n){if(!e||e.size===0)return t;let s=null,r=1/0;if(e.forEach((a,l)=>{if(l===i)return;let h=this.getShapePageBounds(a);if(t.x>h.x&&t.x<h.x+h.width&&t.y>h.y&&t.y<h.y+h.height){let d=(a.x-n.x)**2+(a.y-n.y)**2;d<r&&(r=d,s=a);}}),!s)return t;let o=s;return this.nearestPointOnShapeOutline(o,{x:o.x,y:o.y},t)}nearestPointOnShapeOutline(t,e,i){let n=i.x-e.x,s=i.y-e.y,r=Math.hypot(n,s);if(r<1e-6)return {x:e.x,y:e.y};let o=n/r,a=s/r,l=Math.max(1e4,r*2),h={x:e.x+o*l,y:e.y+a*l},d=this.intersectShapeOutlineWithSegment(t,e,h);if(d.length===0)return i;let c=d[0],g=(c.x-e.x)**2+(c.y-e.y)**2;for(let y=1;y<d.length;y++){let p=(d[y].x-e.x)**2+(d[y].y-e.y)**2;p<g&&(g=p,c=d[y]);}return c}intersectShapeOutlineWithSegment(t,e,i){let n={x:e.x-t.x,y:e.y-t.y},s={x:i.x-t.x,y:i.y-t.y};return this.intersectLocalShapeOutlineWithSegment(t,n,s).map(o=>({x:o.x+t.x,y:o.y+t.y}))}intersectLocalShapeOutlineWithSegment(t,e,i){switch(t.type){case "rectangle":case "rounded-rectangle":{let n=t.width,s=t.height,r={x:-n/2,y:-s/2,width:n,height:s};return this.intersectSegmentRect(e,i,r)}case "ellipse":case "diamond":case "triangle":{let n=t.width,s=t.height,r={x:-n/2,y:-s/2,width:n,height:s};return this.intersectSegmentRect(e,i,r)}case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":{let n=t.width,s=t.height,r={x:-n/2,y:-s/2,width:n,height:s};return this.intersectSegmentRect(e,i,r)}case "circle":{let n=t.radius;return this.intersectSegmentCircle(e,i,{x:0,y:0},n)}default:return []}}intersectSegmentRect(t,e,i){let n=i.x,s=i.y,r=i.width,o=i.height,a=[[{x:n,y:s},{x:n+r,y:s}],[{x:n+r,y:s},{x:n+r,y:s+o}],[{x:n+r,y:s+o},{x:n,y:s+o}],[{x:n,y:s+o},{x:n,y:s}]],l=[];for(let[h,d]of a){let c=this.segmentIntersection(t,e,h,d);c&&l.push(c);}return l}segmentIntersection(t,e,i,n){let s=t.x,r=t.y,o=e.x,a=e.y,l=i.x,h=i.y,d=n.x,c=n.y,g=(s-o)*(h-c)-(r-a)*(l-d);if(Math.abs(g)<1e-9)return null;let y=((s-l)*(h-c)-(r-h)*(l-d))/g,p=-((s-o)*(r-h)-(r-a)*(s-l))/g;return y<0||y>1||p<0||p>1?null:{x:s+y*(o-s),y:r+y*(a-r)}}intersectSegmentCircle(t,e,i,n){let s=e.x-t.x,r=e.y-t.y,o=t.x-i.x,a=t.y-i.y,l=s*s+r*r,h=2*(o*s+a*r),d=o*o+a*a-n*n,c=h*h-4*l*d;if(c<0)return [];c=Math.sqrt(c);let g=(-h-c)/(2*l),y=(-h+c)/(2*l),p=[];return g>=0&&g<=1&&p.push({x:t.x+g*s,y:t.y+g*r}),y>=0&&y<=1&&Math.abs(y-g)>1e-6&&p.push({x:t.x+y*s,y:t.y+y*r}),p}drawArrowHead(t,e,i){let n=Math.atan2(i.y-e.y,i.x-e.x),s=12;t.beginPath(),t.moveTo(i.x,i.y),t.lineTo(i.x-s*Math.cos(n-Math.PI/6),i.y-s*Math.sin(n-Math.PI/6)),t.moveTo(i.x,i.y),t.lineTo(i.x-s*Math.cos(n+Math.PI/6),i.y-s*Math.sin(n+Math.PI/6)),t.stroke();}drawInheritanceHead(t,e,i){let n=Math.atan2(i.y-e.y,i.x-e.x),s=14,r=i.x,o=i.y,a=i.x-s*Math.cos(n),l=i.y-s*Math.sin(n),h=a+s*.5*Math.cos(n-Math.PI/2),d=l+s*.5*Math.sin(n-Math.PI/2),c=a+s*.5*Math.cos(n+Math.PI/2),g=l+s*.5*Math.sin(n+Math.PI/2);t.save(),t.fillStyle="#ffffff",t.beginPath(),t.moveTo(r,o),t.lineTo(h,d),t.lineTo(c,g),t.closePath(),t.fill(),t.stroke(),t.restore();}drawDiamondHead(t,e,i,n){let s=Math.atan2(i.y-e.y,i.x-e.x),r=14,o=i.x,a=i.y,l=i.x-r*.5*Math.cos(s),h=i.y-r*.5*Math.sin(s),d=i.x-r*Math.cos(s),c=i.y-r*Math.sin(s),g=l+r*.4*Math.cos(s-Math.PI/2),y=h+r*.4*Math.sin(s-Math.PI/2),p=l+r*.4*Math.cos(s+Math.PI/2),f=h+r*.4*Math.sin(s+Math.PI/2);t.save(),t.fillStyle=n?"#1f2937":"#ffffff",t.beginPath(),t.moveTo(o,a),t.lineTo(g,y),t.lineTo(d,c),t.lineTo(p,f),t.closePath(),t.fill(),t.stroke(),t.restore();}drawArrowHeads(t,e,i){if(i!=="none"){if(i==="start"||i==="both"){let n=e[0],s=e[1];this.drawArrowHead(t,s,n);}if(i==="end"||i==="both"){let n=e.length,s=e[n-2],r=e[n-1];this.drawArrowHead(t,s,r);}if(i==="inheritance"){let n=e.length,s=e[n-2],r=e[n-1];this.drawInheritanceHead(t,s,r);}if(i==="aggregation"){let n=e[0],s=e[1];this.drawDiamondHead(t,s,n,false);}if(i==="composition"){let n=e[0],s=e[1];this.drawDiamondHead(t,s,n,true);}}}drawText(t,e){if(!e.text)return;let i=e.fontSize;t.font=`${i}px sans-serif`,t.textAlign="center",t.textBaseline="middle";let n=e.width,s=e.height,r=i*1.3,o=this.getDisplayLines(t,e,e.text,i,n,s),a=o.length*r,l=-a/2+r/2;if(e.highlightColor&&e.highlightColor!=="transparent"){t.fillStyle=e.highlightColor;let h=i*.15;t.beginPath(),t.roundRect(-n/2-h,-a/2-h,n+h*2,a+h*2,4),t.fill();}t.fillStyle=e.textColor||"#1f2937",o.forEach((h,d)=>{let c=l+d*r;t.fillText(h,0,c);});}drawFreehand(t,e){if(e.points.length<2)return;let i=e.points.map(n=>({x:n.x-e.x,y:n.y-e.y}));t.beginPath(),t.moveTo(i[0].x,i[0].y);for(let n=1;n<i.length-1;n++){let s=(i[n].x+i[n+1].x)/2,r=(i[n].y+i[n+1].y)/2;t.quadraticCurveTo(i[n].x,i[n].y,s,r);}t.lineTo(i[i.length-1].x,i[i.length-1].y),t.stroke();}drawSelectionOutline(t,e,i){let n=this.getShapeBounds(e);t.strokeStyle=G.selection.outline,t.lineWidth=1/i,t.setLineDash([]),t.save(),t.shadowColor=G.selection.shadow,t.shadowBlur=8,t.shadowOffsetX=0,t.shadowOffsetY=0,t.beginPath(),t.roundRect(n.x-2,n.y-2,n.width+4,n.height+4,4),t.stroke(),t.restore(),t.fillStyle=G.selection.outlineFill,t.beginPath(),t.roundRect(n.x-2,n.y-2,n.width+4,n.height+4,4),t.fill();}drawSelectionHandles(t,e,i){let n=8/i;if((e.type==="line"||e.type==="arrow"||e.type==="draw")&&e.points&&e.points.length>=2){let r=e.points.map(d=>({x:d.x-e.x,y:d.y-e.y})),o=r[0],a=r[r.length-1];t.fillStyle=G.selection.handleFill,t.strokeStyle=G.selection.handleStroke,t.lineWidth=2/i,t.setLineDash([]),[o,a].forEach(d=>{t.beginPath(),t.arc(d.x,d.y,n/2,0,Math.PI*2),t.fill(),t.stroke();});let l=(o.x+a.x)/2,h=(o.y+a.y)/2;t.beginPath(),t.arc(l,h,7/i,0,Math.PI*2),t.fillStyle="#ffffff",t.fill(),t.strokeStyle="#3b82f6",t.lineWidth=1.5/i,t.stroke(),t.beginPath(),t.arc(l,h,3/i,0,Math.PI*2),t.fillStyle="#3b82f6",t.fill();}else {let r=this.getShapeBounds(e),o=n,a=[{x:r.x,y:r.y},{x:r.x+r.width,y:r.y},{x:r.x+r.width,y:r.y+r.height},{x:r.x,y:r.y+r.height}];t.fillStyle=G.selection.handleFill,t.strokeStyle=G.selection.handleStroke,t.lineWidth=2/i,t.setLineDash([]),a.forEach(y=>{t.beginPath(),t.roundRect(y.x-o/2,y.y-o/2,o,o,2),t.fill(),t.stroke();});let l=9/i;[{x:r.x+r.width/2,y:r.y},{x:r.x+r.width,y:r.y+r.height/2},{x:r.x+r.width/2,y:r.y+r.height},{x:r.x,y:r.y+r.height/2}].forEach(y=>{t.beginPath(),t.arc(y.x,y.y,l,0,Math.PI*2),t.fillStyle="#ffffff",t.fill(),t.strokeStyle="#3b82f6",t.lineWidth=1.5/i,t.stroke(),t.beginPath(),t.arc(y.x,y.y,l*.45,0,Math.PI*2),t.fillStyle="#3b82f6",t.fill();});let d=26/i,c=r.x+r.width/2,g=r.y-d;t.strokeStyle="#94a3b8",t.lineWidth=1.5/i,t.setLineDash([]),t.beginPath(),t.moveTo(c,r.y),t.lineTo(c,g),t.stroke(),t.beginPath(),t.arc(c,g,8/i,0,Math.PI*2),t.fillStyle="#ffffff",t.fill(),t.strokeStyle="#3b82f6",t.lineWidth=1.6/i,t.stroke(),this.drawRotateIcon(t,c,g,i);}}drawRotateIcon(t,e,i,n){let s=8/n;t.save(),t.translate(e,i),t.strokeStyle="#3b82f6",t.lineWidth=1.8/n,t.lineCap="round",t.beginPath(),t.arc(0,0,s*.5,Math.PI*.18,Math.PI*1.6,false),t.stroke();let r=Math.PI*.18,o=Math.cos(r)*s*.5,a=Math.sin(r)*s*.5,l=1;t.beginPath(),t.moveTo(o,a),t.lineTo(o-Math.cos(r)*s*.26+l*Math.sin(r)*s*.16,a-Math.sin(r)*s*.26-l*Math.cos(r)*s*.16),t.moveTo(o,a),t.lineTo(o-Math.cos(r)*s*.26-l*Math.sin(r)*s*.16,a-Math.sin(r)*s*.26+l*Math.cos(r)*s*.16),t.stroke(),t.restore();}getShapeBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return {x:-t.width/2,y:-t.height/2,width:t.width,height:t.height};case "circle":return {x:-t.radius,y:-t.radius,width:t.radius*2,height:t.radius*2};case "line":case "arrow":case "draw":if(t.points.length===0)return {x:0,y:0,width:0,height:0};let e=1/0,i=1/0,n=-1/0,s=-1/0;return t.points.forEach(r=>{e=Math.min(e,r.x-t.x),i=Math.min(i,r.y-t.y),n=Math.max(n,r.x-t.x),s=Math.max(s,r.y-t.y);}),{x:e,y:i,width:n-e,height:s-i};case "text":{let r=t;return {x:-r.width/2,y:-r.height/2,width:r.width,height:r.height}}default:return {x:0,y:0,width:0,height:0}}}};var X={create(u=0,t=0){return {x:u,y:t}},add(u,t){return {x:u.x+t.x,y:u.y+t.y}},subtract(u,t){return {x:u.x-t.x,y:u.y-t.y}},multiply(u,t){return {x:u.x*t,y:u.y*t}},divide(u,t){return {x:u.x/t,y:u.y/t}},dot(u,t){return u.x*t.x+u.y*t.y},length(u){return Math.sqrt(u.x*u.x+u.y*u.y)},normalize(u){let t=X.length(u);return t===0?{x:0,y:0}:{x:u.x/t,y:u.y/t}},distance(u,t){return X.length(X.subtract(u,t))},rotate(u,t){let e=Math.cos(t),i=Math.sin(t);return {x:u.x*e-u.y*i,y:u.x*i+u.y*e}},clone(u){return {x:u.x,y:u.y}},equals(u,t){return u.x===t.x&&u.y===t.y}};function W(u,t,e,i=8){let n=[];if(e==="horizontal"){let s=Math.min(u.x,t.x),r=Math.max(u.x+u.width,t.x+t.width),o=Math.abs(u.y-t.y)<=i,a=Math.abs(u.y+u.height-(t.y+t.height))<=i,l=Math.abs(u.y+u.height/2-(t.y+t.height/2))<=i;o&&n.push({type:"horizontal",position:(u.y+t.y)/2,start:s,end:r}),a&&n.push({type:"horizontal",position:(u.y+u.height+t.y+t.height)/2,start:s,end:r}),l&&!(o&&a)&&n.push({type:"horizontal",position:(u.y+u.height/2+t.y+t.height/2)/2,start:s,end:r});}else {let s=Math.min(u.y,t.y),r=Math.max(u.y+u.height,t.y+t.height),o=Math.abs(u.x-t.x)<=i,a=Math.abs(u.x+u.width-(t.x+t.width))<=i,l=Math.abs(u.x+u.width/2-(t.x+t.width/2))<=i;o&&n.push({type:"vertical",position:(u.x+t.x)/2,start:s,end:r}),a&&n.push({type:"vertical",position:(u.x+u.width+t.x+t.width)/2,start:s,end:r}),l&&!(o&&a)&&n.push({type:"vertical",position:(u.x+u.width/2+t.x+t.width/2)/2,start:s,end:r});}return n}var Q=class u extends H{type="select";state="idle";translatingState=null;resizingState=null;generatingState=null;hoverGenPoint=null;rotatingState=null;draggingArrowId=null;draggingTerminal=null;pendingHistoryCommit=false;brushStart={x:0,y:0};brushShiftKey=false;defaultGenType(t){if(t){let e=this.context.getShapes().find(i=>i.id===t);if(e&&rt(e.type))return e.type}return "rectangle"}genPreviewOf(t,e){return {from:t.point,to:e,type:this.defaultGenType(t.sourceShapeId)}}genContext(t,e){return {from:t.point,to:e,sourceShapeId:t.sourceShapeId,edge:t.edge}}onPointerDown(t,e){let i=this.context.getShapes(),n=this.context.getSelectedIds(),s=this.hitTestArrowTerminal(t,i);if(s&&n.includes(s.shapeId)){this.state="dragging-terminal",this.draggingArrowId=s.shapeId,this.draggingTerminal=s.terminal,this.pendingHistoryCommit=true,this.context.updateArrowBinding(s.shapeId,s.terminal,null);return}let r=this.hitGenPoint(t,i,n);if(r){this.state="generating",this.generatingState={source:r,startPoint:t,hasMoved:false},this.pendingHistoryCommit=true,this.context.setGenerationPreview(this.genPreviewOf(r,this.genPreviewTarget(r,t,false)));return}let o=this.hitTest(t,i,n);if(o.handle==="rotation"){this.state="rotating",this.pendingHistoryCommit=true;let a=o.shape;this.rotatingState={shapeId:a.id,center:{x:a.x,y:a.y},startAngle:Math.atan2(t.y-a.y,t.x-a.x),startRotation:a.rotation};return}if(o.handle){this.state="resizing",this.resizingState={startPoint:t,startShape:o.shape,handle:o.handle},this.pendingHistoryCommit=true;return}if(o.shape){let a=o.shape,l=a.type==="line"||a.type==="arrow"||a.type==="draw",h=n.includes(a.id);if(l&&!h){this.context.selectShape(a.id,e.shiftKey);return}this.state="translating",this.pendingHistoryCommit=true,h||this.context.selectShape(a.id,e.shiftKey);let d=this.context.getSelectedIds(),c=new Map,g=new Map,y=new Map;d.forEach(p=>{let f=i.find(T=>T.id===p);f&&(c.set(p,{x:f.x,y:f.y}),g.set(p,f.rotation),y.set(p,this.getShapeWorldBounds(f)));}),this.translatingState={startPoint:t,startPositions:c,startRotation:g,startBounds:y};}else this.state="brushing",this.brushStart=t,this.brushShiftKey=e.shiftKey,e.shiftKey||this.context.selectShape("",false);}onPointerMove(t,e){let i=this.context.getShapes();if(this.state==="translating"&&this.translatingState)this.pendingHistoryCommit&&(this.context.commitHistory(),this.pendingHistoryCommit=false),this.handleTranslating(t,e);else if(this.state==="generating"&&this.generatingState)this.pendingHistoryCommit&&(this.context.commitHistory(),this.pendingHistoryCommit=false),this.handleGenerating(t,i);else if(this.state==="rotating"&&this.rotatingState)this.pendingHistoryCommit&&(this.context.commitHistory(),this.pendingHistoryCommit=false),this.handleRotating(t,e);else if(this.state==="resizing"&&this.resizingState)this.pendingHistoryCommit&&(this.context.commitHistory(),this.pendingHistoryCommit=false),this.handleResizing(t);else if(this.state==="dragging-terminal"&&this.draggingArrowId&&this.draggingTerminal){this.pendingHistoryCommit&&(this.context.commitHistory(),this.pendingHistoryCommit=false);let n=i.find(s=>s.id===this.draggingArrowId);if(n){let s=this.draggingTerminal==="start"?t:n.points[n.points.length-1],r=this.draggingTerminal==="start"?n.points[0]:t,o=this.computeSnapGuidesForArrow(s,r,t,n,i);this.context.setSnapGuides(o);let a=t;if(o.length>0){let d=o.filter(g=>g.type==="horizontal"),c=o.filter(g=>g.type==="vertical");if(d.length>0){let g=d[0].position,y=Math.abs(t.y-g);for(let p of d){let f=Math.abs(t.y-p.position);f<y&&(y=f,g=p.position);}a={...a,y:g};}else if(c.length>0){let g=c[0].position,y=Math.abs(t.x-g);for(let p of c){let f=Math.abs(t.x-p.position);f<y&&(y=f,g=p.position);}a={...a,x:g};}}let l=this.draggingTerminal==="start"?0:n.points.length-1,h=[...n.points];h[l]=a,this.context.updateShape(this.draggingArrowId,{points:h});}}else if(this.state==="brushing")this.handleBrushing(t);else {let n=this.context.getSelectedIds(),s=this.hitGenPoint(t,i,n);s?(this.hoverGenPoint=s,this.context.setGenerationPreview(this.genPreviewOf(s,this.genPreviewTarget(s,t,false)))):this.hoverGenPoint&&(this.hoverGenPoint=null,this.context.setGenerationPreview(null));}}onPointerUp(t,e){if(this.state==="dragging-terminal"&&this.draggingArrowId&&this.draggingTerminal){let i=this.context.getShapes(),n=this.findBindingNearPoint(t,i,this.draggingArrowId);n&&this.context.updateArrowBinding(this.draggingArrowId,this.draggingTerminal,n.toShapeId,n.binding);}if(this.state==="generating"&&this.generatingState){let i=this.generatingState,n=this.context.getShapes(),s=i.source,r=this.findBindingNearPoint(t,n,s.sourceShapeId);if(r)this.context.connectToExisting(this.genContext(s,t),r.toShapeId,r.binding);else if(!i.hasMoved)this.context.createGeneratedConnection(this.genContext(s,this.genPreviewTarget(s,s.point,false)),{type:this.defaultGenType(s.sourceShapeId)});else {let o=this.context.createFloatingLine(this.genContext(s,t));this.context.openGenerationMenu({context:this.genContext(s,t),existingArrowId:o,defaultShapeType:this.defaultGenType(s.sourceShapeId)});}this.generatingState=null,this.context.setGenerationPreview(null);}this.state==="brushing"&&this.context.setBrushRect(null),this.context.setConnectingTarget(null),this.hoverGenPoint=null,this.pendingHistoryCommit=false,this.state="idle",this.draggingArrowId=null,this.draggingTerminal=null,this.translatingState=null,this.resizingState=null,this.rotatingState=null,this.context.setSnapGuides([]);}onKeyDown(t){if(t.key==="Delete"||t.key==="Backspace"){let e=this.context.getSelectedIds();e.length>0&&(this.context.commitHistory(),e.forEach(i=>{this.context.deleteShape(i);}));}t.key==="Escape"&&(this.context.selectShape("",false),this.context.setSnapGuides([]),this.state="idle"),(t.key==="+"||t.key==="=")&&t.shiftKey&&(this.context.commitHistory(),this.handleRotation(15)),t.key==="-"&&t.shiftKey&&(this.context.commitHistory(),this.handleRotation(-15)),t.key==="ArrowUp"&&(t.preventDefault(),this.context.commitHistory(),this.handleNudge(0,-5)),t.key==="ArrowDown"&&(t.preventDefault(),this.context.commitHistory(),this.handleNudge(0,5)),t.key==="ArrowLeft"&&(t.preventDefault(),this.context.commitHistory(),this.handleNudge(-5,0)),t.key==="ArrowRight"&&(t.preventDefault(),this.context.commitHistory(),this.handleNudge(5,0));}onKeyUp(t){}getCursor(t){switch(this.state){case "translating":return "grabbing";case "generating":return "crosshair";case "rotating":return "grabbing";case "resizing":return this.resizingState?this.getHandleCursor(this.resizingState.handle,this.resizingState.startShape):"default";case "dragging-terminal":return "crosshair";case "brushing":return "crosshair"}let e=this.context.getShapes(),i=this.context.getSelectedIds();if(this.hitGenPoint(t,e,i))return "crosshair";if(this.hitTestArrowTerminal(t,e)&&i.includes(this.hitTestArrowTerminal(t,e).shapeId))return "move";let n=this.hitTest(t,e,i);return n.handle==="rotation"?"grab":n.handle?this.getHandleCursor(n.handle,n.shape):n.shape?"move":null}getHandleCursor(t,e){let n={e:0,se:45,s:90,sw:135,w:180,nw:225,n:270,ne:315}[t];if(n===void 0)return "grab";let s=((n+e.rotation*180/Math.PI)%360+360)%360,r=Math.round(s/45)%4;return ["ew-resize","nwse-resize","ns-resize","nesw-resize"][r]}static ALIGN_SNAP_THRESHOLD=8;handleTranslating(t,e){if(!this.translatingState)return;let i=X.subtract(t,this.translatingState.startPoint),n={...i};e.shiftKey&&(Math.abs(i.x)>Math.abs(i.y)?n.y=0:n.x=0);let s=this.context.getShapes(),r=this.context.getSelectedIds(),o=new Set(r),a=[],l=[];if(this.translatingState.startBounds.forEach(h=>{l.push({x:h.x+n.x,y:h.y+n.y,width:h.width,height:h.height});}),!e.shiftKey&&l.length>0){let h=[];for(let g of s)o.has(g.id)||g.type==="line"||g.type==="arrow"||g.type==="draw"||h.push(this.getShapeWorldBounds(g));let d=[],c=[];for(let g of l){let y=[g.y,g.y+g.height/2,g.y+g.height],p=[g.x,g.x+g.width/2,g.x+g.width];for(let f of h){let T=[f.y,f.y+f.height/2,f.y+f.height];for(let I of y)for(let b of T){let w=b-I;Math.abs(w)<=u.ALIGN_SNAP_THRESHOLD&&d.push(w);}let S=[f.x,f.x+f.width/2,f.x+f.width];for(let I of p)for(let b of S){let w=b-I;Math.abs(w)<=u.ALIGN_SNAP_THRESHOLD&&c.push(w);}}}if(d.length>0){let g=d[0];for(let y of d)Math.abs(y)<Math.abs(g)&&(g=y);n.y+=g;}if(c.length>0){let g=c[0];for(let y of c)Math.abs(y)<Math.abs(g)&&(g=y);n.x+=g;}this.translatingState.startBounds.forEach(g=>{let y={x:g.x+n.x,y:g.y+n.y,width:g.width,height:g.height};for(let p of h)a.push(...W(y,p,"horizontal",1)),a.push(...W(y,p,"vertical",1));});}this.translatingState.startPositions.forEach((h,d)=>{this.context.updateShape(d,{x:h.x+n.x,y:h.y+n.y});}),this.context.setSnapGuides(this.mergeGuides(a));}handleResizing(t){if(!this.resizingState)return;let e=this.resizingState.startShape,i=this.resizingState.handle,n=this.resizingState.startPoint;if(!i||i==="rotation")return;let r={nw:[-1,-1],n:[0,-1],ne:[1,-1],w:[-1,0],e:[1,0],sw:[-1,1],s:[0,1],se:[1,1]}[i];if(!r)return;let[o,a]=r,l=X.subtract(t,n),h=[];for(let w of this.context.getShapes())w.id!==e.id&&(w.type==="line"||w.type==="arrow"||w.type==="draw"||h.push(this.getShapeWorldBounds(w)));let d=20,c=this.getShapeWorldBounds(e),g=c.x,y=c.y,p=c.x+c.width,f=c.y+c.height;o===1?p=Math.max(g+d,p+l.x):o===-1&&(g=Math.min(p-d,g+l.x)),a===1?f=Math.max(y+d,f+l.y):a===-1&&(y=Math.min(f-d,y+l.y));let T=(w,M)=>{let C=null;for(let O of M){let U=O-w;Math.abs(U)<=u.ALIGN_SNAP_THRESHOLD&&(C===null||Math.abs(U)<Math.abs(C))&&(C=U);}return C};if(o!==0&&h.length>0){let w=[];for(let C of h)w.push(C.x,C.x+C.width/2,C.x+C.width);let M=T(o===1?p:g,w);M!==null&&(o===1?p+=M:g+=M);}if(a!==0&&h.length>0){let w=[];for(let C of h)w.push(C.y,C.y+C.height/2,C.y+C.height);let M=T(a===1?f:y,w);M!==null&&(a===1?f+=M:y+=M);}let S={x:g,y,width:p-g,height:f-y},I=[];for(let w of h)o!==0&&I.push(...W(S,w,"vertical",1)),a!==0&&I.push(...W(S,w,"horizontal",1));this.context.setSnapGuides(this.mergeGuides(I));let b={};if(e.type==="circle"){let w=Math.max(10,Math.max(o!==0?S.width/2:0,a!==0?S.height/2:0));if(b.radius=w,o!==0){let M=o===1?c.x:c.x+c.width;b.x=o===1?M+w:M-w;}if(a!==0){let M=a===1?c.y:c.y+c.height;b.y=a===1?M+w:M-w;}}else "width"in e&&o!==0&&(b.width=S.width,b.x=S.x+S.width/2),"height"in e&&a!==0&&(b.height=S.height,b.y=S.y+S.height/2);Object.keys(b).length>0&&this.context.updateShape(e.id,b);}mergeGuides(t){let e=new Map;for(let i of t){let n=`${i.type}\0${Math.round(i.position*2)/2}`,s=e.get(n);s?(s.start=Math.min(s.start,i.start),s.end=Math.max(s.end,i.end)):e.set(n,{...i});}return [...e.values()]}handleRotation(t){let e=this.context.getSelectedIds(),i=this.context.getShapes();e.forEach(n=>{let s=i.find(r=>r.id===n);s&&this.context.updateShape(n,{rotation:s.rotation+t*Math.PI/180});});}handleNudge(t,e){let i=this.context.getSelectedIds(),n=this.context.getShapes();i.forEach(s=>{let r=n.find(o=>o.id===s);r&&this.context.updateShape(s,{x:r.x+t,y:r.y+e});});}handleBrushing(t){let e=Math.min(this.brushStart.x,t.x),i=Math.min(this.brushStart.y,t.y),n=Math.abs(t.x-this.brushStart.x),s=Math.abs(t.y-this.brushStart.y);this.context.setBrushRect({x:e,y:i,width:n,height:s});let r=this.context.getShapes(),o=[];for(let a of r){let l=this.getShapeWorldBounds(a);this.rectsIntersect({x:e,y:i,width:n,height:s},l)&&o.push(a.id);}if(this.brushShiftKey){let a=this.context.getSelectedIds(),l=new Set([...a,...o]);this.context.selectShapes([...l]);}else this.context.selectShapes(o);}getShapeWorldBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return {x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height};case "circle":return {x:t.x-t.radius,y:t.y-t.radius,width:t.radius*2,height:t.radius*2};case "line":case "arrow":case "draw":{if(!t.points||t.points.length===0)return {x:t.x,y:t.y,width:0,height:0};let e=1/0,i=1/0,n=-1/0,s=-1/0;return t.points.forEach(r=>{e=Math.min(e,r.x),i=Math.min(i,r.y),n=Math.max(n,r.x),s=Math.max(s,r.y);}),{x:e,y:i,width:n-e,height:s-i}}case "text":{let e=t;return {x:t.x-e.width/2,y:t.y-e.height/2,width:e.width,height:e.height}}default:{let e=t;return {x:e.x,y:e.y,width:0,height:0}}}}rectsIntersect(t,e){return t.x<e.x+e.width&&t.x+t.width>e.x&&t.y<e.y+e.height&&t.y+t.height>e.y}hitTest(t,e,i){for(let n=e.length-1;n>=0;n--){let s=e[n];if(i.includes(s.id)){let r=this.getHitHandle(t,s);if(r)return {shape:s,handle:r}}if(this.isPointInShape(t,s))return {shape:s,handle:null}}return {shape:null,handle:null}}getHitHandle(t,e){let n=this.getShapeBounds(e),s=Math.cos(-e.rotation),r=Math.sin(-e.rotation),o=t.x-e.x,a=t.y-e.y,l=o*s-a*r,h=o*r+a*s,d=[{type:"nw",x:n.x,y:n.y},{type:"ne",x:n.x+n.width,y:n.y},{type:"se",x:n.x+n.width,y:n.y+n.height},{type:"sw",x:n.x,y:n.y+n.height},{type:"rotation",x:n.x+n.width/2,y:n.y-26}];for(let c of d)if(Math.abs(l-c.x)<14&&Math.abs(h-c.y)<14)return c.type;return null}static GEN_HIT_THRESHOLD=14;static CLICK_MOVE_THRESHOLD=6;computeGenPoints(t,e){let i=[];for(let n of e){let s=t.find(r=>r.id===n);if(s){if(s.type==="line"||s.type==="arrow"||s.type==="draw"){if(s.points&&s.points.length>=2){let r=this.getShapeWorldBounds(s);i.push({point:{x:r.x+r.width/2,y:r.y+r.height/2},sourceShapeId:s.id});}}else if(s.type==="rectangle"||s.type==="rounded-rectangle"||s.type==="ellipse"||s.type==="diamond"||s.type==="triangle"||s.type==="hexagon"||s.type==="parallelogram"||s.type==="trapezoid"||s.type==="pentagon"||s.type==="octagon"||s.type==="star"||s.type==="uml-class"||s.type==="uml-interface"||s.type==="circle"){let r=this.getShapeBounds(s),o={x:s.x,y:s.y};i.push({point:{x:o.x+r.x+r.width/2,y:o.y+r.y},edge:"n",sourceShapeId:s.id}),i.push({point:{x:o.x+r.x+r.width,y:o.y+r.y+r.height/2},edge:"e",sourceShapeId:s.id}),i.push({point:{x:o.x+r.x+r.width/2,y:o.y+r.y+r.height},edge:"s",sourceShapeId:s.id}),i.push({point:{x:o.x+r.x,y:o.y+r.y+r.height/2},edge:"w",sourceShapeId:s.id});}}}return i}hitGenPoint(t,e,i){let n=u.GEN_HIT_THRESHOLD,s=null,r=n;for(let o of this.computeGenPoints(e,i)){let a=Math.hypot(o.point.x-t.x,o.point.y-t.y);a<=r&&(r=a,s=o);}return s}genPreviewTarget(t,e,i){if(i)return e;let n=96;if(t.edge){this.getShapeWorldBounds(this.context.getShapes().find(r=>r.id===t.sourceShapeId));switch(t.edge){case "n":return {x:t.point.x,y:t.point.y-n};case "s":return {x:t.point.x,y:t.point.y+n};case "e":return {x:t.point.x+n,y:t.point.y};case "w":return {x:t.point.x-n,y:t.point.y}}}return {x:t.point.x,y:t.point.y-n}}handleGenerating(t,e){if(!this.generatingState)return;let i=this.generatingState,n=i.source;Math.hypot(t.x-i.startPoint.x,t.y-i.startPoint.y)>u.CLICK_MOVE_THRESHOLD&&(i.hasMoved=true);let r=this.findBindingNearPoint(t,e,n.sourceShapeId),o=r?this.bindingPosition(r.toShapeId,r.binding,e):i.hasMoved?t:this.genPreviewTarget(n,t,false);this.context.setConnectingTarget(r?r.toShapeId:null),this.context.setGenerationPreview({from:n.point,to:o,type:r?void 0:this.defaultGenType(n.sourceShapeId)});}handleRotating(t,e){if(!this.rotatingState)return;let i=this.rotatingState,n=Math.atan2(t.y-i.center.y,t.x-i.center.x),s=i.startRotation+(n-i.startAngle);e.shiftKey&&(s=Math.round(s/(Math.PI/12))*(Math.PI/12)),this.context.updateShape(i.shapeId,{rotation:s});}edgeMidpointWorld(t,e,i){let n=t.x,s=t.y;switch(i){case "n":return {x:n+e.x+e.width/2,y:s+e.y};case "s":return {x:n+e.x+e.width/2,y:s+e.y+e.height};case "e":return {x:n+e.x+e.width,y:s+e.y+e.height/2};case "w":return {x:n+e.x,y:s+e.y+e.height/2}}}bindingPosition(t,e,i){let n=i.find(r=>r.id===t);if(!n)return {x:0,y:0};let s=this.getShapeWorldBounds(n);if(e.handle)switch(e.handle){case "top":return {x:n.x,y:s.y};case "bottom":return {x:n.x,y:s.y+s.height};case "left":return {x:s.x,y:n.y};case "right":return {x:s.x+s.width,y:n.y}}return e.normalizedAnchor?{x:s.x+s.width*e.normalizedAnchor.x,y:s.y+s.height*e.normalizedAnchor.y}:{x:n.x,y:n.y}}isPointInShape(t,e){let i=t.x-e.x,n=t.y-e.y;switch(e.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return Math.abs(i)<=e.width/2&&Math.abs(n)<=e.height/2;case "circle":return i*i+n*n<=e.radius*e.radius;case "hexagon":return this.isPointInHexagon(i,n,e.width,e.height);case "line":case "arrow":case "draw":for(let s=0;s<e.points.length-1;s++){let r=e.points[s],o=e.points[s+1];if(this.pointToLineDistance(t,r,o)<10)return true}return false;case "text":return Math.abs(i)<=50&&Math.abs(n)<=e.fontSize/2;default:return false}}isPointInHexagon(t,e,i,n){let s=Math.abs(t),r=Math.abs(e),o=i/2,a=n/2;return s<=o&&r<=a&&s/o+r/a<=1.5}pointToLineDistance(t,e,i){let n=t.x-e.x,s=t.y-e.y,r=i.x-e.x,o=i.y-e.y,a=n*r+s*o,l=r*r+o*o,h=-1;l!==0&&(h=a/l);let d,c;h<0?(d=e.x,c=e.y):h>1?(d=i.x,c=i.y):(d=e.x+h*r,c=e.y+h*o);let g=t.x-d,y=t.y-c;return Math.sqrt(g*g+y*y)}getShapeBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return {x:-t.width/2,y:-t.height/2,width:t.width,height:t.height};case "circle":return {x:-t.radius,y:-t.radius,width:t.radius*2,height:t.radius*2};case "hexagon":return {x:-t.width/2,y:-t.height/2,width:t.width,height:t.height};case "line":case "arrow":case "draw":if(t.points.length===0)return {x:0,y:0,width:0,height:0};let e=1/0,i=1/0,n=-1/0,s=-1/0;return t.points.forEach(r=>{e=Math.min(e,r.x-t.x),i=Math.min(i,r.y-t.y),n=Math.max(n,r.x-t.x),s=Math.max(s,r.y-t.y);}),{x:e,y:i,width:n-e,height:s-i};case "text":return {x:-50,y:-t.fontSize/2,width:100,height:t.fontSize};default:return {x:0,y:0,width:0,height:0}}}hitTestArrowTerminal(t,e){for(let n of e){if(n.type!=="arrow")continue;let s=n;if(s.points.length<2)continue;let r=s.points[0],o=s.points[s.points.length-1],a=Math.sqrt(Math.pow(t.x-r.x,2)+Math.pow(t.y-r.y,2)),l=Math.sqrt(Math.pow(t.x-o.x,2)+Math.pow(t.y-o.y,2));if(a<20)return {shapeId:s.id,terminal:"start"};if(l<20)return {shapeId:s.id,terminal:"end"}}return null}findShapeNearPoint(t,e,i){for(let s=e.length-1;s>=0;s--){let r=e[s];if(i&&r.id===i||r.type==="arrow"||r.type==="line"||r.type==="draw")continue;let o;switch(r.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":o=this.pointToRectDistance(t,{x:r.x-r.width/2,y:r.y-r.height/2,width:r.width,height:r.height});break;case "circle":o=Math.sqrt(Math.pow(t.x-r.x,2)+Math.pow(t.y-r.y,2))-r.radius;break;case "hexagon":{let a=r.width/2,l=r.height/2,h=Math.abs(t.x-r.x),d=Math.abs(t.y-r.y);o=h/a+d/l-1;break}default:continue}if(o<=30)return r}return null}pointToRectDistance(t,e){let i=Math.max(e.x,Math.min(t.x,e.x+e.width)),n=Math.max(e.y,Math.min(t.y,e.y+e.height));return Math.sqrt(Math.pow(t.x-i,2)+Math.pow(t.y-n,2))}static SNAP_THRESHOLD=32;static HANDLE_SNAP_DISTANCE=18;static ANCHOR_EPS=.001;computeSnapGuidesForArrow(t,e,i,n,s){let r=this.draggingTerminal==="start"?n.endBinding:n.startBinding,o=r?s.find(y=>y.id===r.toShapeId):null;if(!o)return [];let a=this.findShapeNearPoint(i,s,n.id);if(!a)return [];let l=this.getShapeWorldBounds(o),h=this.getShapeWorldBounds(a),d=e.x-t.x,c=e.y-t.y,g=Math.abs(d)>=Math.abs(c)?"horizontal":"vertical";return W(l,h,g)}findBindingNearPoint(t,e,i){let n;for(let s=e.length-1;s>=0;s--){let r=e[s];if(i&&r.id===i||r.type==="arrow"||r.type==="line"||r.type==="draw")continue;let o=this.getPageBounds(r),a=u.SNAP_THRESHOLD;if(t.x<o.x-a||t.x>o.x+o.width+a||t.y<o.y-a||t.y>o.y+o.height+a)continue;let l={x:r.x,y:r.y},h=[{handle:"top",pos:{x:l.x,y:o.y}},{handle:"bottom",pos:{x:l.x,y:o.y+o.height}},{handle:"left",pos:{x:o.x,y:l.y}},{handle:"right",pos:{x:o.x+o.width,y:l.y}}];for(let c of h){let g=Math.hypot(c.pos.x-t.x,c.pos.y-t.y);g<=u.HANDLE_SNAP_DISTANCE&&(!n||g<n.distance)&&(n={toShapeId:r.id,binding:{toShapeId:r.id,handle:c.handle,isPrecise:false},distance:g});}let d=this.nearestPointOnShapeOutline(r,t);if(d&&d.distance<=u.SNAP_THRESHOLD&&(!n||d.distance<n.distance)){let c=this.computeNormalizedAnchor(o,d.point);n={toShapeId:r.id,binding:{toShapeId:r.id,isPrecise:true,normalizedAnchor:c},distance:d.distance};}}if(n)return {toShapeId:n.toShapeId,binding:n.binding}}getPageBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return {x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height};case "circle":return {x:t.x-t.radius,y:t.y-t.radius,width:t.radius*2,height:t.radius*2};default:return {x:t.x-50,y:t.y-50,width:100,height:100}}}computeNormalizedAnchor(t,e){let i=u.ANCHOR_EPS,n=t.width>0?(e.x-t.x)/t.width:.5,s=t.height>0?(e.y-t.y)/t.height:.5;return n=Math.max(i,Math.min(1-i,n)),s=Math.max(i,Math.min(1-i,s)),{x:n,y:s}}nearestPointOnShapeOutline(t,e){let i={x:e.x-t.x,y:e.y-t.y},n=this.nearestLocalPointOnShapeOutline(t,i);if(!n)return null;let s={x:n.point.x+t.x,y:n.point.y+t.y};return {point:s,distance:Math.hypot(s.x-e.x,s.y-e.y)}}nearestLocalPointOnShapeOutline(t,e){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":{let i=t.width,n=t.height;return this.nearestPointOnRectOutlineLocal(e,{x:-i/2,y:-n/2,width:i,height:n})}case "circle":{let i=t.radius;return this.nearestPointOnCircleOutlineLocal(e,{x:0,y:0},i)}default:return null}}nearestPointOnRectOutlineLocal(t,e){let i=[[{x:e.x,y:e.y},{x:e.x+e.width,y:e.y}],[{x:e.x+e.width,y:e.y},{x:e.x+e.width,y:e.y+e.height}],[{x:e.x+e.width,y:e.y+e.height},{x:e.x,y:e.y+e.height}],[{x:e.x,y:e.y+e.height},{x:e.x,y:e.y}]],n=null;for(let[s,r]of i){let o=this.nearestPointOnSegment(t,s,r);(!n||o.distance<n.distance)&&(n=o);}return n}nearestPointOnCircleOutlineLocal(t,e,i){let n=t.x-e.x,s=t.y-e.y,r=Math.hypot(n,s);if(r<1e-9)return {point:{x:e.x+i,y:e.y},distance:i};let o=e.x+n/r*i,a=e.y+s/r*i,l={x:o,y:a};return {point:l,distance:Math.hypot(l.x-t.x,l.y-t.y)}}nearestPointOnSegment(t,e,i){let n=i.x-e.x,s=i.y-e.y,r=n*n+s*s,o=r>0?((t.x-e.x)*n+(t.y-e.y)*s)/r:0;o=Math.max(0,Math.min(1,o));let a=e.x+o*n,l=e.y+o*s,h={x:a,y:l};return {point:h,distance:Math.hypot(h.x-t.x,h.y-t.y)}}};var tt=class u extends H{type="hand";state="idle";startScreenPoint={x:0,y:0};initialCameraPos={x:0,y:0};static DRAG_THRESHOLD=4;onPointerDown(t,e){this.startScreenPoint={x:e.clientX,y:e.clientY};let i=this.context.getCamera();this.initialCameraPos={x:i.x,y:i.y},this.state="pointing";}onPointerMove(t,e){if(this.state!=="idle"){if(this.state==="pointing"){let i=e.clientX-this.startScreenPoint.x,n=e.clientY-this.startScreenPoint.y;if(Math.hypot(i,n)<u.DRAG_THRESHOLD)return;this.state="dragging",this.applyCameraDelta(i,n);return}if(this.state==="dragging"){let i=e.clientX-this.startScreenPoint.x,n=e.clientY-this.startScreenPoint.y;this.applyCameraDelta(i,n);}}}onPointerUp(t,e){if(this.state!=="idle"){if(this.state==="dragging"){let i=this.context.getCamera();this.initialCameraPos={x:i.x,y:i.y},this.startScreenPoint={x:e.clientX,y:e.clientY};}this.state="idle";}}onKeyDown(t){t.key==="Escape"&&this.state!=="idle"&&(this.context.setCameraPosition(this.initialCameraPos.x,this.initialCameraPos.y),this.state="idle");}onKeyUp(t){}getCursor(t){return this.state==="dragging"||this.state==="pointing"?"grabbing":"grab"}applyCameraDelta(t,e){this.context.setCameraPosition(this.initialCameraPos.x+t,this.initialCameraPos.y+e);}};var mt={strokeColor:"#1f2937",fillColor:"transparent",strokeWidth:2,strokeDash:null},et=class extends H{type="line";isDrawing=false;startPoint={x:0,y:0};currentShape=null;onPointerDown(t,e){this.isDrawing=true,this.startPoint=t,this.currentShape={id:`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,type:"line",x:t.x,y:t.y,points:[t],rotation:0,style:{...mt}},this.context.commitHistory(),this.context.addShape(this.currentShape);}onPointerMove(t,e){!this.isDrawing||!this.currentShape||this.context.updateShape(this.currentShape.id,{points:[this.startPoint,t]});}onPointerUp(t,e){this.isDrawing=false,this.currentShape=null,this.context.setActiveTool("select");}onKeyDown(t){}onKeyUp(t){}getCursor(){return "crosshair"}};var xt={strokeColor:"#1f2937",fillColor:"transparent",strokeWidth:4,strokeDash:null},St={strokeColor:"rgba(255, 235, 59, 0.5)",fillColor:"transparent",strokeWidth:16,strokeDash:null},wt=4e3,Z=class extends H{type;mode;isDrawing=false;points=[];currentShapeId=null;committed=false;fadeTimer=null;constructor(t,e){super(t),this.mode=e,this.type=e==="pen"?"pen":"highlighter";}onPointerDown(t,e){this.isDrawing=true,this.committed=false,this.currentShapeId=null,this.points=[{...t}];}onPointerMove(t,e){if(this.isDrawing)if(this.points.push({...t}),!this.committed&&this.points.length>=2){this.context.commitHistory();let i=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;this.currentShapeId=i;let n=this.points[0],s=this.mode==="pen"?{...xt}:{...St},r={id:i,type:"draw",x:n.x,y:n.y,points:this.points.map(o=>({...o})),rotation:0,style:s};this.context.addShape(r),this.committed=true;}else this.committed&&this.currentShapeId&&this.context.updateShape(this.currentShapeId,{points:this.points.map(i=>({...i}))});}onPointerUp(t,e){if(!this.isDrawing||(this.isDrawing=false,!this.committed||!this.currentShapeId))return;let i=this.currentShapeId;this.currentShapeId=null,this.mode==="highlighter"&&(this.fadeTimer=setTimeout(()=>{this.context.deleteShape(i);},wt));}onKeyDown(t){}onKeyUp(t){}getCursor(){return "crosshair"}dispose(){this.fadeTimer&&(clearTimeout(this.fadeTimer),this.fadeTimer=null);}};var bt={strokeColor:"#1f2937",fillColor:"transparent",strokeWidth:2,strokeDash:null},nt=class extends H{type="arrow";isDrawing=false;startPoint={x:0,y:0};currentShapeId=null;startBindingInfo=null;onPointerDown(t,e){this.isDrawing=true,this.startPoint=t,this.startBindingInfo=null;let i=this.context.getShapes(),n=this.findShapeBinding(t,i),s=t;n&&(s=this.getBindingPosition(n.toShapeId,n.handle,i),this.startBindingInfo={...n,position:s});let r=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;this.currentShapeId=r;let o={id:r,type:"arrow",x:s.x,y:s.y,points:[s],rotation:0,style:{...bt},startBinding:n?{toShapeId:n.toShapeId,handle:n.handle}:void 0,connectionType:"straight",arrowHead:"end",dashed:false};this.context.commitHistory(),this.context.addShape(o);}onPointerMove(t,e){if(!this.isDrawing||!this.currentShapeId)return;let i=this.context.getShapes(),n=this.findShapeBinding(t,i),s=t;n&&(s=this.getBindingPosition(n.toShapeId,n.handle,i));let r=this.startBindingInfo?.position||this.startPoint,o=this.computeSnapGuides(r,s,n,i);this.context.setSnapGuides(o),this.context.updateShape(this.currentShapeId,{points:[r,s],endBinding:n?{toShapeId:n.toShapeId,handle:n.handle}:void 0});}onPointerUp(t,e){this.isDrawing=false,this.currentShapeId=null,this.startBindingInfo=null,this.context.setSnapGuides([]),this.context.setActiveTool("select");}onKeyDown(t){}onKeyUp(t){}getCursor(){return "crosshair"}computeSnapGuides(t,e,i,n){let s=this.startBindingInfo?n.find(c=>c.id===this.startBindingInfo.toShapeId):null,r=i?n.find(c=>c.id===i.toShapeId):null;if(!s||!r)return [];let o=this.getShapeBounds(s),a=this.getShapeBounds(r),l=e.x-t.x,h=e.y-t.y,d=Math.abs(l)>=Math.abs(h)?"horizontal":"vertical";return W(o,a,d)}getShapeBounds(t){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":return {x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height};case "circle":return {x:t.x-t.radius,y:t.y-t.radius,width:t.radius*2,height:t.radius*2};case "text":return {x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height};default:return {x:t.x-50,y:t.y-20,width:100,height:40}}}findShapeBinding(t,e){for(let n=e.length-1;n>=0;n--){let s=e[n];if(s.type==="arrow"||s.type==="line"||s.type==="draw")continue;let r;switch(s.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "uml-class":case "uml-interface":r=this.pointToRectDistance(t,{x:s.x-s.width/2,y:s.y-s.height/2,width:s.width,height:s.height});break;case "circle":{let o=Math.sqrt(Math.pow(t.x-s.x,2)+Math.pow(t.y-s.y,2));r=Math.abs(o-s.radius);break}case "hexagon":{let o=s.width/2,a=s.height/2,l=Math.abs(t.x-s.x),h=Math.abs(t.y-s.y);r=l/o+h/a-1;break}default:continue}if(r<=40){let o=this.getClosestHandle(t,s);return {toShapeId:s.id,handle:o}}}}pointToRectDistance(t,e){let i=Math.max(e.x,Math.min(t.x,e.x+e.width)),n=Math.max(e.y,Math.min(t.y,e.y+e.height));return Math.sqrt(Math.pow(t.x-i,2)+Math.pow(t.y-n,2))}getClosestHandle(t,e){let i=t.x-e.x,n=t.y-e.y;if(e.type==="circle"||e.type==="hexagon"){let l=Math.atan2(n,i);return l>=-Math.PI/4&&l<Math.PI/4?"right":l>=Math.PI/4&&l<3*Math.PI/4?"bottom":l>=3*Math.PI/4||l<-(3*Math.PI)/4?"left":"top"}let s="width"in e?e.width:100,r="height"in e?e.height:100,o=Math.abs(i)/(s/2),a=Math.abs(n)/(r/2);return o>a?i>0?"right":"left":n>0?"bottom":"top"}getBindingPosition(t,e,i){let n=i.find(s=>s.id===t);if(!n)return {x:0,y:0};switch(n.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":return this.getRectBindingPosition(n,e);case "circle":return this.getCircleBindingPosition(n,e);case "hexagon":return this.getRectBindingPosition(n,e);default:return {x:n.x,y:n.y}}}getRectBindingPosition(t,e){switch(e){case "top":return {x:t.x,y:t.y-t.height/2};case "bottom":return {x:t.x,y:t.y+t.height/2};case "left":return {x:t.x-t.width/2,y:t.y};case "right":return {x:t.x+t.width/2,y:t.y}}}getCircleBindingPosition(t,e){switch(e){case "top":return {x:t.x,y:t.y-t.radius};case "bottom":return {x:t.x,y:t.y+t.radius};case "left":return {x:t.x-t.radius,y:t.y};case "right":return {x:t.x+t.radius,y:t.y}}}};var vt={strokeColor:"transparent",fillColor:"transparent",strokeWidth:0,strokeDash:null},at=class extends H{type="text";onPointerDown(t,e){let i=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,n={id:i,type:"text",x:t.x,y:t.y,rotation:0,style:{...vt},text:"",fontSize:16,textColor:"#1f2937",width:200,height:24};this.context.commitHistory(),this.context.addShape(n),this.context.enterEditing(i);}onPointerMove(t,e){}onPointerUp(t,e){}onKeyDown(t){}onKeyUp(t){}getCursor(){return "text"}};var dt="drawui-canvas-data",Tt=50,ct=class{canvas;renderer;camera;tools=new Map;activeTool="select";shapes=[];selectedIds=[];listeners=new Set;notificationListeners=new Set;uiListeners=new Set;isPanning=false;panStart={x:0,y:0};dragStartPositions=new Map;lastClickTime=0;lastClickShapeId=null;editingShapeId=null;textInput=null;textOverlay=null;isNewTextShape=false;originalTextValue="";undoStack=[];redoStack=[];isUndoing=false;brushRect=null;snapGuides=[];connectingTargetId=null;genPreview=null;pendingGen=null;renderScheduled=false;pointerIsDown=false;notifyScheduled=false;constructor(t){this.canvas=t;let e=t.getBoundingClientRect();this.renderer=new J(t),this.renderer.resize(e.width,e.height),this.camera=new q;let i={addShape:n=>this.addShape(n),updateShape:(n,s)=>this.updateShape(n,s),deleteShape:n=>this.deleteShape(n),selectShape:(n,s)=>this.selectShape(n,s),selectShapes:n=>this.selectShapes(n),getShapes:()=>[...this.shapes],getSelectedIds:()=>[...this.selectedIds],setActiveTool:n=>this.setActiveTool(n),screenToPage:n=>this.screenToPage(n),getCamera:()=>this.camera,panBy:(n,s)=>this.panBy(n,s),setCameraPosition:(n,s)=>this.setCameraPosition(n,s),getShapeAtPoint:(n,s)=>this.getShapeAtPoint(n,s),updateArrowBinding:(n,s,r,o)=>this.updateArrowBinding(n,s,r,o),enterEditing:n=>this.enterEditing(n),exitEditing:()=>this.exitEditing(),commitHistory:()=>this.commitHistory(),setBrushRect:n=>this.setBrushRect(n),setSnapGuides:n=>this.setSnapGuides(n),setConnectingTarget:n=>this.setConnectingTarget(n),setGenerationPreview:n=>this.setGenerationPreview(n),openGenerationMenu:n=>this.openGenerationMenu(n),createGeneratedConnection:(n,s)=>this.createGeneratedConnection(n,s),createFloatingLine:n=>this.createFloatingLine(n),connectToExisting:(n,s,r)=>this.connectToExisting(n,s,r),updateSelectedShapesStyle:n=>this.updateSelectedShapesStyle(n),updateSelectedShapesText:n=>this.updateSelectedShapesText(n),updateConnectionType:n=>this.updateConnectionType(n)};this.tools.set("select",new Q(i)),this.tools.set("hand",new tt(i)),this.tools.set("rectangle",new R(i,"rectangle")),this.tools.set("rounded-rectangle",new R(i,"rounded-rectangle")),this.tools.set("circle",new R(i,"circle")),this.tools.set("ellipse",new R(i,"ellipse")),this.tools.set("diamond",new R(i,"diamond")),this.tools.set("triangle",new R(i,"triangle")),this.tools.set("hexagon",new R(i,"hexagon")),this.tools.set("parallelogram",new R(i,"parallelogram")),this.tools.set("trapezoid",new R(i,"trapezoid")),this.tools.set("pentagon",new R(i,"pentagon")),this.tools.set("octagon",new R(i,"octagon")),this.tools.set("star",new R(i,"star")),this.tools.set("uml-class",new R(i,"uml-class")),this.tools.set("uml-interface",new R(i,"uml-interface")),this.tools.set("line",new et(i)),this.tools.set("arrow",new nt(i)),this.tools.set("text",new at(i)),this.tools.set("pen",new Z(i,"pen")),this.tools.set("highlighter",new Z(i,"highlighter")),this.setupEventListeners(),this.loadFromStorage(),this.render();}setupEventListeners(){this.canvas.addEventListener("pointerdown",this.handlePointerDown),this.canvas.addEventListener("pointermove",this.handlePointerMove),this.canvas.addEventListener("pointerup",this.handlePointerUp),this.canvas.addEventListener("pointerleave",this.handlePointerUp),this.canvas.addEventListener("wheel",this.handleWheel,{passive:false}),window.addEventListener("keydown",this.handleKeyDown),window.addEventListener("keyup",this.handleKeyUp),window.addEventListener("resize",this.handleResize);}handlePointerDown=t=>{if(this.editingShapeId)return this.textInput&&t.target===this.textInput,void 0;if(this.pointerIsDown=true,t.button===1||t.button===0&&t.altKey){this.isPanning=true,this.panStart={x:t.clientX,y:t.clientY},this.canvas.setPointerCapture(t.pointerId),this.canvas.style.cursor="grabbing";return}let e=this.screenToPage({x:t.clientX,y:t.clientY});if(this.activeTool==="select"){let n=Date.now(),s=this.getShapeAtPoint(e);if(n-this.lastClickTime<400&&this.lastClickShapeId&&s&&s.id===this.lastClickShapeId&&this.canEditShape(s)){this.enterEditing(s.id),this.lastClickTime=0,this.lastClickShapeId=null;return}this.lastClickTime=n,this.lastClickShapeId=s?.id||null,s&&(this.dragStartPositions.clear(),this.selectedIds.forEach(r=>{let o=this.shapes.find(a=>a.id===r);o&&this.dragStartPositions.set(r,{x:o.x,y:o.y});}));}this.tools.get(this.activeTool)?.onPointerDown(e,t),this.updateCursor(e),this.render();};handlePointerMove=t=>{if(this.editingShapeId)return;if(this.isPanning){let n=t.clientX-this.panStart.x,s=t.clientY-this.panStart.y;this.camera.translate(n,s),this.panStart={x:t.clientX,y:t.clientY},this.scheduleRender(),this.notifyListeners();return}let e=this.screenToPage({x:t.clientX,y:t.clientY});this.tools.get(this.activeTool)?.onPointerMove(e,t),this.updateCursor(e),this.scheduleRender();};handlePointerUp=t=>{if(this.pointerIsDown=false,this.editingShapeId)return;if(this.isPanning){this.isPanning=false,this.updateCursor(this.screenToPage({x:t.clientX,y:t.clientY}));return}let e=this.screenToPage({x:t.clientX,y:t.clientY});this.tools.get(this.activeTool)?.onPointerUp(e,t),this.updateCursor(e),this.scheduleRender();};updateCursor(t){let i=this.tools.get(this.activeTool)?.getCursor(t)??"default";this.canvas.style.cursor!==i&&(this.canvas.style.cursor=i);}handleWheel=t=>{t.preventDefault();let e=t.deltaY>0?.9:1.1;this.camera.zoomAt({x:t.clientX,y:t.clientY},e),this.scheduleRender(),this.notifyListeners();};handleKeyDown=t=>{if(this.editingShapeId)return;if((t.ctrlKey||t.metaKey)&&t.key.toLowerCase()==="z"){t.preventDefault(),t.shiftKey?this.redo():this.undo();return}if((t.ctrlKey||t.metaKey)&&t.key.toLowerCase()==="y"){t.preventDefault(),this.redo();return}if((t.ctrlKey||t.metaKey)&&t.key.toLowerCase()==="s"){t.preventDefault(),this.saveToStorage();return}if(!t.ctrlKey&&!t.metaKey&&!t.altKey){if(t.key==="v"||t.key==="V"){this.setActiveTool("select"),t.preventDefault();return}if(t.key==="h"||t.key==="H"){this.setActiveTool("hand"),t.preventDefault();return}}this.tools.get(this.activeTool)?.onKeyDown(t),this.scheduleRender();};handleKeyUp=t=>{if(this.editingShapeId)return;this.tools.get(this.activeTool)?.onKeyUp(t),this.scheduleRender();};handleResize=()=>{this.renderer.resize(window.innerWidth,window.innerHeight),this.scheduleRender();};screenToPage(t){let e=this.canvas.getBoundingClientRect();return this.camera.screenToPage({x:t.x-e.left,y:t.y-e.top})}addShape(t){this.shapes.push(t),this.notifyListeners();}updateShape(t,e){let i=this.shapes.findIndex(n=>n.id===t);i!==-1&&(this.shapes[i]={...this.shapes[i],...e},(e.x!==void 0||e.y!==void 0)&&this.updateBoundArrows(t),this.notifyListeners());}updateBoundArrows(t){let e=this.shapes.find(i=>i.id===t);e&&this.shapes.forEach(i=>{if(i.type==="arrow"){let n=i,s={};if(n.startBinding?.toShapeId===t){let r=this.getBindingPosition(e,n.startBinding.handle||"top");s.points=n.points.map((o,a)=>a===0?r:o);}if(n.endBinding?.toShapeId===t){let r=this.getBindingPosition(e,n.endBinding.handle||"top"),o=s.points||[...n.points];s.points=o.map((a,l)=>l===o.length-1?r:a);}s.points&&(this.shapes[this.shapes.indexOf(i)]={...n,...s});}});}getBindingPosition(t,e){switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":switch(e){case "top":return {x:t.x,y:t.y-t.height/2};case "bottom":return {x:t.x,y:t.y+t.height/2};case "left":return {x:t.x-t.width/2,y:t.y};case "right":return {x:t.x+t.width/2,y:t.y}}case "circle":switch(e){case "top":return {x:t.x,y:t.y-t.radius};case "bottom":return {x:t.x,y:t.y+t.radius};case "left":return {x:t.x-t.radius,y:t.y};case "right":return {x:t.x+t.radius,y:t.y}}case "hexagon":switch(e){case "top":return {x:t.x,y:t.y-t.height/2};case "bottom":return {x:t.x,y:t.y+t.height/2};case "left":return {x:t.x-t.width/2,y:t.y};case "right":return {x:t.x+t.width/2,y:t.y}}default:return {x:t.x,y:t.y}}}deleteShape(t){this.shapes=this.shapes.filter(e=>e.id!==t),this.selectedIds=this.selectedIds.filter(e=>e!==t),this.shapes=this.shapes.map(e=>{if(e.type==="arrow"){let i=e;if(i.startBinding?.toShapeId===t)return {...i,startBinding:void 0};if(i.endBinding?.toShapeId===t)return {...i,endBinding:void 0}}return e}),this.notifyListeners(),this.scheduleRender();}selectShape(t,e){if(!t)this.selectedIds=[];else if(e){let i=this.selectedIds.indexOf(t);i===-1?this.selectedIds.push(t):this.selectedIds.splice(i,1);}else this.selectedIds=[t];this.notifyListeners(),this.scheduleRender();}selectShapes(t){this.selectedIds=[...t],this.notifyListeners(),this.scheduleRender();}setBrushRect(t){this.brushRect=t,this.scheduleRender();}setSnapGuides(t){this.snapGuides=t;}setConnectingTarget(t){this.connectingTargetId!==t&&(this.connectingTargetId=t,this.scheduleRender());}setGenerationPreview(t){this.genPreview?.from.x===t?.from.x&&this.genPreview?.from.y===t?.from.y&&this.genPreview?.to.x===t?.to.x&&this.genPreview?.to.y===t?.to.y&&this.genPreview?.type===t?.type||(this.genPreview=t,this.scheduleRender());}openGenerationMenu(t){this.pendingGen={...t.context,existingArrowId:t.existingArrowId};let e=this.camera.getZoom(),i=(t.context.to.x+this.camera.x)*e,n=(t.context.to.y+this.camera.y)*e;this.emitUi({kind:"shapeTypeMenu",screenX:i,screenY:n,context:t.context,existingArrowId:t.existingArrowId,defaultShapeType:t.defaultShapeType});}lineStyle(){return {strokeColor:"#1f2937",fillColor:"transparent",strokeWidth:2,strokeDash:null}}computeSourceBinding(t){if(!t.sourceShapeId||!t.edge)return;let e=this.shapes.find(n=>n.id===t.sourceShapeId);if(!e)return;let i={n:"top",e:"right",s:"bottom",w:"left"};return {toShapeId:e.id,handle:i[t.edge]}}connectHandle(t,e){let i=e.x-t.x,n=e.y-t.y;return Math.abs(i)>=Math.abs(n)?i>=0?"left":"right":n>=0?"top":"bottom"}createFloatingLine(t){this.commitHistory();let e=t.from,i=t.to,n=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,s={id:n,type:"arrow",x:e.x,y:e.y,points:[{x:e.x,y:e.y},{x:i.x,y:i.y}],rotation:0,style:this.lineStyle(),startBinding:this.computeSourceBinding(t),connectionType:"polyline",arrowHead:"end",dashed:false};return this.addShape(s),this.selectedIds=[n],this.pendingGen={...t,existingArrowId:n},this.notifyListeners(),this.render(),n}createGeneratedConnection(t,e){this.commitHistory();let i=e.type,n;if(t.sourceShapeId){let a=this.shapes.find(l=>l.id===t.sourceShapeId);a&&rt(a.type)&&(n={...a.style});}let s=lt(i,t.to,n);this.addShape(s);let r=t.from,o={toShapeId:s.id,handle:this.connectHandle(r,t.to)};if(e.existingArrowId)this.updateArrowBinding(e.existingArrowId,"end",s.id,{toShapeId:s.id,handle:this.connectHandle(r,t.to)});else {let l={id:`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,type:"arrow",x:r.x,y:r.y,points:[{x:r.x,y:r.y},{x:t.to.x,y:t.to.y}],rotation:0,style:this.lineStyle(),startBinding:this.computeSourceBinding(t),endBinding:o,connectionType:"polyline",arrowHead:"end",dashed:false};this.addShape(l);}this.selectedIds=[s.id],this.pendingGen=null,this.notifyListeners(),this.render();}connectToExisting(t,e,i){this.commitHistory();let n=this.shapes.find(h=>h.id===e);if(!n)return;let s=t.from,r=i?.handle??this.connectHandle(s,{x:n.x,y:n.y}),o=this.getBindingPosition(n,r),a=`shape_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,l={id:a,type:"arrow",x:s.x,y:s.y,points:[{x:s.x,y:s.y},{x:o.x,y:o.y}],rotation:0,style:this.lineStyle(),startBinding:this.computeSourceBinding(t),endBinding:{toShapeId:e,handle:r},connectionType:"polyline",arrowHead:"end",dashed:false};this.addShape(l),this.selectedIds=[a],this.pendingGen=null,this.notifyListeners(),this.render();}subscribeUi(t){return this.uiListeners.add(t),()=>this.uiListeners.delete(t)}emitUi(t){this.uiListeners.forEach(e=>e(t));}updateSelectedShapesStyle(t){this.selectedIds.length!==0&&(this.commitHistory(),this.selectedIds.forEach(e=>{let i=this.shapes.findIndex(n=>n.id===e);i!==-1&&(this.shapes[i]={...this.shapes[i],style:{...this.shapes[i].style,...t}});}),this.notifyListeners(),this.render());}updateSelectedShapesText(t){this.selectedIds.length!==0&&(this.commitHistory(),this.selectedIds.forEach(e=>{let i=this.shapes.findIndex(n=>n.id===e);i!==-1&&(this.shapes[i]={...this.shapes[i],...t});}),this.notifyListeners(),this.render());}updateConnectionType(t){this.selectedIds.length!==0&&(this.commitHistory(),this.selectedIds.forEach(e=>{let i=this.shapes.findIndex(n=>n.id===e);if(i!==-1){let n=this.shapes[i];(n.type==="line"||n.type==="arrow")&&(this.shapes[i]={...n,connectionType:t});}}),this.notifyListeners(),this.render());}updateArrowHeadType(t){this.selectedIds.length!==0&&(this.commitHistory(),this.selectedIds.forEach(e=>{let i=this.shapes.findIndex(n=>n.id===e);if(i!==-1){let n=this.shapes[i];n.type==="arrow"&&(this.shapes[i]={...n,arrowHead:t});}}),this.notifyListeners(),this.render());}updateArrowDashed(t){this.selectedIds.length!==0&&(this.commitHistory(),this.selectedIds.forEach(e=>{let i=this.shapes.findIndex(n=>n.id===e);if(i!==-1){let n=this.shapes[i];n.type==="arrow"&&(this.shapes[i]={...n,dashed:t});}}),this.notifyListeners(),this.render());}updateUmlClass(t,e){let i=this.shapes.findIndex(h=>h.id===t);if(i===-1)return;let n=this.shapes[i];if(n.type!=="uml-class")return;this.commitHistory();let s=e.className??n.className,r=e.attributes??n.attributes,o=e.methods??n.methods,{width:a,height:l}=ot(s,r,o);this.shapes[i]={...n,className:s,attributes:r,methods:o,width:a,height:l},this.notifyListeners(),this.render();}updateUmlInterface(t,e){let i=this.shapes.findIndex(y=>y.id===t);if(i===-1)return;let n=this.shapes[i];if(n.type!=="uml-interface")return;this.commitHistory();let s=n,r=e.className??s.className,o=e.methods??s.methods,a=$*.6,l=o.map(y=>`${y.visibility} ${y.name}(${y.params}): ${y.returnType}`),h=Math.max(r.length,...l.map(y=>y.length),0),d=Math.max(j,h*a+N*2),c=o.length>0?o.length*L+D*2:D*2,g=Y+c;this.shapes[i]={...n,className:r,methods:o,width:d,height:g},this.notifyListeners(),this.render();}getSelectedUml(){if(this.selectedIds.length!==1)return null;let t=this.shapes.find(e=>e.id===this.selectedIds[0]);return t&&(t.type==="uml-class"||t.type==="uml-interface")?t:null}getUmlById(t){let e=this.shapes.find(i=>i.id===t);return e&&(e.type==="uml-class"||e.type==="uml-interface")?e:null}addUmlAttribute(t){let e=this.shapes.find(n=>n.id===t);if(!e||e.type!=="uml-class")return;let i={id:`attr_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,visibility:"+",name:"newField",type:"string"};this.updateUmlClass(t,{attributes:[...e.attributes,i]});}addUmlMethod(t){let e=this.getUmlById(t);if(!e)return;let i={id:`meth_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,visibility:"+",name:"newMethod",params:"",returnType:"void"};e.type==="uml-class"?this.updateUmlClass(t,{methods:[...e.methods,i]}):this.updateUmlInterface(t,{methods:[...e.methods,i]});}removeUmlAttribute(t,e){let i=this.shapes.find(n=>n.id===t);!i||i.type!=="uml-class"||this.updateUmlClass(t,{attributes:i.attributes.filter(n=>n.id!==e)});}removeUmlMethod(t,e){let i=this.getUmlById(t);if(!i)return;let n=i.methods.filter(s=>s.id!==e);i.type==="uml-class"?this.updateUmlClass(t,{methods:n}):this.updateUmlInterface(t,{methods:n});}reorderUmlAttribute(t,e,i){let n=this.shapes.find(o=>o.id===t);if(!n||n.type!=="uml-class"||e===i)return;let s=[...n.attributes];if(e<0||e>=s.length||i<0||i>=s.length)return;let[r]=s.splice(e,1);s.splice(i,0,r),this.updateUmlClass(t,{attributes:s});}reorderUmlMethod(t,e,i){let n=this.getUmlById(t);if(!n||e===i)return;let s=[...n.methods];if(e<0||e>=s.length||i<0||i>=s.length)return;let[r]=s.splice(e,1);s.splice(i,0,r),n.type==="uml-class"?this.updateUmlClass(t,{methods:s}):this.updateUmlInterface(t,{methods:s});}updateUmlRowStyle(t,e,i,n){let s=this.getUmlById(t);if(s)if(e==="attribute"){if(s.type!=="uml-class")return;let r=s.attributes.map(o=>o.id===i?{...o,textColor:n.textColor===null?void 0:n.textColor??o.textColor,fontSize:n.fontSize===null?void 0:n.fontSize??o.fontSize,strikethrough:n.strikethrough===null?void 0:n.strikethrough??o.strikethrough}:o);this.updateUmlClass(t,{attributes:r});}else {let r=s.methods.map(o=>o.id===i?{...o,textColor:n.textColor===null?void 0:n.textColor??o.textColor,fontSize:n.fontSize===null?void 0:n.fontSize??o.fontSize,strikethrough:n.strikethrough===null?void 0:n.strikethrough??o.strikethrough}:o);s.type==="uml-class"?this.updateUmlClass(t,{methods:r}):this.updateUmlInterface(t,{methods:r});}}getUmlRowAtScreenPoint(t){let e=this.camera.screenToPage(t);for(let i=this.shapes.length-1;i>=0;i--){let n=this.shapes[i];if(n.type!=="uml-class"&&n.type!=="uml-interface")continue;let s=n.width/2,r=n.height/2;if(e.x<n.x-s||e.x>n.x+s||e.y<n.y-r||e.y>n.y+r)continue;let o=Y,a=e.y-n.y;if(a>=-r&&a<=-r+o)return {shape:n,section:"className",rowIndex:-1,text:n.className};if(n.type==="uml-interface"){let y=-r+o,p=r;if(n.methods.length>0&&a>=y&&a<=p){let f=Math.floor((a-y-D)/L);if(f>=0&&f<n.methods.length){let T=n.methods[f];return {shape:n,section:"method",rowIndex:f,text:`${T.visibility} ${T.name}(${T.params}): ${T.returnType}`}}return {shape:n,section:"method",rowIndex:-1,text:""}}return {shape:n,section:"className",rowIndex:-1,text:n.className}}let l=n.attributes.length>0?n.attributes.length*L+D*2:D*2,h=-r+o,d=h+l;if(n.attributes.length>0&&a>=h&&a<=d){let y=Math.floor((a-h-D)/L);if(y>=0&&y<n.attributes.length){let p=n.attributes[y];return {shape:n,section:"attribute",rowIndex:y,text:`${p.visibility} ${p.name}: ${p.type}`}}return {shape:n,section:"attribute",rowIndex:-1,text:""}}let c=d,g=r;if(n.methods.length>0&&a>=c&&a<=g){let y=Math.floor((a-c-D)/L);if(y>=0&&y<n.methods.length){let p=n.methods[y];return {shape:n,section:"method",rowIndex:y,text:`${p.visibility} ${p.name}(${p.params}): ${p.returnType}`}}return {shape:n,section:"method",rowIndex:-1,text:""}}return {shape:n,section:"className",rowIndex:-1,text:n.className}}return null}setActiveTool(t){this.tools.has(t)&&(this.activeTool=t,this.notifyListeners());}getActiveTool(){return this.activeTool}getShapes(){return [...this.shapes]}getSelectedIds(){return [...this.selectedIds]}getCamera(){return this.camera}isPointerDown(){return this.pointerIsDown}zoomIn(){this.camera.zoomAt({x:window.innerWidth/2,y:window.innerHeight/2},1.2),this.scheduleRender(),this.notifyListeners();}zoomOut(){this.camera.zoomAt({x:window.innerWidth/2,y:window.innerHeight/2},.8),this.scheduleRender(),this.notifyListeners();}resetZoom(){this.camera.reset(),this.scheduleRender(),this.notifyListeners();}panBy(t,e){this.camera.translate(t,e),this.scheduleRender(),this.notifyListeners();}setCameraPosition(t,e){this.camera.x=t,this.camera.y=e,this.scheduleRender(),this.notifyListeners();}requestRender(){this.scheduleRender(),this.notifyListeners();}setShowGrid(t){this.renderer.setShowGrid(t),this.scheduleRender();}render(){this.renderer.render(this.shapes,this.camera,new Set(this.selectedIds),this.brushRect,this.snapGuides,this.connectingTargetId,this.genPreview);}scheduleRender(){this.renderScheduled||(this.renderScheduled=true,requestAnimationFrame(()=>{this.renderScheduled=false,this.render();}));}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}subscribeNotifications(t){return this.notificationListeners.add(t),()=>this.notificationListeners.delete(t)}notify(t){this.notificationListeners.forEach(e=>e(t));}notifyListeners(){this.notifyScheduled||(this.notifyScheduled=true,queueMicrotask(()=>{this.notifyScheduled=false,this.listeners.forEach(t=>t());}));}cloneShapes(t){return t.map(e=>{let i={...e,style:{...e.style}};return Array.isArray(e.points)&&(i.points=e.points.map(n=>({...n}))),e.startBinding&&(i.startBinding={...e.startBinding}),e.endBinding&&(i.endBinding={...e.endBinding}),i})}commitHistory(){if(this.isUndoing)return;let t={shapes:this.cloneShapes(this.shapes),selectedIds:[...this.selectedIds]};this.undoStack.push(t),this.undoStack.length>Tt&&this.undoStack.shift(),this.redoStack=[];}mark(){this.commitHistory();}undo(){if(this.undoStack.length!==0){this.isUndoing=true;try{this.redoStack.push({shapes:this.cloneShapes(this.shapes),selectedIds:[...this.selectedIds]});let t=this.undoStack.pop();this.shapes=this.cloneShapes(t.shapes),this.selectedIds=[...t.selectedIds];}finally{this.isUndoing=false;}this.notifyListeners(),this.render();}}redo(){if(this.redoStack.length!==0){this.isUndoing=true;try{this.undoStack.push({shapes:this.cloneShapes(this.shapes),selectedIds:[...this.selectedIds]});let t=this.redoStack.pop();this.shapes=this.cloneShapes(t.shapes),this.selectedIds=[...t.selectedIds];}finally{this.isUndoing=false;}this.notifyListeners(),this.render();}}canUndo(){return this.undoStack.length>0}canRedo(){return this.redoStack.length>0}saveToStorage(){try{let t=JSON.stringify({version:1,shapes:this.shapes,savedAt:Date.now()});return localStorage.setItem(dt,t),this.notifyListeners(),this.notify("\u5DF2\u4FDD\u5B58"),!0}catch(t){return console.error("\u4FDD\u5B58\u5931\u8D25:",t),this.notify("\u4FDD\u5B58\u5931\u8D25"),false}}loadFromStorage(){try{let t=localStorage.getItem(dt);if(!t)return !1;let e=JSON.parse(t);return e.shapes&&Array.isArray(e.shapes)?(this.shapes=e.shapes,this.selectedIds=[],this.undoStack=[],this.redoStack=[],this.notifyListeners(),this.scheduleRender(),!0):!1}catch(t){return console.error("\u52A0\u8F7D\u5931\u8D25:",t),false}}clearStorage(){localStorage.removeItem(dt);}exportJSON(){return JSON.stringify({version:1,shapes:this.shapes,savedAt:Date.now()},null,2)}importJSON(t){try{let e=JSON.parse(t);return e.shapes&&Array.isArray(e.shapes)?(this.commitHistory(),this.shapes=e.shapes,this.selectedIds=[],this.notifyListeners(),this.render(),!0):!1}catch(e){return console.error("\u5BFC\u5165\u5931\u8D25:",e),false}}getEditingShapeId(){return this.editingShapeId}canEditShape(t){return !(t.type==="line"||t.type==="arrow"||t.type==="draw"||t.type==="uml-class"||t.type==="uml-interface")}enterEditing(t){let e=this.shapes.find(i=>i.id===t);!e||!this.canEditShape(e)||(this.editingShapeId=t,this.selectedIds=[t],this.isNewTextShape=e.type==="text"&&!e.text,this.originalTextValue=e.text||"",this.notifyListeners(),this.scheduleRender(),requestAnimationFrame(()=>{this.createTextInput(e);}));}exitEditing(){if(!this.editingShapeId)return;let t=this.editingShapeId,e=this.shapes.find(n=>n.id===t),i=this.textInput?this.textInput.value:"";if(this.removeTextInput(),e)if(e.type==="text")if(this.commitHistory(),!i.trim()&&this.isNewTextShape)this.shapes=this.shapes.filter(n=>n.id!==t),this.selectedIds=this.selectedIds.filter(n=>n!==t);else {let n=e,s=n.fontSize||16,r=s*1.3,o=n.width||200,a=i.split(`
3
+ `),l=0;for(let c of a){let g="";for(let y of c){let p=g+y;p.length*s*.6>o&&g?(l++,g=y):g=p;}g&&l++;}let h={...n,text:i,height:Math.max(l*r,r)},d=this.shapes.indexOf(e);d!==-1&&(this.shapes[d]=h),i.trim()||(this.shapes=this.shapes.filter(c=>c.id!==t),this.selectedIds=this.selectedIds.filter(c=>c!==t));}else this.commitHistory(),this.updateShape(t,{text:i,textColor:e.textColor||G.text,fontSize:e.fontSize||16});this.editingShapeId=null,this.isNewTextShape=false,this.originalTextValue="",this.setActiveTool("select"),this.notifyListeners(),this.render();}removeTextInput(){this.textOverlay&&(this.textOverlay.remove(),this.textOverlay=null),this.textInput&&(this.textInput.remove(),this.textInput=null);}createTextInput(t){this.removeTextInput();let e=this.canvas.parentElement;if(!e)return;let i=this.canvas.getBoundingClientRect(),n=e.getBoundingClientRect(),s=i.left-n.left,r=i.top-n.top;this.textOverlay=document.createElement("div"),this.textOverlay.style.position="absolute",this.textOverlay.style.left=`${s}px`,this.textOverlay.style.top=`${r}px`,this.textOverlay.style.width=`${i.width}px`,this.textOverlay.style.height=`${i.height}px`,this.textOverlay.style.background="transparent",this.textOverlay.style.zIndex="1000",this.textOverlay.style.cursor="text",this.textOverlay.addEventListener("pointerdown",d=>{this.textInput&&d.target!==this.textInput&&(d.preventDefault(),this.exitEditing());}),e.appendChild(this.textOverlay);let o=this.getShapeScreenBounds(t),a=this.camera.getZoom(),l=(t.fontSize||16)*a,h=document.createElement("textarea");h.style.position="absolute",h.style.left=`${s+o.x}px`,h.style.top=`${r+o.y}px`,h.style.width=`${Math.max(o.width,60)}px`,h.style.height=`${Math.max(o.height,24)}px`,h.style.fontSize=`${l}px`,h.style.color=t.textColor||"#1f2937",h.style.textAlign="center",h.style.border=`2px solid ${G.editBorder}`,h.style.borderRadius="4px",h.style.background="rgba(255, 255, 255, 0.95)",h.style.resize="none",h.style.padding="4px 8px",h.style.boxSizing="border-box",h.style.lineHeight="1.4",h.style.fontFamily="sans-serif",h.style.outline="none",h.style.whiteSpace="pre-wrap",h.style.wordBreak="break-word",h.style.overflow="hidden",h.style.zIndex="1001",h.value=t.text||"",h.dataset.shapeId=t.id,h.addEventListener("pointerdown",d=>{d.stopPropagation();}),h.addEventListener("mousedown",d=>{d.stopPropagation();}),h.addEventListener("keydown",d=>{if(d.stopPropagation(),d.key==="Escape"&&(d.preventDefault(),this.textInput&&(this.textInput.value=this.originalTextValue),this.exitEditing()),d.key==="Enter"&&!d.shiftKey&&(d.preventDefault(),this.exitEditing()),d.key==="Enter"&&d.shiftKey){d.preventDefault();let c=h.selectionStart,g=h.selectionEnd;h.value=h.value.substring(0,c)+`
4
+ `+h.value.substring(g),h.selectionStart=h.selectionEnd=c+1;}}),e.appendChild(h),h.focus(),t.type==="text"&&!t.text&&(h.placeholder="\u8F93\u5165\u6587\u672C..."),this.textInput=h;}getShapeScreenBounds(t){let e=this.camera.getZoom(),i=this.camera.pageToScreen({x:t.x,y:t.y});switch(t.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "hexagon":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":return {x:i.x-t.width*e/2,y:i.y-t.height*e/2,width:t.width*e,height:t.height*e};case "circle":return {x:i.x-t.radius*e,y:i.y-t.radius*e,width:t.radius*2*e,height:t.radius*2*e};case "text":{let n=t,s=n.width*e,r=n.height*e;return {x:i.x-s/2,y:i.y-r/2,width:s,height:r}}default:return {x:i.x-50*e,y:i.y-20*e,width:100*e,height:40*e}}}getShapeAtScreenPoint(t){let e=this.screenToPage(t);return this.getShapeAtPoint(e)}getShapeAtPoint(t,e){for(let i=this.shapes.length-1;i>=0;i--){let n=this.shapes[i];if(!(e&&n.id===e)&&this.isPointInShape(t,n))return n}return null}isPointInShape(t,e){let i=t.x-e.x,n=t.y-e.y;switch(e.type){case "rectangle":case "rounded-rectangle":case "ellipse":case "diamond":case "triangle":case "parallelogram":case "trapezoid":case "pentagon":case "octagon":case "star":case "uml-class":case "uml-interface":return Math.abs(i)<=e.width/2&&Math.abs(n)<=e.height/2;case "circle":return i*i+n*n<=e.radius*e.radius;case "hexagon":{let s=e.width/2,r=e.height/2;return Math.abs(i)<=s&&Math.abs(n)<=r&&Math.abs(i)/s+Math.abs(n)/r<=1.5}case "text":{let s=e;return Math.abs(i)<=s.width/2&&Math.abs(n)<=s.height/2}default:return false}}updateArrowBinding(t,e,i,n){let s=this.shapes.find(a=>a.id===t);if(!s||s.type!=="arrow")return;let r=s,o={};if(i){let a=this.shapes.find(c=>c.id===i);if(!a)return;let l=n?.handle||this.getClosestHandle(r,a),h=this.getBindingPosition(a,l),d=n?{...n,toShapeId:i}:{toShapeId:i,handle:l};e==="start"?(o.startBinding=d,o.points=r.points.map((c,g)=>g===0?h:c)):(o.endBinding=d,o.points=r.points.map((c,g)=>g===r.points.length-1?h:c));}else e==="start"?o.startBinding=void 0:o.endBinding=void 0;this.updateShape(t,o);}getClosestHandle(t,e){let i=t.points[0],n=t.points[t.points.length-1],s=t.endBinding?n:i,r=s.x-e.x,o=s.y-e.y;return Math.abs(r)>Math.abs(o)?r>0?"right":"left":o>0?"bottom":"top"}dispose(){this.canvas.removeEventListener("pointerdown",this.handlePointerDown),this.canvas.removeEventListener("pointermove",this.handlePointerMove),this.canvas.removeEventListener("pointerup",this.handlePointerUp),this.canvas.removeEventListener("pointerleave",this.handlePointerUp),this.canvas.removeEventListener("wheel",this.handleWheel),window.removeEventListener("keydown",this.handleKeyDown),window.removeEventListener("keyup",this.handleKeyUp),window.removeEventListener("resize",this.handleResize),this.removeTextInput(),this.tools.forEach(t=>t.dispose()),this.listeners.clear();}};export{nt as ArrowTool,q as Camera,ct as Editor,Z as FreehandTool,tt as HandTool,et as LineTool,K as Mat2d,J as Renderer,Q as SelectTool,R as ShapeTool,G as THEME,$ as UML_FONT_SIZE,Y as UML_HEADER_HEIGHT,L as UML_LINE_HEIGHT,j as UML_MIN_WIDTH,N as UML_PADDING_X,D as UML_PADDING_Y,X as Vec,ot as calcUmlClassSize,gt as getTheme};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "drawui-core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Framework-agnostic canvas drawing engine",
6
+ "license": "MIT",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "sideEffects": false,
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
24
+ }
25
+ },
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "prepublishOnly": "pnpm build",
29
+ "typecheck": "tsc --noEmit",
30
+ "dev": "tsup --watch"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "devDependencies": {
36
+ "tsup": "^8.4.0",
37
+ "typescript": "^5.6.3"
38
+ }
39
+ }