diff --git a/cmd/mdl/svg_e2e_test.go b/cmd/mdl/svg_e2e_test.go index 1fbeaa7f..c1cfbf84 100644 --- a/cmd/mdl/svg_e2e_test.go +++ b/cmd/mdl/svg_e2e_test.go @@ -20,21 +20,30 @@ import ( "github.com/jaschaephraim/lrserver" ) -type nodeOverflow struct { - Name string `json:"name"` - Top float64 `json:"top"` - Right float64 `json:"right"` - Bottom float64 `json:"bottom"` - Left float64 `json:"left"` -} +type ( + nodeOverflow struct { + Name string `json:"name"` + Top float64 `json:"top"` + Right float64 `json:"right"` + Bottom float64 `json:"bottom"` + Left float64 `json:"left"` + } -type verticalEdgeLabelOverlap struct { - Source string `json:"source"` - Destination string `json:"destination"` - LineX float64 `json:"lineX"` - LabelLeft float64 `json:"labelLeft"` - LabelRight float64 `json:"labelRight"` -} + verticalEdgeLabelOverlap struct { + Source string `json:"source"` + Destination string `json:"destination"` + LineX float64 `json:"lineX"` + LabelLeft float64 `json:"labelLeft"` + LabelRight float64 `json:"labelRight"` + } + + exportDimensions struct { + Width float64 `json:"width"` + Height float64 `json:"height"` + ExpectedWidth float64 `json:"expectedWidth"` + ExpectedHeight float64 `json:"expectedHeight"` + } +) func hasChrome() bool { if os.Getenv("CHROME_BIN") != "" { @@ -158,9 +167,16 @@ func TestManualEditAfterAutoLayout(t *testing.T) { await graph.autoLayout(); const node = graph.nodes()[0]; graph.moveNode(node, node.x + 20, node.y, true); - return graph.exportSVG().length; + const markup = graph.exportSVG(); + const exported = new DOMParser().parseFromString(markup, "image/svg+xml").documentElement; + return { + width: Number(exported.getAttribute("width")), + height: Number(exported.getAttribute("height")), + expectedWidth: graph.validatedLayout.bounds.width + 100, + expectedHeight: graph.validatedLayout.bounds.height + 100, + }; })()` - var svgLength int + var dimensions exportDimensions if err := chromedp.Run( testContext, chromedp.Navigate(pageURL), @@ -170,7 +186,7 @@ func TestManualEditAfterAutoLayout(t *testing.T) { ), chromedp.Evaluate( script, - &svgLength, + &dimensions, func(params *cdruntime.EvaluateParams) *cdruntime.EvaluateParams { return params.WithAwaitPromise(true) }, @@ -178,8 +194,14 @@ func TestManualEditAfterAutoLayout(t *testing.T) { ); err != nil { t.Fatalf("auto layout, edit, and export: %v", err) } - if svgLength == 0 { - t.Fatal("manual export is empty") + if dimensions.Width != dimensions.ExpectedWidth || dimensions.Height != dimensions.ExpectedHeight { + t.Fatalf( + "exported dimensions %.1fx%.1f do not match validated dimensions %.1fx%.1f", + dimensions.Width, + dimensions.Height, + dimensions.ExpectedWidth, + dimensions.ExpectedHeight, + ) } } @@ -300,6 +322,9 @@ func TestSVGEdgeLabelsAvoidElements(t *testing.T) { if collisions := inspectEdgeLabelCollisions(t, path); len(collisions) > 0 { t.Fatalf("relationship labels overlap diagram elements: %v", collisions) } + if overflows := inspectEdgeLabelCanvasOverflows(t, path); len(overflows) > 0 { + t.Fatalf("relationship labels exceed the exported SVG canvas: %v", overflows) + } } func inspectNodeTextFit(t *testing.T, path string) ([]nodeOverflow, float64, []string, int) { @@ -416,6 +441,40 @@ func inspectEdgeLabelCollisions(t *testing.T, path string) []string { return collisions } +// inspectEdgeLabelCanvasOverflows returns labels clipped by the exported SVG. +func inspectEdgeLabelCanvasOverflows(t *testing.T, path string) []string { + t.Helper() + testContext, cleanup := newChromeContext(t) + defer cleanup() + + var overflows []string + fileURL := (&url.URL{Scheme: "file", Path: path}).String() + const overflowScript = `(() => { + const tolerance = 0.5; + const canvas = document.querySelector("svg#graph").getBoundingClientRect(); + return [...document.querySelectorAll("g.edge")].flatMap((edge) => { + const label = edge.querySelector(":scope > rect"); + if (!label) return []; + const rect = label.getBoundingClientRect(); + if (rect.left >= canvas.left - tolerance && + rect.top >= canvas.top - tolerance && + rect.right <= canvas.right + tolerance && + rect.bottom <= canvas.bottom + tolerance) { + return []; + } + return [edge.querySelector(":scope > text")?.textContent.trim() || edge.id]; + }); + })()` + if err := chromedp.Run(testContext, + chromedp.Navigate(fileURL), + chromedp.WaitVisible("g.edge", chromedp.ByQuery), + chromedp.Evaluate(overflowScript, &overflows), + ); err != nil { + t.Fatalf("inspect relationship label canvas bounds: %v", err) + } + return overflows +} + func inspectNodeOrientation(t *testing.T, path string) string { t.Helper() testContext, cleanup := newChromeContext(t) diff --git a/cmd/mdl/webapp/dist/286.js b/cmd/mdl/webapp/dist/286.js index 27095713..067ee3f1 100644 --- a/cmd/mdl/webapp/dist/286.js +++ b/cmd/mdl/webapp/dist/286.js @@ -1,2 +1,2 @@ -"use strict";(self.webpackChunkapp=self.webpackChunkapp||[]).push([[286],{247(t,e,n){n.d(e,["AV",0,(t,e)=>{Object.keys(e).forEach(n=>{const o=e[n];"number"==typeof o?t.style.setProperty(n,o.toString()):t.style.setProperty(n,o)})},"M5",0,{nodeBorder:{fill:"rgba(255, 255, 255, 0.86)",stroke:"#aaa",filter:"url(#shadow)"},nodeText:{"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},edgeText:{"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},edgeRect:{fill:"none",stroke:"none"},groupRect:{fill:"rgba(0, 0, 0, 0.02)",stroke:"#666","stroke-width":3,"stroke-dasharray":4},groupText:{fill:"#666","font-size":22,"font-weight":"500","font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",cursor:"default"}},"Y$",0,{thickness:3,color:"#999",opacity:1,fontSize:22,dashed:!0},"gd",0,(t,e)=>Math.sqrt((e.x-t.x)*(e.x-t.x)+(e.y-t.y)*(e.y-t.y)),"nD",0,{width:280,height:180,background:"rgba(255, 255, 255, .9)",color:"#666",opacity:.9,stroke:"#999",fontSize:22,shape:"Box"}])},977(t,e,n){n.d(e,{IW:()=>r,Zr:()=>s,l:()=>i});var o=n(247);function i(t){const e=t.from,n=t.to;let o=t.vertices?t.vertices.concat():[];o.unshift(e),o.push(n);const i=o.slice(1,-1),r=i.find(t=>!t.label)??n;let s=e;for(let t=i.length-1;t>=0;t--)if(!i[t].label){s=i[t];break}if(!e.intersect||!n.intersect)throw new Error(`edge ${t.id} nodes must be drawn before routing`);return o[0]=e.intersect(r),o[o.length-1]=n.intersect(s),o}function r(t,e,n){let i,r={x:n.x,y:n.y};const s=t.findIndex(t=>t.label);let a=!0;if(s>=0){const e=t[s];r=e,a=!0===e.auto;const n=[];s>0&&n.push({p:t[s-1],q:r}),st?(0,o.gd)(e.p,e.q)>(0,o.gd)(t.p,t.q)?e:t:e,void 0)}else{const n=t.slice(1).reduce((e,n,i)=>e+(0,o.gd)(t[i],n),0)*e;let s=0;for(let e=1;e0&&s+d>=n){const t=(n-s)/d;r={x:a.p.x+(a.q.x-a.p.x)*t,y:a.p.y+(a.q.y-a.p.y)*t},i=a;break}s+=d}}const d=i?Math.abs(i.q.x-i.p.x):0,l=i?Math.abs(i.q.y-i.p.y):0;return{...r,orientation:l>d?"vertical":"horizontal",segment:i,movable:a}}function s(t){if(t.length<2)throw new Error("edge route must contain at least two points");return t.slice(1).reduce((t,e)=>`${t} L${e.x},${e.y}`,`M${t[0].x},${t[0].y}`)}},20(t,e,n){n.d(e,{r:()=>i}),Symbol("ValidatedLayout");class o extends Error{issues;constructor(t){super(t.map(t=>t.message).join("\n")),this.name="GeometryValidationError",this.issues=t}}function i(t,e){const n=[];t.id!==e.diagramId&&n.push(C("missing_element",[t.id,e.diagramId],`layout ${e.diagramId} does not resolve measured diagram ${t.id}`)),r("measured node",t.nodes.map(t=>t.id),n),r("measured group",t.groups.map(t=>t.id),n),r("measured edge",t.edges.map(t=>t.id),n),r("resolved node",e.nodes.map(t=>t.id),n),r("resolved group",e.groups.map(t=>t.id),n),r("resolved edge",e.edges.map(t=>t.id),n),s("node",t.nodes.map(t=>t.id),e.nodes.map(t=>t.id),n),s("group",t.groups.map(t=>t.id),e.groups.map(t=>t.id),n),s("edge",t.edges.map(t=>t.id),e.edges.map(t=>t.id),n);const i=new Map(t.nodes.map(t=>[t.id,t])),b=new Map(t.groups.map(t=>[t.id,t])),y=new Map(e.nodes.map(t=>[t.id,t])),v=new Map(e.groups.map(t=>[t.id,t]));a("diagram",e.diagramId,e.bounds,n);for(const t of e.nodes){a("node",t.id,t.bounds,n);const o=i.get(t.id);!o||x(o.size.width,t.bounds.width)&&x(o.size.height,t.bounds.height)||n.push(C("size_mismatch",[t.id],`node ${t.id} size changed after measurement`)),o&&(o.contentSize.width>t.bounds.width||o.contentSize.height>t.bounds.height)&&n.push(C("content_overflow",[t.id],`node ${t.id} content exceeds its resolved bounds`)),d("node",t.id,t.bounds,e.bounds,n)}for(const t of e.groups){a("group",t.id,t.bounds,n),a("group title",t.id,t.titleBounds,n),d("group",t.id,t.bounds,e.bounds,n),g(t.bounds,t.titleBounds)||n.push(C("false_containment",[t.id],`group ${t.id} does not contain its title`));const o=b.get(t.id);!o||x(o.titleSize.width,t.titleBounds.width)&&x(o.titleSize.height,t.titleBounds.height)||n.push(C("size_mismatch",[t.id],`group ${t.id} title size changed after measurement`))}if(function(t,e){w(t,(t,n)=>{A(t.bounds,n.bounds)&&e.push(C("node_overlap",[t.id,n.id],`nodes ${t.id} and ${n.id} overlap`))})}(e.nodes,n),function(t,e){w(t,(t,n)=>{!A(t.bounds,n.bounds)||g(t.bounds,n.bounds)||g(n.bounds,t.bounds)||e.push(C("group_overlap",[t.id,n.id],`groups ${t.id} and ${n.id} partially overlap`))})}(e.groups,n),function(t,e,n,o,i){for(const e of t){const t=n.get(e.id);if(t&&t.parentId!==e.parentId&&i.push(C("false_containment",[e.id],`node ${e.id} has the wrong parent ID`)),!e.parentId)continue;const r=o.get(e.parentId);t&&r&&!g(r.bounds,t.bounds)&&i.push(C("false_containment",[e.id,e.parentId],`node ${e.id} lies outside owning group ${e.parentId}`))}for(const t of e){const e=o.get(t.id);if(e&&e.parentId!==t.parentId&&i.push(C("false_containment",[t.id],`group ${t.id} has the wrong parent ID`)),!t.parentId)continue;const n=o.get(t.parentId);e&&n&&!g(n.bounds,e.bounds)&&i.push(C("false_containment",[t.id,t.parentId],`group ${t.id} lies outside owning group ${t.parentId}`))}}(t.nodes,t.groups,y,v,n),function(t,e,n,o,i,r,s){const g=new Map(t.edges.map(t=>[t.id,t])),b=[];for(const t of e.edges){const y=g.get(t.id);if(y)if(t.sourceId===y.sourceId&&t.targetId===y.targetId||s.push(C("disconnected_edge",[t.id,t.sourceId,t.targetId],`edge ${t.id} endpoints do not match its measured contract`)),0===t.sections.length||t.sections.some(t=>t.length<2))s.push(C("disconnected_edge",[t.id],`edge ${t.id} has no complete route section`));else{l(t,n,s);for(const n of t.sections)for(const o of n)if(!m(o)||!p(o,e.bounds,!0)){s.push(C("out_of_bounds",[t.id],`edge ${t.id} has a route point outside the resolved view`));break}for(const e of n.values())e.id!==t.sourceId&&e.id!==t.targetId&&u(t.sections,f(e.bounds,.5))&&s.push(C("edge_through_node",[t.id,e.id],`edge ${t.id} crosses unrelated node ${e.id}`));for(const e of o.values()){const n=c(t.sourceId,e.id,i,r);n===c(t.targetId,e.id,i,r)&&h(t.sections,e.bounds,n)&&s.push(C("edge_through_group",[t.id,e.id],`edge ${t.id} violates group ${e.id} boundary`))}if(t.labelBounds){a("edge label",t.id,t.labelBounds,s),d("edge label",t.id,t.labelBounds,e.bounds,s),!y.labelSize||x(y.labelSize.width,t.labelBounds.width)&&x(y.labelSize.height,t.labelBounds.height)||s.push(C("size_mismatch",[t.id],`edge ${t.id} label size changed after measurement`));for(const e of n.values())A(t.labelBounds,e.bounds)&&s.push(C("label_node_overlap",[t.id,e.id],`edge ${t.id} label overlaps node ${e.id}`));for(const e of o.values())A(t.labelBounds,e.titleBounds)&&s.push(C("label_title_overlap",[t.id,e.id],`edge ${t.id} label overlaps group ${e.id} title`));b.push({id:t.id,bounds:t.labelBounds})}else y.labelSize&&s.push(C("missing_element",[t.id],`edge ${t.id} is missing its measured label`))}}w(b,(t,e)=>{A(t.bounds,e.bounds)&&s.push(C("label_label_overlap",[t.id,e.id],`edge labels ${t.id} and ${e.id} overlap`))});for(const t of b)for(const n of e.edges)u(n.sections,f(t.bounds,.5))&&s.push(C("label_edge_overlap",[t.id,n.id],`edge ${n.id} crosses label ${t.id}`))}(t,e,y,v,i,b,n),n.length>0)throw new o(n);return e}function r(t,e,n){const o=new Set;for(const i of e)o.has(i)&&n.push(C("duplicate_id",[i],`${t} ${i} is duplicated`)),o.add(i)}function s(t,e,n,o){const i=new Set(e),r=new Set(n);for(const e of i)r.has(e)||o.push(C("missing_element",[e],`${t} ${e} is missing from resolved layout`));for(const e of r)i.has(e)||o.push(C("unexpected_element",[e],`${t} ${e} is not in the measured diagram`))}function a(t,e,n,o){(!function(t){return m(t)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}(n)||n.width<0||n.height<0)&&o.push(C("non_finite",[e],`${t} ${e} has invalid geometry`))}function d(t,e,n,o,i){g(o,n)||i.push(C("out_of_bounds",[e],`${t} ${e} lies outside the resolved view bounds`))}function l(t,e,n){for(let e=1;et.some((n,o)=>o>0&&function(t,e,n){const o=e.x-t.x,i=e.y-t.y;let r=0,s=1;for(const[e,a,d,l]of[[t.x,o,n.x,n.x+n.width],[t.y,i,n.y,n.y+n.height]]){if(Math.abs(a)=l)return!1;continue}const t=(d-e)/a,n=(l-e)/a;if(r=Math.max(r,Math.min(t,n)),s=Math.min(s,Math.max(t,n)),r>=s)return!1}return s>0&&r<1}(t[o-1],n,e)))}function A(t,e){return t.x=e.x+e.width&&t.y+t.height>=e.y+e.height}function p(t,e,n){return n?t.x>=e.x&&t.y>=e.y&&t.x<=e.x+e.width&&t.y<=e.y+e.height:t.x>e.x&&t.y>e.y&&t.x=e.x-.1&&t.x<=e.x+e.width+.1,o=t.y>=e.y-.1&&t.y<=e.y+e.height+.1,i=x(t.x,e.x)||x(t.x,e.x+e.width),r=x(t.y,e.y)||x(t.y,e.y+e.height);return o&&i||n&&r}function x(t,e){return Math.abs(t-e)<=.1}function w(t,e){for(let n=0;nM,Qy:()=>U,ZG:()=>N,oP:()=>F,IX:()=>O,F_:()=>et,Kp:()=>tt,a_:()=>H});const o=new Map;let i;function r(t,e){const n=function(t,e){const n=Object.entries(e).sort(([t],[e])=>t.localeCompare(e));return JSON.stringify([t,n])}(t,e),r=o.get(n);if(r)return r;const s=document.createElementNS("http://www.w3.org/2000/svg","text");s.setAttribute("x","0"),s.setAttribute("y","0");for(const[t,n]of Object.entries(e))s.setAttribute(t,n);s.textContent=t,(i?.isConnected||(i=document.createElementNS("http://www.w3.org/2000/svg","svg"),i.setAttribute("aria-hidden","true"),i.style.position="absolute",i.style.visibility="hidden",i.style.pointerEvents="none",document.body.appendChild(i)),i).appendChild(s);const a=s.getBBox();s.remove();const d={width:a.width,height:a.height};return o.set(n,d),d}function s(t,e,n){let o=0;return{lines:t.trim().split("\n").flatMap(t=>{const i=[];let s=[];for(const d of t.trim().split(/\s+/)){if(r(d,n).width>e){s.length>0&&(i.push(s.join(" ")),s=[]);const t=a(d,e,n);for(const e of t.slice(0,-1))i.push(e),o=Math.max(o,r(e,n).width);s=t.slice(-1);continue}const t=[...s,d],l=r(t.join(" "),n);l.width>e&&s.length>0?(i.push(s.join(" ")),s=[d]):(o=Math.max(o,l.width),s=t)}if(s.length>0){const t=s.join(" ");i.push(t),o=Math.max(o,r(t,n).width)}return i}),maxW:o}}function a(t,e,n){const o=[];let i="";for(const s of t){const t=i+s;r(t,n).width>e&&i.length>0?(o.push(i),i=s):i=t}return i.length>0&&o.push(i),o}const d={element(t,e={},n){const o=document.createElementNS("http://www.w3.org/2000/svg",t);return Object.entries(e).forEach(([t,e])=>o.setAttribute(t,String(e))),n&&o.classList.add(n),o},use(t,e={}){const n=this.element("use",e);return n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+t),n},path(t,e={},n){return this.element("path",{...e,d:t},n)},text(t,e={}){const n=this.element("text",e);return t&&(n.textContent=t),n},textArea(t,e,n,o,i=0,r=0,a=""){const d={"font-size":`${n}px`,"font-weight":o?"bold":"normal"},{lines:l,maxW:c}=s(t,e,d),h=this.text("",{x:0,y:r,"text-anchor":a||void 0});return l.forEach((t,e)=>{const o=this.element("tspan",{x:i,dy:`${n+2}px`,...d});o.textContent=t,h.append(o)}),{txt:h,dy:(l.length+1)*(n+2),maxW:c}},rect(t,e,n=0,o=0,i=0,r){return this.element("rect",{x:n,y:o,rx:i,ry:i,width:t,height:e},r)},icon(t,e=0,n=0){return this.use(t,{x:e,y:n})},expand(t,e,n){const o=this.element("g",{transform:`translate(${t},${e})`},"expand");return o.append(this.rect(19,19,0,0,1),this.text(n?"-":"+",{x:10,y:14,"text-anchor":"middle"})),o}};function l(t,e,n){t.setAttribute("transform",`translate(${e},${n})`)}function c(t,e,n=!0){return n?t.x>e.x-e.width/2&&t.xe.y-e.height/2&&t.ye.x&&t.xe.y&&t.yfunction(t,e,n,o){let i,r,s,a,d,l={x:null,y:null,onLine1:!1,onLine2:!1};return i=(o.y-n.y)*(e.x-t.x)-(o.x-n.x)*(e.y-t.y),0==i||(r=t.y-n.y,s=t.x-n.x,a=(o.x-n.x)*r-(o.y-n.y)*s,d=(e.x-t.x)*r-(e.y-t.y)*s,r=a/i,s=d/i,l.x=t.x+r*(e.x-t.x),l.y=t.y+r*(e.y-t.y),r>0&&r<1&&(l.onLine1=!0),s>=0&&s<=1&&(l.onLine2=!0)),l}(t,e,n.p,n.q)).filter(t=>t.onLine1&&t.onLine2)}(t,e,t)[0]||{x:t.x,y:t.y}}function u(t,e,n,o,i){const r={x:i.x-t.x,y:i.y-t.y},s=o.x-t.x,a=o.y-t.y;s==r.x&&(r.x+=1e-7);const d=(a-r.y)/(s-r.x),l=a-d*s,c=n*n+e*e*d*d,h=2*e*e*l*d,u=e*e*l*l-e*e*n*n,A=Math.sqrt(h*h-4*c*u),g=r.x>s?(-h+A)/(2*c):(-h-A)/(2*c),p={x:g,y:d*g+l};return p.x+=t.x,p.y+=t.y,p}function A(t,e,n){let o=n.x-e.x,i=n.y-e.y,r=o*o+i*i,s=(t.x-e.x)*o+(t.y-e.y)*i,a=Math.min(1,Math.max(0,s/r));return{x:e.x+o*a,y:e.y+i*a}}function g(t,e){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function p(t){return t/2/(5.5+t/70)}function f(t,e,n){switch(t.toLowerCase()){case"cylinder":return 2*p(e);case"person":return.4*n;case"folder":return e/10;case"robot":return.35*n;case"webbrowser":return n/8;default:return 0}}class m{_el;constructor(t){this._el=t}node(){return this._el}attr(t,e){return this._el.setAttribute(t,String(e)),this}insert(t,e){const n=document.createElementNS("http://www.w3.org/2000/svg",t),o=this._el.insertBefore(n,this._el.querySelector(e));return new m(o)}}function b(t,e,n,o=!1){const i=t.insert("rect",":first-child").attr("rx",o?n.width/8:3).attr("ry",o?n.width/8:3).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return h(n,t)},i}function y(t,e,n,o,i){const r=t.insert("ellipse",":first-child").attr("cx",0).attr("cy",0).attr("rx",o).attr("ry",i).attr("width",n.width).attr("height",n.height);return n.intersect=function(t){return u(n,o,i,n,t)},r}function x(t,e,n){const o=n.width/8,i=n.width/14,r=t.insert("g",":first-child");return r.insert("path",":first-child").attr("d",`M${-n.width/2},${-n.height/2} l${n.width},0 M${-n.width/2},${n.height/2} l${n.width},0`),r.insert("circle",":first-child").attr("cx",0).attr("cy",n.height/2+o/2).attr("r",.4*i),r.insert("rect",":first-child").attr("x",-i).attr("y",-n.height/2-o/2-.2*i).attr("width",2*i).attr("height",.4*i),r.insert("rect",":first-child").attr("rx",i).attr("ry",i).attr("x",-e.width/2).attr("y",-e.height/2-o).attr("width",e.width).attr("height",e.height+2*o),n.intersect=function(t){return h({x:n.x,y:n.y,width:n.width,height:n.height+2*o},t)},r}const w={box:(t,e)=>b(new m(t),e,e).node(),roundedbox:(t,e)=>b(new m(t),e,e,!0).node(),component:(t,e)=>function(t,e,n){const o=n.width/10,i=t.insert("g",":first-child");return i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2-o).attr("y",-n.height/2+o).attr("width",2*o).attr("height",o),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2-o).attr("y",-n.height/2+2.5*o).attr("width",2*o).attr("height",o),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2).attr("width",n.width).attr("height",n.height),n.intersect=function(t){return h({x:n.x-o/2,y:n.y,width:n.width+o,height:n.height},t)},i}(new m(t),0,e).node(),cylinder:(t,e)=>function(t,e,n){const o=e.width,i=o/2,r=p(o),s=e.height,a=`M 0,${r} a${i},${r} 0,0,0 ${o} 0 a ${i},${r} 0,0,0 ${-o} 0 l 0,${s-2*r} a ${i},${r} 0,0,0 ${o} 0 l 0,${2*r-s}`,d=t.attr("label-offset-y",f("cylinder",o,s)).insert("path",":first-child").attr("d",a).attr("transform","translate("+-o/2+","+-s/2+")");return n.intersect=function(t){const e=h(n,t);let o=n.y+n.height/2-r;return e.y>o?u({x:n.x,y:o},i,r,n,t):(o=n.y-n.height/2+r,e.yfunction(t,e,n){const o=e.width,i=e.height,r=`M ${.38*o},${i/3} A${o/2},${i/2} 0,0,0 0 ${i/2}\n\t\tL${o/11},${i} L${o-o/11},${i} L${o},${i/2}\n\t\tA${o/2},${i/2} 0,0,0 ${o-.38*o} ${i/3} \n\t\tA${o/6},${o/6} 0,1,0 ${.38*o} ${i/3}`,s=t.attr("label-offset-y",f("person",o,i)).insert("path",":first-child").attr("d",r).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(t){return h(n,t)},s}(new m(t),e,e).node(),circle:(t,e)=>function(t,e,n){return y(t,0,n,n.width/2,n.width/2)}(new m(t),0,e).node(),ellipse:(t,e)=>function(t,e,n){return y(t,0,n,.55*n.width,.45*n.width)}(new m(t),0,e).node(),hexagon:(t,e)=>function(t,e,n){const o=n.width/2,i=t.insert("polygon",":first-child").attr("points",[.5,.866,1,0,.5,-.866,-.5,-.866,-1,-0,-.5,.866,.5,.866].map(t=>t*o).join(",")).attr("width",n.width).attr("height",n.height);return n.intersect=function(t){return u(n,n.width/2,n.width/2,n,t)},i}(new m(t),0,e).node(),folder:(t,e)=>function(t,e,n){const o=n.width/20,i=t.attr("label-offset-y",f("folder",n.width,n.height)).insert("g",":first-child");return i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2+2*o).attr("width",n.width).attr("height",n.height-2*o),i.insert("path",":first-child").attr("d",`M0,${-n.height/2+2*o} l${o},${-2*o} h${n.width/2-2*o} v${2*o}`),n.intersect=function(t){return h({x:n.x,y:n.y+o/2,width:n.width,height:n.height+o},t)},i}(new m(t),0,e).node(),mobiledevicelandscape:(t,e)=>function(t,e,n){const o=n.width/8,i=n.width/14,r=t.insert("g",":first-child");return r.insert("path",":first-child").attr("d",`M${-n.width/2},${-n.height/2} l0,${n.height} M${n.width/2},${-n.height/2} l0,${n.height}`),r.insert("circle",":first-child").attr("cx",-n.width/2-o/2).attr("cy",0).attr("r",.4*i),r.insert("rect",":first-child").attr("x",n.width/2+o/2-.2*i).attr("y",-i).attr("width",.4*i).attr("height",2*i),r.insert("rect",":first-child").attr("rx",i).attr("ry",i).attr("x",-e.width/2-o).attr("y",-e.height/2).attr("width",e.width+2*o).attr("height",e.height),n.intersect=function(t){return h({x:n.x,y:n.y,width:n.width+2*o,height:n.height},t)},r}(new m(t),e,e).node(),mobiledeviceportrait:(t,e)=>x(new m(t),e,e).node(),mobiledevice:(t,e)=>x(new m(t),e,e).node(),pipe:(t,e)=>function(t,e,n){const o=n.width,i=n.height,r=i/2,s=r/(2.5+o/70),a=`M${-s},0\n\t\ta${s},${r} 0,0,1 0,${i}\n\t\ta${s},${r} 0,0,1 0,${-i}\n\t\tl${o},0\n\t\ta${s},${r} 0,0,1 0,${i}\n\t\tl${-o},0`,d=t.insert("path",":first-child").attr("d",a).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(t){return h({x:n.x-s,y:n.y,width:n.width+2*s,height:n.height},t)},d}(new m(t),0,e).node(),robot:(t,e)=>function(t,e,n){const o=n.width,i=n.height,r=Math.min(.28*o,.25*i),s=.2*r,a=.25*r,d=.08*r,l=.12*r,c=.22*r,u=.12*r,A=.3*r,g=o,p=-i/2+a+r,m=i-a-r,b=t.attr("label-offset-y",f("robot",o,i)).insert("g",":first-child");b.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-g/2).attr("y",p).attr("width",g).attr("height",m);const y=-i/2+a;b.insert("rect",":first-child").attr("rx",s).attr("ry",s).attr("x",-r/2).attr("y",y).attr("width",r).attr("height",r),b.insert("line",":first-child").attr("class","robot-antenna").attr("x1",0).attr("y1",y).attr("x2",0).attr("y2",-i/2+2*d).attr("stroke-width",.6*d).attr("stroke-linecap","round"),b.insert("circle",":first-child").attr("class","robot-antenna-ball").attr("cx",0).attr("cy",-i/2+2*d).attr("r",1.2*d);const x=y+.4*r;b.insert("circle",":first-child").attr("class","robot-eye").attr("cx",-c).attr("cy",x).attr("r",l),b.insert("circle",":first-child").attr("class","robot-eye").attr("cx",c).attr("cy",x).attr("r",l);const w=y+.7*r,C=.28*r;return b.insert("path",":first-child").attr("class","robot-mouth").attr("d",`M${-C/2},${w} Q0,${w+.3*C} ${C/2},${w}`).attr("fill","none").attr("stroke-width",.5*d).attr("stroke-linecap","round"),b.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",-r/2-u-1).attr("y",x-A/2).attr("width",u).attr("height",A),b.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",r/2+1).attr("y",x-A/2).attr("width",u).attr("height",A),n.intersect=function(t){return h(n,t)},b}(new m(t),0,e).node(),webbrowser:(t,e)=>function(t,e,n){const o=n.height/8,i=t.attr("label-offset-y",f("webbrowser",n.width,n.height)).insert("g",":first-child");return i.insert("path",":first-child").attr("d",`\n\t\t\tM${-n.width/2},${-n.height/2+o} h${n.width}\n\t\t\tM${-n.width/2+o/4},${-n.height/2+o/4} h${o/2} v${o/2} h${-o/2} z\n\t\t\tM${-n.width/2+o},${-n.height/2+o/4} h${n.width-o-o/4} v${o/2} h${-n.width+o+o/4} z\n\t\t`),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2).attr("width",n.width).attr("height",n.height),n.intersect=function(t){return h(n,t)},i}(new m(t),0,e).node()};var C=n(983);class v{versions=[];pos=0;lastSavedPos=0;exportDoc;importDoc;change;tmpPreviousState=null;constructor(t,e,n){this.exportDoc=e,this.importDoc=n,this.change=function(t){let e;return function(){const n=this;clearTimeout(e),e=setTimeout(function(){e=null,t.apply(n)},300)}}(()=>this.saveNow())}beforeChange(){this.tmpPreviousState||(this.tmpPreviousState=this.deepClone(this.exportDoc()))}length(){return this.versions.length}currentState(){return this.deepClone(this.versions[this.pos-1])}saveNow(){if(!this.tmpPreviousState)throw Error("undo.change() was called without previously calling undo.beforeChange()!");this.versions[this.pos]=this.deepClone(this.exportDoc()),this.versions[this.pos-1]=this.tmpPreviousState,this.tmpPreviousState=null,this.pos+=1,this.versions.splice(this.pos)}deepClone(t){return"undefined"!=typeof structuredClone?structuredClone(t):JSON.parse(JSON.stringify(t))}undo(){if(this.pos<2)return;this.pos-=1;const t=this.versions[this.pos-1];this.importDoc(this.deepClone(t))}redo(){if(this.pos>this.versions.length-1)return;const t=this.versions[this.pos];this.importDoc(this.deepClone(t)),this.pos+=1}changed(){return this.pos!==this.lastSavedPos}setSaved(){this.lastSavedPos=this.pos}}var B=n(264),I=n(247),k=n(977);function E(t,e,n,o,i,r){const a=s(t,e,{"font-family":String(I.M5.nodeText["font-family"]),"font-size":`${n}px`,"font-weight":o?"bold":"normal"});return{lines:a.lines.length>0?a.lines:[""],fontSize:n,lineHeight:n+2,bold:o,field:r,gapAfter:i}}n(20);const S=I.Y$,$=I.nD;class M{id;name;nodesMap;edges;edgeVertices;groupsMap;metadata;layoutDirection;validatedLayout;colorToVarMap=new Map;_undo;_gridVisible=!1;_snapToGrid=!0;_gridSize=25;_skipAutoFit=!1;constructor(t,e){this.id=t,this.name=e,this.edges=[],this.edgeVertices=new Map,this.nodesMap=new Map,this.groupsMap=new Map,this._undo=new v(this.id,()=>this.exportLayout(!0),t=>this.importLayout(t,!0)),window.graph=this}init(t){t&&this.importLayout(t),this._undo=new v(this.id,()=>this.exportLayout(!0),t=>this.importLayout(t,!0)),this._undo.length()&&this.importLayout(this._undo.currentState()),this._undo.beforeChange(),this._undo.change()}addNode(t,e,n,o,i,r){if(this.nodesMap.has(t))throw Error("duplicate node: "+t);const s={...$,...i},a=(s.shape||"Box").toLowerCase(),d="person"===a?240:180,l=Math.max(280,s.width||0),c=function(t,e,n,o,i){const r=Math.max(o-36,80),s=[E(t,r,i,!0,6,"name"),E(`[${e}]`,r,.75*i,!1,10),E(n,r,Math.min(.8*i,16),!1,0,"description")],a=s.reduce((t,e)=>t+e.lines.length*e.lineHeight+e.gapAfter,0);return{blocks:s,textHeight:a,minimumHeight:a+36}}(e,n,o,l,s.fontSize||22);let h=Math.max(d,s.height||0,c.minimumHeight);for(let t=0;t<20;t++){const t=c.minimumHeight+Math.abs(f(a,l,h));if(t<=h+.1)break;h=t}const u={id:t,title:e,sub:n,description:o,style:s,x:0,y:0,width:l,height:h,intersect:null,link:r,contentLayout:c};this.nodesMap.set(u.id,u)}nodes(){return Array.from(this.nodesMap.values())}addEdge(t,e,n,o,i,r){i&&i.forEach((e,n)=>{const o=e;o.id=`v-${t}-${n}`,this.edgeVertices.set(o.id,o)});const s={id:t,from:this.nodesMap.get(e),to:this.nodesMap.get(n),label:o,vertices:null,style:{...S,...r},initVertex:t=>{const e=t;return e.id||(e.id=((t,e)=>`v-${t}-a-${(t=>{let e=2166136261;for(let n=0;n>>0).toString(36)})(`${t}:${e.x}:${e.y}`)}`)(s.id,t),this.edgeVertices.set(e.id,e)),e.edge=s,t},userDeletedVertices:!1};this.edges.push(s),i&&(s.vertices=i.map(t=>s.initVertex(t)))}addGroup(t,e,n,o){if(this.groupsMap.has(t))return void console.error(`Group exists: ${t} ${e}`);const i={id:t,name:e,x:null,y:null,width:null,height:null,nodes:n.map(n=>{const o=this.nodesMap.get(n)||this.groupsMap.get(n);return o||console.error(`Node or group ${n} not found for group ${t} "${e}"`),o}).filter(Boolean),style:o};this.groupsMap.set(t,i)}setNodeSelected(t,e){t.selected=e,e?t.ref.classList.add("selected"):t.ref.classList.remove("selected"),this.updateEdgesSel()}updateEdgesSel(){this.edges.forEach(t=>{t.to.selected||t.from.selected?t.ref.classList.add("selected"):t.ref.classList.remove("selected")})}clearResolvedLayout(){this.validatedLayout=void 0,this.edges.forEach(t=>{t.layoutSections=void 0,t.layoutLabelBounds=void 0}),this.groupsMap.forEach(t=>{t.layoutBounds=void 0,t.layoutTitleBounds=void 0})}moveNode(t,e,n,o=!1,i=!1){if(t){if(this._snapToGrid&&!o){const t=this.snapToGrid(e,n);e=t.x,n=t.y}t.x==e&&t.y==n||(i||this._undo.beforeChange(),this.clearResolvedLayout(),t.x=e,t.y=n,l(t.ref,e,n),this.redrawEdges(t),this.redrawGroups(t),i||this._undo.change())}}moveEdgeVertex(t,e,n,o=!1,i=!1){if(this._snapToGrid&&!o){const t=this.snapToGrid(e,n);e=t.x,n=t.y}t.x==e&&t.y==n||(i||this._undo.beforeChange(),this.clearResolvedLayout(),t.x=e,t.y=n,this.redrawEdge(t.edge),i||this._undo.change())}moveSelected(t,e,n=!1){this.nodes().forEach(o=>o.selected&&this.moveNode(o,o.x+t,o.y+e,n,!1)),this.edgeVertices.forEach(o=>o.selected&&this.moveEdgeVertex(o,o.x+t,o.y+e,n,!1))}insertEdgeVertex(t,e,n,o){this._undo.beforeChange(),this.clearResolvedLayout();const i=t.initVertex(e);i.selected=!0,o&&(t.vertices.forEach(t=>t.label=!1),i.label=!0),t.vertices.splice(n-1,0,i),this.redrawEdge(t),this._undo.change()}deleteEdgeVertex(t){this._undo.beforeChange(),this.clearResolvedLayout();const e=t.edge.vertices.indexOf(t);e>=0&&(t.edge.vertices.splice(e,1),this.edgeVertices.delete(t.id),t.edge.userDeletedVertices=!0),this.redrawEdge(t.edge),this._undo.change()}changed(){return this._undo.changed()}undo(){this._undo.undo()}redo(){this._undo.redo()}alignTopLeft(){const t=this.calculateContentBounds(),e=100-t.x,n=100-t.y;this._skipAutoFit=!0,this._undo.beforeChange(),this.nodesMap.forEach(t=>{this.moveNode(t,t.x+e,t.y+n,!0,!0)}),this.edgeVertices.forEach(t=>{this.moveEdgeVertex(t,t.x+e,t.y+n,!0,!0)}),this._undo.change()}resetPanTransform(){const t=O(),e=z.querySelector("g.zoom");e&&(e.setAttribute("transform",`scale(${t}) translate(0, 0)`),function(t){const e=t.calculateContentBounds(),n=O(),o=Math.max(z.parentElement.clientWidth/n,e.x+e.width+20),i=Math.max(z.parentElement.clientHeight/n,e.y+e.height+20);z.setAttribute("width",String(o*n)),z.setAttribute("height",String(i*n))}(this)),nt(this.id),this._skipAutoFit=!1}shouldSkipAutoFit(){return this._skipAutoFit}resetView(){const t=z.querySelector("g.zoom");t&&(t.setAttribute("transform","scale(1) translate(0, 0)"),X()),nt(this.id)}redrawEdges(t){this.edges.forEach(e=>(t==e.from||t==e.to)&&this.redrawEdge(e)),this.updateEdgesSel()}redrawEdge(t){const e=t.ref.parentElement;e.removeChild(t.ref),t.ref=j(t),e.append(t.ref)}redrawGroups(t){this.groupsMap.forEach(t=>{const e=t.ref.parentElement;e.removeChild(t.ref),W(t),e.append(t.ref)})}exportSVG(){this.validatedLayout||(this.validatedLayout=(0,C.U)(this));const t=document.querySelector("svg#graph"),e=(t.querySelector("rect.elastic"),t.cloneNode(!0));e.querySelectorAll("a.nodeLink[data-export-href]").forEach(t=>{t.setAttribute("href",t.getAttribute("data-export-href")||""),t.removeAttribute("data-export-href")});const n=e.querySelector("rect.elastic");n&&n.remove();const o=this.calculateContentBounds(),i=o.width+100,r=o.height+100,s=50-o.x,a=50-o.y,d=e.querySelector("g.zoom");d&&d.setAttribute("transform",`scale(1) translate(${s}, ${a})`),e.setAttribute("viewBox",`0 0 ${i} ${r}`),e.setAttribute("width",String(i)),e.setAttribute("height",String(r)),e.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.convertStylesToCustomProperties(e);const l=document.createElement("script");return l.setAttribute("type","application/json"),this.metadata.layout=this.exportLayout(),l.append("/g,"]]]>]>"),e.insertBefore(l,e.firstChild),e.outerHTML}convertStylesToCustomProperties(t){0!==this.colorToVarMap.size&&(t.querySelectorAll("[fill]").forEach(t=>{const e=t.getAttribute("fill");e&&this.colorToVarMap.has(e)&&t.setAttribute("fill",`var(${this.colorToVarMap.get(e)}, ${e})`)}),t.querySelectorAll("[stroke]").forEach(t=>{const e=t.getAttribute("stroke");e&&this.colorToVarMap.has(e)&&t.setAttribute("stroke",`var(${this.colorToVarMap.get(e)}, ${e})`)}))}calculateContentBounds(){let t=1/0,e=1/0,n=-1/0,o=-1/0;return this.nodes().forEach(i=>{const r=i.x-i.width/2,s=i.x+i.width/2,a=i.y-i.height/2,d=i.y+i.height/2;t=Math.min(t,r),n=Math.max(n,s),e=Math.min(e,a),o=Math.max(o,d)}),this.edgeVertices.forEach(i=>{t=Math.min(t,i.x-5),n=Math.max(n,i.x+5),e=Math.min(e,i.y-5),o=Math.max(o,i.y+5)}),this.groupsMap.forEach(i=>{const r=i.x-i.width/2,s=i.x+i.width/2,a=i.y-i.height/2,d=i.y+i.height/2;t=Math.min(t,r),n=Math.max(n,s),e=Math.min(e,a),o=Math.max(o,d)}),this.edges.forEach(i=>{if(t=Math.min(t,i.from.x-10,i.to.x-10),n=Math.max(n,i.from.x+10,i.to.x+10),e=Math.min(e,i.from.y-10,i.to.y-10),o=Math.max(o,i.from.y+10,i.to.y+10),i.vertices&&i.vertices.forEach(i=>{t=Math.min(t,i.x-10),n=Math.max(n,i.x+10),e=Math.min(e,i.y-10),o=Math.max(o,i.y+10)}),i.label&&i.label.trim()){const r=(i.from.x+i.to.x)/2,s=(i.from.y+i.to.y)/2,a=10*i.label.length+50;t=Math.min(t,r-a),n=Math.max(n,r+a),e=Math.min(e,s-25),o=Math.max(o,s+25)}}),t===1/0?{x:0,y:0,width:100,height:100}:{x:t,y:e,width:n-t,height:o-e}}exportLayout(t=!1){const e={};return this.nodes().forEach(t=>e[t.id]={x:t.x,y:t.y}),this.edges.forEach(n=>{if(!n.vertices)return;const o=n.vertices.map(t=>({x:t.x,y:t.y,label:t.label,auto:t.auto}));(o.length||t)&&(e[`e-${n.id}`]=o),n.userDeletedVertices&&(e[`e-${n.id}-deleted`]=!0)}),e}setSaved(){this._undo.setSaved()}importLayout(t,e=!1){this.clearResolvedLayout();for(const[e,n]of Object.entries(t)){const t=this.nodesMap.get(e);if(t){const o=L(e,n);t.x=o.x,t.y=o.y;continue}if(e.endsWith("-deleted")){const t=e.slice(2,-8),o=this.edges.find(e=>e.id===t);if(!o||!0!==n)throw new Error(`invalid deleted-edge layout entry ${e}`);o.userDeletedVertices=!0;continue}if(e.startsWith("e-")){const t=this.edges.find(t=>t.id===e.slice(2));if(!t||!Array.isArray(n))throw new Error(`invalid edge layout entry ${e}`);for(const e of t.vertices??[])this.edgeVertices.delete(e.id);t.vertices=n.map((n,o)=>{const i=L(`${e}[${o}]`,n),r=t.initVertex(i);return r.label=n.label,r.auto=n.auto,r});continue}throw new Error(`layout contains unknown element ${e}`)}for(const e of this.nodes())if(!(e.id in t))throw new Error(`layout is missing node ${e.id}`);e&&(this.nodes().forEach(t=>l(t.ref,t.x,t.y)),this.edges.forEach(t=>this.redrawEdge(t)),this.updateEdgesSel(),this.redrawGroups(null))}async autoLayout(t){const e=await(0,C.k)(this,t);this._undo.beforeChange();for(const t of e.nodes){const e=this.nodesMap.get(t.id);if(!e)throw new Error(`layout contains unknown node ${t.id}`);e.x=t.x,e.y=t.y,l(e.ref,e.x,e.y)}for(const t of e.groups){const e=this.groupsMap.get(t.id);if(!e)throw new Error(`layout contains unknown group ${t.id}`);e.layoutBounds=t.bounds,e.layoutTitleBounds=t.titleBounds,e.x=t.bounds.x+t.bounds.width/2,e.y=t.bounds.y+t.bounds.height/2,e.width=t.bounds.width,e.height=t.bounds.height}for(const t of e.edges){const e=this.edges.find(e=>e.id===t.id);if(!e)throw new Error(`layout contains unknown edge ${t.id}`);for(const t of e.vertices??[])this.edgeVertices.delete(t.id);e.layoutSections=t.sections,e.layoutLabelBounds=t.labelBounds,e.vertices=_(t.sections).map(t=>{const n=e.initVertex(t);return n.auto=!0,n}),e.userDeletedVertices=!1}this.validatedLayout=e.validated,this.edges.forEach(t=>this.redrawEdge(t)),this.redrawGroups(null),this.fitToView(),this._undo.change()}alignSelectionV(){const t=this.nodes().filter(t=>t.selected);t.push(...Array.from(this.edgeVertices.values()).filter(t=>t.selected));let e=Math.min(...t.map(t=>t.y));this.nodesMap.forEach(t=>t.selected&&this.moveNode(t,t.x,e,!1,!1)),this.edgeVertices.forEach(t=>t.selected&&this.moveEdgeVertex(t,t.x,e,!1,!1))}alignSelectionH(){const t=this.nodes().filter(t=>t.selected);t.push(...Array.from(this.edgeVertices.values()).filter(t=>t.selected));let e=Math.min(...t.map(t=>t.x));this.nodesMap.forEach(t=>t.selected&&this.moveNode(t,e,t.y,!1,!1)),this.edgeVertices.forEach(t=>t.selected&&this.moveEdgeVertex(t,e,t.y,!1,!1))}distributeSelectionH(){const t=this.nodes().filter(t=>t.selected),e=Array.from(this.edgeVertices.values()).filter(t=>t.selected);if(t.length+e.length<3)return;this._undo.beforeChange();const n=[...t,...e];n.sort((t,e)=>t.x-e.x);const o=n[0].x,i=(n[n.length-1].x-o)/(n.length-1);n.forEach((t,e)=>{const n=o+e*i;"title"in t?this.moveNode(t,n,t.y,!1,!0):this.moveEdgeVertex(t,n,t.y,!1,!0)}),this._undo.change()}distributeSelectionV(){const t=this.nodes().filter(t=>t.selected),e=Array.from(this.edgeVertices.values()).filter(t=>t.selected);if(t.length+e.length<3)return;this._undo.beforeChange();const n=[...t,...e];n.sort((t,e)=>t.y-e.y);const o=n[0].y,i=(n[n.length-1].y-o)/(n.length-1);n.forEach((t,e)=>{const n=o+e*i;"title"in t?this.moveNode(t,t.x,n,!1,!0):this.moveEdgeVertex(t,t.x,n,!1,!0)}),this._undo.change()}setEdgeSelected(t,e){e&&(this.setNodeSelected(t.from,!0),this.setNodeSelected(t.to,!0)),this.updateEdgesSel()}fitToView(){const t=this.calculateContentBounds();if(0===t.width||0===t.height)return;const e=z.parentElement?.clientWidth||800,n=z.parentElement?.clientHeight||600,o=(e-80)/t.width,i=(n-80)/t.height,r=Math.min(o,i),s=Math.max(Math.min(r,2),.1),a=e/2-(t.x+t.width/2)*s,d=n/2-(t.y+t.height/2)*s,l=z.querySelector("g.zoom");l&&l.setAttribute("transform",`translate(${a}, ${d}) scale(${s})`),X(),tt(this.id)}saveLayoutState(){return this.exportLayout(!0)}restoreLayoutState(t){this.importLayout(t,!0)}isGridVisible(){return this._gridVisible}isSnapToGrid(){return this._snapToGrid}getGridSize(){return this._gridSize}toggleGrid(){this._gridVisible=!this._gridVisible,this.updateGridDisplay(),window.dispatchEvent(new CustomEvent("gridStateChanged"))}toggleSnapToGrid(){this._snapToGrid=!this._snapToGrid,window.dispatchEvent(new CustomEvent("gridStateChanged"))}snapAllToGrid(){this._snapToGrid&&(this._undo.beforeChange(),this.nodes().forEach(t=>{const e=Math.round(t.x/this._gridSize)*this._gridSize,n=Math.round(t.y/this._gridSize)*this._gridSize;this.moveNode(t,e,n,!1,!0)}),this._undo.change())}snapToGrid(t,e){return{x:Math.round(t/this._gridSize)*this._gridSize,y:Math.round(e/this._gridSize)*this._gridSize}}updateGridDisplay(){if(!z)return;const t=z.querySelector("#grid-pattern");t&&t.remove();const e=z.querySelector("#grid-background");if(e&&e.remove(),!this._gridVisible)return;let n=z.querySelector("defs");n||(n=document.createElementNS("http://www.w3.org/2000/svg","defs"),z.insertBefore(n,z.firstChild));const o=document.createElementNS("http://www.w3.org/2000/svg","pattern");o.id="grid-pattern",o.setAttribute("width",this._gridSize.toString()),o.setAttribute("height",this._gridSize.toString()),o.setAttribute("patternUnits","userSpaceOnUse");const i=document.createElementNS("http://www.w3.org/2000/svg","path");i.setAttribute("d",`M ${this._gridSize} 0 L 0 0 0 ${this._gridSize}`),i.setAttribute("fill","none"),i.setAttribute("stroke","#d0d0d0"),i.setAttribute("stroke-width","1"),i.setAttribute("opacity","0.8"),o.appendChild(i),n.appendChild(o);const r=document.createElementNS("http://www.w3.org/2000/svg","rect");r.id="grid-background",r.setAttribute("x","-10000"),r.setAttribute("y","-10000"),r.setAttribute("width","20000"),r.setAttribute("height","20000"),r.setAttribute("fill","url(#grid-pattern)"),r.setAttribute("pointer-events","none");const s=z.querySelector("g.zoom");s&&s.insertBefore(r,s.firstChild)}}function _(t){const e=[];for(const n of t)for(const t of n){const n=e[e.length-1];n&&n.x===t.x&&n.y===t.y||e.push(t)}return e.slice(1,-1)}function L(t,e){if("object"!=typeof e||Array.isArray(e)||!Number.isFinite(e.x)||!Number.isFinite(e.y))throw new Error(`layout entry ${t} must contain valid x and y coordinates`);return e}let D,z=document.querySelector("svg#graph");z||(z=document.createElementNS("http://www.w3.org/2000/svg","svg"),z.setAttribute("id","graph"),z.addEventListener("click",t=>D(t))),z.setAttribute("width","100%"),z.setAttribute("height","100%");let V,T=!1;const N=(t,e,n)=>{z.innerHTML='\n\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n',document.body.append(z),z.__data=t,V=e,D=t=>{},q(t);const o=d.rect(300,300,50,50,0,"elastic");return z.append(o),t.updateGridDisplay(),U(z,n),{svg:z,setZoom:P}},F=t=>(z=document.createElementNS("http://www.w3.org/2000/svg","svg"),z.setAttribute("id","graph"),q(t),z),q=t=>{const e=d.element("g",{},"zoom"),n=d.element("g",{},"nodes"),o=d.element("g",{},"edges"),i=d.element("g",{},"groups");e.append(i,o,n),t.nodesMap.forEach(e=>{!function(t,e){window.gdata=e;const n=d.element("g",{},"node");n.setAttribute("id",t.id),t.selected&&n.classList.add("selected"),l(n,t.x,t.y);const o=t.link?d.element("a",{href:t.link.href,"data-export-href":t.link.exportHref,"aria-label":`Open ${t.title}`},"nodeLink"):null,i=o||n;o&&(n.classList.add("linked"),n.append(o));const r=t.style.shape||"Box",s=(w[r.toLowerCase()]||w.box)(i,t);s.classList.add("nodeBorder"),(0,I.AV)(s,Q.nodeBorder),s.setAttribute("fill",t.style.background),s.setAttribute("stroke",t.style.stroke),s.setAttribute("stroke-width","3"),s.setAttribute("opacity",String(t.style.opacity)),K(s,t.style.border);const a=function(t,e){const n=d.element("g");let o=-t.textHeight/2;return t.blocks.forEach(t=>{const i=d.text("",{"text-anchor":"middle"});(0,I.AV)(i,I.M5.nodeText),e&&i.setAttribute("fill",e),t.field&&i.setAttribute("data-field",t.field),t.lines.forEach((e,n)=>{const r=d.element("tspan",{x:0,y:o+t.fontSize+n*t.lineHeight,"font-size":`${t.fontSize}px`,"font-weight":t.bold?"bold":"normal"});r.textContent=e,i.append(r)}),n.append(i),o+=t.lines.length*t.lineHeight+t.gapAfter}),n}(t.contentLayout,t.style.color);l(a,0,(Number(i.getAttribute("label-offset-y"))||0)/2),i.append(a),n.__data=t,t.ref=n}(e,t),n.append(e.ref)}),t.edges.forEach(t=>{t.labelBounds=void 0}),t.edges.forEach(t=>{j(t),o.append(t.ref)}),t.groupsMap.forEach(t=>{W(t),i.append(t.ref)}),z.append(e)};function j(t){const e=t.from,n=d.element("g",{},"edge");n.setAttribute("id",t.id),n.setAttribute("data-from",t.from.id),n.setAttribute("data-to",t.to.id);const o=(t.style.position||50)/100,i=t.layoutSections?function(t){const e=[];for(const n of t)for(const t of n){const n=e[e.length-1];n&&n.x===t.x&&n.y===t.y||e.push(t)}return e}(t.layoutSections):(0,k.l)(t),{bg:r,txt:s}=t.layoutLabelBounds?function(t,e){const n=t.x+t.width/2,{txt:o}=(t.y,t.height,d.textArea(e.label,200,e.style.fontSize,!1,n,t.y,"middle"));(0,I.AV)(o,Q.edgeText),o.setAttribute("stroke","none"),o.setAttribute("font-size",String(e.style.fontSize)),o.setAttribute("fill",e.style.color),o.setAttribute("data-field","label");const i=d.rect(t.width,t.height,t.x,t.y);return(0,I.AV)(i,Q.edgeRect),{bg:i,txt:o}}(t.layoutLabelBounds,t):function(t,e){const n=e.style.fontSize;let{txt:o,dy:i,maxW:r}=d.textArea(e.label,200,n,!1,t.x,t.y,"middle");i-=n/2,r+=n;const s="vertical"===t.orientation?t.x+r/2+12:t.x,a="vertical"===t.orientation?t.y:t.y-i/2-12,l={x:s-r/2,y:a-i/2,width:r,height:i};o.querySelectorAll("tspan").forEach(t=>{t.setAttribute("x",String(s))}),o.setAttribute("y",String(a-i/2)),(0,I.AV)(o,Q.edgeText),o.setAttribute("stroke","none"),o.setAttribute("font-size",String(e.style.fontSize)),o.setAttribute("fill",e.style.color);const c=d.rect(l.width,l.height,l.x,l.y);return(0,I.AV)(c,Q.edgeRect),o.setAttribute("data-field","label"),e.labelBounds=l,{bg:c,txt:o}}((0,k.IW)(i,o,e),t);n.append(r,s);const a=(0,k.Zr)(i),l=d.path(a,{"marker-end":"url(#arrow)"},"edge");l.setAttribute("fill","none"),l.setAttribute("stroke",t.style.color),l.setAttribute("stroke-width",String(t.style.thickness)),l.setAttribute("stroke-linecap","round"),t.style.dashed&&l.setAttribute("stroke-dasharray","8"),n.append(l);for(const e of t.vertices??[]){const t=e;t.ref=d.element("circle",{id:t.id,cx:e.x,cy:e.y,r:7,fill:"none"},"v-dot"),t.selected&&t.ref.classList.add("selected"),t.auto&&t.ref.classList.add("auto"),n.append(t.ref)}return t.ref=n,n}function W(t){if(0==t.nodes.length)return;const e=d.element("g",{},"group");if(t.layoutBounds&&t.layoutTitleBounds){const n=t.layoutBounds,o=t.layoutTitleBounds,i=d.rect(n.width,n.height,n.x,n.y);(0,I.AV)(i,Q.groupRect),t.style.stroke&&i.setAttribute("stroke",t.style.stroke),t.style.background&&i.setAttribute("fill",t.style.background);const r=d.text(t.name,{x:o.x,y:o.y,"dominant-baseline":"hanging"});return(0,I.AV)(r,Q.groupText),t.style.color&&r.setAttribute("fill",t.style.color),e.append(i,r),void(t.ref=e)}let n={x:1e100,y:1e100},o={x:0,y:0};t.nodes.forEach(t=>{const e=t.style?.shape?.toLowerCase()||"box";let i=t.height/2,r=t.height/2;if("robot"===e){const e=.12*t.height;i=t.height/2+e,r=t.height/2}else if("hexagon"===e){const e=t.width/2*.866;i=e,r=e}const s={x:t.x-t.width/2,y:t.y-i,width:t.width,height:i+r};n.x=Math.min(n.x,s.x),n.y=Math.min(n.y,s.y),o.x=Math.max(o.x,s.x+s.width),o.y=Math.max(o.y,s.y+s.height)});const i=Math.max(o.x-n.x,200),r=o.y-n.y,s={x:n.x-25,y:n.y-25,width:i+50,height:r+50+30},a=d.rect(s.width,s.height,s.x,s.y);t.x=s.x+s.width/2,t.y=s.y+s.height/2,t.width=s.width,t.height=s.height,(0,I.AV)(a,Q.groupRect),t.style.stroke&&a.setAttribute("stroke",t.style.stroke),t.style.background&&a.setAttribute("fill",t.style.background);const l=d.text(t.name,{x:n.x,y:s.y+s.height-Q.groupText["font-size"]});(0,I.AV)(l,Q.groupText),t.style.color&&l.setAttribute("fill",t.style.color),e.append(a,l),t.ref=e}function R(t,e){let n={dst:Number.POSITIVE_INFINITY,pos:-1,edge:null,prj:null};return t.edges.forEach(t=>{const o=t.vertices||[],i=[t.from,...o,t.to];for(let o=1;o50||s3||Math.abs(a)>3)&&(h=!0,c=null,l)){const t=e.getSelection();t.length=0,t.push(l.node),e.setSelection(t),o=[{x:l.node.x,y:l.node.y,n:l.node}],l=null}if(r)!function(e,n){const o=t.querySelector("g.zoom");if(!o)return;const i=O();o.setAttribute("transform",`translate(${e}, ${n}) scale(${i})`)}(d.x+s,d.y+a);else if(o.length>0&&h){const t=e.getZoom(),n=s/t,i=a/t;o.forEach(t=>{e.moveNode(t.n,t.x+n,t.y+i)}),e.setDragging(!0)}else i&&(i.update(n),e.setDragging(!0))}(n=f(n),n.clientX-p.ex,n.clientY-p.ey)}function b(n){document.removeEventListener("touchmove",m),document.removeEventListener("mousemove",m),document.removeEventListener("mouseup",b),document.removeEventListener("touchend",b),function(n){e.setDragging(!1);const s=h?null:c;if(h&&(u=!0,window.setTimeout(()=>{u=!1},0)),l&&!h){const t=e.getSelection();t.length=0,t.push(l.node),e.setSelection(t)}if(i){const t=i.end();t?e.boxSelection(t,n.shiftKey):o.length||e.setSelection([]),i=null}if(r&&h){const e=t.__data;e&&e.id&&tt(e.id)}l=null,c=null,h=!1,r=!1,e.updatePanning(),s&&(window.location.href=s)}(f(n)),p=null}function y(u){u=f(u),p={ex:u.clientX,ey:u.clientY},function(u){u.preventDefault(),h=!1,l=null;const A=u.target,g=A instanceof Element?A.closest("a.nodeLink"):null;c=u.shiftKey?null:g?.getAttribute("href")||null;const p=e.nodeFromEvent(u),f=u.shiftKey?"pan"===n?"select":"pan":n;if(p)if("pan"===f){r=!1,i=null;const t=e.getSelection();e.isSelected(p)?o=t.map(t=>({x:t.x,y:t.y,n:t})):(e.setSelection([p]),o=[{x:p.x,y:p.y,n:p}])}else{r=!1,i=null;const t=e.getSelection();if(u.shiftKey&&"select"===n){if(e.isSelected(p)){const e=t.findIndex(t=>t.id===p.id);e>=0&&t.splice(e,1)}else t.push(p);e.setSelection(t),o=t.map(t=>({x:t.x,y:t.y,n:t}))}else e.isSelected(p)?(o=t.map(t=>({x:t.x,y:t.y,n:t})),l=null):(l={node:p,shiftKey:u.shiftKey},o=[{x:p.x,y:p.y,n:p}])}else"pan"===f?(r=!0,i=null,s=u.clientX,a=u.clientY,d=function(){const e=t.querySelector("g.zoom");if(!e)return{x:0,y:0};const n=(e.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return n?{x:parseFloat(n[1])||0,y:parseFloat(n[2])||0}:{x:0,y:0}}(),o=[],e.setSelection([])):(r=!1,i=function(){let e=0,n=0,o=null;return{ini(i){const r=G(i);e=r.x,n=r.y,o=document.createElementNS("http://www.w3.org/2000/svg","rect"),o.setAttribute("fill","rgba(0, 100, 255, 0.1)"),o.setAttribute("stroke","rgba(0, 100, 255, 0.5)"),o.setAttribute("stroke-width","1"),o.setAttribute("stroke-dasharray","3,3"),o.setAttribute("x",String(e)),o.setAttribute("y",String(n)),o.setAttribute("width","0"),o.setAttribute("height","0");const s=t.querySelector("g.zoom");s?s.appendChild(o):t.appendChild(o)},update(t){if(!o)return;const i=G(t),r=i.x,s=i.y,a=Math.min(e,r),d=Math.min(n,s),l=Math.abs(r-e),c=Math.abs(s-n);o.setAttribute("x",String(a)),o.setAttribute("y",String(d)),o.setAttribute("width",String(l)),o.setAttribute("height",String(c))},end(){if(!o)return null;const t=parseFloat(o.getAttribute("x")||"0"),e=parseFloat(o.getAttribute("y")||"0"),n=parseFloat(o.getAttribute("width")||"0"),i=parseFloat(o.getAttribute("height")||"0");return o.remove(),o=null,n>5&&i>5?{x:t,y:e,width:n,height:i,left:t,top:e,right:t+n,bottom:e+i}:null}}}(),i&&i.ini(u),o=[])}(u),document.addEventListener("touchmove",m),document.addEventListener("mousemove",m),document.addEventListener("mouseup",b),document.addEventListener("touchend",b)}g.addEventListener("mousedown",y),g.addEventListener("touchstart",y),A.push({element:g,event:"mousedown",handler:y},{element:g,event:"touchstart",handler:y})}(t),t.addEventListener("click",g),A.push({element:t,event:"click",handler:g}),()=>{A.forEach(({element:t,event:e,handler:n})=>{t.removeEventListener(e,n)})}}function U(t,e){const n=t.__cursorInteractionCleanup;function o(t){return t.__data}n&&n();const i=()=>o(t),r=[],s=t=>{i().changed()&&(t.preventDefault(),t.returnValue="")};function a(e,n){e.selected=n;const o=t.querySelector("#"+e.id);e.selected?o.classList.add("selected"):o.classList.remove("selected")}window.addEventListener("beforeunload",s),r.push({element:window,event:"beforeunload",handler:s});const l=e=>{if(!e.altKey)return;const n=R(i(),G(e));if(n){const{prj:e}=n,o=t.querySelector("g.edges");let i=o.querySelector("#prj");i||(i=d.element("circle",{id:"prj",cx:e.x,cy:e.y,r:7}),o.append(i)),i.setAttribute("cx",String(e.x)),i.setAttribute("cy",String(e.y))}else u()};t.addEventListener("mousemove",l),r.push({element:t,event:"mousemove",handler:l});const h=t=>{const e=(0,B.Yp)(t,!0);e!=B.Zj&&e!=B._s&&u()};function u(){const e=t.querySelector("g.edges #prj");e&&e.parentElement.removeChild(e)}window.addEventListener("keyup",h),r.push({element:window,event:"keyup",handler:h});const A=t=>{const e=(0,B.Yp)(t,!0);if(e!=B._s&&e!=B.Zj)return;const n=R(i(),G(t));if(n){const{edge:t,pos:o,prj:r}=n;i().insertEdgeVertex(t,r,o,e==B._s),u()}};t.addEventListener("click",A),r.push({element:t,event:"click",handler:A});const g=e=>{const n=.1*Math.sign(e.deltaY),o=O(),i=Math.max(.1,Math.min(5,o-n));if(i!==o){const n=t.getBoundingClientRect();H(i,e.clientX-n.left,e.clientY-n.top),e.preventDefault();const o=t.__data;o&&o.id&&tt(o.id)}};t.addEventListener("wheel",g),r.push({element:t,event:"wheel",handler:g});const p=t=>{const e=(0,B.Yp)(t);switch(e&&t.preventDefault(),e){case B.bl:Array.from(i().edgeVertices.values()).filter(t=>t.selected).forEach(t=>{i().deleteEdgeVertex(t)});break;case"undo":i().undo();break;case"redo":i().redo();break;case B.Ur:H(Math.min(5,1.2*O())),tt(i().id);break;case B.hU:H(Math.max(.1,O()/1.2)),tt(i().id);break;case B.i1:H(1),tt(i().id);break;case B.mD:i().fitToView();break;case B.F:i().nodes().forEach(t=>i().setNodeSelected(t,!0)),i().edgeVertices.forEach(t=>a(t,!0));break;case B.Gn:i().nodes().forEach(t=>i().setNodeSelected(t,!1)),i().edgeVertices.forEach(t=>a(t,!1))}};window.addEventListener("keydown",p),r.push({element:window,event:"keydown",handler:p});const f=Y(t,{nodeFromEvent(t){t.preventDefault();let e=t.target.closest("g.nodes g.node");return e?o(e):(e=t.target.closest("g.edges g.edge .v-dot"),e?i().edgeVertices.get(e.id):null)},setSelection(t){i().nodes().forEach(e=>i().setNodeSelected(e,t.some(t=>t.id==e.id))),i().edgeVertices.forEach(e=>a(e,t.some(t=>t.id==e.id))),V(i().nodes().find(t=>t.selected))},setDragging(t){T=t},isSelected:t=>t.selected,getSelection(){const t=i().nodes().filter(t=>t.selected);return i().edgeVertices.forEach(e=>e.selected&&t.push(e)),t},getZoom:O,moveNode(t,e,n){i().nodesMap.has(t.id)?i().moveNode(t,e,n):(t.auto=!1,i().moveEdgeVertex(t,e,n))},boxSelection(t,e){i().nodesMap.forEach(n=>{var o,r,s;(o={x:(s=n).x-s.width/2,y:s.y-s.height/2,width:s.width,height:s.height}).x<(r=t).x+r.width&&o.yr.x&&o.y+o.height>r.y?i().setNodeSelected(n,!n.selected):e||i().setNodeSelected(n,!1)}),i().edgeVertices.forEach(n=>{c(n,t,!1)?a(n,!n.selected):e||a(n,!1)}),V(i().nodes().find(t=>t.selected))},updatePanning:X},e);t.__cursorInteractionCleanup=()=>{r.forEach(({element:t,event:e,handler:n})=>{t.removeEventListener(e,n)}),f&&f()}}function O(){if(!z)return 1;const t=z.querySelector("g.zoom");if(!t)return 1;const e=(t.getAttribute("transform")||"").match(/scale\(([^)]+)\)/);return e&&parseFloat(e[1])||1}function P(t){if(!z)return;const e=z.querySelector("g.zoom");if(!e)return;const n=Z();e.setAttribute("transform",`translate(${n.x}, ${n.y}) scale(${t})`),X()}function H(t,e,n){const o=z.querySelector("g.zoom"),i=O();if(void 0===e||void 0===n){const t=z.parentElement;t?(e=t.clientWidth/2,n=t.clientHeight/2):(e=z.clientWidth/2,n=z.clientHeight/2)}const r=Z(),s=e-(e-r.x)/i*t,a=n-(n-r.y)/i*t;o.setAttribute("transform",`translate(${s}, ${a}) scale(${t})`),X()}function Z(){if(!z)return{x:0,y:0};const t=z.querySelector("g.zoom");if(!t)return{x:0,y:0};const e=(t.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return e?{x:parseFloat(e[1])||0,y:parseFloat(e[2])||0}:{x:0,y:0}}function X(){if(!z)return;const t=z.querySelector("g.zoom");if(!t)return;const e=t.getBBox(),n=O();if(!z.parentElement)return;const o=Math.max(z.parentElement.clientWidth/n,e.x+e.width+20),i=Math.max(z.parentElement.clientHeight/n,e.y+e.height+20);z.setAttribute("width",String(o*n)),z.setAttribute("height",String(i*n))}const K=(t,e)=>{"Dashed"==e?t.setAttribute("stroke-dasharray","4"):"Dotted"==e&&t.setAttribute("stroke-dasharray","2")},Q={nodeBorder:{filter:"url(#shadow)"},nodeText:{"font-family":"Arial, sans-serif",stroke:"none"},edgeText:{"font-family":"Arial, sans-serif",stroke:"none"},edgeRect:{fill:"none",stroke:"none"},groupRect:{fill:"rgba(0, 0, 0, 0.02)",stroke:"#666","stroke-width":3,"stroke-dasharray":4},groupText:{"font-family":"Arial, sans-serif",fill:"#666","font-size":22,"font-weight":"bold",cursor:"default"}},J=new Map;function tt(t){if(!z)return;const e=O(),n=Z(),o={zoom:e,transform:{x:n.x,y:n.y}};J.set(t,o)}function et(t){if(!z||!J.has(t))return!1;const e=J.get(t);if(!e)return!1;const n=z.querySelector("g.zoom");return n&&(n.setAttribute("transform",`scale(${e.zoom}) translate(${e.transform.x}, ${e.transform.y})`),X()),!0}function nt(t){J.delete(t)}},983(t,e,n){n.d(e,{k:()=>x,U:()=>w});var o=n(20);const i={direction:"DOWN",nodeSpacing:80,layerSpacing:80};async function r(t,e={}){const o=function(t,e){const n=new Map(t.nodes.map(t=>[t.id,t])),o=new Map(t.groups.map(t=>[t.id,t]));!function(t,e,n){for(const e of t.nodes)if(e.parentId&&!n.has(e.parentId))throw new Error(`node ${e.id} has missing parent group ${e.parentId}`);for(const e of t.groups)if(e.parentId&&!n.has(e.parentId))throw new Error(`group ${e.id} has missing parent group ${e.parentId}`);for(const n of t.edges)if(!e.has(n.sourceId)||!e.has(n.targetId))throw new Error(`edge ${n.id} connects missing node ${n.sourceId} or ${n.targetId}`)}(t,n,o);const i=new Map;for(const e of t.nodes)b(i,e.parentId,e);const r=new Map;for(const e of t.groups)b(r,e.parentId,e);const h=new Map,u=t=>{const n={id:t.id,children:[...(r.get(t.id)??[]).map(u),...(i.get(t.id)??[]).map(s)],edges:[],labels:[{text:t.id,width:t.titleSize.width,height:t.titleSize.height}],layoutOptions:l(e,t.titleSize.height)};return h.set(t.id,n),n},A={id:t.id,children:[...(r.get(void 0)??[]).map(u),...(i.get(void 0)??[]).map(s)],edges:[],layoutOptions:d(e)};for(const e of t.edges){const t=c(e,n,o),i=t?h.get(t):A;if(!i)throw new Error(`edge ${e.id} has missing layout group ${t}`);i.edges?.push(a(e))}return A}(t,{...i,...e}),r=new(await n.e(726).then(n.t.bind(n,862,23)).then(t=>t.default));return function(t,e){if(void 0===e.width||void 0===e.height)throw new Error(`ELK did not size diagram ${t.id}`);const n=[],o=[],i=[];return u(e,0,0,new Map(t.nodes.map(t=>[t.id,t])),new Map(t.groups.map(t=>[t.id,t])),n,o,i),{diagramId:t.id,bounds:{x:0,y:0,width:e.width,height:e.height},nodes:n,groups:o,edges:i}}(t,await r.layout(o))}function s(t){return{id:t.id,width:t.size.width,height:t.size.height,layoutOptions:{"elk.nodeSize.constraints":"FIXED_SIZE","elk.portConstraints":"FREE"}}}function a(t){return{id:t.id,sources:[t.sourceId],targets:[t.targetId],labels:t.labelSize?[{text:t.id,width:t.labelSize.width,height:t.labelSize.height,layoutOptions:{"elk.edgeLabels.inline":"false","elk.edgeLabels.placement":"CENTER"}}]:[]}}function d(t){return{"elk.algorithm":"layered","elk.direction":t.direction,"elk.edgeRouting":"ORTHOGONAL","elk.hierarchyHandling":"INCLUDE_CHILDREN","elk.padding":"[top=40,left=40,bottom=40,right=40]","elk.spacing.nodeNode":String(t.nodeSpacing),"elk.spacing.componentComponent":String(t.nodeSpacing),"elk.layered.spacing.nodeNodeBetweenLayers":String(t.layerSpacing),"elk.layered.spacing.edgeNodeBetweenLayers":"30","elk.layered.spacing.edgeEdgeBetweenLayers":"20","elk.layered.nodePlacement.strategy":"NETWORK_SIMPLEX","elk.layered.nodePlacement.favorStraightEdges":"true","elk.layered.crossingMinimization.strategy":"LAYER_SWEEP","elk.layered.unnecessaryBendpoints":"true","elk.edgeLabels.inline":"false","elk.edgeLabels.avoidOverlap":"true","elk.layered.edgeLabels.sideSelection":"SMART_DOWN","elk.spacing.edgeLabel":"12"}}function l(t,e){return{...d(t),"elk.padding":`[top=${e+30},left=30,bottom=30,right=30]`,"elk.nodeLabels.placement":"[H_LEFT,V_TOP,INSIDE]","elk.spacing.labelNode":"10"}}function c(t,e,n){const o=h(e.get(t.sourceId)?.parentId,n),i=new Set(h(e.get(t.targetId)?.parentId,n));return o.find(t=>i.has(t))}function h(t,e){const n=[];let o=t;for(;o;)n.push(o),o=e.get(o)?.parentId;return n}function u(t,e,n,o,i,r,s,a){const d=e+(t.x??0),l=n+(t.y??0);for(const e of t.children??[]){const t=d+p(e.x,e.id,"x"),n=l+p(e.y,e.id,"y"),c=p(e.width,e.id,"width"),h=p(e.height,e.id,"height"),A=o.get(e.id);if(A){r.push({id:e.id,parentId:A.parentId,bounds:{x:t,y:n,width:c,height:h}});continue}const g=i.get(e.id);if(!g)throw new Error(`ELK returned unknown node ${e.id}`);const f=m(e.labels?.[0],e.id);s.push({id:e.id,parentId:g.parentId,bounds:{x:t,y:n,width:c,height:h},titleBounds:{x:t+p(f.x,e.id,"title x"),y:n+p(f.y,e.id,"title y"),width:p(f.width,e.id,"title width"),height:p(f.height,e.id,"title height")}}),u(e,d,l,o,i,r,s,a)}for(const e of t.edges??[])a.push(A(e,d,l))}function A(t,e,n){const o=(t.sections??[]).map(t=>[g(t.startPoint,e,n),...(t.bendPoints??[]).map(t=>g(t,e,n)),g(t.endPoint,e,n)]),i=t.labels?.[0];return{id:t.id,sourceId:f(t.sources,t.id,"source"),targetId:f(t.targets,t.id,"target"),sections:o,labelBounds:i?{x:e+p(i.x,t.id,"label x"),y:n+p(i.y,t.id,"label y"),width:p(i.width,t.id,"label width"),height:p(i.height,t.id,"label height")}:void 0}}function g(t,e,n){return{x:e+t.x,y:n+t.y}}function p(t,e,n){if(void 0===t)throw new Error(`ELK omitted ${n} for ${e}`);return t}function f(t,e,n){if(1!==t.length)throw new Error(`ELK returned ${t.length} ${n}s for edge ${e}`);return t[0]}function m(t,e){if(!t)throw new Error(`ELK omitted title for group ${e}`);return t}function b(t,e,n){const o=t.get(e)??[];o.push(n),t.set(e,o)}n(828);var y=n(977);async function x(t,e={}){await document.fonts.ready;const n=C(t),i=e.compactLayout?56:80,s=await r(n,{direction:e.direction??t.layoutDirection??"DOWN",nodeSpacing:e.nodeSpacing??i,layerSpacing:e.layerSpacing??i}),a=(0,o.r)(n,s),d=new Map(n.nodes.map(t=>[t.id,t]));return{validated:a,nodes:a.nodes.map(t=>{const e=d.get(t.id);if(!e)throw new Error(`validated layout contains unknown node ${t.id}`);return{id:t.id,x:t.bounds.x+e.centerOffset.x,y:t.bounds.y+e.centerOffset.y}}),groups:a.groups.map(t=>({id:t.id,bounds:t.bounds,titleBounds:t.titleBounds})),edges:a.edges.map(t=>({id:t.id,sections:t.sections,labelBounds:t.labelBounds}))}}function w(t){const e=C(t),n=new Map(e.nodes.map(t=>[t.id,t])),i=t.nodes().map(t=>{const e=n.get(t.id);if(!e)throw new Error(`measured diagram omitted node ${t.id}`);return{id:t.id,parentId:e.parentId,bounds:{x:t.x-e.centerOffset.x,y:t.y-e.centerOffset.y,...e.size}}}),r=Array.from(t.groupsMap.values()).map(t=>{if(!t.ref?.isConnected)throw new Error(`group ${t.id} must be drawn before saving`);const n=t.ref.querySelector("rect"),o=t.ref.querySelector("text");if(!(n instanceof SVGGraphicsElement&&o instanceof SVGGraphicsElement))throw new Error(`group ${t.id} is missing its boundary or title`);return{id:t.id,parentId:e.groups.find(e=>e.id===t.id)?.parentId,bounds:k(n.getBBox()),titleBounds:k(o.getBBox())}}),s=t.edges.map(t=>{const e=t.layoutSections??[(0,y.l)(t)],n=t.layoutLabelBounds??t.labelBounds;if(t.label.trim().length>0&&!n)throw new Error(`edge ${t.id} is missing its label position`);return{id:t.id,sourceId:t.from.id,targetId:t.to.id,sections:e,labelBounds:n?{...n}:void 0}}),a={diagramId:t.id,bounds:I(i.map(t=>t.bounds),r,s),nodes:i,groups:r,edges:s};return function(t,e){return(0,o.r)(t,e)}(e,a)}function C(t){const{nodeParents:e,groupParents:n}=function(t){const e=new Map,n=new Map;for(const o of t.groupsMap.values())for(const t of o.nodes){const i=E(t)?n:e,r=i.get(t.id);if(r&&r!==o.id)throw new Error(`${E(t)?"group":"node"} ${t.id} belongs to both ${r} and ${o.id}`);i.set(t.id,o.id)}return{nodeParents:e,groupParents:n}}(t);return{id:t.id,nodes:t.nodes().map(t=>function(t,e){if(!t.ref?.isConnected)throw new Error(`node ${t.id} must be drawn before layout`);const n=t.ref.getBBox();return B(t.id,n),{id:t.id,parentId:e,size:{width:n.width,height:n.height},contentSize:{width:n.width,height:n.height},centerOffset:{x:-n.x,y:-n.y},shape:t.style.shape??"Box"}}(t,e.get(t.id))),groups:Array.from(t.groupsMap.values()).map(t=>function(t,e){if(!t.ref?.isConnected)throw new Error(`group ${t.id} must be drawn before layout`);const n=t.ref.querySelector("text");if(!(n instanceof SVGGraphicsElement))throw new Error(`group ${t.id} is missing its drawn title`);const o=n.getBBox();return B(t.id,o),{id:t.id,parentId:e,titleSize:{width:o.width,height:o.height}}}(t,n.get(t.id))),edges:t.edges.map(v)}}function v(t){const e=t.label.trim().length>0;if(e&&!t.labelBounds)throw new Error(`edge ${t.id} must be drawn before layout`);return{id:t.id,sourceId:t.from.id,targetId:t.to.id,labelSize:e?{width:t.labelBounds?.width??0,height:t.labelBounds?.height??0}:void 0}}function B(t,e){if(!Number.isFinite(e.x)||!Number.isFinite(e.y)||!Number.isFinite(e.width)||!Number.isFinite(e.height)||e.width<=0||e.height<=0)throw new Error(`browser returned invalid measured box for ${t}`)}function I(t,e,n){const o=[...t,...e.flatMap(t=>[t.bounds,t.titleBounds]),...n.flatMap(t=>t.labelBounds?[t.labelBounds]:[])],i=n.flatMap(t=>t.sections.flat());if(0===o.length&&0===i.length)return{x:0,y:0,width:0,height:0};const r=Math.min(...o.map(t=>t.x),...i.map(t=>t.x)),s=Math.min(...o.map(t=>t.y),...i.map(t=>t.y));return{x:r,y:s,width:Math.max(...o.map(t=>t.x+t.width),...i.map(t=>t.x))-r,height:Math.max(...o.map(t=>t.y+t.height),...i.map(t=>t.y))-s}}function k(t){return{x:t.x,y:t.y,width:t.width,height:t.height}}function E(t){return"nodes"in t}},71(t,e,n){var o=n(828);const i={TopBottom:"DOWN",BottomTop:"UP",LeftRight:"RIGHT",RightLeft:"LEFT"};n.d(e,["B",0,t=>{const e=[];return Object.keys(t.views).filter(t=>t.endsWith("Views")).forEach(n=>{t.views[n].forEach(t=>{e.push({key:t.key,title:t.title||t.key,section:n})})}),e},"R",0,(t,e,n)=>{const r=new Map,s=new Map,a=t=>{Array.isArray(t.relationships)&&t.relationships.forEach(t=>{s.set(t.id,t)})};if(t.model.people&&t.model.people.forEach(t=>{r.set(t.id,t),Array.isArray(t.relationships)&&t.relationships.forEach(t=>{s.set(t.id,t)})}),t.model.softwareSystems&&t.model.softwareSystems.forEach(t=>{r.set(t.id,t),a(t),Array.isArray(t.containers)&&t.containers.forEach(e=>{e.parent=t,r.set(e.id,e),a(e),Array.isArray(e.components)&&e.components.forEach(t=>{t.parent=e,r.set(t.id,t),a(t)})})}),t.model.deploymentNodes){const e=t=>{t.containerInstances&&t.containerInstances.forEach(e=>{const n={...r.get(e.containerId),id:e.id};r.set(n.id,n),n.parent=t,a(e)})},n=(t,o)=>{t.parent=o,r.set(t.id,t),a(t),e(t),t.children&&t.children.forEach(e=>n(e,t)),t.infrastructureNodes&&t.infrastructureNodes.forEach(e=>n(e,t))};t.model.deploymentNodes.forEach(t=>n(t,null))}const{view:d,section:l}=function(t,e){let n=null,o="";return Object.keys(t.views).filter(t=>t.endsWith("Views")).some(i=>t.views[i].some(t=>{if(t.key==e)return n=t,o=i,!0})),{view:n,section:o}}(t,n);if(!d)return null;const c=new o.jg(d.key,d.title||d.key),h=d.automaticLayout?.rankDirection;c.layoutDirection=h?i[h]:void 0;const u={name:c.name,description:d.description,version:t.version,elements:[]};if(c.metadata=u,!d.elements)return c;const A={};if("deploymentViews"==l||"containerViews"==l)d.elements.forEach(t=>{const e=r.get(t.id);e?.parent&&(A[e.parent.id]=!0)});else if(d.softwareSystemId)d.elements.find(t=>t.id==d.softwareSystemId)||(A[d.softwareSystemId]=!0);else if("systemLandscapeViews"==l){const e={id:"__enterprise__",...t.model.enterprise};r.set(e.id,e),t.model.people&&t.model.people.filter(t=>"External"!=t.location).forEach(t=>t.parent=e),t.model.softwareSystems&&t.model.softwareSystems.filter(t=>"External"!=t.location).forEach(t=>t.parent=e),A[e.id]=!0}const g=t.views.styles,p=t=>t.toLowerCase().replace(/[^a-z0-9-]/g,"-");g?.elements&&g.elements.forEach(t=>{if(t.tag){const e=`--mdl-${p(t.tag)}`;t.background&&c.colorToVarMap.set(t.background,`${e}-bg`),t.color&&c.colorToVarMap.set(t.color,`${e}-color`),t.stroke&&c.colorToVarMap.set(t.stroke,`${e}-stroke`)}}),g?.relationships&&g.relationships.forEach(t=>{if(t.tag){const e=`--mdl-rel-${p(t.tag)}`;t.color&&c.colorToVarMap.set(t.color,`${e}-color`)}}),d.elements.forEach(e=>{if(A[e.id])return;const n=r.get(e.id),o=n?function(t,e){const n=t.views.containerViews?.find(t=>t.softwareSystemId==e);return n?.key}(t,n.id):void 0;let i="",s={};if(n){const t=n.tags.split(",");i=t[t.length-1],n.technology&&(i+=": "+n.technology),t.forEach(t=>{const e=g&&g.elements&&g.elements.find(e=>e.tag==t);e&&(s={...s,...e})})}c.addNode(e.id,n&&n.name||e.id,i,n&&n.description?n.description:"",s,function(t,e){if(e){const t=encodeURIComponent(e);return{href:`?id=${t}`,exportHref:`${t}.svg`}}if(t?.url)return{href:t.url,exportHref:t.url}}(n,o)),n&&u.elements.push({id:n.id,tags:n.tags,location:n.location,properties:n.properties,elementViewKey:o,technology:n.technology,url:n.url})}),Array.isArray(d.relationships)&&d.relationships.forEach(t=>{const e=s.get(t.id);if(!e)return;if(!c.nodesMap.has(e.sourceId)){if(r.has(e.sourceId)){const t=r.get(e.sourceId);console.warn("Element not found in this view: ",t.id,t.name)}else console.warn("Element not found: ",e.sourceId);return}if(!c.nodesMap.has(e.destinationId)){if(r.has(e.destinationId)){const t=r.get(e.destinationId);console.warn("Element not found in this view: ",t.id,t.name)}else console.warn("Element not found: ",e.destinationId);return}let n={};e.tags.split(",").forEach(t=>{const e=g&&g.relationships&&g.relationships.find(e=>e.tag==t);e&&(n={...n,...e})}),t.routing&&(n.routing=t.routing),c.addEdge(e.id,e.sourceId,e.destinationId,e.description,t.vertices,n)});const f=t=>{let e=0;for(let n=t.parent;n;n=n.parent)e++;return e};return Object.keys(A).map(t=>r.get(t)).sort((t,e)=>f(t)>f(e)?-1:1).forEach(t=>{let e={};"deploymentViews"==l&&r.get(t.id).tags.split(",").forEach(t=>{const n=g&&g.elements&&g.elements.find(e=>e.tag==t);n&&(e={...e,...n})});const n=d.elements.map(t=>r.get(t.id)).filter(e=>!(!e||e.parent!==t||"systemLandscapeViews"===l&&"__enterprise__"===t.id&&"External"===e.location)).map(t=>t.id);n.length>0&&c.addGroup(t.id,t.name,n,e)}),c.init(e[c.id]),c}])},264(t,e,n){n(763);var o=n(686),i=n(987);const r="add-vertex",s="add-label-vertex",a="del-vertex",d="zoom-in",l="zoom-out",c="zoom-fit",h="zoom-100",u="select-all",A="deselect",g="move-left",p="move-right",f="move-up",m="move-down",b="move-left-fine",y="move-right-fine",x="move-up-fine",w="move-down-fine",C="toggle_drag_mode",v="align_horizontal",B="align_vertical",I="distribute_horizontal",k="distribute_vertical",E="auto_layout",S="reset_position",$="toggle_grid",M="toggle_snap_to_grid",_="snap_all_to_grid",L=[{name:"Help",list:[{id:"help",help:"Show/hide this help",combinations:[{key:"?",shift:!0},{key:"F1",shift:!0}]}]},{name:"File",list:[{id:"save",help:"Save",combinations:[{key:"s",ctrl:!0}]}]},{name:"History",list:[{id:"undo",help:"Undo",combinations:[{ctrl:!0,key:"z"}]},{id:"redo",help:"Redo",combinations:[{ctrl:!0,shift:!0,key:"z"},{ctrl:!0,key:"y"}]}]},{name:"Relationship editing",list:[{id:r,help:"Add relationship vertex",combinations:[{alt:!0,click:!0}]},{id:s,help:"Add label anchor relationship vertex",combinations:[{alt:!0,shift:!0,click:!0}]},{id:a,help:"Remove relationship vertex",combinations:[{key:"DELETE"},{key:"BACKSPACE"}]}]},{name:"Zoom",list:[{id:d,help:"Zoom in",combinations:[{ctrl:!0,key:"="}]},{id:l,help:"Zoom out",combinations:[{ctrl:!0,key:"-"}]},{id:c,help:"Zoom - fit",combinations:[{ctrl:!0,key:"9"}]},{id:h,help:"Zoom 100%",combinations:[{ctrl:!0,key:"0"}]},{id:"wheel_zoom",help:"Zoom in/out with mouse wheel",combinations:[{wheel:!0}]}]},{name:"Mouse Interactions",list:[{id:"pan-view",help:"Pan view (drag empty space)",combinations:[{click:!0}]},{id:"select-element",help:"Select element",combinations:[{click:!0}]},{id:"multi-select",help:"Add/remove from selection",combinations:[{shift:!0,click:!0}]},{id:"box-select",help:"Box selection (drag empty space)",combinations:[{shift:!0,click:!0}]},{id:"move-elements",help:"Move selected elements",combinations:[{click:!0}]}]},{name:"Select",list:[{id:u,help:"Select All",combinations:[{ctrl:!0,key:"a"}]},{id:A,help:"Deselect",combinations:[{key:"ESC"}]}]},{name:"Move",list:[{id:f,help:"Move up (grid increment)",combinations:[{key:"UP"}]},{id:x,help:"Move up (1 pixel)",combinations:[{key:"UP",shift:!0}]},{id:p,help:"Move right (grid increment)",combinations:[{key:"RIGHT"}]},{id:y,help:"Move right (1 pixel)",combinations:[{key:"RIGHT",shift:!0}]},{id:m,help:"Move down (grid increment)",combinations:[{key:"DOWN"}]},{id:w,help:"Move down (1 pixel)",combinations:[{key:"DOWN",shift:!0}]},{id:g,help:"Move left (grid increment)",combinations:[{key:"LEFT"}]},{id:b,help:"Move left (1 pixel)",combinations:[{key:"LEFT",shift:!0}]}]},{name:"View",list:[{id:C,help:"Toggle between pan and select mode",combinations:[{key:"t"}]},{id:S,help:"Reset position and view",combinations:[{key:"Home",ctrl:!0}]}]},{name:"Alignment",list:[{id:v,help:"Align selected elements horizontally",combinations:[{key:"h",ctrl:!0,shift:!0}]},{id:B,help:"Align selected elements vertically",combinations:[{key:"a",ctrl:!0,shift:!0}]},{id:I,help:"Distribute selected elements horizontally",combinations:[{key:"h",ctrl:!0,alt:!0}]},{id:k,help:"Distribute selected elements vertically",combinations:[{key:"v",ctrl:!0,alt:!0}]}]},{name:"Layout",list:[{id:E,help:"Auto layout all elements",combinations:[{key:"l",ctrl:!0}]}]},{name:"Grid",list:[{id:$,help:"Toggle grid visibility",combinations:[{key:"g",ctrl:!0}]},{id:M,help:"Toggle snap to grid",combinations:[{key:"g",ctrl:!0,shift:!0}]},{id:_,help:"Snap all elements to grid",combinations:[{key:"g",ctrl:!0,alt:!0}]}]}],D=L.reduce((t,e)=>t.concat(e.list),[]).reduce((t,e)=>(t[e.id]=e,t),{}),z=t=>[t.ctrl&&(0,o.sy)().toUpperCase(),t.shift&&"SHIFT",t.alt&&"ALT",t.key&&(t.key.length>1?t.key:`"${t.key.toUpperCase()}"`),t.click&&"CLICK",t.wheel&&"WHEEL"].filter(Boolean).join(" + ");n.d(e,["DE",0,I,"F",0,u,"Gg",0,g,"Gn",0,A,"Hd",0,E,"Help",0,()=>(0,i.jsxs)("div",{className:"popover",children:[(0,i.jsx)("h1",{children:"Shortcuts"}),(0,i.jsx)("table",{children:(0,i.jsx)("tbody",{children:L.map(t=>(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("tr",{children:(0,i.jsx)("th",{colSpan:2,children:t.name})}),t.list.map(t=>(0,i.jsxs)("tr",{children:[(0,i.jsx)("td",{children:t.combinations.map(z).join(", ")}),(0,i.jsx)("td",{children:t.help})]}))]}))})})]}),"J8",0,b,"Jk",0,B,"OE",0,_,"Op",0,$,"Ur",0,d,"Vy",0,k,"Yp",0,(t,e=!1,n=!1)=>{const i=Object.keys(D).filter(i=>((t,e,n,i)=>e.combinations.some(e=>{if(Boolean(e.shift)!=t.shiftKey)return!1;if(e.ctrl&&!(0,o.SA)(t))return!1;if(Boolean(e.alt)!=t.altKey)return!1;if(n)return e.click;if(i)return e.wheel;if(e.key){const n=t;return"DELETE"==e.key?"Delete"==n.key:"BACKSPACE"==e.key?"Backspace"==n.key:"ESC"==e.key?"Escape"==n.key:"UP"==e.key?"ArrowUp"==n.key:"DOWN"==e.key?"ArrowDown"==n.key:"LEFT"==e.key?"ArrowLeft"==n.key:"RIGHT"==e.key?"ArrowRight"==n.key:e.key&&n.key&&e.key.toLowerCase()==n.key.toLowerCase()}return!1}))(t,D[i],e,n));if(0!==i.length)return 1===i.length?i[0]:i.sort((t,e)=>{const n=D[t],o=D[e],i=n.combinations[0],r=o.combinations[0],s=(i.shift?1:0)+(i.ctrl?1:0)+(i.alt?1:0);return(r.shift?1:0)+(r.ctrl?1:0)+(r.alt?1:0)-s})[0]},"Zj",0,r,"_s",0,s,"_t",0,S,"aX",0,C,"b3",0,p,"bl",0,a,"hU",0,l,"hZ",0,M,"i1",0,h,"iD",0,y,"l8",0,x,"mD",0,c,"mt",0,w,"rB",0,m,"t9",0,v,"uK",0,f])},686(t,e,n){const o=()=>{if("undefined"==typeof navigator)return!1;if("userAgentData"in navigator&&navigator.userAgentData){const t=navigator.userAgentData.platform;if(t&&t.toLowerCase().includes("mac"))return!0}const t=navigator.userAgent.toLowerCase();if(t.includes("mac os")||t.includes("macintosh"))return!0;if(navigator.platform){const t=navigator.platform.toLowerCase();if(t.includes("mac")||t.includes("darwin"))return!0}try{if(void 0!==new KeyboardEvent("keydown",{metaKey:!0}).metaKey)return/mac|darwin|os x/i.test(navigator.userAgent)}catch(t){}return!1};n.d(e,["SA",0,t=>o()?t.metaKey:t.ctrlKey,"sy",0,()=>o()?"Cmd":"Ctrl"])},183(t,e,n){var o=n(220),i=n.n(o),r=n(716),s=n.n(r),a=n(628),d=n.n(a),l=new URL(n(660),n.b),c=new URL(n(620),n.b),h=new URL(n(749),n.b),u=new URL(n(690),n.b),A=s()(i()),g=d()(l),p=d()(c),f=d()(h),m=d()(u);A.push([t.id,`/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url(${g}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url(${p}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url(${f}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url(${m}) format("woff2");\n}\n`,"",{version:3,sources:["webpack://./src/fonts.css"],names:[],mappings:"AAAA,uFAAuF;AACvF;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF",sourcesContent:['/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url("@fontsource/inter/files/inter-latin-400-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url("@fontsource/inter/files/inter-latin-500-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url("@fontsource/inter/files/inter-latin-600-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url("@fontsource/inter/files/inter-latin-700-normal.woff2") format("woff2");\n}\n'],sourceRoot:""}]);const b=A;n.d(e,["A",0,b])},870(t,e,n){var o=n(220),i=n.n(o),r=n(716),s=n.n(r)()(i());s.push([t.id,"html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}","",{version:3,sources:["webpack://./src/style.css"],names:[],mappings:"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",sourcesContent:["html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}"],sourceRoot:""}]);const a=s;n.d(e,["A",0,a])},574(t,e,n){var o=n(582),i=n.n(o),r=n(991),s=n.n(r),a=n(725),d=n.n(a),l=n(798),c=n.n(l),h=n(754),u=n.n(h),A=n(567),g=n.n(A),p=n(183),f={};f.styleTagTransform=g(),f.setAttributes=c(),f.insert=d().bind(null,"head"),f.domAPI=s(),f.insertStyleElement=u(),i()(p.A,f),p.A&&p.A.locals&&p.A.locals},919(t,e,n){var o=n(582),i=n.n(o),r=n(991),s=n.n(r),a=n(725),d=n.n(a),l=n(798),c=n.n(l),h=n(754),u=n.n(h),A=n(567),g=n.n(A),p=n(870),f={};f.styleTagTransform=g(),f.setAttributes=c(),f.insert=d().bind(null,"head"),f.domAPI=s(),f.insertStyleElement=u(),i()(p.A,f),p.A&&p.A.locals&&p.A.locals}}]); +"use strict";(self.webpackChunkapp=self.webpackChunkapp||[]).push([[286],{247(t,e,n){n.d(e,["AV",0,(t,e)=>{Object.keys(e).forEach(n=>{const o=e[n];"number"==typeof o?t.style.setProperty(n,o.toString()):t.style.setProperty(n,o)})},"M5",0,{nodeBorder:{fill:"rgba(255, 255, 255, 0.86)",stroke:"#aaa",filter:"url(#shadow)"},nodeText:{"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},edgeText:{"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},edgeRect:{fill:"none",stroke:"none"},groupRect:{fill:"rgba(0, 0, 0, 0.02)",stroke:"#666","stroke-width":3,"stroke-dasharray":4},groupText:{fill:"#666","font-size":22,"font-weight":"500","font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",cursor:"default"}},"Y$",0,{thickness:3,color:"#999",opacity:1,fontSize:22,dashed:!0},"gd",0,(t,e)=>Math.sqrt((e.x-t.x)*(e.x-t.x)+(e.y-t.y)*(e.y-t.y)),"nD",0,{width:280,height:180,background:"rgba(255, 255, 255, .9)",color:"#666",opacity:.9,stroke:"#999",fontSize:22,shape:"Box"}])},977(t,e,n){n.d(e,{IW:()=>r,Zr:()=>s,l:()=>i});var o=n(247);function i(t){const e=t.from,n=t.to;let o=t.vertices?t.vertices.concat():[];o.unshift(e),o.push(n);const i=o.slice(1,-1),r=i.find(t=>!t.label)??n;let s=e;for(let t=i.length-1;t>=0;t--)if(!i[t].label){s=i[t];break}if(!e.intersect||!n.intersect)throw new Error(`edge ${t.id} nodes must be drawn before routing`);return o[0]=e.intersect(r),o[o.length-1]=n.intersect(s),o}function r(t,e,n){let i,r={x:n.x,y:n.y};const s=t.findIndex(t=>t.label);let a=!0;if(s>=0){const e=t[s];r=e,a=!0===e.auto;const n=[];s>0&&n.push({p:t[s-1],q:r}),st?(0,o.gd)(e.p,e.q)>(0,o.gd)(t.p,t.q)?e:t:e,void 0)}else{const n=t.slice(1).reduce((e,n,i)=>e+(0,o.gd)(t[i],n),0)*e;let s=0;for(let e=1;e0&&s+d>=n){const t=(n-s)/d;r={x:a.p.x+(a.q.x-a.p.x)*t,y:a.p.y+(a.q.y-a.p.y)*t},i=a;break}s+=d}}const d=i?Math.abs(i.q.x-i.p.x):0,l=i?Math.abs(i.q.y-i.p.y):0;return{...r,orientation:l>d?"vertical":"horizontal",segment:i,movable:a}}function s(t){if(t.length<2)throw new Error("edge route must contain at least two points");return t.slice(1).reduce((t,e)=>`${t} L${e.x},${e.y}`,`M${t[0].x},${t[0].y}`)}},20(t,e,n){n.d(e,{r:()=>i}),Symbol("ValidatedLayout");class o extends Error{issues;constructor(t){super(t.map(t=>t.message).join("\n")),this.name="GeometryValidationError",this.issues=t}}function i(t,e){const n=[];t.id!==e.diagramId&&n.push(C("missing_element",[t.id,e.diagramId],`layout ${e.diagramId} does not resolve measured diagram ${t.id}`)),r("measured node",t.nodes.map(t=>t.id),n),r("measured group",t.groups.map(t=>t.id),n),r("measured edge",t.edges.map(t=>t.id),n),r("resolved node",e.nodes.map(t=>t.id),n),r("resolved group",e.groups.map(t=>t.id),n),r("resolved edge",e.edges.map(t=>t.id),n),s("node",t.nodes.map(t=>t.id),e.nodes.map(t=>t.id),n),s("group",t.groups.map(t=>t.id),e.groups.map(t=>t.id),n),s("edge",t.edges.map(t=>t.id),e.edges.map(t=>t.id),n);const i=new Map(t.nodes.map(t=>[t.id,t])),b=new Map(t.groups.map(t=>[t.id,t])),y=new Map(e.nodes.map(t=>[t.id,t])),v=new Map(e.groups.map(t=>[t.id,t]));a("diagram",e.diagramId,e.bounds,n);for(const t of e.nodes){a("node",t.id,t.bounds,n);const o=i.get(t.id);!o||x(o.size.width,t.bounds.width)&&x(o.size.height,t.bounds.height)||n.push(C("size_mismatch",[t.id],`node ${t.id} size changed after measurement`)),o&&(o.contentSize.width>t.bounds.width||o.contentSize.height>t.bounds.height)&&n.push(C("content_overflow",[t.id],`node ${t.id} content exceeds its resolved bounds`)),d("node",t.id,t.bounds,e.bounds,n)}for(const t of e.groups){a("group",t.id,t.bounds,n),a("group title",t.id,t.titleBounds,n),d("group",t.id,t.bounds,e.bounds,n),g(t.bounds,t.titleBounds)||n.push(C("false_containment",[t.id],`group ${t.id} does not contain its title`));const o=b.get(t.id);!o||x(o.titleSize.width,t.titleBounds.width)&&x(o.titleSize.height,t.titleBounds.height)||n.push(C("size_mismatch",[t.id],`group ${t.id} title size changed after measurement`))}if(function(t,e){w(t,(t,n)=>{A(t.bounds,n.bounds)&&e.push(C("node_overlap",[t.id,n.id],`nodes ${t.id} and ${n.id} overlap`))})}(e.nodes,n),function(t,e){w(t,(t,n)=>{!A(t.bounds,n.bounds)||g(t.bounds,n.bounds)||g(n.bounds,t.bounds)||e.push(C("group_overlap",[t.id,n.id],`groups ${t.id} and ${n.id} partially overlap`))})}(e.groups,n),function(t,e,n,o,i){for(const e of t){const t=n.get(e.id);if(t&&t.parentId!==e.parentId&&i.push(C("false_containment",[e.id],`node ${e.id} has the wrong parent ID`)),!e.parentId)continue;const r=o.get(e.parentId);t&&r&&!g(r.bounds,t.bounds)&&i.push(C("false_containment",[e.id,e.parentId],`node ${e.id} lies outside owning group ${e.parentId}`))}for(const t of e){const e=o.get(t.id);if(e&&e.parentId!==t.parentId&&i.push(C("false_containment",[t.id],`group ${t.id} has the wrong parent ID`)),!t.parentId)continue;const n=o.get(t.parentId);e&&n&&!g(n.bounds,e.bounds)&&i.push(C("false_containment",[t.id,t.parentId],`group ${t.id} lies outside owning group ${t.parentId}`))}}(t.nodes,t.groups,y,v,n),function(t,e,n,o,i,r,s){const g=new Map(t.edges.map(t=>[t.id,t])),b=[];for(const t of e.edges){const y=g.get(t.id);if(y)if(t.sourceId===y.sourceId&&t.targetId===y.targetId||s.push(C("disconnected_edge",[t.id,t.sourceId,t.targetId],`edge ${t.id} endpoints do not match its measured contract`)),0===t.sections.length||t.sections.some(t=>t.length<2))s.push(C("disconnected_edge",[t.id],`edge ${t.id} has no complete route section`));else{l(t,n,s);for(const n of t.sections)for(const o of n)if(!m(o)||!p(o,e.bounds,!0)){s.push(C("out_of_bounds",[t.id],`edge ${t.id} has a route point outside the resolved view`));break}for(const e of n.values())e.id!==t.sourceId&&e.id!==t.targetId&&u(t.sections,f(e.bounds,.5))&&s.push(C("edge_through_node",[t.id,e.id],`edge ${t.id} crosses unrelated node ${e.id}`));for(const e of o.values()){const n=c(t.sourceId,e.id,i,r);n===c(t.targetId,e.id,i,r)&&h(t.sections,e.bounds,n)&&s.push(C("edge_through_group",[t.id,e.id],`edge ${t.id} violates group ${e.id} boundary`))}if(t.labelBounds){a("edge label",t.id,t.labelBounds,s),d("edge label",t.id,t.labelBounds,e.bounds,s),!y.labelSize||x(y.labelSize.width,t.labelBounds.width)&&x(y.labelSize.height,t.labelBounds.height)||s.push(C("size_mismatch",[t.id],`edge ${t.id} label size changed after measurement`));for(const e of n.values())A(t.labelBounds,e.bounds)&&s.push(C("label_node_overlap",[t.id,e.id],`edge ${t.id} label overlaps node ${e.id}`));for(const e of o.values())A(t.labelBounds,e.titleBounds)&&s.push(C("label_title_overlap",[t.id,e.id],`edge ${t.id} label overlaps group ${e.id} title`));b.push({id:t.id,bounds:t.labelBounds})}else y.labelSize&&s.push(C("missing_element",[t.id],`edge ${t.id} is missing its measured label`))}}w(b,(t,e)=>{A(t.bounds,e.bounds)&&s.push(C("label_label_overlap",[t.id,e.id],`edge labels ${t.id} and ${e.id} overlap`))});for(const t of b)for(const n of e.edges)u(n.sections,f(t.bounds,.5))&&s.push(C("label_edge_overlap",[t.id,n.id],`edge ${n.id} crosses label ${t.id}`))}(t,e,y,v,i,b,n),n.length>0)throw new o(n);return e}function r(t,e,n){const o=new Set;for(const i of e)o.has(i)&&n.push(C("duplicate_id",[i],`${t} ${i} is duplicated`)),o.add(i)}function s(t,e,n,o){const i=new Set(e),r=new Set(n);for(const e of i)r.has(e)||o.push(C("missing_element",[e],`${t} ${e} is missing from resolved layout`));for(const e of r)i.has(e)||o.push(C("unexpected_element",[e],`${t} ${e} is not in the measured diagram`))}function a(t,e,n,o){(!function(t){return m(t)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}(n)||n.width<0||n.height<0)&&o.push(C("non_finite",[e],`${t} ${e} has invalid geometry`))}function d(t,e,n,o,i){g(o,n)||i.push(C("out_of_bounds",[e],`${t} ${e} lies outside the resolved view bounds`))}function l(t,e,n){for(let e=1;et.some((n,o)=>o>0&&function(t,e,n){const o=e.x-t.x,i=e.y-t.y;let r=0,s=1;for(const[e,a,d,l]of[[t.x,o,n.x,n.x+n.width],[t.y,i,n.y,n.y+n.height]]){if(Math.abs(a)=l)return!1;continue}const t=(d-e)/a,n=(l-e)/a;if(r=Math.max(r,Math.min(t,n)),s=Math.min(s,Math.max(t,n)),r>=s)return!1}return s>0&&r<1}(t[o-1],n,e)))}function A(t,e){return t.x=e.x+e.width&&t.y+t.height>=e.y+e.height}function p(t,e,n){return n?t.x>=e.x&&t.y>=e.y&&t.x<=e.x+e.width&&t.y<=e.y+e.height:t.x>e.x&&t.y>e.y&&t.x=e.x-.1&&t.x<=e.x+e.width+.1,o=t.y>=e.y-.1&&t.y<=e.y+e.height+.1,i=x(t.x,e.x)||x(t.x,e.x+e.width),r=x(t.y,e.y)||x(t.y,e.y+e.height);return o&&i||n&&r}function x(t,e){return Math.abs(t-e)<=.1}function w(t,e){for(let n=0;nM,Qy:()=>U,ZG:()=>N,oP:()=>F,IX:()=>O,F_:()=>et,Kp:()=>tt,a_:()=>H});const o=new Map;let i;function r(t,e){const n=function(t,e){const n=Object.entries(e).sort(([t],[e])=>t.localeCompare(e));return JSON.stringify([t,n])}(t,e),r=o.get(n);if(r)return r;const s=document.createElementNS("http://www.w3.org/2000/svg","text");s.setAttribute("x","0"),s.setAttribute("y","0");for(const[t,n]of Object.entries(e))s.setAttribute(t,n);s.textContent=t,(i?.isConnected||(i=document.createElementNS("http://www.w3.org/2000/svg","svg"),i.setAttribute("aria-hidden","true"),i.style.position="absolute",i.style.visibility="hidden",i.style.pointerEvents="none",document.body.appendChild(i)),i).appendChild(s);const a=s.getBBox();s.remove();const d={width:a.width,height:a.height};return o.set(n,d),d}function s(t,e,n){let o=0;return{lines:t.trim().split("\n").flatMap(t=>{const i=[];let s=[];for(const d of t.trim().split(/\s+/)){if(r(d,n).width>e){s.length>0&&(i.push(s.join(" ")),s=[]);const t=a(d,e,n);for(const e of t.slice(0,-1))i.push(e),o=Math.max(o,r(e,n).width);s=t.slice(-1);continue}const t=[...s,d],l=r(t.join(" "),n);l.width>e&&s.length>0?(i.push(s.join(" ")),s=[d]):(o=Math.max(o,l.width),s=t)}if(s.length>0){const t=s.join(" ");i.push(t),o=Math.max(o,r(t,n).width)}return i}),maxW:o}}function a(t,e,n){const o=[];let i="";for(const s of t){const t=i+s;r(t,n).width>e&&i.length>0?(o.push(i),i=s):i=t}return i.length>0&&o.push(i),o}const d={element(t,e={},n){const o=document.createElementNS("http://www.w3.org/2000/svg",t);return Object.entries(e).forEach(([t,e])=>o.setAttribute(t,String(e))),n&&o.classList.add(n),o},use(t,e={}){const n=this.element("use",e);return n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+t),n},path(t,e={},n){return this.element("path",{...e,d:t},n)},text(t,e={}){const n=this.element("text",e);return t&&(n.textContent=t),n},textArea(t,e,n,o,i=0,r=0,a=""){const d={"font-size":`${n}px`,"font-weight":o?"bold":"normal"},{lines:l,maxW:c}=s(t,e,d),h=this.text("",{x:0,y:r,"text-anchor":a||void 0});return l.forEach((t,e)=>{const o=this.element("tspan",{x:i,dy:`${n+2}px`,...d});o.textContent=t,h.append(o)}),{txt:h,dy:(l.length+1)*(n+2),maxW:c}},rect(t,e,n=0,o=0,i=0,r){return this.element("rect",{x:n,y:o,rx:i,ry:i,width:t,height:e},r)},icon(t,e=0,n=0){return this.use(t,{x:e,y:n})},expand(t,e,n){const o=this.element("g",{transform:`translate(${t},${e})`},"expand");return o.append(this.rect(19,19,0,0,1),this.text(n?"-":"+",{x:10,y:14,"text-anchor":"middle"})),o}};function l(t,e,n){t.setAttribute("transform",`translate(${e},${n})`)}function c(t,e,n=!0){return n?t.x>e.x-e.width/2&&t.xe.y-e.height/2&&t.ye.x&&t.xe.y&&t.yfunction(t,e,n,o){let i,r,s,a,d,l={x:null,y:null,onLine1:!1,onLine2:!1};return i=(o.y-n.y)*(e.x-t.x)-(o.x-n.x)*(e.y-t.y),0==i||(r=t.y-n.y,s=t.x-n.x,a=(o.x-n.x)*r-(o.y-n.y)*s,d=(e.x-t.x)*r-(e.y-t.y)*s,r=a/i,s=d/i,l.x=t.x+r*(e.x-t.x),l.y=t.y+r*(e.y-t.y),r>0&&r<1&&(l.onLine1=!0),s>=0&&s<=1&&(l.onLine2=!0)),l}(t,e,n.p,n.q)).filter(t=>t.onLine1&&t.onLine2)}(t,e,t)[0]||{x:t.x,y:t.y}}function u(t,e,n,o,i){const r={x:i.x-t.x,y:i.y-t.y},s=o.x-t.x,a=o.y-t.y;s==r.x&&(r.x+=1e-7);const d=(a-r.y)/(s-r.x),l=a-d*s,c=n*n+e*e*d*d,h=2*e*e*l*d,u=e*e*l*l-e*e*n*n,A=Math.sqrt(h*h-4*c*u),g=r.x>s?(-h+A)/(2*c):(-h-A)/(2*c),p={x:g,y:d*g+l};return p.x+=t.x,p.y+=t.y,p}function A(t,e,n){let o=n.x-e.x,i=n.y-e.y,r=o*o+i*i,s=(t.x-e.x)*o+(t.y-e.y)*i,a=Math.min(1,Math.max(0,s/r));return{x:e.x+o*a,y:e.y+i*a}}function g(t,e){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function p(t){return t/2/(5.5+t/70)}function f(t,e,n){switch(t.toLowerCase()){case"cylinder":return 2*p(e);case"person":return.4*n;case"folder":return e/10;case"robot":return.35*n;case"webbrowser":return n/8;default:return 0}}class m{_el;constructor(t){this._el=t}node(){return this._el}attr(t,e){return this._el.setAttribute(t,String(e)),this}insert(t,e){const n=document.createElementNS("http://www.w3.org/2000/svg",t),o=this._el.insertBefore(n,this._el.querySelector(e));return new m(o)}}function b(t,e,n,o=!1){const i=t.insert("rect",":first-child").attr("rx",o?n.width/8:3).attr("ry",o?n.width/8:3).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return h(n,t)},i}function y(t,e,n,o,i){const r=t.insert("ellipse",":first-child").attr("cx",0).attr("cy",0).attr("rx",o).attr("ry",i).attr("width",n.width).attr("height",n.height);return n.intersect=function(t){return u(n,o,i,n,t)},r}function x(t,e,n){const o=n.width/8,i=n.width/14,r=t.insert("g",":first-child");return r.insert("path",":first-child").attr("d",`M${-n.width/2},${-n.height/2} l${n.width},0 M${-n.width/2},${n.height/2} l${n.width},0`),r.insert("circle",":first-child").attr("cx",0).attr("cy",n.height/2+o/2).attr("r",.4*i),r.insert("rect",":first-child").attr("x",-i).attr("y",-n.height/2-o/2-.2*i).attr("width",2*i).attr("height",.4*i),r.insert("rect",":first-child").attr("rx",i).attr("ry",i).attr("x",-e.width/2).attr("y",-e.height/2-o).attr("width",e.width).attr("height",e.height+2*o),n.intersect=function(t){return h({x:n.x,y:n.y,width:n.width,height:n.height+2*o},t)},r}const w={box:(t,e)=>b(new m(t),e,e).node(),roundedbox:(t,e)=>b(new m(t),e,e,!0).node(),component:(t,e)=>function(t,e,n){const o=n.width/10,i=t.insert("g",":first-child");return i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2-o).attr("y",-n.height/2+o).attr("width",2*o).attr("height",o),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2-o).attr("y",-n.height/2+2.5*o).attr("width",2*o).attr("height",o),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2).attr("width",n.width).attr("height",n.height),n.intersect=function(t){return h({x:n.x-o/2,y:n.y,width:n.width+o,height:n.height},t)},i}(new m(t),0,e).node(),cylinder:(t,e)=>function(t,e,n){const o=e.width,i=o/2,r=p(o),s=e.height,a=`M 0,${r} a${i},${r} 0,0,0 ${o} 0 a ${i},${r} 0,0,0 ${-o} 0 l 0,${s-2*r} a ${i},${r} 0,0,0 ${o} 0 l 0,${2*r-s}`,d=t.attr("label-offset-y",f("cylinder",o,s)).insert("path",":first-child").attr("d",a).attr("transform","translate("+-o/2+","+-s/2+")");return n.intersect=function(t){const e=h(n,t);let o=n.y+n.height/2-r;return e.y>o?u({x:n.x,y:o},i,r,n,t):(o=n.y-n.height/2+r,e.yfunction(t,e,n){const o=e.width,i=e.height,r=`M ${.38*o},${i/3} A${o/2},${i/2} 0,0,0 0 ${i/2}\n\t\tL${o/11},${i} L${o-o/11},${i} L${o},${i/2}\n\t\tA${o/2},${i/2} 0,0,0 ${o-.38*o} ${i/3} \n\t\tA${o/6},${o/6} 0,1,0 ${.38*o} ${i/3}`,s=t.attr("label-offset-y",f("person",o,i)).insert("path",":first-child").attr("d",r).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(t){return h(n,t)},s}(new m(t),e,e).node(),circle:(t,e)=>function(t,e,n){return y(t,0,n,n.width/2,n.width/2)}(new m(t),0,e).node(),ellipse:(t,e)=>function(t,e,n){return y(t,0,n,.55*n.width,.45*n.width)}(new m(t),0,e).node(),hexagon:(t,e)=>function(t,e,n){const o=n.width/2,i=t.insert("polygon",":first-child").attr("points",[.5,.866,1,0,.5,-.866,-.5,-.866,-1,-0,-.5,.866,.5,.866].map(t=>t*o).join(",")).attr("width",n.width).attr("height",n.height);return n.intersect=function(t){return u(n,n.width/2,n.width/2,n,t)},i}(new m(t),0,e).node(),folder:(t,e)=>function(t,e,n){const o=n.width/20,i=t.attr("label-offset-y",f("folder",n.width,n.height)).insert("g",":first-child");return i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2+2*o).attr("width",n.width).attr("height",n.height-2*o),i.insert("path",":first-child").attr("d",`M0,${-n.height/2+2*o} l${o},${-2*o} h${n.width/2-2*o} v${2*o}`),n.intersect=function(t){return h({x:n.x,y:n.y+o/2,width:n.width,height:n.height+o},t)},i}(new m(t),0,e).node(),mobiledevicelandscape:(t,e)=>function(t,e,n){const o=n.width/8,i=n.width/14,r=t.insert("g",":first-child");return r.insert("path",":first-child").attr("d",`M${-n.width/2},${-n.height/2} l0,${n.height} M${n.width/2},${-n.height/2} l0,${n.height}`),r.insert("circle",":first-child").attr("cx",-n.width/2-o/2).attr("cy",0).attr("r",.4*i),r.insert("rect",":first-child").attr("x",n.width/2+o/2-.2*i).attr("y",-i).attr("width",.4*i).attr("height",2*i),r.insert("rect",":first-child").attr("rx",i).attr("ry",i).attr("x",-e.width/2-o).attr("y",-e.height/2).attr("width",e.width+2*o).attr("height",e.height),n.intersect=function(t){return h({x:n.x,y:n.y,width:n.width+2*o,height:n.height},t)},r}(new m(t),e,e).node(),mobiledeviceportrait:(t,e)=>x(new m(t),e,e).node(),mobiledevice:(t,e)=>x(new m(t),e,e).node(),pipe:(t,e)=>function(t,e,n){const o=n.width,i=n.height,r=i/2,s=r/(2.5+o/70),a=`M${-s},0\n\t\ta${s},${r} 0,0,1 0,${i}\n\t\ta${s},${r} 0,0,1 0,${-i}\n\t\tl${o},0\n\t\ta${s},${r} 0,0,1 0,${i}\n\t\tl${-o},0`,d=t.insert("path",":first-child").attr("d",a).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(t){return h({x:n.x-s,y:n.y,width:n.width+2*s,height:n.height},t)},d}(new m(t),0,e).node(),robot:(t,e)=>function(t,e,n){const o=n.width,i=n.height,r=Math.min(.28*o,.25*i),s=.2*r,a=.25*r,d=.08*r,l=.12*r,c=.22*r,u=.12*r,A=.3*r,g=o,p=-i/2+a+r,m=i-a-r,b=t.attr("label-offset-y",f("robot",o,i)).insert("g",":first-child");b.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-g/2).attr("y",p).attr("width",g).attr("height",m);const y=-i/2+a;b.insert("rect",":first-child").attr("rx",s).attr("ry",s).attr("x",-r/2).attr("y",y).attr("width",r).attr("height",r),b.insert("line",":first-child").attr("class","robot-antenna").attr("x1",0).attr("y1",y).attr("x2",0).attr("y2",-i/2+2*d).attr("stroke-width",.6*d).attr("stroke-linecap","round"),b.insert("circle",":first-child").attr("class","robot-antenna-ball").attr("cx",0).attr("cy",-i/2+2*d).attr("r",1.2*d);const x=y+.4*r;b.insert("circle",":first-child").attr("class","robot-eye").attr("cx",-c).attr("cy",x).attr("r",l),b.insert("circle",":first-child").attr("class","robot-eye").attr("cx",c).attr("cy",x).attr("r",l);const w=y+.7*r,C=.28*r;return b.insert("path",":first-child").attr("class","robot-mouth").attr("d",`M${-C/2},${w} Q0,${w+.3*C} ${C/2},${w}`).attr("fill","none").attr("stroke-width",.5*d).attr("stroke-linecap","round"),b.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",-r/2-u-1).attr("y",x-A/2).attr("width",u).attr("height",A),b.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",r/2+1).attr("y",x-A/2).attr("width",u).attr("height",A),n.intersect=function(t){return h(n,t)},b}(new m(t),0,e).node(),webbrowser:(t,e)=>function(t,e,n){const o=n.height/8,i=t.attr("label-offset-y",f("webbrowser",n.width,n.height)).insert("g",":first-child");return i.insert("path",":first-child").attr("d",`\n\t\t\tM${-n.width/2},${-n.height/2+o} h${n.width}\n\t\t\tM${-n.width/2+o/4},${-n.height/2+o/4} h${o/2} v${o/2} h${-o/2} z\n\t\t\tM${-n.width/2+o},${-n.height/2+o/4} h${n.width-o-o/4} v${o/2} h${-n.width+o+o/4} z\n\t\t`),i.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-n.width/2).attr("y",-n.height/2).attr("width",n.width).attr("height",n.height),n.intersect=function(t){return h(n,t)},i}(new m(t),0,e).node()};var C=n(983);class v{versions=[];pos=0;lastSavedPos=0;exportDoc;importDoc;change;tmpPreviousState=null;constructor(t,e,n){this.exportDoc=e,this.importDoc=n,this.change=function(t){let e;return function(){const n=this;clearTimeout(e),e=setTimeout(function(){e=null,t.apply(n)},300)}}(()=>this.saveNow())}beforeChange(){this.tmpPreviousState||(this.tmpPreviousState=this.deepClone(this.exportDoc()))}length(){return this.versions.length}currentState(){return this.deepClone(this.versions[this.pos-1])}saveNow(){if(!this.tmpPreviousState)throw Error("undo.change() was called without previously calling undo.beforeChange()!");this.versions[this.pos]=this.deepClone(this.exportDoc()),this.versions[this.pos-1]=this.tmpPreviousState,this.tmpPreviousState=null,this.pos+=1,this.versions.splice(this.pos)}deepClone(t){return"undefined"!=typeof structuredClone?structuredClone(t):JSON.parse(JSON.stringify(t))}undo(){if(this.pos<2)return;this.pos-=1;const t=this.versions[this.pos-1];this.importDoc(this.deepClone(t))}redo(){if(this.pos>this.versions.length-1)return;const t=this.versions[this.pos];this.importDoc(this.deepClone(t)),this.pos+=1}changed(){return this.pos!==this.lastSavedPos}setSaved(){this.lastSavedPos=this.pos}}var B=n(264),I=n(247),k=n(977);function E(t,e,n,o,i,r){const a=s(t,e,{"font-family":String(I.M5.nodeText["font-family"]),"font-size":`${n}px`,"font-weight":o?"bold":"normal"});return{lines:a.lines.length>0?a.lines:[""],fontSize:n,lineHeight:n+2,bold:o,field:r,gapAfter:i}}n(20);const S=I.Y$,$=I.nD;class M{id;name;nodesMap;edges;edgeVertices;groupsMap;metadata;layoutDirection;validatedLayout;colorToVarMap=new Map;_undo;_gridVisible=!1;_snapToGrid=!0;_gridSize=25;_skipAutoFit=!1;constructor(t,e){this.id=t,this.name=e,this.edges=[],this.edgeVertices=new Map,this.nodesMap=new Map,this.groupsMap=new Map,this._undo=new v(this.id,()=>this.exportLayout(!0),t=>this.importLayout(t,!0)),window.graph=this}init(t){t&&this.importLayout(t),this._undo=new v(this.id,()=>this.exportLayout(!0),t=>this.importLayout(t,!0)),this._undo.length()&&this.importLayout(this._undo.currentState()),this._undo.beforeChange(),this._undo.change()}addNode(t,e,n,o,i,r){if(this.nodesMap.has(t))throw Error("duplicate node: "+t);const s={...$,...i},a=(s.shape||"Box").toLowerCase(),d="person"===a?240:180,l=Math.max(280,s.width||0),c=function(t,e,n,o,i){const r=Math.max(o-36,80),s=[E(t,r,i,!0,6,"name"),E(`[${e}]`,r,.75*i,!1,10),E(n,r,Math.min(.8*i,16),!1,0,"description")],a=s.reduce((t,e)=>t+e.lines.length*e.lineHeight+e.gapAfter,0);return{blocks:s,textHeight:a,minimumHeight:a+36}}(e,n,o,l,s.fontSize||22);let h=Math.max(d,s.height||0,c.minimumHeight);for(let t=0;t<20;t++){const t=c.minimumHeight+Math.abs(f(a,l,h));if(t<=h+.1)break;h=t}const u={id:t,title:e,sub:n,description:o,style:s,x:0,y:0,width:l,height:h,intersect:null,link:r,contentLayout:c};this.nodesMap.set(u.id,u)}nodes(){return Array.from(this.nodesMap.values())}addEdge(t,e,n,o,i,r){i&&i.forEach((e,n)=>{const o=e;o.id=`v-${t}-${n}`,this.edgeVertices.set(o.id,o)});const s={id:t,from:this.nodesMap.get(e),to:this.nodesMap.get(n),label:o,vertices:null,style:{...S,...r},initVertex:t=>{const e=t;return e.id||(e.id=((t,e)=>`v-${t}-a-${(t=>{let e=2166136261;for(let n=0;n>>0).toString(36)})(`${t}:${e.x}:${e.y}`)}`)(s.id,t),this.edgeVertices.set(e.id,e)),e.edge=s,t},userDeletedVertices:!1};this.edges.push(s),i&&(s.vertices=i.map(t=>s.initVertex(t)))}addGroup(t,e,n,o){if(this.groupsMap.has(t))return void console.error(`Group exists: ${t} ${e}`);const i={id:t,name:e,x:null,y:null,width:null,height:null,nodes:n.map(n=>{const o=this.nodesMap.get(n)||this.groupsMap.get(n);return o||console.error(`Node or group ${n} not found for group ${t} "${e}"`),o}).filter(Boolean),style:o};this.groupsMap.set(t,i)}setNodeSelected(t,e){t.selected=e,e?t.ref.classList.add("selected"):t.ref.classList.remove("selected"),this.updateEdgesSel()}updateEdgesSel(){this.edges.forEach(t=>{t.to.selected||t.from.selected?t.ref.classList.add("selected"):t.ref.classList.remove("selected")})}clearResolvedLayout(){this.validatedLayout=void 0,this.edges.forEach(t=>{t.layoutSections=void 0,t.layoutLabelBounds=void 0}),this.groupsMap.forEach(t=>{t.layoutBounds=void 0,t.layoutTitleBounds=void 0})}moveNode(t,e,n,o=!1,i=!1){if(t){if(this._snapToGrid&&!o){const t=this.snapToGrid(e,n);e=t.x,n=t.y}t.x==e&&t.y==n||(i||this._undo.beforeChange(),this.clearResolvedLayout(),t.x=e,t.y=n,l(t.ref,e,n),this.redrawEdges(t),this.redrawGroups(t),i||this._undo.change())}}moveEdgeVertex(t,e,n,o=!1,i=!1){if(this._snapToGrid&&!o){const t=this.snapToGrid(e,n);e=t.x,n=t.y}t.x==e&&t.y==n||(i||this._undo.beforeChange(),this.clearResolvedLayout(),t.x=e,t.y=n,this.redrawEdge(t.edge),i||this._undo.change())}moveSelected(t,e,n=!1){this.nodes().forEach(o=>o.selected&&this.moveNode(o,o.x+t,o.y+e,n,!1)),this.edgeVertices.forEach(o=>o.selected&&this.moveEdgeVertex(o,o.x+t,o.y+e,n,!1))}insertEdgeVertex(t,e,n,o){this._undo.beforeChange(),this.clearResolvedLayout();const i=t.initVertex(e);i.selected=!0,o&&(t.vertices.forEach(t=>t.label=!1),i.label=!0),t.vertices.splice(n-1,0,i),this.redrawEdge(t),this._undo.change()}deleteEdgeVertex(t){this._undo.beforeChange(),this.clearResolvedLayout();const e=t.edge.vertices.indexOf(t);e>=0&&(t.edge.vertices.splice(e,1),this.edgeVertices.delete(t.id),t.edge.userDeletedVertices=!0),this.redrawEdge(t.edge),this._undo.change()}changed(){return this._undo.changed()}undo(){this._undo.undo()}redo(){this._undo.redo()}alignTopLeft(){const t=this.calculateContentBounds(),e=100-t.x,n=100-t.y;this._skipAutoFit=!0,this._undo.beforeChange(),this.nodesMap.forEach(t=>{this.moveNode(t,t.x+e,t.y+n,!0,!0)}),this.edgeVertices.forEach(t=>{this.moveEdgeVertex(t,t.x+e,t.y+n,!0,!0)}),this._undo.change()}resetPanTransform(){const t=O(),e=z.querySelector("g.zoom");e&&(e.setAttribute("transform",`scale(${t}) translate(0, 0)`),function(t){const e=t.calculateContentBounds(),n=O(),o=Math.max(z.parentElement.clientWidth/n,e.x+e.width+20),i=Math.max(z.parentElement.clientHeight/n,e.y+e.height+20);z.setAttribute("width",String(o*n)),z.setAttribute("height",String(i*n))}(this)),nt(this.id),this._skipAutoFit=!1}shouldSkipAutoFit(){return this._skipAutoFit}resetView(){const t=z.querySelector("g.zoom");t&&(t.setAttribute("transform","scale(1) translate(0, 0)"),X()),nt(this.id)}redrawEdges(t){this.edges.forEach(e=>(t==e.from||t==e.to)&&this.redrawEdge(e)),this.updateEdgesSel()}redrawEdge(t){const e=t.ref.parentElement;e.removeChild(t.ref),t.ref=j(t),e.append(t.ref)}redrawGroups(t){this.groupsMap.forEach(t=>{const e=t.ref.parentElement;e.removeChild(t.ref),W(t),e.append(t.ref)})}exportSVG(){this.validatedLayout||(this.validatedLayout=(0,C.U)(this));const t=document.querySelector("svg#graph"),e=(t.querySelector("rect.elastic"),t.cloneNode(!0));e.querySelectorAll("a.nodeLink[data-export-href]").forEach(t=>{t.setAttribute("href",t.getAttribute("data-export-href")||""),t.removeAttribute("data-export-href")});const n=e.querySelector("rect.elastic");n&&n.remove();const o=this.validatedLayout.bounds,i=o.width+100,r=o.height+100,s=50-o.x,a=50-o.y,d=e.querySelector("g.zoom");d&&d.setAttribute("transform",`scale(1) translate(${s}, ${a})`),e.setAttribute("viewBox",`0 0 ${i} ${r}`),e.setAttribute("width",String(i)),e.setAttribute("height",String(r)),e.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.convertStylesToCustomProperties(e);const l=document.createElement("script");return l.setAttribute("type","application/json"),this.metadata.layout=this.exportLayout(),l.append("/g,"]]]>]>"),e.insertBefore(l,e.firstChild),e.outerHTML}convertStylesToCustomProperties(t){0!==this.colorToVarMap.size&&(t.querySelectorAll("[fill]").forEach(t=>{const e=t.getAttribute("fill");e&&this.colorToVarMap.has(e)&&t.setAttribute("fill",`var(${this.colorToVarMap.get(e)}, ${e})`)}),t.querySelectorAll("[stroke]").forEach(t=>{const e=t.getAttribute("stroke");e&&this.colorToVarMap.has(e)&&t.setAttribute("stroke",`var(${this.colorToVarMap.get(e)}, ${e})`)}))}calculateContentBounds(){let t=1/0,e=1/0,n=-1/0,o=-1/0;return this.nodes().forEach(i=>{const r=i.x-i.width/2,s=i.x+i.width/2,a=i.y-i.height/2,d=i.y+i.height/2;t=Math.min(t,r),n=Math.max(n,s),e=Math.min(e,a),o=Math.max(o,d)}),this.edgeVertices.forEach(i=>{t=Math.min(t,i.x-5),n=Math.max(n,i.x+5),e=Math.min(e,i.y-5),o=Math.max(o,i.y+5)}),this.groupsMap.forEach(i=>{const r=i.x-i.width/2,s=i.x+i.width/2,a=i.y-i.height/2,d=i.y+i.height/2;t=Math.min(t,r),n=Math.max(n,s),e=Math.min(e,a),o=Math.max(o,d)}),this.edges.forEach(i=>{if(t=Math.min(t,i.from.x-10,i.to.x-10),n=Math.max(n,i.from.x+10,i.to.x+10),e=Math.min(e,i.from.y-10,i.to.y-10),o=Math.max(o,i.from.y+10,i.to.y+10),i.vertices&&i.vertices.forEach(i=>{t=Math.min(t,i.x-10),n=Math.max(n,i.x+10),e=Math.min(e,i.y-10),o=Math.max(o,i.y+10)}),i.label&&i.label.trim()){const r=(i.from.x+i.to.x)/2,s=(i.from.y+i.to.y)/2,a=10*i.label.length+50;t=Math.min(t,r-a),n=Math.max(n,r+a),e=Math.min(e,s-25),o=Math.max(o,s+25)}}),t===1/0?{x:0,y:0,width:100,height:100}:{x:t,y:e,width:n-t,height:o-e}}exportLayout(t=!1){const e={};return this.nodes().forEach(t=>e[t.id]={x:t.x,y:t.y}),this.edges.forEach(n=>{if(!n.vertices)return;const o=n.vertices.map(t=>({x:t.x,y:t.y,label:t.label,auto:t.auto}));(o.length||t)&&(e[`e-${n.id}`]=o),n.userDeletedVertices&&(e[`e-${n.id}-deleted`]=!0)}),e}setSaved(){this._undo.setSaved()}importLayout(t,e=!1){this.clearResolvedLayout();for(const[e,n]of Object.entries(t)){const t=this.nodesMap.get(e);if(t){const o=L(e,n);t.x=o.x,t.y=o.y;continue}if(e.endsWith("-deleted")){const t=e.slice(2,-8),o=this.edges.find(e=>e.id===t);if(!o||!0!==n)throw new Error(`invalid deleted-edge layout entry ${e}`);o.userDeletedVertices=!0;continue}if(e.startsWith("e-")){const t=this.edges.find(t=>t.id===e.slice(2));if(!t||!Array.isArray(n))throw new Error(`invalid edge layout entry ${e}`);for(const e of t.vertices??[])this.edgeVertices.delete(e.id);t.vertices=n.map((n,o)=>{const i=L(`${e}[${o}]`,n),r=t.initVertex(i);return r.label=n.label,r.auto=n.auto,r});continue}throw new Error(`layout contains unknown element ${e}`)}for(const e of this.nodes())if(!(e.id in t))throw new Error(`layout is missing node ${e.id}`);e&&(this.nodes().forEach(t=>l(t.ref,t.x,t.y)),this.edges.forEach(t=>this.redrawEdge(t)),this.updateEdgesSel(),this.redrawGroups(null))}async autoLayout(t){const e=await(0,C.k)(this,t);this._undo.beforeChange();for(const t of e.nodes){const e=this.nodesMap.get(t.id);if(!e)throw new Error(`layout contains unknown node ${t.id}`);e.x=t.x,e.y=t.y,l(e.ref,e.x,e.y)}for(const t of e.groups){const e=this.groupsMap.get(t.id);if(!e)throw new Error(`layout contains unknown group ${t.id}`);e.layoutBounds=t.bounds,e.layoutTitleBounds=t.titleBounds,e.x=t.bounds.x+t.bounds.width/2,e.y=t.bounds.y+t.bounds.height/2,e.width=t.bounds.width,e.height=t.bounds.height}for(const t of e.edges){const e=this.edges.find(e=>e.id===t.id);if(!e)throw new Error(`layout contains unknown edge ${t.id}`);for(const t of e.vertices??[])this.edgeVertices.delete(t.id);e.layoutSections=t.sections,e.layoutLabelBounds=t.labelBounds,e.vertices=_(t.sections).map(t=>{const n=e.initVertex(t);return n.auto=!0,n}),e.userDeletedVertices=!1}this.validatedLayout=e.validated,this.edges.forEach(t=>this.redrawEdge(t)),this.redrawGroups(null),this.fitToView(),this._undo.change()}alignSelectionV(){const t=this.nodes().filter(t=>t.selected);t.push(...Array.from(this.edgeVertices.values()).filter(t=>t.selected));let e=Math.min(...t.map(t=>t.y));this.nodesMap.forEach(t=>t.selected&&this.moveNode(t,t.x,e,!1,!1)),this.edgeVertices.forEach(t=>t.selected&&this.moveEdgeVertex(t,t.x,e,!1,!1))}alignSelectionH(){const t=this.nodes().filter(t=>t.selected);t.push(...Array.from(this.edgeVertices.values()).filter(t=>t.selected));let e=Math.min(...t.map(t=>t.x));this.nodesMap.forEach(t=>t.selected&&this.moveNode(t,e,t.y,!1,!1)),this.edgeVertices.forEach(t=>t.selected&&this.moveEdgeVertex(t,e,t.y,!1,!1))}distributeSelectionH(){const t=this.nodes().filter(t=>t.selected),e=Array.from(this.edgeVertices.values()).filter(t=>t.selected);if(t.length+e.length<3)return;this._undo.beforeChange();const n=[...t,...e];n.sort((t,e)=>t.x-e.x);const o=n[0].x,i=(n[n.length-1].x-o)/(n.length-1);n.forEach((t,e)=>{const n=o+e*i;"title"in t?this.moveNode(t,n,t.y,!1,!0):this.moveEdgeVertex(t,n,t.y,!1,!0)}),this._undo.change()}distributeSelectionV(){const t=this.nodes().filter(t=>t.selected),e=Array.from(this.edgeVertices.values()).filter(t=>t.selected);if(t.length+e.length<3)return;this._undo.beforeChange();const n=[...t,...e];n.sort((t,e)=>t.y-e.y);const o=n[0].y,i=(n[n.length-1].y-o)/(n.length-1);n.forEach((t,e)=>{const n=o+e*i;"title"in t?this.moveNode(t,t.x,n,!1,!0):this.moveEdgeVertex(t,t.x,n,!1,!0)}),this._undo.change()}setEdgeSelected(t,e){e&&(this.setNodeSelected(t.from,!0),this.setNodeSelected(t.to,!0)),this.updateEdgesSel()}fitToView(){const t=this.calculateContentBounds();if(0===t.width||0===t.height)return;const e=z.parentElement?.clientWidth||800,n=z.parentElement?.clientHeight||600,o=(e-80)/t.width,i=(n-80)/t.height,r=Math.min(o,i),s=Math.max(Math.min(r,2),.1),a=e/2-(t.x+t.width/2)*s,d=n/2-(t.y+t.height/2)*s,l=z.querySelector("g.zoom");l&&l.setAttribute("transform",`translate(${a}, ${d}) scale(${s})`),X(),tt(this.id)}saveLayoutState(){return this.exportLayout(!0)}restoreLayoutState(t){this.importLayout(t,!0)}isGridVisible(){return this._gridVisible}isSnapToGrid(){return this._snapToGrid}getGridSize(){return this._gridSize}toggleGrid(){this._gridVisible=!this._gridVisible,this.updateGridDisplay(),window.dispatchEvent(new CustomEvent("gridStateChanged"))}toggleSnapToGrid(){this._snapToGrid=!this._snapToGrid,window.dispatchEvent(new CustomEvent("gridStateChanged"))}snapAllToGrid(){this._snapToGrid&&(this._undo.beforeChange(),this.nodes().forEach(t=>{const e=Math.round(t.x/this._gridSize)*this._gridSize,n=Math.round(t.y/this._gridSize)*this._gridSize;this.moveNode(t,e,n,!1,!0)}),this._undo.change())}snapToGrid(t,e){return{x:Math.round(t/this._gridSize)*this._gridSize,y:Math.round(e/this._gridSize)*this._gridSize}}updateGridDisplay(){if(!z)return;const t=z.querySelector("#grid-pattern");t&&t.remove();const e=z.querySelector("#grid-background");if(e&&e.remove(),!this._gridVisible)return;let n=z.querySelector("defs");n||(n=document.createElementNS("http://www.w3.org/2000/svg","defs"),z.insertBefore(n,z.firstChild));const o=document.createElementNS("http://www.w3.org/2000/svg","pattern");o.id="grid-pattern",o.setAttribute("width",this._gridSize.toString()),o.setAttribute("height",this._gridSize.toString()),o.setAttribute("patternUnits","userSpaceOnUse");const i=document.createElementNS("http://www.w3.org/2000/svg","path");i.setAttribute("d",`M ${this._gridSize} 0 L 0 0 0 ${this._gridSize}`),i.setAttribute("fill","none"),i.setAttribute("stroke","#d0d0d0"),i.setAttribute("stroke-width","1"),i.setAttribute("opacity","0.8"),o.appendChild(i),n.appendChild(o);const r=document.createElementNS("http://www.w3.org/2000/svg","rect");r.id="grid-background",r.setAttribute("x","-10000"),r.setAttribute("y","-10000"),r.setAttribute("width","20000"),r.setAttribute("height","20000"),r.setAttribute("fill","url(#grid-pattern)"),r.setAttribute("pointer-events","none");const s=z.querySelector("g.zoom");s&&s.insertBefore(r,s.firstChild)}}function _(t){const e=[];for(const n of t)for(const t of n){const n=e[e.length-1];n&&n.x===t.x&&n.y===t.y||e.push(t)}return e.slice(1,-1)}function L(t,e){if("object"!=typeof e||Array.isArray(e)||!Number.isFinite(e.x)||!Number.isFinite(e.y))throw new Error(`layout entry ${t} must contain valid x and y coordinates`);return e}let D,z=document.querySelector("svg#graph");z||(z=document.createElementNS("http://www.w3.org/2000/svg","svg"),z.setAttribute("id","graph"),z.addEventListener("click",t=>D(t))),z.setAttribute("width","100%"),z.setAttribute("height","100%");let V,T=!1;const N=(t,e,n)=>{z.innerHTML='\n\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n',document.body.append(z),z.__data=t,V=e,D=t=>{},q(t);const o=d.rect(300,300,50,50,0,"elastic");return z.append(o),t.updateGridDisplay(),U(z,n),{svg:z,setZoom:P}},F=t=>(z=document.createElementNS("http://www.w3.org/2000/svg","svg"),z.setAttribute("id","graph"),q(t),z),q=t=>{const e=d.element("g",{},"zoom"),n=d.element("g",{},"nodes"),o=d.element("g",{},"edges"),i=d.element("g",{},"groups");e.append(i,o,n),t.nodesMap.forEach(e=>{!function(t,e){window.gdata=e;const n=d.element("g",{},"node");n.setAttribute("id",t.id),t.selected&&n.classList.add("selected"),l(n,t.x,t.y);const o=t.link?d.element("a",{href:t.link.href,"data-export-href":t.link.exportHref,"aria-label":`Open ${t.title}`},"nodeLink"):null,i=o||n;o&&(n.classList.add("linked"),n.append(o));const r=t.style.shape||"Box",s=(w[r.toLowerCase()]||w.box)(i,t);s.classList.add("nodeBorder"),(0,I.AV)(s,Q.nodeBorder),s.setAttribute("fill",t.style.background),s.setAttribute("stroke",t.style.stroke),s.setAttribute("stroke-width","3"),s.setAttribute("opacity",String(t.style.opacity)),K(s,t.style.border);const a=function(t,e){const n=d.element("g");let o=-t.textHeight/2;return t.blocks.forEach(t=>{const i=d.text("",{"text-anchor":"middle"});(0,I.AV)(i,I.M5.nodeText),e&&i.setAttribute("fill",e),t.field&&i.setAttribute("data-field",t.field),t.lines.forEach((e,n)=>{const r=d.element("tspan",{x:0,y:o+t.fontSize+n*t.lineHeight,"font-size":`${t.fontSize}px`,"font-weight":t.bold?"bold":"normal"});r.textContent=e,i.append(r)}),n.append(i),o+=t.lines.length*t.lineHeight+t.gapAfter}),n}(t.contentLayout,t.style.color);l(a,0,(Number(i.getAttribute("label-offset-y"))||0)/2),i.append(a),n.__data=t,t.ref=n}(e,t),n.append(e.ref)}),t.edges.forEach(t=>{t.labelBounds=void 0}),t.edges.forEach(t=>{j(t),o.append(t.ref)}),t.groupsMap.forEach(t=>{W(t),i.append(t.ref)}),z.append(e)};function j(t){const e=t.from,n=d.element("g",{},"edge");n.setAttribute("id",t.id),n.setAttribute("data-from",t.from.id),n.setAttribute("data-to",t.to.id);const o=(t.style.position||50)/100,i=t.layoutSections?function(t){const e=[];for(const n of t)for(const t of n){const n=e[e.length-1];n&&n.x===t.x&&n.y===t.y||e.push(t)}return e}(t.layoutSections):(0,k.l)(t),{bg:r,txt:s}=t.layoutLabelBounds?function(t,e){const n=t.x+t.width/2,{txt:o}=(t.y,t.height,d.textArea(e.label,200,e.style.fontSize,!1,n,t.y,"middle"));(0,I.AV)(o,Q.edgeText),o.setAttribute("stroke","none"),o.setAttribute("font-size",String(e.style.fontSize)),o.setAttribute("fill",e.style.color),o.setAttribute("data-field","label");const i=d.rect(t.width,t.height,t.x,t.y);return(0,I.AV)(i,Q.edgeRect),{bg:i,txt:o}}(t.layoutLabelBounds,t):function(t,e){const n=e.style.fontSize;let{txt:o,dy:i,maxW:r}=d.textArea(e.label,200,n,!1,t.x,t.y,"middle");i-=n/2,r+=n;const s="vertical"===t.orientation?t.x+r/2+12:t.x,a="vertical"===t.orientation?t.y:t.y-i/2-12,l={x:s-r/2,y:a-i/2,width:r,height:i};o.querySelectorAll("tspan").forEach(t=>{t.setAttribute("x",String(s))}),o.setAttribute("y",String(a-i/2)),(0,I.AV)(o,Q.edgeText),o.setAttribute("stroke","none"),o.setAttribute("font-size",String(e.style.fontSize)),o.setAttribute("fill",e.style.color);const c=d.rect(l.width,l.height,l.x,l.y);return(0,I.AV)(c,Q.edgeRect),o.setAttribute("data-field","label"),e.labelBounds=l,{bg:c,txt:o}}((0,k.IW)(i,o,e),t);n.append(r,s);const a=(0,k.Zr)(i),l=d.path(a,{"marker-end":"url(#arrow)"},"edge");l.setAttribute("fill","none"),l.setAttribute("stroke",t.style.color),l.setAttribute("stroke-width",String(t.style.thickness)),l.setAttribute("stroke-linecap","round"),t.style.dashed&&l.setAttribute("stroke-dasharray","8"),n.append(l);for(const e of t.vertices??[]){const t=e;t.ref=d.element("circle",{id:t.id,cx:e.x,cy:e.y,r:7,fill:"none"},"v-dot"),t.selected&&t.ref.classList.add("selected"),t.auto&&t.ref.classList.add("auto"),n.append(t.ref)}return t.ref=n,n}function W(t){if(0==t.nodes.length)return;const e=d.element("g",{},"group");if(t.layoutBounds&&t.layoutTitleBounds){const n=t.layoutBounds,o=t.layoutTitleBounds,i=d.rect(n.width,n.height,n.x,n.y);(0,I.AV)(i,Q.groupRect),t.style.stroke&&i.setAttribute("stroke",t.style.stroke),t.style.background&&i.setAttribute("fill",t.style.background);const r=d.text(t.name,{x:o.x,y:o.y,"dominant-baseline":"hanging"});return(0,I.AV)(r,Q.groupText),t.style.color&&r.setAttribute("fill",t.style.color),e.append(i,r),void(t.ref=e)}let n={x:1e100,y:1e100},o={x:0,y:0};t.nodes.forEach(t=>{const e=t.style?.shape?.toLowerCase()||"box";let i=t.height/2,r=t.height/2;if("robot"===e){const e=.12*t.height;i=t.height/2+e,r=t.height/2}else if("hexagon"===e){const e=t.width/2*.866;i=e,r=e}const s={x:t.x-t.width/2,y:t.y-i,width:t.width,height:i+r};n.x=Math.min(n.x,s.x),n.y=Math.min(n.y,s.y),o.x=Math.max(o.x,s.x+s.width),o.y=Math.max(o.y,s.y+s.height)});const i=Math.max(o.x-n.x,200),r=o.y-n.y,s={x:n.x-25,y:n.y-25,width:i+50,height:r+50+30},a=d.rect(s.width,s.height,s.x,s.y);t.x=s.x+s.width/2,t.y=s.y+s.height/2,t.width=s.width,t.height=s.height,(0,I.AV)(a,Q.groupRect),t.style.stroke&&a.setAttribute("stroke",t.style.stroke),t.style.background&&a.setAttribute("fill",t.style.background);const l=d.text(t.name,{x:n.x,y:s.y+s.height-Q.groupText["font-size"]});(0,I.AV)(l,Q.groupText),t.style.color&&l.setAttribute("fill",t.style.color),e.append(a,l),t.ref=e}function R(t,e){let n={dst:Number.POSITIVE_INFINITY,pos:-1,edge:null,prj:null};return t.edges.forEach(t=>{const o=t.vertices||[],i=[t.from,...o,t.to];for(let o=1;o50||s3||Math.abs(a)>3)&&(h=!0,c=null,l)){const t=e.getSelection();t.length=0,t.push(l.node),e.setSelection(t),o=[{x:l.node.x,y:l.node.y,n:l.node}],l=null}if(r)!function(e,n){const o=t.querySelector("g.zoom");if(!o)return;const i=O();o.setAttribute("transform",`translate(${e}, ${n}) scale(${i})`)}(d.x+s,d.y+a);else if(o.length>0&&h){const t=e.getZoom(),n=s/t,i=a/t;o.forEach(t=>{e.moveNode(t.n,t.x+n,t.y+i)}),e.setDragging(!0)}else i&&(i.update(n),e.setDragging(!0))}(n=f(n),n.clientX-p.ex,n.clientY-p.ey)}function b(n){document.removeEventListener("touchmove",m),document.removeEventListener("mousemove",m),document.removeEventListener("mouseup",b),document.removeEventListener("touchend",b),function(n){e.setDragging(!1);const s=h?null:c;if(h&&(u=!0,window.setTimeout(()=>{u=!1},0)),l&&!h){const t=e.getSelection();t.length=0,t.push(l.node),e.setSelection(t)}if(i){const t=i.end();t?e.boxSelection(t,n.shiftKey):o.length||e.setSelection([]),i=null}if(r&&h){const e=t.__data;e&&e.id&&tt(e.id)}l=null,c=null,h=!1,r=!1,e.updatePanning(),s&&(window.location.href=s)}(f(n)),p=null}function y(u){u=f(u),p={ex:u.clientX,ey:u.clientY},function(u){u.preventDefault(),h=!1,l=null;const A=u.target,g=A instanceof Element?A.closest("a.nodeLink"):null;c=u.shiftKey?null:g?.getAttribute("href")||null;const p=e.nodeFromEvent(u),f=u.shiftKey?"pan"===n?"select":"pan":n;if(p)if("pan"===f){r=!1,i=null;const t=e.getSelection();e.isSelected(p)?o=t.map(t=>({x:t.x,y:t.y,n:t})):(e.setSelection([p]),o=[{x:p.x,y:p.y,n:p}])}else{r=!1,i=null;const t=e.getSelection();if(u.shiftKey&&"select"===n){if(e.isSelected(p)){const e=t.findIndex(t=>t.id===p.id);e>=0&&t.splice(e,1)}else t.push(p);e.setSelection(t),o=t.map(t=>({x:t.x,y:t.y,n:t}))}else e.isSelected(p)?(o=t.map(t=>({x:t.x,y:t.y,n:t})),l=null):(l={node:p,shiftKey:u.shiftKey},o=[{x:p.x,y:p.y,n:p}])}else"pan"===f?(r=!0,i=null,s=u.clientX,a=u.clientY,d=function(){const e=t.querySelector("g.zoom");if(!e)return{x:0,y:0};const n=(e.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return n?{x:parseFloat(n[1])||0,y:parseFloat(n[2])||0}:{x:0,y:0}}(),o=[],e.setSelection([])):(r=!1,i=function(){let e=0,n=0,o=null;return{ini(i){const r=G(i);e=r.x,n=r.y,o=document.createElementNS("http://www.w3.org/2000/svg","rect"),o.setAttribute("fill","rgba(0, 100, 255, 0.1)"),o.setAttribute("stroke","rgba(0, 100, 255, 0.5)"),o.setAttribute("stroke-width","1"),o.setAttribute("stroke-dasharray","3,3"),o.setAttribute("x",String(e)),o.setAttribute("y",String(n)),o.setAttribute("width","0"),o.setAttribute("height","0");const s=t.querySelector("g.zoom");s?s.appendChild(o):t.appendChild(o)},update(t){if(!o)return;const i=G(t),r=i.x,s=i.y,a=Math.min(e,r),d=Math.min(n,s),l=Math.abs(r-e),c=Math.abs(s-n);o.setAttribute("x",String(a)),o.setAttribute("y",String(d)),o.setAttribute("width",String(l)),o.setAttribute("height",String(c))},end(){if(!o)return null;const t=parseFloat(o.getAttribute("x")||"0"),e=parseFloat(o.getAttribute("y")||"0"),n=parseFloat(o.getAttribute("width")||"0"),i=parseFloat(o.getAttribute("height")||"0");return o.remove(),o=null,n>5&&i>5?{x:t,y:e,width:n,height:i,left:t,top:e,right:t+n,bottom:e+i}:null}}}(),i&&i.ini(u),o=[])}(u),document.addEventListener("touchmove",m),document.addEventListener("mousemove",m),document.addEventListener("mouseup",b),document.addEventListener("touchend",b)}g.addEventListener("mousedown",y),g.addEventListener("touchstart",y),A.push({element:g,event:"mousedown",handler:y},{element:g,event:"touchstart",handler:y})}(t),t.addEventListener("click",g),A.push({element:t,event:"click",handler:g}),()=>{A.forEach(({element:t,event:e,handler:n})=>{t.removeEventListener(e,n)})}}function U(t,e){const n=t.__cursorInteractionCleanup;function o(t){return t.__data}n&&n();const i=()=>o(t),r=[],s=t=>{i().changed()&&(t.preventDefault(),t.returnValue="")};function a(e,n){e.selected=n;const o=t.querySelector("#"+e.id);e.selected?o.classList.add("selected"):o.classList.remove("selected")}window.addEventListener("beforeunload",s),r.push({element:window,event:"beforeunload",handler:s});const l=e=>{if(!e.altKey)return;const n=R(i(),G(e));if(n){const{prj:e}=n,o=t.querySelector("g.edges");let i=o.querySelector("#prj");i||(i=d.element("circle",{id:"prj",cx:e.x,cy:e.y,r:7}),o.append(i)),i.setAttribute("cx",String(e.x)),i.setAttribute("cy",String(e.y))}else u()};t.addEventListener("mousemove",l),r.push({element:t,event:"mousemove",handler:l});const h=t=>{const e=(0,B.Yp)(t,!0);e!=B.Zj&&e!=B._s&&u()};function u(){const e=t.querySelector("g.edges #prj");e&&e.parentElement.removeChild(e)}window.addEventListener("keyup",h),r.push({element:window,event:"keyup",handler:h});const A=t=>{const e=(0,B.Yp)(t,!0);if(e!=B._s&&e!=B.Zj)return;const n=R(i(),G(t));if(n){const{edge:t,pos:o,prj:r}=n;i().insertEdgeVertex(t,r,o,e==B._s),u()}};t.addEventListener("click",A),r.push({element:t,event:"click",handler:A});const g=e=>{const n=.1*Math.sign(e.deltaY),o=O(),i=Math.max(.1,Math.min(5,o-n));if(i!==o){const n=t.getBoundingClientRect();H(i,e.clientX-n.left,e.clientY-n.top),e.preventDefault();const o=t.__data;o&&o.id&&tt(o.id)}};t.addEventListener("wheel",g),r.push({element:t,event:"wheel",handler:g});const p=t=>{const e=(0,B.Yp)(t);switch(e&&t.preventDefault(),e){case B.bl:Array.from(i().edgeVertices.values()).filter(t=>t.selected).forEach(t=>{i().deleteEdgeVertex(t)});break;case"undo":i().undo();break;case"redo":i().redo();break;case B.Ur:H(Math.min(5,1.2*O())),tt(i().id);break;case B.hU:H(Math.max(.1,O()/1.2)),tt(i().id);break;case B.i1:H(1),tt(i().id);break;case B.mD:i().fitToView();break;case B.F:i().nodes().forEach(t=>i().setNodeSelected(t,!0)),i().edgeVertices.forEach(t=>a(t,!0));break;case B.Gn:i().nodes().forEach(t=>i().setNodeSelected(t,!1)),i().edgeVertices.forEach(t=>a(t,!1))}};window.addEventListener("keydown",p),r.push({element:window,event:"keydown",handler:p});const f=Y(t,{nodeFromEvent(t){t.preventDefault();let e=t.target.closest("g.nodes g.node");return e?o(e):(e=t.target.closest("g.edges g.edge .v-dot"),e?i().edgeVertices.get(e.id):null)},setSelection(t){i().nodes().forEach(e=>i().setNodeSelected(e,t.some(t=>t.id==e.id))),i().edgeVertices.forEach(e=>a(e,t.some(t=>t.id==e.id))),V(i().nodes().find(t=>t.selected))},setDragging(t){T=t},isSelected:t=>t.selected,getSelection(){const t=i().nodes().filter(t=>t.selected);return i().edgeVertices.forEach(e=>e.selected&&t.push(e)),t},getZoom:O,moveNode(t,e,n){i().nodesMap.has(t.id)?i().moveNode(t,e,n):(t.auto=!1,i().moveEdgeVertex(t,e,n))},boxSelection(t,e){i().nodesMap.forEach(n=>{var o,r,s;(o={x:(s=n).x-s.width/2,y:s.y-s.height/2,width:s.width,height:s.height}).x<(r=t).x+r.width&&o.yr.x&&o.y+o.height>r.y?i().setNodeSelected(n,!n.selected):e||i().setNodeSelected(n,!1)}),i().edgeVertices.forEach(n=>{c(n,t,!1)?a(n,!n.selected):e||a(n,!1)}),V(i().nodes().find(t=>t.selected))},updatePanning:X},e);t.__cursorInteractionCleanup=()=>{r.forEach(({element:t,event:e,handler:n})=>{t.removeEventListener(e,n)}),f&&f()}}function O(){if(!z)return 1;const t=z.querySelector("g.zoom");if(!t)return 1;const e=(t.getAttribute("transform")||"").match(/scale\(([^)]+)\)/);return e&&parseFloat(e[1])||1}function P(t){if(!z)return;const e=z.querySelector("g.zoom");if(!e)return;const n=Z();e.setAttribute("transform",`translate(${n.x}, ${n.y}) scale(${t})`),X()}function H(t,e,n){const o=z.querySelector("g.zoom"),i=O();if(void 0===e||void 0===n){const t=z.parentElement;t?(e=t.clientWidth/2,n=t.clientHeight/2):(e=z.clientWidth/2,n=z.clientHeight/2)}const r=Z(),s=e-(e-r.x)/i*t,a=n-(n-r.y)/i*t;o.setAttribute("transform",`translate(${s}, ${a}) scale(${t})`),X()}function Z(){if(!z)return{x:0,y:0};const t=z.querySelector("g.zoom");if(!t)return{x:0,y:0};const e=(t.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return e?{x:parseFloat(e[1])||0,y:parseFloat(e[2])||0}:{x:0,y:0}}function X(){if(!z)return;const t=z.querySelector("g.zoom");if(!t)return;const e=t.getBBox(),n=O();if(!z.parentElement)return;const o=Math.max(z.parentElement.clientWidth/n,e.x+e.width+20),i=Math.max(z.parentElement.clientHeight/n,e.y+e.height+20);z.setAttribute("width",String(o*n)),z.setAttribute("height",String(i*n))}const K=(t,e)=>{"Dashed"==e?t.setAttribute("stroke-dasharray","4"):"Dotted"==e&&t.setAttribute("stroke-dasharray","2")},Q={nodeBorder:{filter:"url(#shadow)"},nodeText:{"font-family":"Arial, sans-serif",stroke:"none"},edgeText:{"font-family":"Arial, sans-serif",stroke:"none"},edgeRect:{fill:"none",stroke:"none"},groupRect:{fill:"rgba(0, 0, 0, 0.02)",stroke:"#666","stroke-width":3,"stroke-dasharray":4},groupText:{"font-family":"Arial, sans-serif",fill:"#666","font-size":22,"font-weight":"bold",cursor:"default"}},J=new Map;function tt(t){if(!z)return;const e=O(),n=Z(),o={zoom:e,transform:{x:n.x,y:n.y}};J.set(t,o)}function et(t){if(!z||!J.has(t))return!1;const e=J.get(t);if(!e)return!1;const n=z.querySelector("g.zoom");return n&&(n.setAttribute("transform",`scale(${e.zoom}) translate(${e.transform.x}, ${e.transform.y})`),X()),!0}function nt(t){J.delete(t)}},983(t,e,n){n.d(e,{k:()=>x,U:()=>w});var o=n(20);const i={direction:"DOWN",nodeSpacing:80,layerSpacing:80};async function r(t,e={}){const o=function(t,e){const n=new Map(t.nodes.map(t=>[t.id,t])),o=new Map(t.groups.map(t=>[t.id,t]));!function(t,e,n){for(const e of t.nodes)if(e.parentId&&!n.has(e.parentId))throw new Error(`node ${e.id} has missing parent group ${e.parentId}`);for(const e of t.groups)if(e.parentId&&!n.has(e.parentId))throw new Error(`group ${e.id} has missing parent group ${e.parentId}`);for(const n of t.edges)if(!e.has(n.sourceId)||!e.has(n.targetId))throw new Error(`edge ${n.id} connects missing node ${n.sourceId} or ${n.targetId}`)}(t,n,o);const i=new Map;for(const e of t.nodes)b(i,e.parentId,e);const r=new Map;for(const e of t.groups)b(r,e.parentId,e);const h=new Map,u=t=>{const n={id:t.id,children:[...(r.get(t.id)??[]).map(u),...(i.get(t.id)??[]).map(s)],edges:[],labels:[{text:t.id,width:t.titleSize.width,height:t.titleSize.height}],layoutOptions:l(e,t.titleSize.height)};return h.set(t.id,n),n},A={id:t.id,children:[...(r.get(void 0)??[]).map(u),...(i.get(void 0)??[]).map(s)],edges:[],layoutOptions:d(e)};for(const e of t.edges){const t=c(e,n,o),i=t?h.get(t):A;if(!i)throw new Error(`edge ${e.id} has missing layout group ${t}`);i.edges?.push(a(e))}return A}(t,{...i,...e}),r=new(await n.e(726).then(n.t.bind(n,862,23)).then(t=>t.default));return function(t,e){if(void 0===e.width||void 0===e.height)throw new Error(`ELK did not size diagram ${t.id}`);const n=[],o=[],i=[];return u(e,0,0,new Map(t.nodes.map(t=>[t.id,t])),new Map(t.groups.map(t=>[t.id,t])),n,o,i),{diagramId:t.id,bounds:{x:0,y:0,width:e.width,height:e.height},nodes:n,groups:o,edges:i}}(t,await r.layout(o))}function s(t){return{id:t.id,width:t.size.width,height:t.size.height,layoutOptions:{"elk.nodeSize.constraints":"FIXED_SIZE","elk.portConstraints":"FREE"}}}function a(t){return{id:t.id,sources:[t.sourceId],targets:[t.targetId],labels:t.labelSize?[{text:t.id,width:t.labelSize.width,height:t.labelSize.height,layoutOptions:{"elk.edgeLabels.inline":"false","elk.edgeLabels.placement":"CENTER"}}]:[]}}function d(t){return{"elk.algorithm":"layered","elk.direction":t.direction,"elk.edgeRouting":"ORTHOGONAL","elk.hierarchyHandling":"INCLUDE_CHILDREN","elk.padding":"[top=40,left=40,bottom=40,right=40]","elk.spacing.nodeNode":String(t.nodeSpacing),"elk.spacing.componentComponent":String(t.nodeSpacing),"elk.layered.spacing.nodeNodeBetweenLayers":String(t.layerSpacing),"elk.layered.spacing.edgeNodeBetweenLayers":"30","elk.layered.spacing.edgeEdgeBetweenLayers":"20","elk.layered.nodePlacement.strategy":"NETWORK_SIMPLEX","elk.layered.nodePlacement.favorStraightEdges":"true","elk.layered.crossingMinimization.strategy":"LAYER_SWEEP","elk.layered.unnecessaryBendpoints":"true","elk.edgeLabels.inline":"false","elk.edgeLabels.avoidOverlap":"true","elk.layered.edgeLabels.sideSelection":"SMART_DOWN","elk.spacing.edgeLabel":"12"}}function l(t,e){return{...d(t),"elk.padding":`[top=${e+30},left=30,bottom=30,right=30]`,"elk.nodeLabels.placement":"[H_LEFT,V_TOP,INSIDE]","elk.spacing.labelNode":"10"}}function c(t,e,n){const o=h(e.get(t.sourceId)?.parentId,n),i=new Set(h(e.get(t.targetId)?.parentId,n));return o.find(t=>i.has(t))}function h(t,e){const n=[];let o=t;for(;o;)n.push(o),o=e.get(o)?.parentId;return n}function u(t,e,n,o,i,r,s,a){const d=e+(t.x??0),l=n+(t.y??0);for(const e of t.children??[]){const t=d+p(e.x,e.id,"x"),n=l+p(e.y,e.id,"y"),c=p(e.width,e.id,"width"),h=p(e.height,e.id,"height"),A=o.get(e.id);if(A){r.push({id:e.id,parentId:A.parentId,bounds:{x:t,y:n,width:c,height:h}});continue}const g=i.get(e.id);if(!g)throw new Error(`ELK returned unknown node ${e.id}`);const f=m(e.labels?.[0],e.id);s.push({id:e.id,parentId:g.parentId,bounds:{x:t,y:n,width:c,height:h},titleBounds:{x:t+p(f.x,e.id,"title x"),y:n+p(f.y,e.id,"title y"),width:p(f.width,e.id,"title width"),height:p(f.height,e.id,"title height")}}),u(e,d,l,o,i,r,s,a)}for(const e of t.edges??[])a.push(A(e,d,l))}function A(t,e,n){const o=(t.sections??[]).map(t=>[g(t.startPoint,e,n),...(t.bendPoints??[]).map(t=>g(t,e,n)),g(t.endPoint,e,n)]),i=t.labels?.[0];return{id:t.id,sourceId:f(t.sources,t.id,"source"),targetId:f(t.targets,t.id,"target"),sections:o,labelBounds:i?{x:e+p(i.x,t.id,"label x"),y:n+p(i.y,t.id,"label y"),width:p(i.width,t.id,"label width"),height:p(i.height,t.id,"label height")}:void 0}}function g(t,e,n){return{x:e+t.x,y:n+t.y}}function p(t,e,n){if(void 0===t)throw new Error(`ELK omitted ${n} for ${e}`);return t}function f(t,e,n){if(1!==t.length)throw new Error(`ELK returned ${t.length} ${n}s for edge ${e}`);return t[0]}function m(t,e){if(!t)throw new Error(`ELK omitted title for group ${e}`);return t}function b(t,e,n){const o=t.get(e)??[];o.push(n),t.set(e,o)}n(828);var y=n(977);async function x(t,e={}){await document.fonts.ready;const n=C(t),i=e.compactLayout?56:80,s=await r(n,{direction:e.direction??t.layoutDirection??"DOWN",nodeSpacing:e.nodeSpacing??i,layerSpacing:e.layerSpacing??i}),a=(0,o.r)(n,s),d=new Map(n.nodes.map(t=>[t.id,t]));return{validated:a,nodes:a.nodes.map(t=>{const e=d.get(t.id);if(!e)throw new Error(`validated layout contains unknown node ${t.id}`);return{id:t.id,x:t.bounds.x+e.centerOffset.x,y:t.bounds.y+e.centerOffset.y}}),groups:a.groups.map(t=>({id:t.id,bounds:t.bounds,titleBounds:t.titleBounds})),edges:a.edges.map(t=>({id:t.id,sections:t.sections,labelBounds:t.labelBounds}))}}function w(t){const e=C(t),n=new Map(e.nodes.map(t=>[t.id,t])),i=t.nodes().map(t=>{const e=n.get(t.id);if(!e)throw new Error(`measured diagram omitted node ${t.id}`);return{id:t.id,parentId:e.parentId,bounds:{x:t.x-e.centerOffset.x,y:t.y-e.centerOffset.y,...e.size}}}),r=Array.from(t.groupsMap.values()).map(t=>{if(!t.ref?.isConnected)throw new Error(`group ${t.id} must be drawn before saving`);const n=t.ref.querySelector("rect"),o=t.ref.querySelector("text");if(!(n instanceof SVGGraphicsElement&&o instanceof SVGGraphicsElement))throw new Error(`group ${t.id} is missing its boundary or title`);return{id:t.id,parentId:e.groups.find(e=>e.id===t.id)?.parentId,bounds:k(n.getBBox()),titleBounds:k(o.getBBox())}}),s=t.edges.map(t=>{const e=t.layoutSections??[(0,y.l)(t)],n=t.layoutLabelBounds??t.labelBounds;if(t.label.trim().length>0&&!n)throw new Error(`edge ${t.id} is missing its label position`);return{id:t.id,sourceId:t.from.id,targetId:t.to.id,sections:e,labelBounds:n?{...n}:void 0}}),a={diagramId:t.id,bounds:I(i.map(t=>t.bounds),r,s),nodes:i,groups:r,edges:s};return function(t,e){return(0,o.r)(t,e)}(e,a)}function C(t){const{nodeParents:e,groupParents:n}=function(t){const e=new Map,n=new Map;for(const o of t.groupsMap.values())for(const t of o.nodes){const i=E(t)?n:e,r=i.get(t.id);if(r&&r!==o.id)throw new Error(`${E(t)?"group":"node"} ${t.id} belongs to both ${r} and ${o.id}`);i.set(t.id,o.id)}return{nodeParents:e,groupParents:n}}(t);return{id:t.id,nodes:t.nodes().map(t=>function(t,e){if(!t.ref?.isConnected)throw new Error(`node ${t.id} must be drawn before layout`);const n=t.ref.getBBox();return B(t.id,n),{id:t.id,parentId:e,size:{width:n.width,height:n.height},contentSize:{width:n.width,height:n.height},centerOffset:{x:-n.x,y:-n.y},shape:t.style.shape??"Box"}}(t,e.get(t.id))),groups:Array.from(t.groupsMap.values()).map(t=>function(t,e){if(!t.ref?.isConnected)throw new Error(`group ${t.id} must be drawn before layout`);const n=t.ref.querySelector("text");if(!(n instanceof SVGGraphicsElement))throw new Error(`group ${t.id} is missing its drawn title`);const o=n.getBBox();return B(t.id,o),{id:t.id,parentId:e,titleSize:{width:o.width,height:o.height}}}(t,n.get(t.id))),edges:t.edges.map(v)}}function v(t){const e=t.label.trim().length>0;if(e&&!t.labelBounds)throw new Error(`edge ${t.id} must be drawn before layout`);return{id:t.id,sourceId:t.from.id,targetId:t.to.id,labelSize:e?{width:t.labelBounds?.width??0,height:t.labelBounds?.height??0}:void 0}}function B(t,e){if(!Number.isFinite(e.x)||!Number.isFinite(e.y)||!Number.isFinite(e.width)||!Number.isFinite(e.height)||e.width<=0||e.height<=0)throw new Error(`browser returned invalid measured box for ${t}`)}function I(t,e,n){const o=[...t,...e.flatMap(t=>[t.bounds,t.titleBounds]),...n.flatMap(t=>t.labelBounds?[t.labelBounds]:[])],i=n.flatMap(t=>t.sections.flat());if(0===o.length&&0===i.length)return{x:0,y:0,width:0,height:0};const r=Math.min(...o.map(t=>t.x),...i.map(t=>t.x)),s=Math.min(...o.map(t=>t.y),...i.map(t=>t.y));return{x:r,y:s,width:Math.max(...o.map(t=>t.x+t.width),...i.map(t=>t.x))-r,height:Math.max(...o.map(t=>t.y+t.height),...i.map(t=>t.y))-s}}function k(t){return{x:t.x,y:t.y,width:t.width,height:t.height}}function E(t){return"nodes"in t}},71(t,e,n){var o=n(828);const i={TopBottom:"DOWN",BottomTop:"UP",LeftRight:"RIGHT",RightLeft:"LEFT"};n.d(e,["B",0,t=>{const e=[];return Object.keys(t.views).filter(t=>t.endsWith("Views")).forEach(n=>{t.views[n].forEach(t=>{e.push({key:t.key,title:t.title||t.key,section:n})})}),e},"R",0,(t,e,n)=>{const r=new Map,s=new Map,a=t=>{Array.isArray(t.relationships)&&t.relationships.forEach(t=>{s.set(t.id,t)})};if(t.model.people&&t.model.people.forEach(t=>{r.set(t.id,t),Array.isArray(t.relationships)&&t.relationships.forEach(t=>{s.set(t.id,t)})}),t.model.softwareSystems&&t.model.softwareSystems.forEach(t=>{r.set(t.id,t),a(t),Array.isArray(t.containers)&&t.containers.forEach(e=>{e.parent=t,r.set(e.id,e),a(e),Array.isArray(e.components)&&e.components.forEach(t=>{t.parent=e,r.set(t.id,t),a(t)})})}),t.model.deploymentNodes){const e=t=>{t.containerInstances&&t.containerInstances.forEach(e=>{const n={...r.get(e.containerId),id:e.id};r.set(n.id,n),n.parent=t,a(e)})},n=(t,o)=>{t.parent=o,r.set(t.id,t),a(t),e(t),t.children&&t.children.forEach(e=>n(e,t)),t.infrastructureNodes&&t.infrastructureNodes.forEach(e=>n(e,t))};t.model.deploymentNodes.forEach(t=>n(t,null))}const{view:d,section:l}=function(t,e){let n=null,o="";return Object.keys(t.views).filter(t=>t.endsWith("Views")).some(i=>t.views[i].some(t=>{if(t.key==e)return n=t,o=i,!0})),{view:n,section:o}}(t,n);if(!d)return null;const c=new o.jg(d.key,d.title||d.key),h=d.automaticLayout?.rankDirection;c.layoutDirection=h?i[h]:void 0;const u={name:c.name,description:d.description,version:t.version,elements:[]};if(c.metadata=u,!d.elements)return c;const A={};if("deploymentViews"==l||"containerViews"==l)d.elements.forEach(t=>{const e=r.get(t.id);e?.parent&&(A[e.parent.id]=!0)});else if(d.softwareSystemId)d.elements.find(t=>t.id==d.softwareSystemId)||(A[d.softwareSystemId]=!0);else if("systemLandscapeViews"==l){const e={id:"__enterprise__",...t.model.enterprise};r.set(e.id,e),t.model.people&&t.model.people.filter(t=>"External"!=t.location).forEach(t=>t.parent=e),t.model.softwareSystems&&t.model.softwareSystems.filter(t=>"External"!=t.location).forEach(t=>t.parent=e),A[e.id]=!0}const g=t.views.styles,p=t=>t.toLowerCase().replace(/[^a-z0-9-]/g,"-");g?.elements&&g.elements.forEach(t=>{if(t.tag){const e=`--mdl-${p(t.tag)}`;t.background&&c.colorToVarMap.set(t.background,`${e}-bg`),t.color&&c.colorToVarMap.set(t.color,`${e}-color`),t.stroke&&c.colorToVarMap.set(t.stroke,`${e}-stroke`)}}),g?.relationships&&g.relationships.forEach(t=>{if(t.tag){const e=`--mdl-rel-${p(t.tag)}`;t.color&&c.colorToVarMap.set(t.color,`${e}-color`)}}),d.elements.forEach(e=>{if(A[e.id])return;const n=r.get(e.id),o=n?function(t,e){const n=t.views.containerViews?.find(t=>t.softwareSystemId==e);return n?.key}(t,n.id):void 0;let i="",s={};if(n){const t=n.tags.split(",");i=t[t.length-1],n.technology&&(i+=": "+n.technology),t.forEach(t=>{const e=g&&g.elements&&g.elements.find(e=>e.tag==t);e&&(s={...s,...e})})}c.addNode(e.id,n&&n.name||e.id,i,n&&n.description?n.description:"",s,function(t,e){if(e){const t=encodeURIComponent(e);return{href:`?id=${t}`,exportHref:`${t}.svg`}}if(t?.url)return{href:t.url,exportHref:t.url}}(n,o)),n&&u.elements.push({id:n.id,tags:n.tags,location:n.location,properties:n.properties,elementViewKey:o,technology:n.technology,url:n.url})}),Array.isArray(d.relationships)&&d.relationships.forEach(t=>{const e=s.get(t.id);if(!e)return;if(!c.nodesMap.has(e.sourceId)){if(r.has(e.sourceId)){const t=r.get(e.sourceId);console.warn("Element not found in this view: ",t.id,t.name)}else console.warn("Element not found: ",e.sourceId);return}if(!c.nodesMap.has(e.destinationId)){if(r.has(e.destinationId)){const t=r.get(e.destinationId);console.warn("Element not found in this view: ",t.id,t.name)}else console.warn("Element not found: ",e.destinationId);return}let n={};e.tags.split(",").forEach(t=>{const e=g&&g.relationships&&g.relationships.find(e=>e.tag==t);e&&(n={...n,...e})}),t.routing&&(n.routing=t.routing),c.addEdge(e.id,e.sourceId,e.destinationId,e.description,t.vertices,n)});const f=t=>{let e=0;for(let n=t.parent;n;n=n.parent)e++;return e};return Object.keys(A).map(t=>r.get(t)).sort((t,e)=>f(t)>f(e)?-1:1).forEach(t=>{let e={};"deploymentViews"==l&&r.get(t.id).tags.split(",").forEach(t=>{const n=g&&g.elements&&g.elements.find(e=>e.tag==t);n&&(e={...e,...n})});const n=d.elements.map(t=>r.get(t.id)).filter(e=>!(!e||e.parent!==t||"systemLandscapeViews"===l&&"__enterprise__"===t.id&&"External"===e.location)).map(t=>t.id);n.length>0&&c.addGroup(t.id,t.name,n,e)}),c.init(e[c.id]),c}])},264(t,e,n){n(763);var o=n(686),i=n(987);const r="add-vertex",s="add-label-vertex",a="del-vertex",d="zoom-in",l="zoom-out",c="zoom-fit",h="zoom-100",u="select-all",A="deselect",g="move-left",p="move-right",f="move-up",m="move-down",b="move-left-fine",y="move-right-fine",x="move-up-fine",w="move-down-fine",C="toggle_drag_mode",v="align_horizontal",B="align_vertical",I="distribute_horizontal",k="distribute_vertical",E="auto_layout",S="reset_position",$="toggle_grid",M="toggle_snap_to_grid",_="snap_all_to_grid",L=[{name:"Help",list:[{id:"help",help:"Show/hide this help",combinations:[{key:"?",shift:!0},{key:"F1",shift:!0}]}]},{name:"File",list:[{id:"save",help:"Save",combinations:[{key:"s",ctrl:!0}]}]},{name:"History",list:[{id:"undo",help:"Undo",combinations:[{ctrl:!0,key:"z"}]},{id:"redo",help:"Redo",combinations:[{ctrl:!0,shift:!0,key:"z"},{ctrl:!0,key:"y"}]}]},{name:"Relationship editing",list:[{id:r,help:"Add relationship vertex",combinations:[{alt:!0,click:!0}]},{id:s,help:"Add label anchor relationship vertex",combinations:[{alt:!0,shift:!0,click:!0}]},{id:a,help:"Remove relationship vertex",combinations:[{key:"DELETE"},{key:"BACKSPACE"}]}]},{name:"Zoom",list:[{id:d,help:"Zoom in",combinations:[{ctrl:!0,key:"="}]},{id:l,help:"Zoom out",combinations:[{ctrl:!0,key:"-"}]},{id:c,help:"Zoom - fit",combinations:[{ctrl:!0,key:"9"}]},{id:h,help:"Zoom 100%",combinations:[{ctrl:!0,key:"0"}]},{id:"wheel_zoom",help:"Zoom in/out with mouse wheel",combinations:[{wheel:!0}]}]},{name:"Mouse Interactions",list:[{id:"pan-view",help:"Pan view (drag empty space)",combinations:[{click:!0}]},{id:"select-element",help:"Select element",combinations:[{click:!0}]},{id:"multi-select",help:"Add/remove from selection",combinations:[{shift:!0,click:!0}]},{id:"box-select",help:"Box selection (drag empty space)",combinations:[{shift:!0,click:!0}]},{id:"move-elements",help:"Move selected elements",combinations:[{click:!0}]}]},{name:"Select",list:[{id:u,help:"Select All",combinations:[{ctrl:!0,key:"a"}]},{id:A,help:"Deselect",combinations:[{key:"ESC"}]}]},{name:"Move",list:[{id:f,help:"Move up (grid increment)",combinations:[{key:"UP"}]},{id:x,help:"Move up (1 pixel)",combinations:[{key:"UP",shift:!0}]},{id:p,help:"Move right (grid increment)",combinations:[{key:"RIGHT"}]},{id:y,help:"Move right (1 pixel)",combinations:[{key:"RIGHT",shift:!0}]},{id:m,help:"Move down (grid increment)",combinations:[{key:"DOWN"}]},{id:w,help:"Move down (1 pixel)",combinations:[{key:"DOWN",shift:!0}]},{id:g,help:"Move left (grid increment)",combinations:[{key:"LEFT"}]},{id:b,help:"Move left (1 pixel)",combinations:[{key:"LEFT",shift:!0}]}]},{name:"View",list:[{id:C,help:"Toggle between pan and select mode",combinations:[{key:"t"}]},{id:S,help:"Reset position and view",combinations:[{key:"Home",ctrl:!0}]}]},{name:"Alignment",list:[{id:v,help:"Align selected elements horizontally",combinations:[{key:"h",ctrl:!0,shift:!0}]},{id:B,help:"Align selected elements vertically",combinations:[{key:"a",ctrl:!0,shift:!0}]},{id:I,help:"Distribute selected elements horizontally",combinations:[{key:"h",ctrl:!0,alt:!0}]},{id:k,help:"Distribute selected elements vertically",combinations:[{key:"v",ctrl:!0,alt:!0}]}]},{name:"Layout",list:[{id:E,help:"Auto layout all elements",combinations:[{key:"l",ctrl:!0}]}]},{name:"Grid",list:[{id:$,help:"Toggle grid visibility",combinations:[{key:"g",ctrl:!0}]},{id:M,help:"Toggle snap to grid",combinations:[{key:"g",ctrl:!0,shift:!0}]},{id:_,help:"Snap all elements to grid",combinations:[{key:"g",ctrl:!0,alt:!0}]}]}],D=L.reduce((t,e)=>t.concat(e.list),[]).reduce((t,e)=>(t[e.id]=e,t),{}),z=t=>[t.ctrl&&(0,o.sy)().toUpperCase(),t.shift&&"SHIFT",t.alt&&"ALT",t.key&&(t.key.length>1?t.key:`"${t.key.toUpperCase()}"`),t.click&&"CLICK",t.wheel&&"WHEEL"].filter(Boolean).join(" + ");n.d(e,["DE",0,I,"F",0,u,"Gg",0,g,"Gn",0,A,"Hd",0,E,"Help",0,()=>(0,i.jsxs)("div",{className:"popover",children:[(0,i.jsx)("h1",{children:"Shortcuts"}),(0,i.jsx)("table",{children:(0,i.jsx)("tbody",{children:L.map(t=>(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("tr",{children:(0,i.jsx)("th",{colSpan:2,children:t.name})}),t.list.map(t=>(0,i.jsxs)("tr",{children:[(0,i.jsx)("td",{children:t.combinations.map(z).join(", ")}),(0,i.jsx)("td",{children:t.help})]}))]}))})})]}),"J8",0,b,"Jk",0,B,"OE",0,_,"Op",0,$,"Ur",0,d,"Vy",0,k,"Yp",0,(t,e=!1,n=!1)=>{const i=Object.keys(D).filter(i=>((t,e,n,i)=>e.combinations.some(e=>{if(Boolean(e.shift)!=t.shiftKey)return!1;if(e.ctrl&&!(0,o.SA)(t))return!1;if(Boolean(e.alt)!=t.altKey)return!1;if(n)return e.click;if(i)return e.wheel;if(e.key){const n=t;return"DELETE"==e.key?"Delete"==n.key:"BACKSPACE"==e.key?"Backspace"==n.key:"ESC"==e.key?"Escape"==n.key:"UP"==e.key?"ArrowUp"==n.key:"DOWN"==e.key?"ArrowDown"==n.key:"LEFT"==e.key?"ArrowLeft"==n.key:"RIGHT"==e.key?"ArrowRight"==n.key:e.key&&n.key&&e.key.toLowerCase()==n.key.toLowerCase()}return!1}))(t,D[i],e,n));if(0!==i.length)return 1===i.length?i[0]:i.sort((t,e)=>{const n=D[t],o=D[e],i=n.combinations[0],r=o.combinations[0],s=(i.shift?1:0)+(i.ctrl?1:0)+(i.alt?1:0);return(r.shift?1:0)+(r.ctrl?1:0)+(r.alt?1:0)-s})[0]},"Zj",0,r,"_s",0,s,"_t",0,S,"aX",0,C,"b3",0,p,"bl",0,a,"hU",0,l,"hZ",0,M,"i1",0,h,"iD",0,y,"l8",0,x,"mD",0,c,"mt",0,w,"rB",0,m,"t9",0,v,"uK",0,f])},686(t,e,n){const o=()=>{if("undefined"==typeof navigator)return!1;if("userAgentData"in navigator&&navigator.userAgentData){const t=navigator.userAgentData.platform;if(t&&t.toLowerCase().includes("mac"))return!0}const t=navigator.userAgent.toLowerCase();if(t.includes("mac os")||t.includes("macintosh"))return!0;if(navigator.platform){const t=navigator.platform.toLowerCase();if(t.includes("mac")||t.includes("darwin"))return!0}try{if(void 0!==new KeyboardEvent("keydown",{metaKey:!0}).metaKey)return/mac|darwin|os x/i.test(navigator.userAgent)}catch(t){}return!1};n.d(e,["SA",0,t=>o()?t.metaKey:t.ctrlKey,"sy",0,()=>o()?"Cmd":"Ctrl"])},183(t,e,n){var o=n(220),i=n.n(o),r=n(716),s=n.n(r),a=n(628),d=n.n(a),l=new URL(n(660),n.b),c=new URL(n(620),n.b),h=new URL(n(749),n.b),u=new URL(n(690),n.b),A=s()(i()),g=d()(l),p=d()(c),f=d()(h),m=d()(u);A.push([t.id,`/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url(${g}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url(${p}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url(${f}) format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url(${m}) format("woff2");\n}\n`,"",{version:3,sources:["webpack://./src/fonts.css"],names:[],mappings:"AAAA,uFAAuF;AACvF;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF",sourcesContent:['/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url("@fontsource/inter/files/inter-latin-400-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url("@fontsource/inter/files/inter-latin-500-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url("@fontsource/inter/files/inter-latin-600-normal.woff2") format("woff2");\n}\n\n@font-face {\n\tfont-family: "Inter";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url("@fontsource/inter/files/inter-latin-700-normal.woff2") format("woff2");\n}\n'],sourceRoot:""}]);const b=A;n.d(e,["A",0,b])},870(t,e,n){var o=n(220),i=n.n(o),r=n(716),s=n.n(r)()(i());s.push([t.id,"html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}","",{version:3,sources:["webpack://./src/style.css"],names:[],mappings:"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",sourcesContent:["html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}"],sourceRoot:""}]);const a=s;n.d(e,["A",0,a])},574(t,e,n){var o=n(582),i=n.n(o),r=n(991),s=n.n(r),a=n(725),d=n.n(a),l=n(798),c=n.n(l),h=n(754),u=n.n(h),A=n(567),g=n.n(A),p=n(183),f={};f.styleTagTransform=g(),f.setAttributes=c(),f.insert=d().bind(null,"head"),f.domAPI=s(),f.insertStyleElement=u(),i()(p.A,f),p.A&&p.A.locals&&p.A.locals},919(t,e,n){var o=n(582),i=n.n(o),r=n(991),s=n.n(r),a=n(725),d=n.n(a),l=n(798),c=n.n(l),h=n(754),u=n.n(h),A=n(567),g=n.n(A),p=n(870),f={};f.styleTagTransform=g(),f.setAttributes=c(),f.insert=d().bind(null,"head"),f.domAPI=s(),f.insertStyleElement=u(),i()(p.A,f),p.A&&p.A.locals&&p.A.locals}}]); //# sourceMappingURL=286.js.map \ No newline at end of file diff --git a/cmd/mdl/webapp/dist/286.js.map b/cmd/mdl/webapp/dist/286.js.map index c10faef8..9ded7a05 100644 --- a/cmd/mdl/webapp/dist/286.js.map +++ b/cmd/mdl/webapp/dist/286.js.map @@ -1 +1 @@ -{"version":3,"file":"286.js","mappings":"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","sources":["webpack://app/./src/graph-view/constants.ts","webpack://app/./src/graph-view/edge-utils.ts","webpack://app/./src/graph-view/geometry.ts","webpack://app/./src/graph-view/defs.ts","webpack://app/./src/graph-view/svg-text.ts","webpack://app/./src/graph-view/svg-create.ts","webpack://app/./src/graph-view/intersect.ts","webpack://app/./src/graph-view/shapes.ts","webpack://app/./src/graph-view/undo.ts","webpack://app/./src/graph-view/node-content.ts","webpack://app/./src/graph-view/graph.ts","webpack://app/./src/graph-view/automatic-layout.ts","webpack://app/./src/graph-view/layout.ts","webpack://app/./src/graph-view/manual-layout.ts","webpack://app/./src/parseModel.ts","webpack://app/./src/shortcuts.tsx","webpack://app/./src/utils/platform.ts","webpack://app/./src/fonts.css","webpack://app/./src/style.css","webpack://app/./src/fonts.css?a158","webpack://app/./src/style.css?dd02"],"sourcesContent":["// Constants and default configurations for the graph view\n\nexport interface Point {\n\tx: number;\n\ty: number;\n}\n\nexport interface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\nexport interface NodeStyle {\n\t// Width of element, in pixels.\n\twidth?: number\n\t// Height of element, in pixels.\n\theight?: number\n\t// Background color of element as HTML RGB hex string (e.g. \"#ffffff\")\n\tbackground?: string\n\t// Stroke color of element as HTML RGB hex string (e.g. \"#000000\")\n\tstroke?: string\n\t// Foreground (text) color of element as HTML RGB hex string (e.g. \"#ffffff\")\n\tcolor?: string\n\t// Standard font size used to render text, in pixels.\n\tfontSize?: number\n\t// Shape used to render element.\n\tshape?: string\n\t// URL of PNG/JPG/GIF file or Base64 data URI representation.\n\ticon?: string\n\t// Type of border used to render element.\n\tborder?: string\n\t// Opacity used to render element; 0-100.\n\topacity?: number\n\t// Whether element metadata should be shown.\n\tmetadata?: boolean\n\t// Whether element description should be shown.\n\tdescription?: boolean\n}\n\nexport interface EdgeStyle {\n\t// Thickness of line, in pixels.\n\tthickness?: number\n\t// Color of line as HTML RGB hex string (e.g. \"#ffffff\").\n\tcolor?: string\n\t// Standard font size used to render relationship annotation, in pixels.\n\tfontSize?: number\n\t// Width of relationship annotation, in pixels.\n\twidth?: number\n\t// Whether line is dashed.\n\tdashed?: boolean\n\t// Position of label along edge (0-100).\n\tposition?: number\n\t// Opacity used to render relationship; 0-100.\n\topacity?: number\n\t// Arrow style for the edge.\n\tarrowStyle?: 'normal' | 'large' | 'small' | 'none'\n}\n\n// Default styles\nexport const DEFAULT_EDGE_STYLE: EdgeStyle = {\n\tthickness: 3,\n\tcolor: '#999',\n\topacity: 1,\n\tfontSize: 22,\n\tdashed: true,\n};\n\nexport const DEFAULT_NODE_STYLE: NodeStyle = {\n\twidth: 280,\n\theight: 180,\n\tbackground: 'rgba(255, 255, 255, .9)',\n\tcolor: '#666',\n\topacity: .9,\n\tstroke: '#999',\n\tfontSize: 22,\n\tshape: 'Box'\n};\n\n// SVG styles\nexport const SVG_STYLES = {\n\tnodeBorder: {\n\t\tfill: \"rgba(255, 255, 255, 0.86)\",\n\t\tstroke: \"#aaa\",\n\t\tfilter: 'url(#shadow)',\n\t},\n\tnodeText: {\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\tedgeText: {\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\tedgeRect: {\n\t\tfill: \"none\",\n\t\tstroke: \"none\",\n\t},\n\tgroupRect: {\n\t\tfill: \"rgba(0, 0, 0, 0.02)\",\n\t\tstroke: \"#666\",\n\t\t'stroke-width': 3,\n\t\t\"stroke-dasharray\": 4,\n\t},\n\tgroupText: {\n\t\tfill: \"#666\",\n\t\t\"font-size\": 22,\n\t\t\"font-weight\": \"500\",\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tcursor: \"default\"\n\t}\n};\n\n// Configuration constants\nexport const SVG_PADDING = 20;\nexport const DEFAULT_GRID_SIZE = 25;\nexport const EDGE_SPREAD_DISTANCE = 70;\nexport const EDGE_SPREAD_DISTANCE_X = 200;\n\n// Utility function to apply styles to SVG elements\nexport const applyStyle = (el: SVGElement, style: { [key: string]: string | number }) => {\n\tObject.keys(style).forEach(key => {\n\t\tconst value = style[key];\n\t\tif (typeof value === 'number') {\n\t\t\tel.style.setProperty(key, value.toString());\n\t\t} else {\n\t\t\tel.style.setProperty(key, value);\n\t\t}\n\t});\n};\n\n// Utility function to calculate distance between two points\nexport const calculateDistance = (p1: Point, p2: Point): number => {\n\treturn Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));\n};","import { Point, calculateDistance } from './constants';\nimport { Segment } from './intersect';\n\n// Define interfaces locally since they're not exported from graph.ts\ninterface NodeStyle {\n\tbackground?: string;\n\tstroke?: string;\n\topacity?: number;\n\tfontSize?: number;\n\tshape?: string;\n\tborder?: string;\n}\n\ninterface Node extends Point {\n\tid: string;\n\ttitle: string;\n\tsub: string;\n\tdescription: string;\n\twidth: number;\n\theight: number;\n\tref?: SVGGElement;\n\tselected?: boolean;\n\tintersect: (p: Point) => Point;\n\tstyle: NodeStyle;\n}\n\ninterface EdgeVertex extends Point {\n\tid: string;\n\tselected?: boolean;\n\tref?: SVGElement;\n\tlabel?: boolean;\n\tauto?: boolean;\n}\n\ninterface EdgeStyle {\n\tcolor?: string;\n\tthickness?: number;\n\tfontSize?: number;\n\tposition?: number;\n\tdashed?: boolean;\n}\n\ninterface Edge {\n\tid: string;\n\tlabel: string;\n\tfrom: Node;\n\tto: Node;\n\tvertices?: EdgeVertex[];\n\tref?: SVGGElement;\n\tstyle: EdgeStyle;\n\tinitVertex: (p: Point) => EdgeVertex;\n\tuserDeletedVertices?: boolean; // Track if user explicitly deleted vertices\n}\n\nexport interface EdgeLabelPlacement extends Point {\n\torientation: 'horizontal' | 'vertical';\n\tsegment?: Segment;\n\tmovable: boolean;\n}\n\n/**\n * Calculate edge vertices, handling multi-edge scenarios and auto-vertices\n */\nexport function calculateEdgeVertices(edge: Edge): Point[] {\n\tconst n1 = edge.from, n2 = edge.to;\n\t\n\t\n\t// if vertices exists, follow them\n\tlet vertices: Point[] = edge.vertices ? edge.vertices.concat() : [];\n\t// Don't remove label vertices - they should be preserved for rendering\n\t// (The autoLayout process handles replacing old ones with new ones)\n\n\tvertices.unshift(n1)\n\tvertices.push(n2)\n\n\tconst interior = vertices.slice(1, -1) as EdgeVertex[];\n\tconst firstRoutingVertex = interior.find(vertex => !vertex.label) ?? n2;\n\tlet lastRoutingVertex: Point = n1;\n\tfor (let index = interior.length - 1; index >= 0; index--) {\n\t\tif (!interior[index].label) {\n\t\t\tlastRoutingVertex = interior[index];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!n1.intersect || !n2.intersect) {\n\t\tthrow new Error(`edge ${edge.id} nodes must be drawn before routing`);\n\t}\n\tvertices[0] = n1.intersect(firstRoutingVertex);\n\tvertices[vertices.length - 1] = n2.intersect(lastRoutingVertex);\n\t\n\treturn vertices;\n}\n\n/**\n * Calculate the label anchor and the orientation of the path segment that owns\n * it. Renderers use the orientation to place text beside the relationship line\n * instead of centering text over vertical segments.\n */\nexport function calculateLabelPlacement(\n\tvertices: Point[],\n\tposition: number,\n\tdefaultPoint: Point,\n): EdgeLabelPlacement {\n\tlet point = {x: defaultPoint.x, y: defaultPoint.y};\n\tlet segment: Segment | undefined;\n\tconst labelIndex = vertices.findIndex(vertex => (vertex as EdgeVertex).label);\n\tlet movable = true;\n\n\tif (labelIndex >= 0) {\n\t\tconst labelVertex = vertices[labelIndex] as EdgeVertex;\n\t\tpoint = labelVertex;\n\t\tmovable = labelVertex.auto === true;\n\t\tconst adjacentSegments: Segment[] = [];\n\t\tif (labelIndex > 0) {\n\t\t\tadjacentSegments.push({p: vertices[labelIndex - 1], q: point});\n\t\t}\n\t\tif (labelIndex < vertices.length - 1) {\n\t\t\tadjacentSegments.push({p: point, q: vertices[labelIndex + 1]});\n\t\t}\n\t\tsegment = adjacentSegments.reduce((longest, candidate) => {\n\t\t\tif (!longest) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\treturn calculateDistance(candidate.p, candidate.q) >\n\t\t\t\tcalculateDistance(longest.p, longest.q)\n\t\t\t\t? candidate\n\t\t\t\t: longest;\n\t\t}, undefined);\n\t} else {\n\t\tconst totalLength = vertices.slice(1).reduce(\n\t\t\t(sum, vertex, index) => sum + calculateDistance(vertices[index], vertex),\n\t\t\t0,\n\t\t);\n\t\tconst targetLength = totalLength * position;\n\t\tlet traversed = 0;\n\t\tfor (let index = 1; index < vertices.length; index++) {\n\t\t\tconst candidate = {p: vertices[index - 1], q: vertices[index]};\n\t\t\tconst length = calculateDistance(candidate.p, candidate.q);\n\t\t\tif (length > 0 && traversed + length >= targetLength) {\n\t\t\t\tconst segmentPosition = (targetLength - traversed) / length;\n\t\t\t\tpoint = {\n\t\t\t\t\tx: candidate.p.x + (candidate.q.x - candidate.p.x) * segmentPosition,\n\t\t\t\t\ty: candidate.p.y + (candidate.q.y - candidate.p.y) * segmentPosition,\n\t\t\t\t};\n\t\t\t\tsegment = candidate;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttraversed += length;\n\t\t}\n\t}\n\n\tconst horizontalDistance = segment ? Math.abs(segment.q.x - segment.p.x) : 0;\n\tconst verticalDistance = segment ? Math.abs(segment.q.y - segment.p.y) : 0;\n\treturn {\n\t\t...point,\n\t\torientation: verticalDistance > horizontalDistance ? 'vertical' : 'horizontal',\n\t\tsegment,\n\t\tmovable,\n\t};\n}\n\n/** createEdgePath draws the given points without cutting or moving the route. */\nexport function createEdgePath(vertices: Point[]): string {\n\tif (vertices.length < 2) {\n\t\tthrow new Error(\"edge route must contain at least two points\");\n\t}\n\treturn vertices.slice(1).reduce(\n\t\t(path, point) => `${path} L${point.x},${point.y}`,\n\t\t`M${vertices[0].x},${vertices[0].y}`,\n\t);\n}","// This file defines the diagram sizes and positions shared by layout and SVG\n// drawing. Automatic and manual layout both produce ResolvedLayout. The SVG\n// drawing code accepts a layout only after this file has checked it.\n\n/** Point is one position in the diagram. */\nexport interface Point {\n\tx: number;\n\ty: number;\n}\n\n/** Size is a width and height. Both values must be zero or greater. */\nexport interface Size {\n\twidth: number;\n\theight: number;\n}\n\n/** Rect is a box that does not rotate. */\nexport interface Rect extends Point, Size {}\n\n/** MeasuredNode holds the exact box and text sizes needed before layout. */\nexport interface MeasuredNode {\n\tid: string;\n\tparentId?: string;\n\tsize: Size;\n\tcontentSize: Size;\n\tcenterOffset: Point;\n\tshape: string;\n}\n\n/** MeasuredGroup describes a boundary that must contain its children and title. */\nexport interface MeasuredGroup {\n\tid: string;\n\tparentId?: string;\n\ttitleSize: Size;\n}\n\n/** MeasuredEdge names the two connected nodes and the exact label size. */\nexport interface MeasuredEdge {\n\tid: string;\n\tsourceId: string;\n\ttargetId: string;\n\tlabelSize?: Size;\n}\n\n/**\n * MeasuredDiagram is the complete input to automatic or manual layout.\n * It contains sizes, but no positions.\n */\nexport interface MeasuredDiagram {\n\tid: string;\n\tnodes: MeasuredNode[];\n\tgroups: MeasuredGroup[];\n\tedges: MeasuredEdge[];\n}\n\n/** ResolvedNode is one measured node placed in the diagram. */\nexport interface ResolvedNode {\n\tid: string;\n\tparentId?: string;\n\tbounds: Rect;\n}\n\n/** ResolvedGroup is one placed boundary and its title box. */\nexport interface ResolvedGroup {\n\tid: string;\n\tparentId?: string;\n\tbounds: Rect;\n\ttitleBounds: Rect;\n}\n\n/**\n * ResolvedEdge contains a complete route. Each section is an ordered list of\n * points. labelBounds is the final label box chosen by layout.\n */\nexport interface ResolvedEdge {\n\tid: string;\n\tsourceId: string;\n\ttargetId: string;\n\tsections: Point[][];\n\tlabelBounds?: Rect;\n}\n\n/**\n * ResolvedLayout is the unchecked result of automatic or manual layout.\n * Drawing code must not use it directly.\n */\nexport interface ResolvedLayout {\n\tdiagramId: string;\n\tbounds: Rect;\n\tnodes: ResolvedNode[];\n\tgroups: ResolvedGroup[];\n\tedges: ResolvedEdge[];\n}\n\n/** GeometryIssueCode names one rule that the layout broke. */\nexport type GeometryIssueCode =\n\t| \"duplicate_id\"\n\t| \"missing_element\"\n\t| \"unexpected_element\"\n\t| \"non_finite\"\n\t| \"size_mismatch\"\n\t| \"content_overflow\"\n\t| \"node_overlap\"\n\t| \"group_overlap\"\n\t| \"false_containment\"\n\t| \"edge_through_node\"\n\t| \"edge_through_group\"\n\t| \"label_node_overlap\"\n\t| \"label_label_overlap\"\n\t| \"label_edge_overlap\"\n\t| \"label_title_overlap\"\n\t| \"out_of_bounds\"\n\t| \"disconnected_edge\";\n\n/** GeometryIssue reports the stable IDs of the affected diagram elements. */\nexport interface GeometryIssue {\n\tcode: GeometryIssueCode;\n\telementIds: string[];\n\tmessage: string;\n}\n\nconst validatedLayoutBrand: unique symbol = Symbol(\"ValidatedLayout\");\n\n/**\n * ValidatedLayout is the only layout the SVG drawing code accepts. The private\n * marker prevents other code from creating one without running the checks.\n */\nexport type ValidatedLayout = ResolvedLayout & {\n\treadonly [validatedLayoutBrand]: true;\n};\n\n/** GeometryValidationError reports every layout problem found in one check. */\nexport class GeometryValidationError extends Error {\n\treadonly issues: GeometryIssue[];\n\n\tconstructor(issues: GeometryIssue[]) {\n\t\tsuper(issues.map(issue => issue.message).join(\"\\n\"));\n\t\tthis.name = \"GeometryValidationError\";\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * validateResolvedLayout checks that every element is present, every number is\n * valid, boundaries show true ownership, elements do not collide, and all\n * content stays inside the diagram.\n */\nexport function validateResolvedLayout(\n\tmeasured: MeasuredDiagram,\n\tresolved: ResolvedLayout,\n): ValidatedLayout {\n\tconst issues: GeometryIssue[] = [];\n\tif (measured.id !== resolved.diagramId) {\n\t\tissues.push(issue(\n\t\t\t\"missing_element\",\n\t\t\t[measured.id, resolved.diagramId],\n\t\t\t`layout ${resolved.diagramId} does not resolve measured diagram ${measured.id}`,\n\t\t));\n\t}\n\n\tcheckUniqueIDs(\"measured node\", measured.nodes.map(node => node.id), issues);\n\tcheckUniqueIDs(\"measured group\", measured.groups.map(group => group.id), issues);\n\tcheckUniqueIDs(\"measured edge\", measured.edges.map(edge => edge.id), issues);\n\tcheckUniqueIDs(\"resolved node\", resolved.nodes.map(node => node.id), issues);\n\tcheckUniqueIDs(\"resolved group\", resolved.groups.map(group => group.id), issues);\n\tcheckUniqueIDs(\"resolved edge\", resolved.edges.map(edge => edge.id), issues);\n\n\tcheckCoverage(\n\t\t\"node\",\n\t\tmeasured.nodes.map(node => node.id),\n\t\tresolved.nodes.map(node => node.id),\n\t\tissues,\n\t);\n\tcheckCoverage(\n\t\t\"group\",\n\t\tmeasured.groups.map(group => group.id),\n\t\tresolved.groups.map(group => group.id),\n\t\tissues,\n\t);\n\tcheckCoverage(\n\t\t\"edge\",\n\t\tmeasured.edges.map(edge => edge.id),\n\t\tresolved.edges.map(edge => edge.id),\n\t\tissues,\n\t);\n\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\tconst measuredGroups = new Map(measured.groups.map(group => [group.id, group]));\n\tconst resolvedNodes = new Map(resolved.nodes.map(node => [node.id, node]));\n\tconst resolvedGroups = new Map(resolved.groups.map(group => [group.id, group]));\n\n\tcheckFiniteRect(\"diagram\", resolved.diagramId, resolved.bounds, issues);\n\tfor (const node of resolved.nodes) {\n\t\tcheckFiniteRect(\"node\", node.id, node.bounds, issues);\n\t\tconst expected = measuredNodes.get(node.id);\n\t\tif (expected && (\n\t\t\t!nearlyEqual(expected.size.width, node.bounds.width) ||\n\t\t\t!nearlyEqual(expected.size.height, node.bounds.height)\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"size_mismatch\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} size changed after measurement`,\n\t\t\t));\n\t\t}\n\t\tif (expected && (\n\t\t\texpected.contentSize.width > node.bounds.width ||\n\t\t\texpected.contentSize.height > node.bounds.height\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"content_overflow\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} content exceeds its resolved bounds`,\n\t\t\t));\n\t\t}\n\t\tcheckRectInBounds(\"node\", node.id, node.bounds, resolved.bounds, issues);\n\t}\n\tfor (const group of resolved.groups) {\n\t\tcheckFiniteRect(\"group\", group.id, group.bounds, issues);\n\t\tcheckFiniteRect(\"group title\", group.id, group.titleBounds, issues);\n\t\tcheckRectInBounds(\"group\", group.id, group.bounds, resolved.bounds, issues);\n\t\tif (!contains(group.bounds, group.titleBounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} does not contain its title`,\n\t\t\t));\n\t\t}\n\t\tconst expected = measuredGroups.get(group.id);\n\t\tif (expected && (\n\t\t\t!nearlyEqual(expected.titleSize.width, group.titleBounds.width) ||\n\t\t\t!nearlyEqual(expected.titleSize.height, group.titleBounds.height)\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"size_mismatch\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} title size changed after measurement`,\n\t\t\t));\n\t\t}\n\t}\n\n\tcheckNodeOverlaps(resolved.nodes, issues);\n\tcheckGroupOverlaps(resolved.groups, issues);\n\tcheckOwnership(\n\t\tmeasured.nodes,\n\t\tmeasured.groups,\n\t\tresolvedNodes,\n\t\tresolvedGroups,\n\t\tissues,\n\t);\n\tcheckEdges(\n\t\tmeasured,\n\t\tresolved,\n\t\tresolvedNodes,\n\t\tresolvedGroups,\n\t\tmeasuredNodes,\n\t\tmeasuredGroups,\n\t\tissues,\n\t);\n\n\tif (issues.length > 0) {\n\t\tthrow new GeometryValidationError(issues);\n\t}\n\treturn resolved as ValidatedLayout;\n}\n\n/** checkUniqueIDs requires each model ID to appear only once. */\nfunction checkUniqueIDs(label: string, ids: string[], issues: GeometryIssue[]): void {\n\tconst seen = new Set();\n\tfor (const id of ids) {\n\t\tif (seen.has(id)) {\n\t\t\tissues.push(issue(\"duplicate_id\", [id], `${label} ${id} is duplicated`));\n\t\t}\n\t\tseen.add(id);\n\t}\n}\n\n/** checkCoverage rejects missing elements and old elements left in a layout. */\nfunction checkCoverage(\n\tlabel: string,\n\texpected: string[],\n\tactual: string[],\n\tissues: GeometryIssue[],\n): void {\n\tconst expectedIDs = new Set(expected);\n\tconst actualIDs = new Set(actual);\n\tfor (const id of expectedIDs) {\n\t\tif (!actualIDs.has(id)) {\n\t\t\tissues.push(issue(\"missing_element\", [id], `${label} ${id} is missing from resolved layout`));\n\t\t}\n\t}\n\tfor (const id of actualIDs) {\n\t\tif (!expectedIDs.has(id)) {\n\t\t\tissues.push(issue(\"unexpected_element\", [id], `${label} ${id} is not in the measured diagram`));\n\t\t}\n\t}\n}\n\n/** checkFiniteRect rejects invalid numbers and negative sizes. */\nfunction checkFiniteRect(\n\tlabel: string,\n\tid: string,\n\trect: Rect,\n\tissues: GeometryIssue[],\n): void {\n\tif (!isFiniteRect(rect) || rect.width < 0 || rect.height < 0) {\n\t\tissues.push(issue(\"non_finite\", [id], `${label} ${id} has invalid geometry`));\n\t}\n}\n\n/** checkRectInBounds requires the whole box to fit inside the diagram. */\nfunction checkRectInBounds(\n\tlabel: string,\n\tid: string,\n\trect: Rect,\n\tbounds: Rect,\n\tissues: GeometryIssue[],\n): void {\n\tif (!contains(bounds, rect)) {\n\t\tissues.push(issue(\"out_of_bounds\", [id], `${label} ${id} lies outside the resolved view bounds`));\n\t}\n}\n\n/** checkNodeOverlaps prevents two nodes from sharing the same area. */\nfunction checkNodeOverlaps(nodes: ResolvedNode[], issues: GeometryIssue[]): void {\n\tforEachPair(nodes, (first, second) => {\n\t\tif (overlaps(first.bounds, second.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"node_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`nodes ${first.id} and ${second.id} overlap`,\n\t\t\t));\n\t\t}\n\t});\n}\n\n/**\n * checkGroupOverlaps allows one real boundary inside another, but rejects\n * boundaries that partly cover each other and suggest false ownership.\n */\nfunction checkGroupOverlaps(groups: ResolvedGroup[], issues: GeometryIssue[]): void {\n\tforEachPair(groups, (first, second) => {\n\t\tif (\n\t\t\toverlaps(first.bounds, second.bounds) &&\n\t\t\t!contains(first.bounds, second.bounds) &&\n\t\t\t!contains(second.bounds, first.bounds)\n\t\t) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"group_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`groups ${first.id} and ${second.id} partially overlap`,\n\t\t\t));\n\t\t}\n\t});\n}\n\n/**\n * checkOwnership requires each child to be inside the parent named by the\n * model. Visual overlap never creates ownership.\n */\nfunction checkOwnership(\n\tnodes: MeasuredNode[],\n\tgroups: MeasuredGroup[],\n\tresolvedNodes: Map,\n\tresolvedGroups: Map,\n\tissues: GeometryIssue[],\n): void {\n\tfor (const node of nodes) {\n\t\tconst child = resolvedNodes.get(node.id);\n\t\tif (child && child.parentId !== node.parentId) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} has the wrong parent ID`,\n\t\t\t));\n\t\t}\n\t\tif (!node.parentId) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parent = resolvedGroups.get(node.parentId);\n\t\tif (child && parent && !contains(parent.bounds, child.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[node.id, node.parentId],\n\t\t\t\t`node ${node.id} lies outside owning group ${node.parentId}`,\n\t\t\t));\n\t\t}\n\t}\n\tfor (const group of groups) {\n\t\tconst child = resolvedGroups.get(group.id);\n\t\tif (child && child.parentId !== group.parentId) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} has the wrong parent ID`,\n\t\t\t));\n\t\t}\n\t\tif (!group.parentId) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parent = resolvedGroups.get(group.parentId);\n\t\tif (child && parent && !contains(parent.bounds, child.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id, group.parentId],\n\t\t\t\t`group ${group.id} lies outside owning group ${group.parentId}`,\n\t\t\t));\n\t\t}\n\t}\n}\n\n/**\n * checkEdges checks connected node IDs, complete routes, crossings, labels,\n * and boundary crossings for every relationship.\n */\nfunction checkEdges(\n\tmeasured: MeasuredDiagram,\n\tresolved: ResolvedLayout,\n\tnodes: Map,\n\tgroups: Map,\n\tmeasuredNodes: Map,\n\tmeasuredGroups: Map,\n\tissues: GeometryIssue[],\n): void {\n\tconst measuredEdges = new Map(measured.edges.map(edge => [edge.id, edge]));\n\tconst labels: Array<{id: string; bounds: Rect}> = [];\n\tfor (const edge of resolved.edges) {\n\t\tconst expected = measuredEdges.get(edge.id);\n\t\tif (!expected) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\tedge.sourceId !== expected.sourceId ||\n\t\t\tedge.targetId !== expected.targetId\n\t\t) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id, edge.sourceId, edge.targetId],\n\t\t\t\t`edge ${edge.id} endpoints do not match its measured contract`,\n\t\t\t));\n\t\t}\n\t\tif (edge.sections.length === 0 || edge.sections.some(section => section.length < 2)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} has no complete route section`,\n\t\t\t));\n\t\t\tcontinue;\n\t\t}\n\t\tcheckSectionConnections(edge, nodes, issues);\n\t\tfor (const section of edge.sections) {\n\t\t\tfor (const point of section) {\n\t\t\t\tif (!isFinitePoint(point) || !pointInRect(point, resolved.bounds, true)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"out_of_bounds\",\n\t\t\t\t\t\t[edge.id],\n\t\t\t\t\t\t`edge ${edge.id} has a route point outside the resolved view`,\n\t\t\t\t\t));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const node of nodes.values()) {\n\t\t\tif (node.id === edge.sourceId || node.id === edge.targetId) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (routeIntersectsRect(edge.sections, inset(node.bounds, 0.5))) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"edge_through_node\",\n\t\t\t\t\t[edge.id, node.id],\n\t\t\t\t\t`edge ${edge.id} crosses unrelated node ${node.id}`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\tfor (const group of groups.values()) {\n\t\t\tconst sourceInside = belongsToGroup(\n\t\t\t\tedge.sourceId,\n\t\t\t\tgroup.id,\n\t\t\t\tmeasuredNodes,\n\t\t\t\tmeasuredGroups,\n\t\t\t);\n\t\t\tconst targetInside = belongsToGroup(\n\t\t\t\tedge.targetId,\n\t\t\t\tgroup.id,\n\t\t\t\tmeasuredNodes,\n\t\t\t\tmeasuredGroups,\n\t\t\t);\n\t\t\tif (\n\t\t\t\tsourceInside === targetInside &&\n\t\t\t\trouteViolatesGroup(edge.sections, group.bounds, sourceInside)\n\t\t\t) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"edge_through_group\",\n\t\t\t\t\t[edge.id, group.id],\n\t\t\t\t\t`edge ${edge.id} violates group ${group.id} boundary`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\tif (edge.labelBounds) {\n\t\t\tcheckFiniteRect(\"edge label\", edge.id, edge.labelBounds, issues);\n\t\t\tcheckRectInBounds(\"edge label\", edge.id, edge.labelBounds, resolved.bounds, issues);\n\t\t\tif (expected.labelSize && (\n\t\t\t\t!nearlyEqual(expected.labelSize.width, edge.labelBounds.width) ||\n\t\t\t\t!nearlyEqual(expected.labelSize.height, edge.labelBounds.height)\n\t\t\t)) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"size_mismatch\",\n\t\t\t\t\t[edge.id],\n\t\t\t\t\t`edge ${edge.id} label size changed after measurement`,\n\t\t\t\t));\n\t\t\t}\n\t\t\tfor (const node of nodes.values()) {\n\t\t\t\tif (overlaps(edge.labelBounds, node.bounds)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"label_node_overlap\",\n\t\t\t\t\t\t[edge.id, node.id],\n\t\t\t\t\t\t`edge ${edge.id} label overlaps node ${node.id}`,\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const group of groups.values()) {\n\t\t\t\tif (overlaps(edge.labelBounds, group.titleBounds)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"label_title_overlap\",\n\t\t\t\t\t\t[edge.id, group.id],\n\t\t\t\t\t\t`edge ${edge.id} label overlaps group ${group.id} title`,\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t\tlabels.push({id: edge.id, bounds: edge.labelBounds});\n\t\t} else if (expected.labelSize) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"missing_element\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} is missing its measured label`,\n\t\t\t));\n\t\t}\n\t}\n\tforEachPair(labels, (first, second) => {\n\t\tif (overlaps(first.bounds, second.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"label_label_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`edge labels ${first.id} and ${second.id} overlap`,\n\t\t\t));\n\t\t}\n\t});\n\tfor (const label of labels) {\n\t\tfor (const edge of resolved.edges) {\n\t\t\tif (routeIntersectsRect(edge.sections, inset(label.bounds, 0.5))) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"label_edge_overlap\",\n\t\t\t\t\t[label.id, edge.id],\n\t\t\t\t\t`edge ${edge.id} crosses label ${label.id}`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * checkSectionConnections requires one ordered route from the source border to\n * the target border. Separate or reversed sections are not a complete route.\n */\nfunction checkSectionConnections(\n\tedge: ResolvedEdge,\n\tnodes: Map,\n\tissues: GeometryIssue[],\n): void {\n\tfor (let index = 1; index < edge.sections.length; index++) {\n\t\tconst previous = edge.sections[index - 1].slice(-1)[0];\n\t\tconst current = edge.sections[index][0];\n\t\tif (!samePoint(previous, current)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} has disconnected route sections`,\n\t\t\t));\n\t\t\treturn;\n\t\t}\n\t}\n\tconst source = nodes.get(edge.sourceId);\n\tconst target = nodes.get(edge.targetId);\n\tconst start = edge.sections[0][0];\n\tconst end = edge.sections.slice(-1)[0].slice(-1)[0];\n\tif (\n\t\t!source ||\n\t\t!target ||\n\t\t!pointOnBorder(start, source.bounds) ||\n\t\t!pointOnBorder(end, target.bounds)\n\t) {\n\t\tissues.push(issue(\n\t\t\t\"disconnected_edge\",\n\t\t\t[edge.id, edge.sourceId, edge.targetId],\n\t\t\t`edge ${edge.id} does not connect both node borders`,\n\t\t));\n\t}\n}\n\n/** belongsToGroup follows parent IDs to decide whether a node belongs to a group. */\nfunction belongsToGroup(\n\tnodeId: string,\n\tgroupId: string,\n\tnodes: Map,\n\tgroups: Map,\n): boolean {\n\tlet parent = nodes.get(nodeId)?.parentId;\n\twhile (parent) {\n\t\tif (parent === groupId) {\n\t\t\treturn true;\n\t\t}\n\t\tparent = groups.get(parent)?.parentId;\n\t}\n\treturn false;\n}\n\n/**\n * routeViolatesGroup checks the middle of each line. When both connected nodes\n * are inside a group, the line must stay inside it. When both are outside, the\n * line must stay outside it.\n */\nfunction routeViolatesGroup(\n\tsections: Point[][],\n\tgroup: Rect,\n\tshouldBeInside: boolean,\n): boolean {\n\tfor (const section of sections) {\n\t\tfor (let index = 1; index < section.length; index++) {\n\t\t\tconst midpoint = {\n\t\t\t\tx: (section[index - 1].x + section[index].x) / 2,\n\t\t\t\ty: (section[index - 1].y + section[index].y) / 2,\n\t\t\t};\n\t\t\tif (pointInRect(midpoint, group, false) !== shouldBeInside) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/** routeIntersectsRect reports whether any part of a route enters a box. */\nfunction routeIntersectsRect(sections: Point[][], rect: Rect): boolean {\n\treturn sections.some(section => section.some((point, index) =>\n\t\tindex > 0 && segmentIntersectsRect(section[index - 1], point, rect),\n\t));\n}\n\n/**\n * segmentIntersectsRect checks both straight and diagonal lines. A line may\n * touch the box border, but it may not enter the box.\n */\nfunction segmentIntersectsRect(start: Point, end: Point, rect: Rect): boolean {\n\tconst dx = end.x - start.x;\n\tconst dy = end.y - start.y;\n\tlet minimum = 0;\n\tlet maximum = 1;\n\tfor (const [origin, delta, lower, upper] of [\n\t\t[start.x, dx, rect.x, rect.x + rect.width],\n\t\t[start.y, dy, rect.y, rect.y + rect.height],\n\t] as const) {\n\t\tif (Math.abs(delta) < Number.EPSILON) {\n\t\t\tif (origin <= lower || origin >= upper) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst first = (lower - origin) / delta;\n\t\tconst second = (upper - origin) / delta;\n\t\tminimum = Math.max(minimum, Math.min(first, second));\n\t\tmaximum = Math.min(maximum, Math.max(first, second));\n\t\tif (minimum >= maximum) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn maximum > 0 && minimum < 1;\n}\n\n/** overlaps treats touching borders as contact, not shared area. */\nfunction overlaps(first: Rect, second: Rect): boolean {\n\treturn (\n\t\tfirst.x < second.x + second.width &&\n\t\tsecond.x < first.x + first.width &&\n\t\tfirst.y < second.y + second.height &&\n\t\tsecond.y < first.y + first.height\n\t);\n}\n\n/** contains requires the whole inner box to be inside the outer box. */\nfunction contains(outer: Rect, inner: Rect): boolean {\n\treturn (\n\t\touter.x <= inner.x &&\n\t\touter.y <= inner.y &&\n\t\touter.x + outer.width >= inner.x + inner.width &&\n\t\touter.y + outer.height >= inner.y + inner.height\n\t);\n}\n\n/** pointInRect can include or exclude points that only touch the border. */\nfunction pointInRect(point: Point, rect: Rect, inclusive: boolean): boolean {\n\tif (inclusive) {\n\t\treturn (\n\t\t\tpoint.x >= rect.x &&\n\t\t\tpoint.y >= rect.y &&\n\t\t\tpoint.x <= rect.x + rect.width &&\n\t\t\tpoint.y <= rect.y + rect.height\n\t\t);\n\t}\n\treturn (\n\t\tpoint.x > rect.x &&\n\t\tpoint.y > rect.y &&\n\t\tpoint.x < rect.x + rect.width &&\n\t\tpoint.y < rect.y + rect.height\n\t);\n}\n\n/** inset makes a box smaller so touching its border does not count as crossing it. */\nfunction inset(rect: Rect, amount: number): Rect {\n\treturn {\n\t\tx: rect.x + amount,\n\t\ty: rect.y + amount,\n\t\twidth: Math.max(0, rect.width - amount * 2),\n\t\theight: Math.max(0, rect.height - amount * 2),\n\t};\n}\n\n/** isFinitePoint accepts only real numbers that SVG can draw. */\nfunction isFinitePoint(point: Point): boolean {\n\treturn Number.isFinite(point.x) && Number.isFinite(point.y);\n}\n\n/** isFiniteRect checks all four numbers in a box. */\nfunction isFiniteRect(rect: Rect): boolean {\n\treturn isFinitePoint(rect) && Number.isFinite(rect.width) && Number.isFinite(rect.height);\n}\n\n/** samePoint compares route points while allowing tiny browser number changes. */\nfunction samePoint(first: Point, second: Point): boolean {\n\treturn nearlyEqual(first.x, second.x) && nearlyEqual(first.y, second.y);\n}\n\n/** pointOnBorder checks that a route endpoint touches one side of a node box. */\nfunction pointOnBorder(point: Point, rect: Rect): boolean {\n\tconst withinX = point.x >= rect.x - 0.1 && point.x <= rect.x + rect.width + 0.1;\n\tconst withinY = point.y >= rect.y - 0.1 && point.y <= rect.y + rect.height + 0.1;\n\tconst onVertical = nearlyEqual(point.x, rect.x) ||\n\t\tnearlyEqual(point.x, rect.x + rect.width);\n\tconst onHorizontal = nearlyEqual(point.y, rect.y) ||\n\t\tnearlyEqual(point.y, rect.y + rect.height);\n\treturn (withinY && onVertical) || (withinX && onHorizontal);\n}\n\n/** nearlyEqual hides only browser rounding smaller than one tenth of a unit. */\nfunction nearlyEqual(first: number, second: number): boolean {\n\treturn Math.abs(first - second) <= 0.1;\n}\n\n/** forEachPair compares each pair once. */\nfunction forEachPair(items: T[], visit: (first: T, second: T) => void): void {\n\tfor (let first = 0; first < items.length; first++) {\n\t\tfor (let second = first + 1; second < items.length; second++) {\n\t\t\tvisit(items[first], items[second]);\n\t\t}\n\t}\n}\n\n/** issue creates one layout problem report. */\nfunction issue(\n\tcode: GeometryIssueCode,\n\telementIds: string[],\n\tmessage: string,\n): GeometryIssue {\n\treturn {code, elementIds, message};\n}\n","export const defs = `\n\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n`\n","type TextAttributes = Record;\n\ninterface TextSize {\n\twidth: number;\n\theight: number;\n}\n\n/** WrappedText contains the exact lines and widest rendered line. */\nexport interface WrappedText {\n\tlines: string[];\n\tmaxW: number;\n}\n\nconst measuredText = new Map();\nlet measurementSVG: SVGSVGElement | undefined;\n\n/**\n * measureSVGText returns the browser's rendered text size. Results are cached\n * because node and edge layout often measures the same words many times.\n */\nexport function measureSVGText(text: string, attrs: TextAttributes): TextSize {\n\tconst key = measurementKey(text, attrs);\n\tconst cached = measuredText.get(key);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tconst node = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n\tnode.setAttribute(\"x\", \"0\");\n\tnode.setAttribute(\"y\", \"0\");\n\tfor (const [name, value] of Object.entries(attrs)) {\n\t\tnode.setAttribute(name, value);\n\t}\n\tnode.textContent = text;\n\n\tconst svg = getMeasurementSVG();\n\tsvg.appendChild(node);\n\tconst bounds = node.getBBox();\n\tnode.remove();\n\tconst size = {width: bounds.width, height: bounds.height};\n\tmeasuredText.set(key, size);\n\treturn size;\n}\n\n/**\n * svgTextWrap splits text into lines that fit the given width. It uses the\n * same browser text sizes that SVG drawing uses.\n */\nexport function svgTextWrap(\n\ttext: string,\n\twidth: number,\n\tattrs: TextAttributes,\n): WrappedText {\n\tlet maxW = 0;\n\tconst lines = text.trim().split(\"\\n\").flatMap(paragraph => {\n\t\tconst paragraphLines: string[] = [];\n\t\tlet currentLine: string[] = [];\n\t\tfor (const word of paragraph.trim().split(/\\s+/)) {\n\t\t\tif (measureSVGText(word, attrs).width > width) {\n\t\t\t\tif (currentLine.length > 0) {\n\t\t\t\t\tparagraphLines.push(currentLine.join(\" \"));\n\t\t\t\t\tcurrentLine = [];\n\t\t\t\t}\n\t\t\t\tconst parts = breakLongWord(word, width, attrs);\n\t\t\t\tfor (const part of parts.slice(0, -1)) {\n\t\t\t\t\tparagraphLines.push(part);\n\t\t\t\t\tmaxW = Math.max(maxW, measureSVGText(part, attrs).width);\n\t\t\t\t}\n\t\t\t\tcurrentLine = parts.slice(-1);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst nextLine = [...currentLine, word];\n\t\t\tconst size = measureSVGText(nextLine.join(\" \"), attrs);\n\t\t\tif (size.width > width && currentLine.length > 0) {\n\t\t\t\tparagraphLines.push(currentLine.join(\" \"));\n\t\t\t\tcurrentLine = [word];\n\t\t\t} else {\n\t\t\t\tmaxW = Math.max(maxW, size.width);\n\t\t\t\tcurrentLine = nextLine;\n\t\t\t}\n\t\t}\n\t\tif (currentLine.length > 0) {\n\t\t\tconst line = currentLine.join(\" \");\n\t\t\tparagraphLines.push(line);\n\t\t\tmaxW = Math.max(maxW, measureSVGText(line, attrs).width);\n\t\t}\n\t\treturn paragraphLines;\n\t});\n\treturn {lines, maxW};\n}\n\n/** getMeasurementSVG creates the single hidden SVG used for text measurement. */\nfunction getMeasurementSVG(): SVGSVGElement {\n\tif (measurementSVG?.isConnected) {\n\t\treturn measurementSVG;\n\t}\n\tmeasurementSVG = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n\tmeasurementSVG.setAttribute(\"aria-hidden\", \"true\");\n\tmeasurementSVG.style.position = \"absolute\";\n\tmeasurementSVG.style.visibility = \"hidden\";\n\tmeasurementSVG.style.pointerEvents = \"none\";\n\tdocument.body.appendChild(measurementSVG);\n\treturn measurementSVG;\n}\n\n/** measurementKey gives equal text and font settings the same cache entry. */\nfunction measurementKey(text: string, attrs: TextAttributes): string {\n\tconst attributes = Object.entries(attrs).sort(([first], [second]) =>\n\t\tfirst.localeCompare(second),\n\t);\n\treturn JSON.stringify([text, attributes]);\n}\n\n/** breakLongWord splits one word only when it cannot fit on an empty line. */\nfunction breakLongWord(\n\tword: string,\n\tmaxWidth: number,\n\tattrs: TextAttributes,\n): string[] {\n\tconst parts: string[] = [];\n\tlet currentPart = \"\";\n\tfor (const character of word) {\n\t\tconst nextPart = currentPart + character;\n\t\tif (measureSVGText(nextPart, attrs).width > maxWidth && currentPart.length > 0) {\n\t\t\tparts.push(currentPart);\n\t\t\tcurrentPart = character;\n\t\t} else {\n\t\t\tcurrentPart = nextPart;\n\t\t}\n\t}\n\tif (currentPart.length > 0) {\n\t\tparts.push(currentPart);\n\t}\n\treturn parts;\n}\n\n\n","import {svgTextWrap} from \"./svg-text\";\n\nexport const create = {\n\telement(type: string, attrs: Record = {}, className?: string) {\n\t\tconst el = document.createElementNS('http://www.w3.org/2000/svg', type);\n\t\tObject.entries(attrs).forEach(([k, v]) => el.setAttribute(k, String(v)));\n\t\tif (className) el.classList.add(className);\n\t\treturn el;\n\t},\n\n\tuse(id: string, attrs: Record = {}) {\n\t\tconst el = this.element('use', attrs);\n\t\tel.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + id);\n\t\treturn el;\n\t},\n\n\tpath(path: string, attrs: Record = {}, className?: string) {\n\t\tconst p = this.element(\"path\", {...attrs, d: path}, className);\n\t\treturn p;\n\t},\n\n\ttext(text: string, attrs: Record = {}) {\n\t\tconst t = this.element('text', attrs) as SVGTextElement;\n\t\tif (text) t.textContent = text;\n\t\treturn t;\n\t},\n\n\ttextArea(text: string, width: number, fontSize: number, bold: boolean, x = 0, y = 0, anchor = '') {\n\t\tconst attrs: Record = {\n\t\t\t'font-size': `${fontSize}px`,\n\t\t\t'font-weight': bold ? 'bold' : 'normal'\n\t\t};\n\t\tconst {lines, maxW} = svgTextWrap(text, width, attrs);\n\t\tconst txt = this.text('', {x: 0, y, 'text-anchor': anchor || undefined});\n\t\t\n\t\tlines.forEach((line, i) => {\n\t\t\tconst span = this.element('tspan', {x, dy: `${fontSize + 2}px`, ...attrs});\n\t\t\tspan.textContent = line;\n\t\t\ttxt.append(span);\n\t\t});\n\t\t\n\t\treturn {txt, dy: (lines.length + 1) * (fontSize + 2), maxW};\n\t},\n\n\trect(width: number, height: number, x = 0, y = 0, r = 0, className?: string) {\n\t\treturn this.element('rect', {x, y, rx: r, ry: r, width, height}, className) as SVGRectElement;\n\t},\n\n\ticon(icon: string, x = 0, y = 0) {\n\t\treturn this.use(icon, {x, y});\n\t},\n\n\texpand(x: number, y: number, expanded: boolean) {\n\t\tconst g = this.element('g', {transform: `translate(${x},${y})`}, 'expand') as SVGGElement;\n\t\tg.append(\n\t\t\tthis.rect(19, 19, 0, 0, 1),\n\t\t\tthis.text(expanded ? '-' : '+', {x: 10, y: 14, 'text-anchor': 'middle'})\n\t\t);\n\t\treturn g;\n\t}\n};\n\nexport function setPosition(g: SVGGElement, x: number, y: number) {\n\tg.setAttribute('transform', `translate(${x},${y})`);\n}","interface Point {\n\tx: number;\n\ty: number;\n}\n\ninterface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\n\nexport function insideBox(p: Point, b: BBox, centeredBox = true): boolean {\n\treturn centeredBox ?\n\t\t(p.x > b.x - b.width / 2 && p.x < b.x + b.width / 2 && p.y > b.y - b.height / 2 && p.y < b.y + b.height / 2) :\n\t\t(p.x > b.x && p.x < b.x + b.width && p.y > b.y && p.y < b.y + b.height)\n}\n\nexport function boxesOverlap(b1: BBox, b2: BBox): boolean {\n\treturn b1.x < b2.x + b2.width && b1.y < b2.y + b2.height && b1.x + b1.width > b2.x && b1.y + b1.height > b2.y\n}\n\nexport function uncenterBox(b: BBox): BBox {\n\treturn {x: b.x - b.width / 2, y: b.y - b.height / 2, width: b.width, height: b.height}\n}\n\nexport function scaleBox(b: BBox, sc: number): BBox {\n\treturn {x: b.x * sc, y: b.y * sc, width: b.width * sc, height: b.height * sc}\n}\n\n// intersect 2 segments (p1->q1) with (p2, q2)\n// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite)\n// and booleans for whether line segment 1 or line segment 2 contain the point\nfunction segmentIntersection(p1: Point, q1: Point, p2: Point, q2: Point) {\n\tlet denominator, a, b, numerator1, numerator2,\n\t\tresult: { x: number, y: number, onLine1: boolean, onLine2: boolean } = {\n\t\t\tx: null,\n\t\t\ty: null,\n\t\t\tonLine1: false,\n\t\t\tonLine2: false\n\t\t};\n\tdenominator = (q2.y - p2.y) * (q1.x - p1.x) - (q2.x - p2.x) * (q1.y - p1.y);\n\tif (denominator == 0) {\n\t\treturn result;\n\t}\n\ta = p1.y - p2.y;\n\tb = p1.x - p2.x;\n\tnumerator1 = ((q2.x - p2.x) * a) - ((q2.y - p2.y) * b);\n\tnumerator2 = ((q1.x - p1.x) * a) - ((q1.y - p1.y) * b);\n\ta = numerator1 / denominator;\n\tb = numerator2 / denominator;\n\n\t// if we cast these lines infinitely in both directions, they intersect here:\n\tresult.x = p1.x + (a * (q1.x - p1.x));\n\tresult.y = p1.y + (a * (q1.y - p1.y));\n\n\t// if line1 is a segment and line2 is infinite, they intersect if:\n\tif (a > 0 && a < 1) {\n\t\tresult.onLine1 = true;\n\t}\n\t// if line2 is a segment and line1 is infinite, they intersect if:\n\tif (b >= 0 && b <= 1) {\n\t\tresult.onLine2 = true;\n\t}\n\t// if line1 and line2 are segments, they intersect if both of the above are true\n\treturn result;\n}\n\n// intersects a segment (p1->p2) with a box\nexport function intersectRectFull(p1: Point, p2: Point, box: BBox): Point[] {\n\tconst w = box.width / 2\n\tconst h = box.height / 2\n\tconst segs: { p: Point; q: Point }[] = [\n\t\t{p: {x: box.x - w, y: box.y - h}, q: {x: box.x - w, y: box.y + h}},\n\t\t{p: {x: box.x - w, y: box.y - h}, q: {x: box.x + w, y: box.y - h}},\n\t\t{p: {x: box.x + w, y: box.y - h}, q: {x: box.x + w, y: box.y + h}},\n\t\t{p: {x: box.x - w, y: box.y + h}, q: {x: box.x + w, y: box.y + h}},\n\t]\n\treturn segs.map(s => segmentIntersection(p1, p2, s.p, s.q)).filter(ret => ret.onLine1 && ret.onLine2)\n}\n\n// intersects a line that goes from p to the center of the box\nexport function intersectRect(box: BBox, p: Point): Point {\n\tif (insideBox(p, box)) return {x: box.x, y: box.y}\n\treturn intersectRectFull(box, p, box)[0] || {x: box.x, y: box.y}\n}\n\nexport function intersectEllipse(ellCenter: Point, rx: number, ry: number, nodeCenter: Point, point: Point) {\n\n\t//translate all to center ellipse\n\tconst p1 = {x: point.x - ellCenter.x, y: point.y - ellCenter.y}\n\tconst p2 = {x: nodeCenter.x - ellCenter.x, y: nodeCenter.y - ellCenter.y}\n\n\tif (p2.x == p1.x) { //hack to avoid singularity\n\t\tp1.x += .0000001\n\t}\n\n\tconst s = (p2.y - p1.y) / (p2.x - p1.x);\n\tconst si = p2.y - (s * p2.x);\n\tconst a = (ry * ry) + (rx * rx * s * s);\n\tconst b = 2 * rx * rx * si * s;\n\tconst c = rx * rx * si * si - rx * rx * ry * ry;\n\n\tconst radicand_sqrt = Math.sqrt((b * b) - (4 * a * c));\n\tconst x = p1.x > p2.x ?\n\t\t(-b + radicand_sqrt) / (2 * a) :\n\t\t(-b - radicand_sqrt) / (2 * a)\n\tconst pos = {\n\t\tx: x,\n\t\ty: s * x + si\n\t}\n\t//translate back\n\tpos.x += ellCenter.x;\n\tpos.y += ellCenter.y\n\n\treturn pos;\n}\n\nexport interface Segment {\n\tp: Point;\n\tq: Point;\n}\n\n// given a polyline as a list of segments, interrupt it over the box so no line is inside the box\nexport function intersectPolylineBox(segments: Segment[], box: BBox) {\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst s = segments[i]\n\t\tif (insideBox(s.p, box)) {\n\t\t\tif (insideBox(s.q, box)) { // segment both ends inside box\n\t\t\t\tsegments.splice(i, 1)\n\t\t\t\ti -= 1\n\t\t\t} else { // segment start inside box\n\t\t\t\ts.p = intersectRectFull(s.p, s.q, box)[0]\n\t\t\t}\n\t\t} else {\n\t\t\tif (insideBox(s.q, box)) { // segment end inside box\n\t\t\t\ts.q = intersectRectFull(s.p, s.q, box)[0]\n\t\t\t} else { // both ends outside\n\t\t\t\tconst ret = intersectRectFull(s.p, s.q, box)\n\t\t\t\tif (ret.length == 2) { // intersects the box, splice segment\n\t\t\t\t\t// order the intersection points, closest first\n\t\t\t\t\tconst dst1 = Math.abs(ret[0].x - s.p.x) + Math.abs(ret[0].y - s.p.y)\n\t\t\t\t\tconst dst2 = Math.abs(ret[1].x - s.p.x) + Math.abs(ret[1].y - s.p.y)\n\t\t\t\t\tif (dst1 > dst2) ret.reverse()\n\t\t\t\t\t// split the segment in 2\n\t\t\t\t\tconst s2 = {p: ret[1], q: s.q}\n\t\t\t\t\ts.q = ret[0]\n\t\t\t\t\tsegments.splice(i + 1, 0, s2)\n\t\t\t\t\ti += 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function project(p: Point, a: Point, b: Point): Point {\n\tlet atob = {x: b.x - a.x, y: b.y - a.y};\n\tlet atop = {x: p.x - a.x, y: p.y - a.y};\n\tlet len = atob.x * atob.x + atob.y * atob.y;\n\tlet dot = atop.x * atob.x + atop.y * atob.y;\n\tlet t = Math.min(1, Math.max(0, dot / len));\n\treturn {\n\t\tx: a.x + atob.x * t,\n\t\ty: a.y + atob.y * t\n\t};\n}\n\nexport function cabDistance(p1: Point, p2: Point): number {\n\treturn Math.abs(p2.x - p1.x) + Math.abs(p2.y - p1.y)\n}","import {intersectEllipse, intersectRect} from \"./intersect\";\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\ninterface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\ninterface D3Node extends BBox {\n\tintersect: (p: Point) => Point\n}\n\nfunction cylinderRadiusY(width: number) {\n\treturn width / 2 / (5.5 + width / 70);\n}\n\nexport function shapeLabelOffsetY(shape: string, width: number, height: number) {\n\tswitch (shape.toLowerCase()) {\n\t\tcase \"cylinder\":\n\t\t\treturn 2 * cylinderRadiusY(width);\n\t\tcase \"person\":\n\t\t\treturn height * 0.4;\n\t\tcase \"folder\":\n\t\t\treturn width / 10;\n\t\tcase \"robot\":\n\t\t\treturn height * 0.35;\n\t\tcase \"webbrowser\":\n\t\t\treturn height / 8;\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\nclass D3Element {\n\tprivate readonly _el: SVGElement\n\n\tconstructor(el: SVGElement) {\n\t\tthis._el = el;\n\t}\n\n\tnode() {\n\t\treturn this._el;\n\t}\n\n\tattr(name: string, value: string | number) {\n\t\tthis._el.setAttribute(name, String(value))\n\t\treturn this;\n\t}\n\n\tinsert(type: string, pos: string) {\n\t\tconst el = document.createElementNS('http://www.w3.org/2000/svg', type)\n\t\tconst el2 = this._el.insertBefore(el, this._el.querySelector(pos))\n\t\treturn new D3Element(el2)\n\t}\n}\n\n\nfunction rect(parent: D3Element, bbox: BBox, node: D3Node, rounded = false) {\n\tconst shapeSvg = parent.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", rounded ? node.width / 8 : 3)\n\t\t.attr(\"ry\", rounded ? node.width / 8 : 3)\n\t\t.attr(\"x\", -bbox.width / 2)\n\t\t.attr(\"y\", -bbox.height / 2)\n\t\t.attr(\"width\", bbox.width)\n\t\t.attr(\"height\", bbox.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\n\nfunction cylinder(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = bbox.width;\n\tconst rx = w / 2;\n\tconst ry = cylinderRadiusY(w);\n\tconst h = bbox.height;\n\n\tconst shape =\n\t\t`M 0,${ry} a${rx},${ry} 0,0,0 ${w} 0 a ${rx},${ry} 0,0,0 ${-w} 0 l 0,${h - 2 * ry} a ${rx},${ry} 0,0,0 ${w} 0 l 0,${-h + 2 * ry}`;\n\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"cylinder\", w, h))\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\tconst pos = intersectRect(node, point)\n\t\tlet cy = node.y + node.height / 2 - ry\n\t\tif (pos.y > cy)\n\t\t\treturn intersectEllipse({x: node.x, y: cy}, rx, ry, node, point)\n\n\t\tcy = node.y - node.height / 2 + ry\n\t\tif (pos.y < cy)\n\t\t\treturn intersectEllipse({x: node.x, y: cy}, rx, ry, node, point)\n\n\t\treturn pos;\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction person(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = bbox.width;\n\tconst h = bbox.height;\n\n\tconst shape =\n\t\t`M ${.38 * w},${h / 3} A${w / 2},${h / 2} 0,0,0 0 ${h / 2}\n\t\tL${w / 11},${h} L${w - w / 11},${h} L${w},${h / 2}\n\t\tA${w / 2},${h / 2} 0,0,0 ${w - .38 * w} ${h / 3} \n\t\tA${w / 6},${w / 6} 0,1,0 ${.38 * w} ${h / 3}`;\n\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"person\", w, h))\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\tconst pos = intersectRect(node, point)\n\t\treturn pos;\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction _ellipse(parent: D3Element, bbox: BBox, node: D3Node, rx: number, ry: number) {\n\tconst shapeSvg = parent.insert(\"ellipse\", \":first-child\")\n\t\t.attr(\"cx\", 0)\n\t\t.attr(\"cy\", 0)\n\t\t.attr('rx', rx)\n\t\t.attr('ry', ry)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectEllipse(node, rx, ry, node, point)\n\t};\n\treturn shapeSvg;\n}\n\nfunction circle(parent: D3Element, bbox: BBox, node: D3Node) {\n\treturn _ellipse(parent, bbox, node, node.width / 2, node.width / 2)\n}\n\nfunction ellipse(parent: D3Element, bbox: BBox, node: D3Node) {\n\treturn _ellipse(parent, bbox, node, node.width * .55, node.width * .45)\n}\n\nfunction hexagon(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst sz = node.width / 2\n\t// drawing a hexagon from polar coords\n\t// [0,1,2,3,4,5,6].map(i=>`${Math.sin(Math.PI/3*i+Math.PI/6).toFixed(4)},${Math.cos(Math.PI/3*i+Math.PI/6).toFixed(4)}`).join(',')\n\tconst shapeSvg = parent.insert(\"polygon\", \":first-child\")\n\t\t.attr(\"points\",\n\t\t\t[0.5000, 0.8660, 1.0000, 0.0000, 0.5000, -0.8660, -0.5000, -0.8660, -1.0000, -0.0000, -0.5000, 0.8660, 0.5000, 0.8660].map(n => n * sz).join(','))\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectEllipse(node, node.width / 2, node.width / 2, node, point)\n\t};\n\treturn shapeSvg;\n}\n\nfunction component(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dx = node.width / 10\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2 - dx)\n\t\t.attr(\"y\", -node.height / 2 + dx)\n\t\t.attr(\"width\", dx * 2)\n\t\t.attr(\"height\", dx);\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2 - dx)\n\t\t.attr(\"y\", -node.height / 2 + dx * 2.5)\n\t\t.attr(\"width\", dx * 2)\n\t\t.attr(\"height\", dx);\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x - dx / 2, y: node.y, width: node.width + dx, height: node.height}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction folder(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.width / 20\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"folder\", node.width, node.height))\n\t\t.insert('g', ':first-child')\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2 + dy * 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height - dy * 2);\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('d', `M0,${-node.height / 2 + 2 * dy} l${dy},${-2 * dy} h${node.width / 2 - dy * 2} v${dy * 2}`)\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y + dy / 2, width: node.width, height: node.height + dy}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction mobiledevicelandscape(parent: D3Element, bbox: BBox, node: D3Node, rounded = false) {\n\tconst dx = node.width / 8\n\tconst r = node.width / 14\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert('path', ':first-child')\n\t\t.attr('d', `M${-node.width / 2},${-node.height / 2} l0,${node.height} M${node.width / 2},${-node.height / 2} l0,${node.height}`)\n\tshapeSvg.insert('circle', ':first-child')\n\t\t.attr('cx', -node.width / 2 - dx / 2)\n\t\t.attr('cy', 0)\n\t\t.attr('r', r * .4)\n\tshapeSvg.insert('rect', ':first-child')\n\t\t.attr('x', node.width / 2 + dx / 2 - r * .2)\n\t\t.attr('y', -r)\n\t\t.attr('width', r * .4)\n\t\t.attr('height', r * 2)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", r)\n\t\t.attr(\"ry\", r)\n\t\t.attr(\"x\", -bbox.width / 2 - dx)\n\t\t.attr(\"y\", -bbox.height / 2)\n\t\t.attr(\"width\", bbox.width + 2 * dx)\n\t\t.attr(\"height\", bbox.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y, width: node.width + 2 * dx, height: node.height}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction mobiledeviceportrait(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.width / 8\n\tconst r = node.width / 14\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert('path', ':first-child')\n\t\t.attr('d', `M${-node.width / 2},${-node.height / 2} l${node.width},0 M${-node.width / 2},${node.height / 2} l${node.width},0`)\n\tshapeSvg.insert('circle', ':first-child')\n\t\t.attr('cx', 0)\n\t\t.attr('cy', node.height / 2 + dy / 2)\n\t\t.attr('r', r * .4)\n\tshapeSvg.insert('rect', ':first-child')\n\t\t.attr('x', -r)\n\t\t.attr('y', -node.height / 2 - dy / 2 - r * .2)\n\t\t.attr('width', r * 2)\n\t\t.attr('height', r * .4)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", r)\n\t\t.attr(\"ry\", r)\n\t\t.attr(\"x\", -bbox.width / 2)\n\t\t.attr(\"y\", -bbox.height / 2 - dy)\n\t\t.attr(\"width\", bbox.width)\n\t\t.attr(\"height\", bbox.height + 2 * dy);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y, width: node.width, height: node.height + 2 * dy}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction pipe(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = node.width;\n\tconst h = node.height;\n\tconst ry = h / 2;\n\tconst rx = ry / (2.5 + w / 70);\n\n\tconst shape =\n\t\t`M${-rx},0\n\t\ta${rx},${ry} 0,0,1 0,${h}\n\t\ta${rx},${ry} 0,0,1 0,${-h}\n\t\tl${w},0\n\t\ta${rx},${ry} 0,0,1 0,${h}\n\t\tl${-w},0`;\n\n\tconst shapeSvg = parent\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\treturn intersectRect({x: node.x - rx, y: node.y, width: node.width + 2 * rx, height: node.height}, point)\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction robot(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = node.width\n\tconst h = node.height\n\t\n\t// Small head at top (like person shape but robot-styled)\n\tconst headSize = Math.min(w * 0.28, h * 0.25)\n\tconst headR = headSize * 0.2\n\tconst antennaH = headSize * 0.25\n\tconst antennaR = headSize * 0.08\n\t\n\t// Eye dimensions\n\tconst eyeR = headSize * 0.12\n\tconst eyeSpacing = headSize * 0.22\n\t\n\t// Ear dimensions \n\tconst earW = headSize * 0.12\n\tconst earH = headSize * 0.3\n\t\n\t// Body fills most of the space for text\n\tconst bodyW = w\n\tconst bodyTop = -h / 2 + antennaH + headSize\n\tconst bodyH = h - antennaH - headSize\n\tconst bodyR = 3\n\t\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"robot\", w, h))\n\t\t.insert('g', ':first-child')\n\t\n\t// Body - main rectangle for text (draw first so it's behind)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", bodyR)\n\t\t.attr(\"ry\", bodyR)\n\t\t.attr('x', -bodyW / 2)\n\t\t.attr('y', bodyTop)\n\t\t.attr('width', bodyW)\n\t\t.attr('height', bodyH)\n\t\n\t// Head\n\tconst headTop = -h / 2 + antennaH\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", headR)\n\t\t.attr(\"ry\", headR)\n\t\t.attr('x', -headSize / 2)\n\t\t.attr('y', headTop)\n\t\t.attr('width', headSize)\n\t\t.attr('height', headSize)\n\t\n\t// Antenna\n\tshapeSvg.insert(\"line\", \":first-child\")\n\t\t.attr('class', 'robot-antenna')\n\t\t.attr('x1', 0)\n\t\t.attr('y1', headTop)\n\t\t.attr('x2', 0)\n\t\t.attr('y2', -h / 2 + antennaR * 2)\n\t\t.attr('stroke-width', antennaR * 0.6)\n\t\t.attr('stroke-linecap', 'round')\n\t\n\t// Antenna ball\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-antenna-ball')\n\t\t.attr('cx', 0)\n\t\t.attr('cy', -h / 2 + antennaR * 2)\n\t\t.attr('r', antennaR * 1.2)\n\t\n\t// Eyes\n\tconst eyeY = headTop + headSize * 0.4\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-eye')\n\t\t.attr('cx', -eyeSpacing)\n\t\t.attr('cy', eyeY)\n\t\t.attr('r', eyeR)\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-eye')\n\t\t.attr('cx', eyeSpacing)\n\t\t.attr('cy', eyeY)\n\t\t.attr('r', eyeR)\n\t\n\t// Mouth - simple smile\n\tconst mouthY = headTop + headSize * 0.7\n\tconst mouthW = headSize * 0.28\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('class', 'robot-mouth')\n\t\t.attr('d', `M${-mouthW / 2},${mouthY} Q0,${mouthY + mouthW * 0.3} ${mouthW / 2},${mouthY}`)\n\t\t.attr('fill', 'none')\n\t\t.attr('stroke-width', antennaR * 0.5)\n\t\t.attr('stroke-linecap', 'round')\n\t\n\t// Ears\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", earW * 0.25)\n\t\t.attr(\"ry\", earW * 0.25)\n\t\t.attr('x', -headSize / 2 - earW - 1)\n\t\t.attr('y', eyeY - earH / 2)\n\t\t.attr('width', earW)\n\t\t.attr('height', earH)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", earW * 0.25)\n\t\t.attr(\"ry\", earW * 0.25)\n\t\t.attr('x', headSize / 2 + 1)\n\t\t.attr('y', eyeY - earH / 2)\n\t\t.attr('width', earW)\n\t\t.attr('height', earH)\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction webbrowser(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.height / 8\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"webbrowser\", node.width, node.height))\n\t\t.insert('g', ':first-child')\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('d', `\n\t\t\tM${-node.width / 2},${-node.height / 2 + dy} h${node.width}\n\t\t\tM${-node.width / 2 + dy / 4},${-node.height / 2 + dy / 4} h${dy / 2} v${dy / 2} h${-dy / 2} z\n\t\t\tM${-node.width / 2 + dy},${-node.height / 2 + dy / 4} h${node.width - dy - dy / 4} v${dy / 2} h${-node.width + dy + dy / 4} z\n\t\t`)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nexport const shapes: { [key: string]: (parent: SVGElement, node: D3Node) => SVGElement } = {\n\tbox: (parent: SVGElement, node: D3Node) => rect(new D3Element(parent), node, node).node(),\n\troundedbox: (parent: SVGElement, node: D3Node) => rect(new D3Element(parent), node, node, true).node(),\n\tcomponent: (parent: SVGElement, node: D3Node) => component(new D3Element(parent), node, node).node(),\n\tcylinder: (parent: SVGElement, node: D3Node) => cylinder(new D3Element(parent), node, node).node(),\n\tperson: (parent: SVGElement, node: D3Node) => person(new D3Element(parent), node, node).node(),\n\tcircle: (parent: SVGElement, node: D3Node) => circle(new D3Element(parent), node, node).node(),\n\tellipse: (parent: SVGElement, node: D3Node) => ellipse(new D3Element(parent), node, node).node(),\n\thexagon: (parent: SVGElement, node: D3Node) => hexagon(new D3Element(parent), node, node).node(),\n\tfolder: (parent: SVGElement, node: D3Node) => folder(new D3Element(parent), node, node).node(),\n\tmobiledevicelandscape: (parent: SVGElement, node: D3Node) => mobiledevicelandscape(new D3Element(parent), node, node).node(),\n\tmobiledeviceportrait: (parent: SVGElement, node: D3Node) => mobiledeviceportrait(new D3Element(parent), node, node).node(),\n\tmobiledevice: (parent: SVGElement, node: D3Node) => mobiledeviceportrait(new D3Element(parent), node, node).node(),\n\tpipe: (parent: SVGElement, node: D3Node) => pipe(new D3Element(parent), node, node).node(),\n\trobot: (parent: SVGElement, node: D3Node) => robot(new D3Element(parent), node, node).node(),\n\twebbrowser: (parent: SVGElement, node: D3Node) => webbrowser(new D3Element(parent), node, node).node(),\n}\n","/**\n * Undo functionality\n * at every change in the document, Undo can save a new version\n * so the user can \"undo\" and \"redo\" changes by reverting to an\n * older version of the document\n */\n\nexport class Undo {\n\tprivate readonly versions: Doc[] = [];\n\tprivate pos: number = 0;\n\tprivate lastSavedPos: number = 0;\n\tprivate readonly exportDoc: () => Doc;\n\tprivate readonly importDoc: (d: Doc) => void;\n\tchange: () => void;\n\tprivate tmpPreviousState: Doc | null = null;\n\n\tconstructor(id: string, exportDoc: () => Doc, importDoc: (d: Doc) => void) {\n\t\tthis.exportDoc = exportDoc;\n\t\tthis.importDoc = importDoc;\n\t\tthis.change = debounce(() => this.saveNow(), 300);\n\t}\n\n\t// Store the state previous to the changes collected in the debounce period\n\tbeforeChange() {\n\t\tif (!this.tmpPreviousState) {\n\t\t\tthis.tmpPreviousState = this.deepClone(this.exportDoc());\n\t\t}\n\t}\n\n\tlength() {\n\t\treturn this.versions.length;\n\t}\n\n\tcurrentState() {\n\t\treturn this.deepClone(this.versions[this.pos - 1]);\n\t}\n\n\tprivate saveNow() {\n\t\tif (!this.tmpPreviousState) {\n\t\t\tthrow Error(\"undo.change() was called without previously calling undo.beforeChange()!\");\n\t\t}\n\t\t\n\t\tthis.versions[this.pos] = this.deepClone(this.exportDoc());\n\t\tthis.versions[this.pos - 1] = this.tmpPreviousState;\n\t\tthis.tmpPreviousState = null;\n\t\tthis.pos += 1;\n\t\t\n\t\t// Remove anything that might be on top of this version\n\t\tthis.versions.splice(this.pos);\n\t}\n\n\tprivate deepClone(doc: Doc): Doc {\n\t\t// Use modern structuredClone if available, fallback to JSON\n\t\tif (typeof structuredClone !== 'undefined') {\n\t\t\treturn structuredClone(doc);\n\t\t}\n\t\treturn JSON.parse(JSON.stringify(doc));\n\t}\n\n\tundo() {\n\t\tif (this.pos < 2) return;\n\t\tthis.pos -= 1;\n\t\tconst doc = this.versions[this.pos - 1];\n\t\tthis.importDoc(this.deepClone(doc));\n\t}\n\n\tredo() {\n\t\tif (this.pos > this.versions.length - 1) return;\n\t\tconst doc = this.versions[this.pos];\n\t\tthis.importDoc(this.deepClone(doc));\n\t\tthis.pos += 1;\n\t}\n\n\tchanged() {\n\t\treturn this.pos !== this.lastSavedPos;\n\t}\n\n\tsetSaved() {\n\t\tthis.lastSavedPos = this.pos;\n\t}\n}\n\nfunction debounce(func: () => void, wait: number) {\n\tlet timeout: ReturnType;\n\treturn function () {\n\t\tconst context = this;\n\t\tconst later = function () {\n\t\t\ttimeout = null;\n\t\t\tfunc.apply(context);\n\t\t};\n\t\tclearTimeout(timeout);\n\t\ttimeout = setTimeout(later, wait);\n\t};\n}","import {applyStyle, SVG_STYLES} from \"./constants\";\nimport {create} from \"./svg-create\";\nimport {svgTextWrap} from \"./svg-text\";\n\ninterface TextBlockLayout {\n\tlines: string[];\n\tfontSize: number;\n\tlineHeight: number;\n\tbold: boolean;\n\tfield?: string;\n\tgapAfter: number;\n}\n\nexport interface NodeContentLayout {\n\tblocks: TextBlockLayout[];\n\ttextHeight: number;\n\tminimumHeight: number;\n}\n\nconst HORIZONTAL_PADDING = 18;\nconst VERTICAL_PADDING = 18;\nconst TITLE_GAP = 6;\nconst METADATA_GAP = 10;\n\nfunction textBlock(\n\ttext: string,\n\twidth: number,\n\tfontSize: number,\n\tbold: boolean,\n\tgapAfter: number,\n\tfield?: string,\n): TextBlockLayout {\n\tconst attrs = {\n\t\t\"font-family\": String(SVG_STYLES.nodeText[\"font-family\"]),\n\t\t\"font-size\": `${fontSize}px`,\n\t\t\"font-weight\": bold ? \"bold\" : \"normal\",\n\t};\n\tconst wrapped = svgTextWrap(text, width, attrs);\n\tconst lines = wrapped.lines.length > 0 ? wrapped.lines : [\"\"];\n\n\treturn {\n\t\tlines,\n\t\tfontSize,\n\t\tlineHeight: fontSize + 2,\n\t\tbold,\n\t\tfield,\n\t\tgapAfter,\n\t};\n}\n\nexport function layoutNodeContent(\n\ttitle: string,\n\tsubtitle: string,\n\tdescription: string,\n\tnodeWidth: number,\n\tfontSize: number,\n): NodeContentLayout {\n\tconst textWidth = Math.max(nodeWidth - HORIZONTAL_PADDING * 2, 80);\n\tconst blocks = [\n\t\ttextBlock(title, textWidth, fontSize, true, TITLE_GAP, \"name\"),\n\t\ttextBlock(`[${subtitle}]`, textWidth, fontSize * 0.75, false, METADATA_GAP),\n\t\ttextBlock(description, textWidth, Math.min(fontSize * 0.8, 16), false, 0, \"description\"),\n\t];\n\tconst textHeight = blocks.reduce(\n\t\t(height, block) => height + block.lines.length * block.lineHeight + block.gapAfter,\n\t\t0,\n\t);\n\n\treturn {\n\t\tblocks,\n\t\ttextHeight,\n\t\tminimumHeight: textHeight + VERTICAL_PADDING * 2,\n\t};\n}\n\nexport function buildNodeContent(layout: NodeContentLayout, color?: string): SVGGElement {\n\tconst group = create.element(\"g\") as SVGGElement;\n\tlet top = -layout.textHeight / 2;\n\n\tlayout.blocks.forEach((block) => {\n\t\tconst text = create.text(\"\", {\"text-anchor\": \"middle\"});\n\t\tapplyStyle(text, SVG_STYLES.nodeText);\n\t\tif (color) {\n\t\t\ttext.setAttribute(\"fill\", color);\n\t\t}\n\t\tif (block.field) {\n\t\t\ttext.setAttribute(\"data-field\", block.field);\n\t\t}\n\n\t\tblock.lines.forEach((line, index) => {\n\t\t\tconst span = create.element(\"tspan\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: top + block.fontSize + index * block.lineHeight,\n\t\t\t\t\"font-size\": `${block.fontSize}px`,\n\t\t\t\t\"font-weight\": block.bold ? \"bold\" : \"normal\",\n\t\t\t});\n\t\t\tspan.textContent = line;\n\t\t\ttext.append(span);\n\t\t});\n\n\t\tgroup.append(text);\n\t\ttop += block.lines.length * block.lineHeight + block.gapAfter;\n\t});\n\n\treturn group;\n}\n","import {defs} from \"./defs\";\nimport {create, setPosition} from \"./svg-create\";\nimport {cursorInteraction} from \"svg-editor-tools/lib/cursor-interaction\";\nimport {shapeLabelOffsetY, shapes} from \"./shapes\";\nimport {\n\tboxesOverlap,\n\tcabDistance,\n\tinsideBox,\n\tintersectPolylineBox,\n\tproject,\n\tscaleBox,\n\tSegment,\n\tuncenterBox\n} from \"./intersect\";\nimport {autoLayout, LayoutOptions, validateCurrentLayout} from \"./layout\";\nimport {Undo} from \"./undo\";\nimport {\n\tADD_LABEL_VERTEX,\n\tADD_VERTEX,\n\tDEL_VERTEX,\n\tDESELECT,\n\tfindShortcut,\n\tREDO,\n\tSELECT_ALL,\n\tUNDO,\n\tZOOM_100,\n\tZOOM_FIT,\n\tZOOM_IN,\n\tZOOM_OUT\n} from \"../shortcuts\";\nimport {\n\tPoint,\n\tBBox,\n\tNodeStyle,\n\tEdgeStyle,\n\tDEFAULT_EDGE_STYLE,\n\tDEFAULT_NODE_STYLE,\n\tSVG_STYLES,\n\tSVG_PADDING,\n\tDEFAULT_GRID_SIZE,\n\tapplyStyle\n} from \"./constants\";\nimport {\n\tcalculateEdgeVertices,\n\tcalculateLabelPlacement,\n\tcreateEdgePath,\n\tEdgeLabelPlacement\n} from \"./edge-utils\";\nimport {\n\tbuildNodeContent,\n\tlayoutNodeContent,\n\tNodeContentLayout\n} from \"./node-content\";\nimport {ValidatedLayout} from \"./geometry\";\n\n\n// Point and BBox interfaces are now imported from constants\n\nexport interface Group extends BBox {\n\tid: string;\n\tname: string;\n\tnodes: (Node | Group)[];\n\tref?: SVGGElement;\n\tstyle: NodeStyle;\n\tlayoutBounds?: BBox;\n\tlayoutTitleBounds?: BBox;\n}\n\nexport interface NodeLink {\n\thref: string;\n\texportHref: string;\n}\n\nexport type LayoutDirection = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT';\n\nexport interface Node extends BBox {\n\tid: string;\n\ttitle: string;\n\tsub: string;\n\tdescription: string;\n\n\tref?: SVGGElement;\n\tselected?: boolean;\n\n\tintersect: (p: Point) => Point\n\n\tstyle: NodeStyle\n\tlink?: NodeLink\n\tcontentLayout: NodeContentLayout\n}\n\n// NodeStyle interface is now imported from constants\n\n// EdgeStyle interface is now imported from constants\n\n// Default styles are now imported from constants\nconst defaultEdgeStyle = DEFAULT_EDGE_STYLE;\nconst defaultNodeStyle = DEFAULT_NODE_STYLE;\n\n// Edge and EdgeVertex interfaces are now defined in edge-utils.ts\n// Using local interfaces for compatibility with existing code\nexport interface Edge {\n\tid: string;\n\tlabel: string;\n\tfrom: Node;\n\tto: Node;\n\tvertices?: EdgeVertex[];\n\tref?: SVGGElement;\n\tstyle: EdgeStyle;\n\tinitVertex: (p: Point) => EdgeVertex;\n\tuserDeletedVertices?: boolean; // Track if user explicitly deleted vertices\n\tlabelVertex?: EdgeVertex; // ELK-calculated label position (separate from routing vertices)\n\tlabelBounds?: BBox;\n\tlayoutSections?: Point[][];\n\tlayoutLabelBounds?: BBox;\n}\n\ninterface EdgeVertex extends Point {\n\tid: string\n\tselected?: boolean\n\tedge: Edge\n\tref?: SVGElement\n\tlabel?: boolean\n\tauto?: boolean\n}\n\ninterface SavedEdgePoint extends Point {\n\tlabel?: boolean;\n\tauto?: boolean;\n}\n\ninterface Layout {\n\t[k: string]: Point | SavedEdgePoint[] | boolean\n}\n\nexport class GraphData {\n\tid: string;\n\tname: string;\n\tnodesMap: Map;\n\tedges: Edge[];\n\tedgeVertices: Map\n\tgroupsMap: Map;\n\tmetadata: any;\n\tlayoutDirection?: LayoutDirection;\n\tvalidatedLayout?: ValidatedLayout;\n\tcolorToVarMap: Map = new Map(); // For CSS custom properties theming\n\tprivate _undo: Undo;\n\tprivate _gridVisible: boolean = false;\n\tprivate _snapToGrid: boolean = true;\n\tprivate _gridSize: number = 25;\n\tprivate _skipAutoFit: boolean = false;\n\n\tconstructor(id?: string, name?: string) {\n\t\tthis.id = id;\n\t\tthis.name = name;\n\n\t\tthis.edges = [];\n\t\tthis.edgeVertices = new Map;\n\t\tthis.nodesMap = new Map;\n\t\tthis.groupsMap = new Map;\n\n\t\tthis._undo = new Undo(\n\t\t\tthis.id,\n\t\t\t() => this.exportLayout(true),\n\t\t\t(lo) => this.importLayout(lo, true)\n\t\t)\n\n\t\t// @ts-ignore\n\t\twindow.graph = this\n\t}\n\n\t// after the graph model is build using addNode, addEdge etc, initialize\n\tinit(layout?: Layout) {\n\t\tlayout && this.importLayout(layout)\n\t\tthis._undo = new Undo(\n\t\t\tthis.id,\n\t\t\t() => this.exportLayout(true),\n\t\t\t(lo) => this.importLayout(lo, true)\n\t\t)\n\t\tif (this._undo.length()) {\n\t\t\tthis.importLayout(this._undo.currentState())\n\t\t}\n\t\t\n\t\t// Save initial state so first action is undoable\n\t\tthis._undo.beforeChange()\n\t\tthis._undo.change()\n\t}\n\n\taddNode(id: string, label: string, sub: string, description: string, style: NodeStyle, link?: NodeLink) {\n\t\tif (this.nodesMap.has(id)) throw Error('duplicate node: ' + id)\n\t\tconst nodeStyle = {...defaultNodeStyle, ...style};\n\t\tconst shape = (nodeStyle.shape || 'Box').toLowerCase();\n\t\tconst minimumWidth = 280;\n\t\tconst minimumHeight = shape === 'person' ? 240 : 180;\n\t\tconst width = Math.max(minimumWidth, nodeStyle.width || 0);\n\t\tconst fontSize = nodeStyle.fontSize || 22;\n\t\tconst contentLayout = layoutNodeContent(label, sub, description, width, fontSize);\n\t\tlet height = Math.max(minimumHeight, nodeStyle.height || 0, contentLayout.minimumHeight);\n\n\t\t// Some shapes shift labels down to reserve visual space for an icon,\n\t\t// header, or curved edge. Grow until the shifted content also fits.\n\t\tfor (let i = 0; i < 20; i++) {\n\t\t\tconst requiredHeight = contentLayout.minimumHeight +\n\t\t\t\tMath.abs(shapeLabelOffsetY(shape, width, height));\n\t\t\tif (requiredHeight <= height + 0.1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\theight = requiredHeight;\n\t\t}\n\n\t\tconst n: Node = {\n\t\t\tid, title: label, sub, description, style: nodeStyle,\n\t\t\tx: 0, y: 0, width, height, intersect: null, link, contentLayout\n\t\t}\n\t\tthis.nodesMap.set(n.id, n)\n\t}\n\n\tnodes() {\n\t\treturn Array.from(this.nodesMap.values())\n\t}\n\n\taddEdge(id: string, fromNode: string, toNode: string, label: string, vertices: Point[], style: EdgeStyle) {\n\t\tvertices && vertices.forEach((p, i) => {\n\t\t\tconst v = p as EdgeVertex\n\t\t\tv.id = `v-${id}-${i}`\n\t\t\tthis.edgeVertices.set(v.id, v)\n\t\t})\n\t\t// Deterministic vertex IDs.\n\t\t//\n\t\t// `mdl svg` renders diagrams headlessly and saves the resulting SVG. If we\n\t\t// generate random vertex IDs, the SVG changes on every run even when the\n\t\t// underlying model and layout are unchanged. Use a stable hash instead.\n\t\tconst fnv1a36 = (input: string) => {\n\t\t\tlet h = 0x811c9dc5\n\t\t\tfor (let i = 0; i < input.length; i++) {\n\t\t\t\th ^= input.charCodeAt(i)\n\t\t\t\th = Math.imul(h, 0x01000193)\n\t\t\t}\n\t\t\treturn (h >>> 0).toString(36)\n\t\t}\n\t\tconst stableVertexID = (edgeID: string, p: Point) => {\n\t\t\tconst x = (p as any).x\n\t\t\tconst y = (p as any).y\n\t\t\treturn `v-${edgeID}-a-${fnv1a36(`${edgeID}:${x}:${y}`)}`\n\t\t}\n\t\tconst initVertex = (p: Point) => {\n\t\t\tconst v = p as EdgeVertex\n\t\t\tif (!v.id) {\n\t\t\t\tv.id = stableVertexID(edge.id, p)\n\t\t\t\tthis.edgeVertices.set(v.id, v)\n\t\t\t}\n\t\t\tv.edge = edge\n\t\t\treturn p as EdgeVertex\n\t\t}\n\t\tconst edge = {\n\t\t\tid,\n\t\t\tfrom: this.nodesMap.get(fromNode),\n\t\t\tto: this.nodesMap.get(toNode),\n\t\t\tlabel,\n\t\t\tvertices: null as EdgeVertex[],\n\t\t\tstyle: {...defaultEdgeStyle, ...style},\n\t\t\tinitVertex,\n\t\t\tuserDeletedVertices: false\n\t\t}\n\t\tthis.edges.push(edge)\n\t\tif (vertices) {\n\t\t\tedge.vertices = vertices.map(p => edge.initVertex(p))\n\t\t}\n\t}\n\n\taddGroup(id: string, name: string, nodesOrGroups: string[], style: NodeStyle) {\n\t\tif (this.groupsMap.has(id)) {\n\t\t\tconsole.error(`Group exists: ${id} ${name}`)\n\t\t\treturn\n\t\t}\n\t\tconst group: Group = {\n\t\t\tid, name, x: null, y: null, width: null, height: null,\n\t\t\tnodes: nodesOrGroups.map(k => {\n\t\t\t\tconst n = this.nodesMap.get(k) || this.groupsMap.get(k)\n\t\t\t\tif (!n) console.error(`Node or group ${k} not found for group ${id} \"${name}\"`)\n\t\t\t\treturn n\n\t\t\t}).filter(Boolean),\n\t\t\tstyle\n\t\t}\n\t\tthis.groupsMap.set(id, group)\n\t}\n\n\t// private rebuildNode(node: Node) {\n\t// \tconst p = node.ref.parentElement;\n\t// \tp.removeChild(node.ref)\n\t// \tnode.ref = buildNode(node, this)\n\t// \tp.appendChild(node.ref)\n\t// \tthis.redrawEdges(node)\n\t// \tthis.redrawGroups(node)\n\t// }\n\n\tsetNodeSelected(node: Node, selected: boolean) {\n\t\tnode.selected = selected\n\t\tselected ?\n\t\t\tnode.ref.classList.add('selected') :\n\t\t\tnode.ref.classList.remove('selected')\n\t\tthis.updateEdgesSel()\n\t}\n\n\tprivate updateEdgesSel() {\n\t\tthis.edges.forEach(e => {\n\t\t\tif (e.to.selected || e.from.selected) {\n\t\t\t\te.ref.classList.add('selected')\n\t\t\t} else {\n\t\t\t\te.ref.classList.remove('selected')\n\t\t\t}\n\t\t})\n\t}\n\n\t/** clearResolvedLayout returns drawing to manual mode after any user edit. */\n\tprivate clearResolvedLayout() {\n\t\tthis.validatedLayout = undefined\n\t\tthis.edges.forEach(edge => {\n\t\t\tedge.layoutSections = undefined\n\t\t\tedge.layoutLabelBounds = undefined\n\t\t})\n\t\tthis.groupsMap.forEach(group => {\n\t\t\tgroup.layoutBounds = undefined\n\t\t\tgroup.layoutTitleBounds = undefined\n\t\t})\n\t}\n\n\tmoveNode(n: Node, x: number, y: number, disableSnap: boolean = false, skipUndo: boolean = false) {\n\t\tif (!n) return\n\t\t\n\t\t// Apply snap-to-grid if enabled and not explicitly disabled\n\t\tif (this._snapToGrid && !disableSnap) {\n\t\t\tconst snapped = this.snapToGrid(x, y);\n\t\t\tx = snapped.x;\n\t\t\ty = snapped.y;\n\t\t}\n\t\t\n\t\tif (n.x == x && n.y == y) return\n\t\t\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.beforeChange()\n\t\t}\n\t\tthis.clearResolvedLayout()\n\t\tn.x = x;\n\t\tn.y = y;\n\t\tsetPosition(n.ref, x, y)\n\t\tthis.redrawEdges(n);\n\t\tthis.redrawGroups(n)\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.change()\n\t\t}\n\t}\n\n\tmoveEdgeVertex(v: EdgeVertex, x: number, y: number, disableSnap: boolean = false, skipUndo: boolean = false) {\n\t\t\n\t\tif (this._snapToGrid && !disableSnap) {\n\t\t\tconst snapped = this.snapToGrid(x, y);\n\t\t\tx = snapped.x;\n\t\t\ty = snapped.y;\n\t\t}\n\t\t// Use exact coordinates (no rounding needed with modern grid system)\n\t\t\n\t\tif (v.x == x && v.y == y) return\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.beforeChange()\n\t\t}\n\t\tthis.clearResolvedLayout()\n\t\tv.x = x;\n\t\tv.y = y;\n\t\tthis.redrawEdge(v.edge)\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.change()\n\t\t}\n\t}\n\n\tmoveSelected(dx: number, dy: number, disableSnap: boolean = false) {\n\t\tthis.nodes().forEach(n => n.selected && this.moveNode(n, n.x + dx, n.y + dy, disableSnap, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, v.x + dx, v.y + dy, disableSnap, false))\n\t}\n\n\tinsertEdgeVertex(edge: Edge, p: Point, pos: number, isLabel: boolean) {\n\t\tthis._undo.beforeChange()\n\t\tthis.clearResolvedLayout()\n\t\tconst v = edge.initVertex(p)\n\t\tv.selected = true\n\t\tif (isLabel) { // when shift down, make it label position\n\t\t\tedge.vertices.forEach(v => v.label = false)\n\t\t\tv.label = true\n\t\t}\n\t\tedge.vertices.splice(pos - 1, 0, v)\n\t\tthis.redrawEdge(edge)\n\t\tthis._undo.change()\n\t}\n\n\tdeleteEdgeVertex(v: EdgeVertex) {\n\t\tthis._undo.beforeChange()\n\t\tthis.clearResolvedLayout()\n\t\t\n\t\tconst index = v.edge.vertices.indexOf(v)\n\t\tif (index >= 0) {\n\t\t\tv.edge.vertices.splice(index, 1)\n\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\n\t\t\t// Mark that user explicitly deleted vertices from this edge\n\t\t\tv.edge.userDeletedVertices = true\n\t\t}\n\t\t\n\t\tthis.redrawEdge(v.edge)\n\t\tthis._undo.change()\n\t}\n\n\tchanged() {\n\t\treturn this._undo.changed()\n\t}\n\n\tundo() {\n\t\tthis._undo.undo()\n\t}\n\n\tredo() {\n\t\tthis._undo.redo()\n\t}\n\n\t// moves the entire graph to be aligned top-left of the drawing area\n\t// used to bring back to visible the nodes that end up at negative coordinates\n\talignTopLeft() {\n\t\tconst contentBounds = this.calculateContentBounds()\n\t\tconst padding = 100 // Reasonable padding for viewport\n\t\t\n\t\tconst offsetX = -contentBounds.x + padding\n\t\tconst offsetY = -contentBounds.y + padding\n\t\t\n\t\t// Set flag to prevent React useEffect from calling fitToView during this operation\n\t\tthis._skipAutoFit = true\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\tthis.nodesMap.forEach(node => {\n\t\t\tthis.moveNode(node, node.x + offsetX, node.y + offsetY, true, true) // Disable snap and undo during reset\n\t\t})\n\t\t\n\t\tthis.edgeVertices.forEach(vertex => {\n\t\t\tthis.moveEdgeVertex(vertex, vertex.x + offsetX, vertex.y + offsetY, true, true) // Disable snap and undo during reset\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t\t\n\t\t// DON'T clear view state here - let resetPanTransform handle it to avoid React useEffect recursion\n\t}\n\t\n\t// Reset pan transform to (0,0) while preserving zoom\n\tresetPanTransform() {\n\t\tconst currentZoom = getZoom()\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.setAttribute('transform', `scale(${currentZoom}) translate(0, 0)`)\n\t\t\tupdatePanningOptimized(this)\n\t\t}\n\t\t\n\t\t// Clear view state so this reset is not overridden\n\t\tclearViewState(this.id)\n\t\t\n\t\t// Reset the skip auto fit flag after reset is complete\n\t\tthis._skipAutoFit = false\n\t}\n\t\n\t// Check if auto-fit should be skipped (used by React useEffect)\n\tshouldSkipAutoFit(): boolean {\n\t\treturn this._skipAutoFit\n\t}\n\n\t// Reset view to default state: 100% zoom, centered at origin\n\tresetView() {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\t// Reset to 100% zoom, centered at origin\n\t\t\tzoomGroup.setAttribute('transform', 'scale(1) translate(0, 0)')\n\t\t\tupdatePanning()\n\t\t}\n\t\t\n\t\t// Clear any saved view state so this reset position is not overridden\n\t\tclearViewState(this.id)\n\t}\n\n\t//redraw connected edges\n\tprivate redrawEdges(n: Node) {\n\t\tthis.edges.forEach(e => (n == e.from || n == e.to) && this.redrawEdge(e))\n\t\tthis.updateEdgesSel()\n\t}\n\n\tredrawEdge(e: Edge) {\n\t\tconst p = e.ref.parentElement;\n\t\tp.removeChild(e.ref)\n\t\te.ref = buildEdge(e)\n\t\tp.append(e.ref)\n\t}\n\n\tprivate redrawGroups(node: Node) {\n\t\tthis.groupsMap.forEach(group => {\n\t\t\t//if (group.nodes.indexOf(node) == -1) return\n\t\t\tconst p = group.ref.parentElement\n\t\t\tp.removeChild(group.ref)\n\t\t\tbuildGroup(group)\n\t\t\tp.append(group.ref)\n\t\t})\n\t}\n\n\texportSVG() {\n\t\tif (!this.validatedLayout) {\n\t\t\tthis.validatedLayout = validateCurrentLayout(this)\n\t\t}\n\t\t// Get the original SVG\n\t\tconst originalSvg: SVGSVGElement = document.querySelector('svg#graph')\n\t\tconst elastic = originalSvg.querySelector('rect.elastic')\n\t\t\n\t\t// Clone the SVG for export (completely separate from the live one)\n\t\tconst exportSvg = originalSvg.cloneNode(true) as SVGSVGElement\n\n\t\t// Standalone SVGs navigate to sibling view files instead of editor routes.\n\t\texportSvg.querySelectorAll('a.nodeLink[data-export-href]').forEach(link => {\n\t\t\tlink.setAttribute('href', link.getAttribute('data-export-href') || '')\n\t\t\tlink.removeAttribute('data-export-href')\n\t\t})\n\t\t\n\t\t// Remove elastic element from export\n\t\tconst exportElastic = exportSvg.querySelector('rect.elastic')\n\t\tif (exportElastic) {\n\t\t\texportElastic.remove()\n\t\t}\n\t\t\n\t\t// Calculate actual content bounds including all elements\n\t\tconst contentBounds = this.calculateContentBounds()\n\t\t\n\t\t// Add padding around content\n\t\tconst padding = 50\n\t\t\n\t\t// Calculate final export dimensions (always positive)\n\t\tconst exportWidth = contentBounds.width + (padding * 2)\n\t\tconst exportHeight = contentBounds.height + (padding * 2)\n\t\t\n\t\t// Calculate offset to move content to start at (padding, padding) within the export area\n\t\tconst offsetX = -contentBounds.x + padding\n\t\tconst offsetY = -contentBounds.y + padding\n\t\t\n\t\t// Apply export positioning to the cloned SVG elements\n\t\tconst exportZoomGroup = exportSvg.querySelector('g.zoom') as SVGGElement\n\t\tif (exportZoomGroup) {\n\t\t\t// Reset zoom to 1 and apply offset transform to position content properly\n\t\t\texportZoomGroup.setAttribute('transform', `scale(1) translate(${offsetX}, ${offsetY})`)\n\t\t}\n\t\t\n\t\t// Set proper viewBox and dimensions for export - viewBox always starts at (0,0)\n\t\texportSvg.setAttribute('viewBox', `0 0 ${exportWidth} ${exportHeight}`)\n\t\texportSvg.setAttribute('width', String(exportWidth))\n\t\texportSvg.setAttribute('height', String(exportHeight))\n\t\t\n\t\t// Add required SVG namespace for browser compatibility\n\t\texportSvg.setAttribute('xmlns', 'http://www.w3.org/2000/svg')\n\t\t\n\t\t// Convert inline styles to CSS custom properties for theming support\n\t\tthis.convertStylesToCustomProperties(exportSvg)\n\t\t\n\t\t// Inject metadata with current layout\n\t\tconst script = document.createElement('script')\n\t\tscript.setAttribute('type', 'application/json')\n\t\tthis.metadata.layout = this.exportLayout()\n\t\tscript.append('')\n\t\texportSvg.insertBefore(script, exportSvg.firstChild)\n\t\t\n\t\t// Get the export SVG as string\n\t\tconst src = exportSvg.outerHTML\n\t\t\n\t\t// No restoration needed since we never touched the original SVG!\n\t\treturn src\n\t}\n\n\t// Convert inline fill/stroke attributes to CSS custom properties with fallbacks.\n\t// This enables theming: container pages can override colors via CSS variables.\n\tprivate convertStylesToCustomProperties(svg: SVGSVGElement) {\n\t\tif (this.colorToVarMap.size === 0) return\n\n\t\t// Process all elements with fill attribute\n\t\tsvg.querySelectorAll('[fill]').forEach(el => {\n\t\t\tconst fill = el.getAttribute('fill')\n\t\t\tif (fill && this.colorToVarMap.has(fill)) {\n\t\t\t\tel.setAttribute('fill', `var(${this.colorToVarMap.get(fill)}, ${fill})`)\n\t\t\t}\n\t\t})\n\n\t\t// Process all elements with stroke attribute\n\t\tsvg.querySelectorAll('[stroke]').forEach(el => {\n\t\t\tconst stroke = el.getAttribute('stroke')\n\t\t\tif (stroke && this.colorToVarMap.has(stroke)) {\n\t\t\t\tel.setAttribute('stroke', `var(${this.colorToVarMap.get(stroke)}, ${stroke})`)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Calculate the actual bounds of all content including nodes, edges, and groups\n\tcalculateContentBounds(): BBox {\n\t\tlet minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n\t\t\n\t\t// Process nodes (including their actual dimensions)\n\t\tthis.nodes().forEach(node => {\n\t\t\tconst left = node.x - node.width / 2\n\t\t\tconst right = node.x + node.width / 2\n\t\t\tconst top = node.y - node.height / 2\n\t\t\tconst bottom = node.y + node.height / 2\n\t\t\t\n\t\t\tminX = Math.min(minX, left)\n\t\t\tmaxX = Math.max(maxX, right)\n\t\t\tminY = Math.min(minY, top)\n\t\t\tmaxY = Math.max(maxY, bottom)\n\t\t})\n\t\t\n\t\t// Process edge vertices (much faster than complex label calculations)\n\t\tthis.edgeVertices.forEach(vertex => {\n\t\t\tminX = Math.min(minX, vertex.x - 5)\n\t\t\tmaxX = Math.max(maxX, vertex.x + 5)\n\t\t\tminY = Math.min(minY, vertex.y - 5)\n\t\t\tmaxY = Math.max(maxY, vertex.y + 5)\n\t\t})\n\t\t\n\t\t// Process groups\n\t\tthis.groupsMap.forEach(group => {\n\t\t\tconst left = group.x - group.width / 2\n\t\t\tconst right = group.x + group.width / 2\n\t\t\tconst top = group.y - group.height / 2\n\t\t\tconst bottom = group.y + group.height / 2\n\t\t\t\n\t\t\tminX = Math.min(minX, left)\n\t\t\tmaxX = Math.max(maxX, right)\n\t\t\tminY = Math.min(minY, top)\n\t\t\tmaxY = Math.max(maxY, bottom)\n\t\t})\n\t\t\n\t\t// Process edges (simplified - just endpoints and vertices, skip complex label calculations)\n\t\tthis.edges.forEach(edge => {\n\t\t\t// Edge endpoints\n\t\t\tminX = Math.min(minX, edge.from.x - 10, edge.to.x - 10)\n\t\t\tmaxX = Math.max(maxX, edge.from.x + 10, edge.to.x + 10)\n\t\t\tminY = Math.min(minY, edge.from.y - 10, edge.to.y - 10)\n\t\t\tmaxY = Math.max(maxY, edge.from.y + 10, edge.to.y + 10)\n\t\t\t\n\t\t\t// Edge vertices (if any)\n\t\t\tif (edge.vertices) {\n\t\t\t\tedge.vertices.forEach(vertex => {\n\t\t\t\t\tminX = Math.min(minX, vertex.x - 10)\n\t\t\t\t\tmaxX = Math.max(maxX, vertex.x + 10)\n\t\t\t\t\tminY = Math.min(minY, vertex.y - 10)\n\t\t\t\t\tmaxY = Math.max(maxY, vertex.y + 10)\n\t\t\t\t})\n\t\t\t}\n\t\t\t\n\t\t\t// Simplified label bounds (avoid expensive path calculations)\n\t\t\tif (edge.label && edge.label.trim()) {\n\t\t\t\t// Just use approximate center between from and to nodes\n\t\t\t\tconst centerX = (edge.from.x + edge.to.x) / 2\n\t\t\t\tconst centerY = (edge.from.y + edge.to.y) / 2\n\t\t\t\tconst approxLabelSize = edge.label.length * 10 + 50 // Rough estimate\n\t\t\t\t\n\t\t\t\tminX = Math.min(minX, centerX - approxLabelSize)\n\t\t\t\tmaxX = Math.max(maxX, centerX + approxLabelSize)\n\t\t\t\tminY = Math.min(minY, centerY - 25)\n\t\t\t\tmaxY = Math.max(maxY, centerY + 25)\n\t\t\t}\n\t\t})\n\t\t\n\t\t// Handle empty graph\n\t\tif (minX === Infinity) {\n\t\t\treturn { x: 0, y: 0, width: 100, height: 100 }\n\t\t}\n\t\t\n\t\treturn {\n\t\t\tx: minX,\n\t\t\ty: minY,\n\t\t\twidth: maxX - minX,\n\t\t\theight: maxY - minY\n\t\t}\n\t}\n\n\n\n\t/**\n\t * @param full when true, the edges without vertices are saved too, used for undo buffer\n\t * for saving, full is false\n\t */\n\texportLayout(full = false) {\n\t\tconst ret: Layout = {}\n\t\tthis.nodes().forEach(n => ret[n.id] = {x: n.x, y: n.y})\n\t\tthis.edges.forEach(e => {\n\t\t\tif (!e.vertices) return\n\t\t\t// Save all vertices (both user and auto-generated), preserving their properties\n\t\t\tconst lst = e.vertices.map(v => ({\n\t\t\t\tx: v.x, \n\t\t\t\ty: v.y, \n\t\t\t\tlabel: v.label,\n\t\t\t\tauto: v.auto // Preserve auto flag so we know which are ELK-generated\n\t\t\t}));\n\t\t\tif (lst.length || full) {\n\t\t\t\tret[`e-${e.id}`] = lst\n\t\t\t}\n\t\t\t// Also save the userDeletedVertices flag as metadata\n\t\t\tif (e.userDeletedVertices) {\n\t\t\t\tret[`e-${e.id}-deleted`] = true\n\t\t\t}\n\t\t})\n\t\treturn ret\n\t}\n\n\tsetSaved() {\n\t\tthis._undo.setSaved()\n\t}\n\n\timportLayout(layout: Layout, rerender = false) {\n\t\tthis.clearResolvedLayout()\n\t\tfor (const [k, v] of Object.entries(layout)) {\n\t\t\tconst n = this.nodesMap.get(k)\n\t\t\tif (n) {\n\t\t\t\tconst point = requiredSavedPoint(k, v)\n\t\t\t\tn.x = point.x\n\t\t\t\tn.y = point.y\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (k.endsWith('-deleted')) {\n\t\t\t\tconst edgeId = k.slice(2, -8); // Remove 'e-' prefix and '-deleted' suffix\n\t\t\t\tconst edge = this.edges.find(candidate => candidate.id === edgeId)\n\t\t\t\tif (!edge || v !== true) {\n\t\t\t\t\tthrow new Error(`invalid deleted-edge layout entry ${k}`)\n\t\t\t\t}\n\t\t\t\tedge.userDeletedVertices = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (k.startsWith('e-')) {\n\t\t\t\tconst edge = this.edges.find(candidate => candidate.id === k.slice(2))\n\t\t\t\tif (!edge || !Array.isArray(v)) {\n\t\t\t\t\tthrow new Error(`invalid edge layout entry ${k}`)\n\t\t\t\t}\n\t\t\t\tfor (const vertex of edge.vertices ?? []) {\n\t\t\t\t\tthis.edgeVertices.delete(vertex.id)\n\t\t\t\t}\n\t\t\t\tedge.vertices = v.map((saved, index) => {\n\t\t\t\t\tconst point = requiredSavedPoint(`${k}[${index}]`, saved)\n\t\t\t\t\tconst vertex = edge.initVertex(point)\n\t\t\t\t\tvertex.label = saved.label\n\t\t\t\t\tvertex.auto = saved.auto\n\t\t\t\t\treturn vertex\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthrow new Error(`layout contains unknown element ${k}`)\n\t\t}\n\t\tfor (const node of this.nodes()) {\n\t\t\tif (!(node.id in layout)) {\n\t\t\t\tthrow new Error(`layout is missing node ${node.id}`)\n\t\t\t}\n\t\t}\n\t\tif (rerender) {\n\t\t\tthis.nodes().forEach(n => setPosition(n.ref, n.x, n.y))\n\t\t\tthis.edges.forEach(e => this.redrawEdge(e))\n\t\t\tthis.updateEdgesSel()\n\t\t\tthis.redrawGroups(null)\n\t\t}\n\t}\n\n\tasync autoLayout(options?: LayoutOptions) {\n\t\tconst auto = await autoLayout(this, options)\n\t\tthis._undo.beforeChange()\n\n\t\tfor (const placedNode of auto.nodes) {\n\t\t\tconst node = this.nodesMap.get(placedNode.id)\n\t\t\tif (!node) {\n\t\t\t\tthrow new Error(`layout contains unknown node ${placedNode.id}`)\n\t\t\t}\n\t\t\tnode.x = placedNode.x\n\t\t\tnode.y = placedNode.y\n\t\t\tsetPosition(node.ref, node.x, node.y)\n\t\t}\n\n\t\tfor (const placedGroup of auto.groups) {\n\t\t\tconst group = this.groupsMap.get(placedGroup.id)\n\t\t\tif (!group) {\n\t\t\t\tthrow new Error(`layout contains unknown group ${placedGroup.id}`)\n\t\t\t}\n\t\t\tgroup.layoutBounds = placedGroup.bounds\n\t\t\tgroup.layoutTitleBounds = placedGroup.titleBounds\n\t\t\tgroup.x = placedGroup.bounds.x + placedGroup.bounds.width / 2\n\t\t\tgroup.y = placedGroup.bounds.y + placedGroup.bounds.height / 2\n\t\t\tgroup.width = placedGroup.bounds.width\n\t\t\tgroup.height = placedGroup.bounds.height\n\t\t}\n\n\t\tfor (const placedEdge of auto.edges) {\n\t\t\tconst edge = this.edges.find(candidate => candidate.id === placedEdge.id)\n\t\t\tif (!edge) {\n\t\t\t\tthrow new Error(`layout contains unknown edge ${placedEdge.id}`)\n\t\t\t}\n\t\t\tfor (const vertex of edge.vertices ?? []) {\n\t\t\t\tthis.edgeVertices.delete(vertex.id)\n\t\t\t}\n\t\t\tedge.layoutSections = placedEdge.sections\n\t\t\tedge.layoutLabelBounds = placedEdge.labelBounds\n\t\t\tedge.vertices = interiorRoutePoints(placedEdge.sections).map(point => {\n\t\t\t\tconst vertex = edge.initVertex(point)\n\t\t\t\tvertex.auto = true\n\t\t\t\treturn vertex\n\t\t\t})\n\t\t\tedge.userDeletedVertices = false\n\t\t}\n\n\t\tthis.validatedLayout = auto.validated\n\t\tthis.edges.forEach(edge => this.redrawEdge(edge))\n\t\tthis.redrawGroups(null)\n\t\tthis.fitToView()\n\t\tthis._undo.change()\n\t}\n\n\talignSelectionV() {\n\t\tconst lst: Point[] = this.nodes().filter(n => n.selected)\n\t\tlst.push(...Array.from(this.edgeVertices.values()).filter(v => v.selected))\n\t\tlet minY = Math.min(...lst.map(p => p.y))\n\t\tthis.nodesMap.forEach(n => n.selected && this.moveNode(n, n.x, minY, false, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, v.x, minY, false, false))\n\t}\n\n\talignSelectionH() {\n\t\tconst lst: Point[] = this.nodes().filter(n => n.selected)\n\t\tlst.push(...Array.from(this.edgeVertices.values()).filter(v => v.selected))\n\t\tlet minX = Math.min(...lst.map(p => p.x))\n\t\tthis.nodesMap.forEach(n => n.selected && this.moveNode(n, minX, n.y, false, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, minX, v.y, false, false))\n\t}\n\n\tdistributeSelectionH() {\n\t\tconst selectedNodes = this.nodes().filter(n => n.selected)\n\t\tconst selectedVertices = Array.from(this.edgeVertices.values()).filter(v => v.selected)\n\t\t\n\t\tif (selectedNodes.length + selectedVertices.length < 3) return // Need at least 3 elements to distribute\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\t// Combine and sort by X coordinate\n\t\tconst allElements = [...selectedNodes, ...selectedVertices]\n\t\tallElements.sort((a, b) => a.x - b.x)\n\t\t\n\t\tconst minX = allElements[0].x\n\t\tconst maxX = allElements[allElements.length - 1].x\n\t\tconst spacing = (maxX - minX) / (allElements.length - 1)\n\t\t\n\t\t// Distribute elements evenly between leftmost and rightmost\n\t\tallElements.forEach((element, index) => {\n\t\t\tconst newX = minX + (index * spacing)\n\t\t\tif ('title' in element) {\n\t\t\t\t// It's a Node\n\t\t\t\tthis.moveNode(element as Node, newX, element.y, false, true)\n\t\t\t} else {\n\t\t\t\t// It's an EdgeVertex\n\t\t\t\tthis.moveEdgeVertex(element as EdgeVertex, newX, element.y, false, true)\n\t\t\t}\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t}\n\n\tdistributeSelectionV() {\n\t\tconst selectedNodes = this.nodes().filter(n => n.selected)\n\t\tconst selectedVertices = Array.from(this.edgeVertices.values()).filter(v => v.selected)\n\t\t\n\t\tif (selectedNodes.length + selectedVertices.length < 3) return // Need at least 3 elements to distribute\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\t// Combine and sort by Y coordinate\n\t\tconst allElements = [...selectedNodes, ...selectedVertices]\n\t\tallElements.sort((a, b) => a.y - b.y)\n\t\t\n\t\tconst minY = allElements[0].y\n\t\tconst maxY = allElements[allElements.length - 1].y\n\t\tconst spacing = (maxY - minY) / (allElements.length - 1)\n\t\t\n\t\t// Distribute elements evenly between topmost and bottommost\n\t\tallElements.forEach((element, index) => {\n\t\t\tconst newY = minY + (index * spacing)\n\t\t\tif ('title' in element) {\n\t\t\t\t// It's a Node\n\t\t\t\tthis.moveNode(element as Node, element.x, newY, false, true)\n\t\t\t} else {\n\t\t\t\t// It's an EdgeVertex\n\t\t\t\tthis.moveEdgeVertex(element as EdgeVertex, element.x, newY, false, true)\n\t\t\t}\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t}\n\n\t// Set edge selection state\n\tsetEdgeSelected(edge: Edge, selected: boolean) {\n\t\t// Mark the edge as selected by selecting its connected nodes\n\t\tif (selected) {\n\t\t\tthis.setNodeSelected(edge.from, true)\n\t\t\tthis.setNodeSelected(edge.to, true)\n\t\t}\n\t\t// Update visual selection\n\t\tthis.updateEdgesSel()\n\t}\n\n\t// Fit the entire graph to the current viewport\n\tfitToView() {\n\t\tconst contentBounds = this.calculateContentBounds()\n\t\t\n\t\t// Handle edge case where there's no content\n\t\tif (contentBounds.width === 0 || contentBounds.height === 0) {\n\t\t\treturn\n\t\t}\n\t\t\n\t\t// Get viewport dimensions\n\t\tconst viewportWidth = svg.parentElement?.clientWidth || 800\n\t\tconst viewportHeight = svg.parentElement?.clientHeight || 600\n\t\t\n\t\t// Add padding around content\n\t\tconst padding = 40\n\t\t\n\t\t// Calculate zoom to fit content with padding\n\t\tconst zoomX = (viewportWidth - padding * 2) / contentBounds.width\n\t\tconst zoomY = (viewportHeight - padding * 2) / contentBounds.height\n\t\tconst optimalZoom = Math.min(zoomX, zoomY)\n\t\t\n\t\t// Clamp zoom between reasonable bounds\n\t\tconst finalZoom = Math.max(Math.min(optimalZoom, 2), 0.1)\n\t\t\n\t\t// Calculate content center in drawing coordinates\n\t\tconst contentCenterX = contentBounds.x + contentBounds.width / 2\n\t\tconst contentCenterY = contentBounds.y + contentBounds.height / 2\n\t\t\n\t\t// Calculate viewport center in screen coordinates\n\t\tconst viewportCenterX = viewportWidth / 2\n\t\tconst viewportCenterY = viewportHeight / 2\n\t\t\n\t\t// Calculate translation needed to center content in viewport\n\t\t// With translate(x,y) scale(zoom), translation is in screen coordinates\n\t\tconst translateX = viewportCenterX - (contentCenterX * finalZoom)\n\t\tconst translateY = viewportCenterY - (contentCenterY * finalZoom)\n\t\t\n\t\t// Apply zoom and translation transform\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.setAttribute('transform', `translate(${translateX}, ${translateY}) scale(${finalZoom})`)\n\t\t}\n\t\t\n\t\t// Update panning\n\t\tupdatePanning()\n\t\t\n\t\t// Save view state so this fit position is preserved after reload\n\t\tsaveViewState(this.id)\n\t}\n\n\t// Save current layout state for restoration\n\tprivate saveLayoutState(): Layout {\n\t\treturn this.exportLayout(true) // Include all vertices for complete state\n\t}\n\n\t// Restore layout state\n\tprivate restoreLayoutState(state: Layout) {\n\t\tthis.importLayout(state, true) // Rerender after restoring\n\t}\n\n\t// Grid functionality\n\tisGridVisible(): boolean {\n\t\treturn this._gridVisible;\n\t}\n\n\tisSnapToGrid(): boolean {\n\t\treturn this._snapToGrid;\n\t}\n\n\tgetGridSize(): number {\n\t\treturn this._gridSize;\n\t}\n\n\ttoggleGrid() {\n\t\tthis._gridVisible = !this._gridVisible;\n\t\tthis.updateGridDisplay();\n\t\t// Force toolbar update by dispatching a custom event\n\t\twindow.dispatchEvent(new CustomEvent('gridStateChanged'));\n\t}\n\n\ttoggleSnapToGrid() {\n\t\tthis._snapToGrid = !this._snapToGrid;\n\t\t// Force toolbar update by dispatching a custom event\n\t\twindow.dispatchEvent(new CustomEvent('gridStateChanged'));\n\t}\n\n\tsnapAllToGrid() {\n\t\tif (!this._snapToGrid) return;\n\t\t\n\t\tthis._undo.beforeChange();\n\t\tthis.nodes().forEach(node => {\n\t\t\tconst snappedX = Math.round(node.x / this._gridSize) * this._gridSize;\n\t\t\tconst snappedY = Math.round(node.y / this._gridSize) * this._gridSize;\n\t\t\tthis.moveNode(node, snappedX, snappedY, false, true);\n\t\t});\n\t\tthis._undo.change();\n\t}\n\n\t// Helper method to snap a point to grid\n\tprivate snapToGrid(x: number, y: number): { x: number, y: number } {\n\t\treturn {\n\t\t\tx: Math.round(x / this._gridSize) * this._gridSize,\n\t\t\ty: Math.round(y / this._gridSize) * this._gridSize\n\t\t};\n\t}\n\n\tupdateGridDisplay() {\n\t\tif (!svg) return;\n\n\t\t// Remove existing grid pattern and background\n\t\tconst existingGrid = svg.querySelector('#grid-pattern');\n\t\tif (existingGrid) {\n\t\t\texistingGrid.remove();\n\t\t}\n\n\t\tconst existingGridRect = svg.querySelector('#grid-background');\n\t\tif (existingGridRect) {\n\t\t\texistingGridRect.remove();\n\t\t}\n\n\t\tif (!this._gridVisible) return;\n\n\t\t// Create grid pattern in defs\n\t\tlet defs = svg.querySelector('defs');\n\t\tif (!defs) {\n\t\t\tdefs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');\n\t\t\tsvg.insertBefore(defs, svg.firstChild);\n\t\t}\n\n\t\tconst pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');\n\t\tpattern.id = 'grid-pattern';\n\t\tpattern.setAttribute('width', this._gridSize.toString());\n\t\tpattern.setAttribute('height', this._gridSize.toString());\n\t\tpattern.setAttribute('patternUnits', 'userSpaceOnUse');\n\n\t\tconst path = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n\t\tpath.setAttribute('d', `M ${this._gridSize} 0 L 0 0 0 ${this._gridSize}`);\n\t\tpath.setAttribute('fill', 'none');\n\t\tpath.setAttribute('stroke', '#d0d0d0');\n\t\tpath.setAttribute('stroke-width', '1');\n\t\tpath.setAttribute('opacity', '0.8');\n\n\t\tpattern.appendChild(path);\n\t\tdefs.appendChild(pattern);\n\n\t\t// Create grid background rectangle\n\t\tconst rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');\n\t\trect.id = 'grid-background';\n\t\trect.setAttribute('x', '-10000');\n\t\trect.setAttribute('y', '-10000');\n\t\trect.setAttribute('width', '20000');\n\t\trect.setAttribute('height', '20000');\n\t\trect.setAttribute('fill', 'url(#grid-pattern)');\n\t\trect.setAttribute('pointer-events', 'none');\n\n\t\t// Insert grid as first child of zoom group so it transforms with content\n\t\tconst zoomGroup = svg.querySelector('g.zoom');\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.insertBefore(rect, zoomGroup.firstChild);\n\t\t}\n\t}\n}\n\n/** interiorRoutePoints returns only the editable points between edge endpoints. */\nfunction interiorRoutePoints(sections: Point[][]): Point[] {\n\tconst route: Point[] = []\n\tfor (const section of sections) {\n\t\tfor (const point of section) {\n\t\t\tconst previous = route[route.length - 1]\n\t\t\tif (!previous || previous.x !== point.x || previous.y !== point.y) {\n\t\t\t\troute.push(point)\n\t\t\t}\n\t\t}\n\t}\n\treturn route.slice(1, -1)\n}\n\n/** requiredSavedPoint checks coordinates when reading saved editor layout data. */\nfunction requiredSavedPoint(\n\tid: string,\n\tvalue: Point | SavedEdgePoint | SavedEdgePoint[] | boolean,\n): SavedEdgePoint {\n\tif (\n\t\ttypeof value !== 'object' ||\n\t\tArray.isArray(value) ||\n\t\t!Number.isFinite(value.x) ||\n\t\t!Number.isFinite(value.y)\n\t) {\n\t\tthrow new Error(`layout entry ${id} must contain valid x and y coordinates`)\n\t}\n\treturn value\n}\n\nfunction escapeCdata(code: string) {\n\treturn code.replace(/]]>/g, ']]]>]> clickListener(e))\n\t// addCursorInteraction(svg) // Call will be updated in buildGraph\n}\nsvg.setAttribute('width', '100%')\nsvg.setAttribute('height', '100%')\n\nlet clickListener: (e: MouseEvent) => void\nlet dragging = false;\nlet selectListener: (n: Node) => void\n\n\nexport const buildGraph = (data: GraphData, onNodeSelect: (n: Node) => void, dragMode: 'pan' | 'select') => {\n\t// empty svg\n\tsvg.innerHTML = defs\n\tdocument.body.append(svg) // make sure svg element is connected, we will measure texts sizes\n\t// @ts-ignore\n\tsvg.__data = data\n\n\tselectListener = onNodeSelect\n\n\t//use event delegation\n\tclickListener = e => {\n\t\tif (dragging) {\n\t\t\treturn;\n\t\t}\n\t\t// const el = (e.target as any).closest('.node > .expand');\n\t}\n\n\t_buildGraph(data)\n\tconst elasticEl = create.rect(300, 300, 50, 50, 0, 'elastic')\n\tsvg.append(elasticEl)\n\n\t// Initialize grid display now that the zoom group exists\n\tdata.updateGridDisplay()\n\n\t// Call addCursorInteraction with dragMode\n\taddCursorInteraction(svg, dragMode)\n\n\treturn {\n\t\tsvg,\n\t\tsetZoom,\n\t}\n}\n\nexport const buildGraphView = (data: GraphData) => {\n\tsvg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n\tsvg.setAttribute('id', 'graph')\n\t_buildGraph(data)\n\treturn svg\n}\n\nconst _buildGraph = (data: GraphData) => {\n\t//toplevel groups\n\tconst zoomG = create.element('g', {}, 'zoom') as SVGGElement\n\tconst nodesG = create.element('g', {}, 'nodes') as SVGGElement\n\tconst edgesG = create.element('g', {}, 'edges') as SVGGElement\n\tconst groupsG = create.element('g', {}, 'groups') as SVGGElement\n\tzoomG.append(groupsG, edgesG, nodesG)\n\n\n\tdata.nodesMap.forEach((n) => {\n\t\tbuildNode(n, data)\n\t\tnodesG.append(n.ref)\n\t})\n\n\tdata.edges.forEach(edge => {\n\t\tedge.labelBounds = undefined\n\t})\n\tdata.edges.forEach(e => {\n\t\tbuildEdge(e)\n\t\tedgesG.append(e.ref)\n\t})\n\n\tdata.groupsMap.forEach((group) => {\n\t\tbuildGroup(group)\n\t\tgroupsG.append(group.ref)\n\t})\n\n\tsvg.append(zoomG)\n}\n\nfunction buildEdge(edge: Edge) {\n\tconst n1 = edge.from;\n\n\tconst g = create.element('g', {}, 'edge') as SVGGElement\n\tg.setAttribute('id', edge.id)\n\tg.setAttribute('data-from', edge.from.id)\n\tg.setAttribute('data-to', edge.to.id)\n\n\tconst position = (edge.style.position || 50) / 100\n\n\tconst vertices = edge.layoutSections\n\t\t? completeRoutePoints(edge.layoutSections)\n\t\t: calculateEdgeVertices(edge)\n\tconst {bg, txt} = edge.layoutLabelBounds\n\t\t? buildPlacedEdgeLabel(edge.layoutLabelBounds, edge)\n\t\t: buildEdgeLabel(calculateLabelPlacement(vertices, position, n1), edge)\n\tg.append(bg, txt)\n\n\tconst path = createEdgePath(vertices)\n\n\tconst p = create.path(path, {'marker-end': 'url(#arrow)'}, 'edge')\n\tp.setAttribute('fill', 'none')\n\tp.setAttribute('stroke', edge.style.color)\n\tp.setAttribute('stroke-width', String(edge.style.thickness))\n\tp.setAttribute('stroke-linecap', 'round')\n\tedge.style.dashed && p.setAttribute('stroke-dasharray', '8')\n\tg.append(p)\n\t\n\t// Debug visualization removed - arrow issue fixed\n\n\t// drag handlers\n\tfor (const p of edge.vertices ?? []) {\n\t\tconst v = p as EdgeVertex\n\t\tv.ref = create.element('circle', {id: v.id, cx: p.x, cy: p.y, r: 7, fill: 'none'}, 'v-dot')\n\t\tv.selected && v.ref.classList.add('selected')\n\t\tv.auto && v.ref.classList.add('auto')\n\t\tg.append(v.ref)\n\t}\n\n\tedge.ref = g\n\treturn g\n}\n\n/** completeRoutePoints joins connected layout sections without duplicate points. */\nfunction completeRoutePoints(sections: Point[][]): Point[] {\n\tconst route: Point[] = []\n\tfor (const section of sections) {\n\t\tfor (const point of section) {\n\t\t\tconst previous = route[route.length - 1]\n\t\t\tif (!previous || previous.x !== point.x || previous.y !== point.y) {\n\t\t\t\troute.push(point)\n\t\t\t}\n\t\t}\n\t}\n\treturn route\n}\n\n/** buildPlacedEdgeLabel draws one label in the box chosen by layout. */\nfunction buildPlacedEdgeLabel(bounds: BBox, edge: Edge) {\n\tconst centerX = bounds.x + bounds.width / 2\n\tconst centerY = bounds.y + bounds.height / 2\n\tconst {txt} = create.textArea(\n\t\tedge.label,\n\t\t200,\n\t\tedge.style.fontSize,\n\t\tfalse,\n\t\tcenterX,\n\t\tbounds.y,\n\t\t'middle',\n\t)\n\tapplyStyle(txt, styles.edgeText)\n\ttxt.setAttribute('stroke', 'none')\n\ttxt.setAttribute('font-size', String(edge.style.fontSize))\n\ttxt.setAttribute('fill', edge.style.color)\n\ttxt.setAttribute('data-field', 'label')\n\tconst bg = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\tapplyStyle(bg, styles.edgeRect)\n\treturn {\n\t\tbg,\n\t\ttxt,\n\t}\n}\n\n/**\n * buildEdgeLabel adapts an older saved label anchor into a complete label box.\n * It uses one fixed side and lets validation reject any resulting collision.\n */\nfunction buildEdgeLabel(placement: EdgeLabelPlacement, edge: Edge) {\n\tconst labelGap = 12;\n\tconst fontSize = edge.style.fontSize\n\tlet {txt, dy, maxW} = create.textArea(edge.label, 200, fontSize, false, placement.x, placement.y, 'middle')\n\tdy -= fontSize / 2\n\tmaxW += fontSize\n\tconst centerX = placement.orientation === 'vertical'\n\t\t? placement.x + maxW / 2 + labelGap\n\t\t: placement.x\n\tconst centerY = placement.orientation === 'vertical'\n\t\t? placement.y\n\t\t: placement.y - dy / 2 - labelGap\n\tconst bounds = {\n\t\tx: centerX - maxW / 2,\n\t\ty: centerY - dy / 2,\n\t\twidth: maxW,\n\t\theight: dy,\n\t}\n\ttxt.querySelectorAll('tspan').forEach((span: SVGTSpanElement) => {\n\t\tspan.setAttribute('x', String(centerX))\n\t})\n\ttxt.setAttribute('y', String(centerY - dy / 2))\n\n\tapplyStyle(txt, styles.edgeText)\n\ttxt.setAttribute('stroke', 'none')\n\ttxt.setAttribute('font-size', String(edge.style.fontSize))\n\ttxt.setAttribute('fill', edge.style.color)\n\n\tconst bg = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\tapplyStyle(bg, styles.edgeRect)\n\ttxt.setAttribute('data-field', 'label')\n\tedge.labelBounds = bounds\n\treturn {bg, txt}\n}\n\n\nfunction buildNode(n: Node, data: GraphData) {\n\t// @ts-ignore\n\twindow.gdata = data\n\n\tconst g = create.element('g', {}, 'node') as SVGGElement\n\tg.setAttribute('id', n.id)\n\tn.selected && g.classList.add('selected')\n\tsetPosition(g, n.x, n.y)\n\tconst link = n.link\n\t\t? create.element('a', {\n\t\t\thref: n.link.href,\n\t\t\t'data-export-href': n.link.exportHref,\n\t\t\t'aria-label': `Open ${n.title}`,\n\t\t}, 'nodeLink') as SVGAElement\n\t\t: null\n\tconst content = link || g\n\tif (link) {\n\t\tg.classList.add('linked')\n\t\tg.append(link)\n\t}\n\n\t// Ensure we use the correct shape from style, defaulting to Box\n\tconst shapeType = n.style.shape || 'Box';\n\tconst shapeFn = shapes[shapeType.toLowerCase()] || shapes.box\n\tconst shape: SVGElement = shapeFn(content, n);\n\n\tshape.classList.add('nodeBorder')\n\n\t// Apply generic styles first\n\tapplyStyle(shape, styles.nodeBorder)\n\t// Then apply tag-specific styles to override generic ones\n\tshape.setAttribute('fill', n.style.background)\n\tshape.setAttribute('stroke', n.style.stroke)\n\t// Consistent border width for all elements\n\tshape.setAttribute('stroke-width', '3')\n\tshape.setAttribute('opacity', String(n.style.opacity))\n\tsetBorderStyle(shape, n.style.border)\n\n\tconst tg = buildNodeContent(n.contentLayout, n.style.color)\n\tconst labelOffsetY = Number(content.getAttribute('label-offset-y')) || 0\n\tsetPosition(tg, 0, labelOffsetY / 2)\n\tcontent.append(tg)\n\n\t// @ts-ignore\n\tg.__data = n;\n\tn.ref = g;\n\n\treturn g\n}\n\n\nfunction buildGroup(group: Group) {\n\tif (group.nodes.length == 0) {\n\t\treturn\n\t}\n\tconst g = create.element('g', {}, 'group') as SVGGElement\n\tif (group.layoutBounds && group.layoutTitleBounds) {\n\t\tconst bounds = group.layoutBounds\n\t\tconst title = group.layoutTitleBounds\n\t\tconst rect = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\t\tapplyStyle(rect, styles.groupRect)\n\t\tgroup.style.stroke && rect.setAttribute('stroke', group.style.stroke)\n\t\tgroup.style.background && rect.setAttribute('fill', group.style.background)\n\t\tconst text = create.text(group.name, {\n\t\t\tx: title.x,\n\t\t\ty: title.y,\n\t\t\t'dominant-baseline': 'hanging',\n\t\t})\n\t\tapplyStyle(text, styles.groupText)\n\t\tgroup.style.color && text.setAttribute('fill', group.style.color)\n\t\tg.append(rect, text)\n\t\tgroup.ref = g\n\t\treturn\n\t}\n\n\tlet p0: Point = {x: 1e100, y: 1e100}, p1: Point = {x: 0, y: 0}\n\tgroup.nodes.forEach(n => {\n\t\t// Calculate visual bounds accounting for shapes that extend beyond center\n\t\tconst shape = (n as Node).style?.shape?.toLowerCase() || 'box'\n\t\t\n\t\tlet topExtension = n.height / 2\n\t\tlet bottomExtension = n.height / 2\n\t\t\n\t\tif (shape === 'robot') {\n\t\t\t// Robot shape extends above with antenna\n\t\t\t// Account for antenna height (h * 0.08) plus some margin\n\t\t\tconst antennaH = n.height * 0.12\n\t\t\ttopExtension = n.height / 2 + antennaH\n\t\t\tbottomExtension = n.height / 2\n\t\t} else if (shape === 'hexagon') {\n\t\t\t// Hexagon extends to ±0.866 * (width/2) vertically\n\t\t\t// For width=280, that's ±121.24px from center\n\t\t\tconst hexHeight = n.width / 2 * 0.866\n\t\t\ttopExtension = hexHeight\n\t\t\tbottomExtension = hexHeight\n\t\t}\n\t\t\n\t\tconst b = {\n\t\t\tx: n.x - n.width / 2,\n\t\t\ty: n.y - topExtension,\n\t\t\twidth: n.width,\n\t\t\theight: topExtension + bottomExtension\n\t\t}\n\t\tp0.x = Math.min(p0.x, b.x)\n\t\tp0.y = Math.min(p0.y, b.y)\n\t\tp1.x = Math.max(p1.x, b.x + b.width)\n\t\tp1.y = Math.max(p1.y, b.y + b.height)\n\t})\n\tconst pad = 25 // Padding around content\n\tconst labelHeight = 30 // Space for the group label at bottom\n\tconst w = Math.max(p1.x - p0.x, 200)\n\tconst h = p1.y - p0.y\n\tconst bb = {\n\t\tx: p0.x - pad,\n\t\ty: p0.y - pad,\n\t\twidth: w + pad * 2,\n\t\theight: h + pad * 2 + labelHeight,\n\t}\n\tconst r = create.rect(bb.width, bb.height, bb.x, bb.y)\n\tgroup.x = bb.x + bb.width / 2\n\tgroup.y = bb.y + bb.height / 2\n\tgroup.width = bb.width\n\tgroup.height = bb.height\n\tapplyStyle(r, styles.groupRect)\n\tgroup.style.stroke && r.setAttribute('stroke', group.style.stroke)\n\tgroup.style.background && r.setAttribute('fill', group.style.background)\n\n\tconst txt = create.text(group.name, {x: p0.x, y: bb.y + bb.height - styles.groupText[\"font-size\"]})\n\tapplyStyle(txt, styles.groupText)\n\tgroup.style.color && txt.setAttribute('fill', group.style.color)\n\n\tg.append(r, txt)\n\tgroup.ref = g\n}\n\nfunction findClosestSegment(graph: GraphData, p: Point) {\n\t// find the closest point on a segment\n\tlet fnd = {dst: Number.POSITIVE_INFINITY, pos: -1, edge: null as Edge, prj: null as Point}\n\tgraph.edges.forEach(edge => {\n\t\tconst vertices = edge.vertices || []\n\t\tconst pts = [edge.from, ...vertices, edge.to]\n\t\tfor (let i = 1; i < pts.length; i++) {\n\t\t\tconst prj = project(p, pts[i - 1], pts[i])\n\t\t\tconst dst = cabDistance(p, prj)\n\t\t\tif (dst > 50) continue\n\t\t\tif (dst < fnd.dst) {\n\t\t\t\tfnd = {dst, pos: i, prj, edge}\n\t\t\t}\n\t\t}\n\t})\n\treturn fnd.edge ? fnd : null\n}\n\nfunction mouseToDrawing(e: MouseEvent): Point {\n\t// transform event coords to drawing coords\n\tconst b = svg.getBoundingClientRect()\n\tconst z = getZoom()\n\t\n\t// Get current pan transform\n\tconst currentTransform = getCurrentTransform()\n\t\n\t// Convert screen coordinates to drawing coordinates accounting for zoom and pan\n\t// The transform values are in screen coordinates, so we need to divide by zoom and subtract\n\treturn {\n\t\tx: (e.clientX - b.x - currentTransform.x) / z,\n\t\ty: (e.clientY - b.y - currentTransform.y) / z\n\t}\n}\n\ninterface Handle extends Point {\n\tid: string\n\tselected?: boolean\n\tref?: SVGElement\n}\n\n// Custom cursor interaction that prioritizes panning over selection\nfunction addCustomCursorInteraction(svg: SVGSVGElement, conn: {\n\tnodeFromEvent(e: MouseEvent): Handle | null;\n\tsetSelection(handles: Handle[]): void;\n\tsetDragging(dragging: boolean): void;\n\tisSelected(handle: Handle): boolean;\n\tgetSelection(): Handle[];\n\tgetZoom(): number;\n\tmoveNode(h: Handle, x: number, y: number): void;\n\tboxSelection(box: DOMRect, add: boolean): void;\n\tupdatePanning(): void;\n}, dragMode: 'pan' | 'select') {\n\tlet ini: { x: number; y: number; n: Handle }[] = []\n\tlet elastic: any = null\n\tlet isPanning = false\n\tlet panStartX = 0\n\tlet panStartY = 0\n\tlet initialTransform = { x: 0, y: 0 }\n\tlet pendingSelectionChange: { node: Handle; shiftKey: boolean } | null = null\n\tlet pendingNavigation: string | null = null\n\tlet hasDragged = false\n\tlet suppressLinkClick = false\n\t\n\t// Store event listeners for cleanup\n\tconst eventListeners: Array<{ element: Element | Window, event: string, handler: EventListener }> = []\n\n\t// Simple elastic selection box implementation - use mouseToDrawing for proper coordinate conversion\n\tfunction createElastic() {\n\t\tlet startDrawingX = 0, startDrawingY = 0, rect: SVGRectElement | null = null\n\t\t\n\t\treturn {\n\t\t\tini(e: MouseEvent) {\n\t\t\t\t// Use mouseToDrawing to get proper drawing coordinates (accounts for zoom and pan)\n\t\t\t\tconst pt = mouseToDrawing(e)\n\t\t\t\tstartDrawingX = pt.x\n\t\t\t\tstartDrawingY = pt.y\n\t\t\t\t\n\t\t\t\trect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\")\n\t\t\t\trect.setAttribute('fill', 'rgba(0, 100, 255, 0.1)')\n\t\t\t\trect.setAttribute('stroke', 'rgba(0, 100, 255, 0.5)')\n\t\t\t\trect.setAttribute('stroke-width', '1')\n\t\t\t\trect.setAttribute('stroke-dasharray', '3,3')\n\t\t\t\trect.setAttribute('x', String(startDrawingX))\n\t\t\t\trect.setAttribute('y', String(startDrawingY))\n\t\t\t\trect.setAttribute('width', '0')\n\t\t\t\trect.setAttribute('height', '0')\n\t\t\t\t\n\t\t\t\t// Add to the zoom group so it transforms with the content\n\t\t\t\tconst zoomGroup = svg.querySelector('g.zoom')\n\t\t\t\tif (zoomGroup) {\n\t\t\t\t\tzoomGroup.appendChild(rect)\n\t\t\t\t} else {\n\t\t\t\t\tsvg.appendChild(rect)\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdate(e: MouseEvent) {\n\t\t\t\tif (!rect) return\n\t\t\t\t\n\t\t\t\t// Convert current mouse position to drawing coordinates (accounts for zoom and pan)\n\t\t\t\tconst currentPt = mouseToDrawing(e)\n\t\t\t\tconst currentDrawingX = currentPt.x\n\t\t\t\tconst currentDrawingY = currentPt.y\n\t\t\t\t\n\t\t\t\t// Calculate rectangle bounds in drawing coordinates\n\t\t\t\tconst x = Math.min(startDrawingX, currentDrawingX)\n\t\t\t\tconst y = Math.min(startDrawingY, currentDrawingY)\n\t\t\t\tconst width = Math.abs(currentDrawingX - startDrawingX)\n\t\t\t\tconst height = Math.abs(currentDrawingY - startDrawingY)\n\t\t\t\t\n\t\t\t\trect.setAttribute('x', String(x))\n\t\t\t\trect.setAttribute('y', String(y))\n\t\t\t\trect.setAttribute('width', String(width))\n\t\t\t\trect.setAttribute('height', String(height))\n\t\t\t},\n\t\t\tend(): DOMRect | null {\n\t\t\t\tif (!rect) return null\n\t\t\t\t\n\t\t\t\t// Get final rectangle bounds in drawing coordinates\n\t\t\t\tconst x = parseFloat(rect.getAttribute('x') || '0')\n\t\t\t\tconst y = parseFloat(rect.getAttribute('y') || '0')\n\t\t\t\tconst width = parseFloat(rect.getAttribute('width') || '0')\n\t\t\t\tconst height = parseFloat(rect.getAttribute('height') || '0')\n\t\t\t\t\n\t\t\t\trect.remove()\n\t\t\t\trect = null\n\t\t\t\t\n\t\t\t\t// Return drawing coordinates directly for boxSelection since we're now working in the same coordinate system\n\t\t\t\tif (width > 5 && height > 5) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tx: x, y: y, width: width, height: height,\n\t\t\t\t\t\tleft: x, top: y, right: x + width, bottom: y + height\n\t\t\t\t\t} as DOMRect\n\t\t\t\t}\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction getCurrentTransformLocal() {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (!zoomGroup) return { x: 0, y: 0 }\n\t\t\n\t\tconst transform = zoomGroup.getAttribute('transform') || ''\n\t\tconst translateMatch = transform.match(/translate\\(([^,]+),([^)]+)\\)/)\n\t\tif (translateMatch) {\n\t\t\treturn {\n\t\t\t\tx: parseFloat(translateMatch[1]) || 0,\n\t\t\t\ty: parseFloat(translateMatch[2]) || 0\n\t\t\t}\n\t\t}\n\t\treturn { x: 0, y: 0 }\n\t}\n\n\tfunction setTransform(x: number, y: number) {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (!zoomGroup) return\n\t\t\n\t\tconst zoom = getZoom()\n\t\tzoomGroup.setAttribute('transform', `translate(${x}, ${y}) scale(${zoom})`)\n\t}\n\n\tfunction onMouseDown(e: MouseEvent) {\n\t\te.preventDefault();\n\t\thasDragged = false\n\t\tpendingSelectionChange = null\n\t\tconst target = e.target\n\t\tconst link = target instanceof Element ? target.closest('a.nodeLink') : null\n\t\tpendingNavigation = e.shiftKey ? null : link?.getAttribute('href') || null\n\n\t\tconst node = conn.nodeFromEvent(e)\n\t\t\n\t\t// Determine effective mode: invert if shift is held\n\t\tconst effectiveMode = e.shiftKey ? (dragMode === 'pan' ? 'select' : 'pan') : dragMode\n\t\t\n\t\tif (!node) { // Clicked on empty space\n\t\t\tif (effectiveMode === 'pan') {\n\t\t\t\t// Pan mode: pan and deselect\n\t\t\t\tisPanning = true;\n\t\t\t\telastic = null;\n\t\t\t\tpanStartX = e.clientX;\n\t\t\t\tpanStartY = e.clientY;\n\t\t\t\tinitialTransform = getCurrentTransformLocal();\n\t\t\t\tini = [];\n\t\t\t\t// Deselect all elements when clicking empty space in pan mode\n\t\t\t\tconn.setSelection([]);\n\t\t\t} else {\n\t\t\t\t// Select mode: ONLY select, no panning\n\t\t\t\tisPanning = false;\n\t\t\t\telastic = createElastic();\n\t\t\t\tif (elastic) elastic.ini(e);\n\t\t\t\tini = [];\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Clicked on a node/vertex - behavior depends on effective mode\n\t\tif (effectiveMode === 'pan') {\n\t\t\t// Pan mode: select the element, don't pan\n\t\t\tisPanning = false;\n\t\t\telastic = null;\n\t\t\t\n\t\t\tconst nodes = conn.getSelection()\n\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t// Clicking on a selected node - prepare to drag all selected elements\n\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t\t// No selection change needed since we're clicking on already selected element\n\t\t\t} else {\n\t\t\t\t// Clicking on an unselected node - select only this element\n\t\t\t\tconn.setSelection([node]);\n\t\t\t\tini = [{ x: node.x, y: node.y, n: node }];\n\t\t\t}\n\t\t} else {\n\t\t\t// Select mode: selection/drag logic\n\t\t\tisPanning = false; // Ensure no panning if a node is clicked\n\t\t\telastic = null; // Ensure no selection box if a node is clicked\n\t\t\tconst nodes = conn.getSelection()\n\t\t\t\n\t\t\tif (e.shiftKey && dragMode === 'select') {\n\t\t\t\t// Shift+click in select mode: immediately change selection (no dragging expected)\n\t\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t\tconst index = nodes.findIndex(n => n.id === node.id)\n\t\t\t\t\tif (index >= 0) nodes.splice(index, 1)\n\t\t\t\t} else {\n\t\t\t\t\tnodes.push(node)\n\t\t\t\t}\n\t\t\t\tconn.setSelection(nodes)\n\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t} else {\n\t\t\t\t// Regular click: defer selection change until we know if it's a drag or click\n\t\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t\t// Clicking on a selected node - prepare to drag all selected elements\n\t\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t\t\t// No pending selection change needed since we're clicking on already selected element\n\t\t\t\t\tpendingSelectionChange = null\n\t\t\t\t} else {\n\t\t\t\t\t// Clicking on an unselected node - defer selection change until we determine if it's a click or drag\n\t\t\t\t\tpendingSelectionChange = { node, shiftKey: e.shiftKey }\n\t\t\t\t\t// For now, prepare to drag just the clicked node (we'll update selection when drag starts)\n\t\t\t\t\tini = [{ x: node.x, y: node.y, n: node }]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction onMouseMove(e: MouseEvent, dx: number, dy: number) {\n\t\t// Check if we've moved enough to consider this a drag\n\t\tconst dragThreshold = 3 // pixels\n\t\tif (!hasDragged && (Math.abs(dx) > dragThreshold || Math.abs(dy) > dragThreshold)) {\n\t\t\thasDragged = true\n\t\t\tpendingNavigation = null\n\t\t\t\n\t\t\t// If we have a pending selection change and we're now dragging, apply it\n\t\t\tif (pendingSelectionChange) {\n\t\t\t\tconst nodes = conn.getSelection()\n\t\t\t\tnodes.length = 0\n\t\t\t\tnodes.push(pendingSelectionChange.node)\n\t\t\t\tconn.setSelection(nodes)\n\t\t\t\t// Update ini to drag only the newly selected node\n\t\t\t\tini = [{ x: pendingSelectionChange.node.x, y: pendingSelectionChange.node.y, n: pendingSelectionChange.node }]\n\t\t\t\tpendingSelectionChange = null\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (isPanning) {\n\t\t\t// Pan the view - apply mouse delta directly (no zoom division needed)\n\t\t\t// setTransform expects screen coordinates, dx/dy are already screen pixel deltas\n\t\t\tconst newX = initialTransform.x + dx\n\t\t\tconst newY = initialTransform.y + dy\n\t\t\tsetTransform(newX, newY)\n\t\t} else if (ini.length > 0 && hasDragged) {\n\t\t\t// Move selected nodes/vertices (only if we've actually started dragging)\n\t\t\t// dx, dy are screen pixel deltas, convert to drawing coordinate deltas\n\t\t\tconst zoom = conn.getZoom()\n\t\t\tconst drawingDx = dx / zoom\n\t\t\tconst drawingDy = dy / zoom\n\t\t\tini.forEach(item => {\n\t\t\t\t// item.x, item.y are initial drawing coordinates\n\t\t\t\t// Add the drawing coordinate delta to get new position\n\t\t\t\tconn.moveNode(item.n, item.x + drawingDx, item.y + drawingDy)\n\t\t\t})\n\t\t\tconn.setDragging(true)\n\t\t} else if (elastic) {\n\t\t\t// Update selection box\n\t\t\telastic.update(e)\n\t\t\tconn.setDragging(true)\n\t\t}\n\t}\n\n\tfunction onMouseUp(e: MouseEvent) {\n\t\tconn.setDragging(false)\n\t\tconst navigation = hasDragged ? null : pendingNavigation\n\t\tif (hasDragged) {\n\t\t\tsuppressLinkClick = true\n\t\t\twindow.setTimeout(() => {\n\t\t\t\tsuppressLinkClick = false\n\t\t\t}, 0)\n\t\t}\n\t\t\n\t\t// If we have a pending selection change and didn't drag, apply it now (it was just a click)\n\t\tif (pendingSelectionChange && !hasDragged) {\n\t\t\tconst nodes = conn.getSelection()\n\t\t\tnodes.length = 0\n\t\t\tnodes.push(pendingSelectionChange.node)\n\t\t\tconn.setSelection(nodes)\n\t\t}\n\t\t\n\t\tif (elastic) {\n\t\t\tconst box = elastic.end()\n\t\t\tif (box) {\n\t\t\t\tconn.boxSelection(box, e.shiftKey)\n\t\t\t} else if (!ini.length) {\n\t\t\t\t// Deselect if no box was drawn\n\t\t\t\tconn.setSelection([])\n\t\t\t}\n\t\t\telastic = null\n\t\t}\n\t\t\n\t\t// Save view state if user was panning (user-initiated view change)\n\t\tif (isPanning && hasDragged) {\n\t\t\tconst graphData = (svg as any).__data as GraphData\n\t\t\tif (graphData && graphData.id) {\n\t\t\t\tsaveViewState(graphData.id)\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Reset state\n\t\tpendingSelectionChange = null\n\t\tpendingNavigation = null\n\t\thasDragged = false\n\t\tisPanning = false\n\t\tconn.updatePanning()\n\t\tif (navigation) {\n\t\t\twindow.location.href = navigation\n\t\t}\n\t}\n\n\tfunction onClick(e: MouseEvent) {\n\t\tconst target = e.target\n\t\tif (!(target instanceof Element) || !target.closest('a.nodeLink')) return\n\t\te.preventDefault()\n\t\tif (!suppressLinkClick) return\n\t\te.stopPropagation()\n\t\tsuppressLinkClick = false\n\t}\n\n\t// Add drag and drop functionality\n\tfunction addDnd(element: SVGSVGElement) {\n\t\tlet md: { ex: number; ey: number } | null = null\n\n\t\tfunction convertEvent(e: MouseEvent | TouchEvent): MouseEvent {\n\t\t\tif ('changedTouches' in e && e.changedTouches) {\n\t\t\t\treturn e.changedTouches[0] as any\n\t\t\t}\n\t\t\treturn e as MouseEvent\n\t\t}\n\n\t\tfunction onMouseMoveHandler(e: MouseEvent | TouchEvent) {\n\t\t\tif (!md) return\n\t\t\te = convertEvent(e)\n\t\t\tonMouseMove(e, e.clientX - md.ex, e.clientY - md.ey)\n\t\t}\n\n\t\tfunction removeListeners() {\n\t\t\tdocument.removeEventListener('touchmove', onMouseMoveHandler as any)\n\t\t\tdocument.removeEventListener('mousemove', onMouseMoveHandler as any)\n\t\t\tdocument.removeEventListener('mouseup', onMouseUpHandler)\n\t\t\tdocument.removeEventListener('touchend', onMouseUpHandler)\n\t\t}\n\n\t\tfunction onMouseUpHandler(e: MouseEvent | TouchEvent) {\n\t\t\tremoveListeners()\n\t\t\tonMouseUp(convertEvent(e))\n\t\t\tmd = null\n\t\t}\n\n\t\tfunction onMouseDownHandler(e: MouseEvent | TouchEvent) {\n\t\t\te = convertEvent(e)\n\t\t\tmd = { ex: e.clientX, ey: e.clientY }\n\t\t\tonMouseDown(e)\n\t\t\tdocument.addEventListener('touchmove', onMouseMoveHandler as any)\n\t\t\tdocument.addEventListener('mousemove', onMouseMoveHandler as any)\n\t\t\tdocument.addEventListener('mouseup', onMouseUpHandler)\n\t\t\tdocument.addEventListener('touchend', onMouseUpHandler)\n\t\t}\n\n\t\telement.addEventListener('mousedown', onMouseDownHandler as any)\n\t\telement.addEventListener('touchstart', onMouseDownHandler as any)\n\t\t\n\t\t// Track these listeners for cleanup\n\t\teventListeners.push(\n\t\t\t{ element, event: 'mousedown', handler: onMouseDownHandler as any },\n\t\t\t{ element, event: 'touchstart', handler: onMouseDownHandler as any }\n\t\t)\n\t}\n\n\taddDnd(svg)\n\tsvg.addEventListener('click', onClick)\n\teventListeners.push({ element: svg, event: 'click', handler: onClick })\n\t\n\t// Return cleanup function\n\treturn () => {\n\t\teventListeners.forEach(({ element, event, handler }) => {\n\t\t\telement.removeEventListener(event, handler)\n\t\t})\n\t}\n}\n\nexport function addCursorInteraction(svg: SVGSVGElement, dragMode: 'pan' | 'select') {\n\t// Clean up any existing event listeners to prevent conflicts\n\tconst existingCleanup = (svg as any).__cursorInteractionCleanup\n\tif (existingCleanup) {\n\t\texistingCleanup()\n\t}\n\n\tfunction getData(el: SVGElement) {\n\t\t// @ts-ignore\n\t\treturn el.__data\n\t}\n\n\tconst gd = () => (getData(svg) as GraphData)\n\t\n\t// Store event listeners for cleanup\n\tconst eventListeners: Array<{ element: Element | Window, event: string, handler: EventListener }> = []\n\n\tconst beforeUnloadHandler = (e: BeforeUnloadEvent) => {\n\t\tif (!gd().changed()) return\n\t\te.preventDefault()\n\t\te.returnValue = ''\n\t}\n\twindow.addEventListener(\"beforeunload\", beforeUnloadHandler)\n\teventListeners.push({ element: window, event: 'beforeunload', handler: beforeUnloadHandler })\n\n\tfunction setDotSelected(d: Handle, selected: boolean) {\n\t\td.selected = selected\n\t\tconst dotEl = svg.querySelector('#' + d.id)\n\t\td.selected ? dotEl.classList.add('selected') : dotEl.classList.remove('selected')\n\t}\n\n\t// show moving dot along edge when ALT is pressed\n\tconst mouseMoveHandler = (e: MouseEvent) => {\n\t\tif (!e.altKey) return\n\t\tconst fnd = findClosestSegment(gd(), mouseToDrawing(e))\n\t\tif (fnd) {\n\t\t\tconst {prj} = fnd\n\t\t\tconst parent = svg.querySelector('g.edges')\n\t\t\tlet dot = parent.querySelector('#prj')\n\t\t\tif (!dot) {\n\t\t\t\tdot = create.element('circle', {id: 'prj', cx: prj.x, cy: prj.y, r: 7})\n\t\t\t\tparent.append(dot)\n\t\t\t}\n\t\t\tdot.setAttribute('cx', String(prj.x))\n\t\t\tdot.setAttribute('cy', String(prj.y))\n\t\t} else {\n\t\t\tremovePrjDot()\n\t\t}\n\t}\n\tsvg.addEventListener('mousemove', mouseMoveHandler)\n\teventListeners.push({ element: svg, event: 'mousemove', handler: mouseMoveHandler })\n\n\tconst keyUpHandler = (e: KeyboardEvent) => {\n\t\tconst key = findShortcut(e, true)\n\t\tif (key == ADD_VERTEX || key == ADD_LABEL_VERTEX) return\n\t\tremovePrjDot()\n\t}\n\twindow.addEventListener('keyup', keyUpHandler)\n\teventListeners.push({ element: window, event: 'keyup', handler: keyUpHandler })\n\n\tfunction removePrjDot() {\n\t\tconst el = svg.querySelector('g.edges #prj')\n\t\tel && el.parentElement.removeChild(el)\n\t}\n\n\tconst clickHandler = (e: MouseEvent) => {\n\t\tconst key = findShortcut(e, true)\n\t\tif (key != ADD_LABEL_VERTEX && key != ADD_VERTEX) return\n\t\tconst fnd = findClosestSegment(gd(), mouseToDrawing(e))\n\t\tif (fnd) {\n\t\t\tconst {edge, pos, prj} = fnd\n\t\t\t// depending on keyboard modifier, make it label position\n\t\t\tgd().insertEdgeVertex(edge, prj, pos, key == ADD_LABEL_VERTEX)\n\t\t\tremovePrjDot()\n\t\t}\n\t}\n\tsvg.addEventListener('click', clickHandler)\n\teventListeners.push({ element: svg, event: 'click', handler: clickHandler })\n\n\tconst wheelHandler = (e: WheelEvent) => {\n\t\t// Handle wheel zoom directly without relying on shortcuts\n\t\t// deltaY > 0 means scrolling down (zoom out), deltaY < 0 means scrolling up (zoom in)\n\t\tconst delta = Math.sign(e.deltaY) * 0.1 // Normalize to 0.1 zoom steps\n\t\tconst currentZoom = getZoom()\n\t\tconst newZoom = Math.max(0.1, Math.min(5, currentZoom - delta)) // Clamp zoom between 0.1 and 5\n\t\t\n\t\tif (newZoom !== currentZoom) {\n\t\t\t// Convert absolute screen coordinates to SVG-relative coordinates\n\t\t\tconst rect = svg.getBoundingClientRect()\n\t\t\tconst svgX = e.clientX - rect.left\n\t\t\tconst svgY = e.clientY - rect.top\n\t\t\tsetZoomCentered(newZoom, svgX, svgY)\n\t\t\te.preventDefault()\n\t\t\t\n\t\t\t// Save view state after user wheel zoom\n\t\t\tconst graphData = (svg as any).__data as GraphData\n\t\t\tif (graphData && graphData.id) {\n\t\t\t\tsaveViewState(graphData.id)\n\t\t\t}\n\t\t}\n\t}\n\tsvg.addEventListener('wheel', wheelHandler)\n\teventListeners.push({ element: svg, event: 'wheel', handler: wheelHandler })\n\n\tconst keyDownHandler = (e: KeyboardEvent) => {\n\t\tconst shortcut = findShortcut(e)\n\t\t\n\t\t\n\t\tif (shortcut) {\n\t\t\te.preventDefault() // Prevent browser default for all recognized shortcuts\n\t\t}\n\t\t\n\t\tswitch (shortcut) {\n\t\t\tcase DEL_VERTEX:\n\t\t\t\tconst selectedVertices = Array.from(gd().edgeVertices.values()).filter(v => v.selected);\n\t\t\t\tselectedVertices.forEach(v => {\n\t\t\t\t\tgd().deleteEdgeVertex(v)\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\tcase UNDO:\n\t\t\t\tgd().undo()\n\t\t\t\tbreak\n\t\t\tcase REDO:\n\t\t\t\tgd().redo()\n\t\t\t\tbreak\n\t\t\tcase ZOOM_IN:\n\t\t\t\tconst newZoomIn = Math.min(5, getZoom() * 1.2)\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(newZoomIn)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_OUT:\n\t\t\t\tconst newZoomOut = Math.max(0.1, getZoom() / 1.2)\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(newZoomOut)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_100:\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(1)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_FIT:\n\t\t\t\tgd().fitToView()\n\t\t\t\t// Don't save view state here - fitToView should not be persisted\n\t\t\t\tbreak\n\t\t\tcase SELECT_ALL:\n\t\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, true))\n\t\t\t\tgd().edgeVertices.forEach(v => setDotSelected(v, true))\n\t\t\t\tbreak\n\t\t\tcase DESELECT:\n\t\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, false))\n\t\t\t\tgd().edgeVertices.forEach(v => setDotSelected(v, false))\n\t\t\t\tbreak\n\t\t}\n\t}\n\twindow.addEventListener('keydown', keyDownHandler)\n\teventListeners.push({ element: window, event: 'keydown', handler: keyDownHandler })\n\n\t// Custom cursor interaction with pan-first behavior\n\tconst customInteractionCleanup = addCustomCursorInteraction(svg, {\n\t\tnodeFromEvent(e: MouseEvent): Handle {\n\t\t\te.preventDefault()\n\t\t\t// node clicked\n\t\t\tlet el = (e.target as SVGElement).closest('g.nodes g.node') as SVGElement\n\t\t\tif (el) return getData(el)\n\t\t\t// vertex dot clicked\n\t\t\tel = (e.target as SVGElement).closest('g.edges g.edge .v-dot') as SVGElement\n\t\t\tif (el) {\n\t\t\t\treturn gd().edgeVertices.get(el.id)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\t\tsetSelection(handles: Handle[]) {\n\t\t\t// nodes\n\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, handles.some(h => h.id == n.id)))\n\t\t\t// dots\n\t\t\tgd().edgeVertices.forEach(d => setDotSelected(d, handles.some(h => h.id == d.id)))\n\t\t\tselectListener(gd().nodes().find(n => n.selected))\n\t\t},\n\t\tsetDragging(d: boolean) {\n\t\t\tdragging = d\n\t\t},\n\t\tisSelected(handle: Handle): boolean {\n\t\t\treturn handle.selected\n\t\t},\n\t\tgetSelection(): Handle[] {\n\t\t\tconst ret: Handle[] = gd().nodes().filter(n => n.selected)\n\t\t\tgd().edgeVertices.forEach(d => d.selected && ret.push(d))\n\t\t\treturn ret\n\t\t},\n\t\tgetZoom: getZoom,\n\t\tmoveNode(h: Handle, x: number, y: number) {\n\t\t\tif (gd().nodesMap.has(h.id))\n\t\t\t\tgd().moveNode(h as Node, x, y)\n\t\t\telse {\n\t\t\t\t(h as EdgeVertex).auto = false\n\t\t\t\tgd().moveEdgeVertex(h as EdgeVertex, x, y)\n\t\t\t}\n\t\t},\n\t\tboxSelection(box: DOMRect, add) {\n\t\t\t// Box is now already in drawing coordinates, no need to scale\n\t\t\t// nodes\n\t\t\tgd().nodesMap.forEach(n => {\n\t\t\t\tconst inBox = boxesOverlap(uncenterBox(n), box)\n\t\t\t\tif (inBox) {\n\t\t\t\t\t// Toggle selection for elements in the box\n\t\t\t\t\tgd().setNodeSelected(n, !n.selected)\n\t\t\t\t} else if (!add) {\n\t\t\t\t\t// If not holding shift and element is outside box, deselect it\n\t\t\t\t\tgd().setNodeSelected(n, false)\n\t\t\t\t}\n\t\t\t})\n\t\t\t// dots\n\t\t\tgd().edgeVertices.forEach(d => {\n\t\t\t\tconst inBox = insideBox(d, box, false)\n\t\t\t\tif (inBox) {\n\t\t\t\t\t// Toggle selection for elements in the box\n\t\t\t\t\tsetDotSelected(d, !d.selected)\n\t\t\t\t} else if (!add) {\n\t\t\t\t\t// If not holding shift and element is outside box, deselect it\n\t\t\t\t\tsetDotSelected(d, false)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tselectListener(gd().nodes().find(n => n.selected))\n\t\t},\n\t\tupdatePanning: updatePanning,\n\t}, dragMode)\n\t\n\t// Store cleanup function on the SVG element for later use\n\tconst cleanup = () => {\n\t\teventListeners.forEach(({ element, event, handler }) => {\n\t\t\telement.removeEventListener(event, handler)\n\t\t})\n\t\tif (customInteractionCleanup) {\n\t\t\tcustomInteractionCleanup()\n\t\t}\n\t}\n\t\n\t// Store cleanup function on SVG element\n\t;(svg as any).__cursorInteractionCleanup = cleanup\n}\n\nexport function getZoom() {\n\tif (!svg) return 1\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return 1\n\t\n\t// Parse zoom from transform attribute to match how we set it\n\tconst transform = el.getAttribute('transform') || ''\n\tconst scaleMatch = transform.match(/scale\\(([^)]+)\\)/)\n\tif (scaleMatch) {\n\t\treturn parseFloat(scaleMatch[1]) || 1\n\t}\n\treturn 1\n}\n\n// svgPadding is now imported as SVG_PADDING from constants.ts\n\nexport function setZoom(zoom: number) {\n\tif (!svg) return\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return\n\t\n\t// Preserve existing translation when setting zoom\n\tconst currentTransform = getCurrentTransform()\n\tel.setAttribute('transform', `translate(${currentTransform.x}, ${currentTransform.y}) scale(${zoom})`)\n\t\n\t// also set panning size\n\tupdatePanning()\n}\n\nexport function setZoomCentered(newZoom: number, centerX?: number, centerY?: number) {\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tconst oldZoom = getZoom()\n\t\n\t// If no center point provided, use viewport center\n\tif (centerX === undefined || centerY === undefined) {\n\t\t// Use the parent container's dimensions for the visible viewport\n\t\t// The SVG might be larger than the visible area due to overflow\n\t\tconst container = svg.parentElement\n\t\tif (container) {\n\t\t\tcenterX = container.clientWidth / 2\n\t\t\tcenterY = container.clientHeight / 2\n\t\t} else {\n\t\t\t// Fallback to SVG dimensions if no parent\n\t\t\tcenterX = svg.clientWidth / 2\n\t\t\tcenterY = svg.clientHeight / 2\n\t\t}\n\t}\n\t\n\t// Get current transform\n\tconst currentTransform = getCurrentTransform()\n\t\n\t// Convert screen coordinates to drawing coordinates\n\t// For transform order translate(tx, ty) scale(s):\n\t// screen_point = (drawing_point * scale) + translation\n\t// So: drawing_point = (screen_point - translation) / scale\n\tconst drawingX = (centerX - currentTransform.x) / oldZoom\n\tconst drawingY = (centerY - currentTransform.y) / oldZoom\n\t\n\t// Calculate new translation to keep the same drawing point at the same screen position\n\t// screen_point = (drawing_point * new_scale) + new_translation\n\t// So: new_translation = screen_point - (drawing_point * new_scale)\n\tconst newTranslateX = centerX - (drawingX * newZoom)\n\tconst newTranslateY = centerY - (drawingY * newZoom)\n\t\n\t// Apply the new transform\n\tel.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY}) scale(${newZoom})`)\n\t\n\t// Update panning\n\tupdatePanning()\n}\n\nfunction getCurrentTransform() {\n\tif (!svg) return { x: 0, y: 0 }\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return { x: 0, y: 0 }\n\t\n\tconst transform = el.getAttribute('transform') || ''\n\tconst translateMatch = transform.match(/translate\\(([^,]+),([^)]+)\\)/)\n\tif (translateMatch) {\n\t\treturn {\n\t\t\tx: parseFloat(translateMatch[1]) || 0,\n\t\t\ty: parseFloat(translateMatch[2]) || 0\n\t\t}\n\t}\n\treturn { x: 0, y: 0 }\n}\n\nfunction updatePanning() {\n\tif (!svg) return\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return\n\tconst bb = el.getBBox()\n\tconst zoom = getZoom()\n\tif (!svg.parentElement) return\n\tconst w = Math.max(svg.parentElement.clientWidth / zoom, bb.x + bb.width + SVG_PADDING)\n\tconst h = Math.max(svg.parentElement.clientHeight / zoom, bb.y + bb.height + SVG_PADDING)\n\tsvg.setAttribute('width', String(w * zoom))\n\tsvg.setAttribute('height', String(h * zoom))\n\t\n\t// Note: View state saving removed from here to prevent interference with reset/fit functions\n\t// View state is now only saved on user interactions and page unload\n}\n\n// Optimized version that uses pre-calculated content bounds instead of expensive getBBox()\nfunction updatePanningOptimized(graphData: GraphData) {\n\tconst bb = graphData.calculateContentBounds() // Use already calculated bounds\n\tconst zoom = getZoom()\n\tconst w = Math.max(svg.parentElement.clientWidth / zoom, bb.x + bb.width + SVG_PADDING)\n\tconst h = Math.max(svg.parentElement.clientHeight / zoom, bb.y + bb.height + SVG_PADDING)\n\tsvg.setAttribute('width', String(w * zoom))\n\tsvg.setAttribute('height', String(h * zoom))\n}\n\nexport const getZoomAuto = () => {\n\t// Get the graph data to calculate proper content bounds\n\tconst graphData = (svg as any).__data as GraphData\n\tif (!graphData) {\n\t\treturn 1 // Default zoom if no graph data\n\t}\n\t\n\t// Use proper content bounds calculation\n\tconst contentBounds = graphData.calculateContentBounds()\n\tconst viewportWidth = svg.parentElement?.clientWidth || 800\n\tconst viewportHeight = svg.parentElement?.clientHeight || 600\n\t\n\t// Add padding around content\n\tconst padding = 40\n\t\n\t// Calculate zoom to fit content with padding\n\tconst zoomX = (viewportWidth - padding * 2) / contentBounds.width\n\tconst zoomY = (viewportHeight - padding * 2) / contentBounds.height\n\tconst zoom = Math.min(zoomX, zoomY)\n\t\n\t// Clamp zoom between reasonable bounds\n\treturn Math.max(Math.min(zoom, 2), 0.1)\n}\n\nconst setBorderStyle = (el: SVGElement, style: string) => {\n\tif (style == 'Dashed') el.setAttribute('stroke-dasharray', '4')\n\telse if (style == 'Dotted') el.setAttribute('stroke-dasharray', '2')\n}\n\nconst styles = {\n\t//node styles\n\tnodeBorder: {\n\t\t// Don't set fill and stroke here - let tag-specific styles handle colors\n\t\tfilter: 'url(#shadow)',\n\t},\n\tnodeText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\n\t//edge styles\n\tedgeText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\n\tedgeRect: {\n\t\tfill: \"none\",\n\t\tstroke: \"none\",\n\t},\n\n\t//group styles\n\tgroupRect: {\n\t\t//fill: \"none\",\n\t\tfill: \"rgba(0, 0, 0, 0.02)\",\n\t\tstroke: \"#666\",\n\t\t'stroke-width': 3,\n\t\t\"stroke-dasharray\": 4,\n\t},\n\tgroupText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tfill: \"#666\",\n\t\t\"font-size\": 22,\n\t\t\"font-weight\": \"bold\",\n\t\tcursor: \"default\"\n\t}\n}\n\n// View state persistence - similar to undo cache but for zoom/pan\nconst viewStateCache = new Map();\n\n// Save current view state (zoom and pan)\nexport function saveViewState(graphId: string) {\n\tif (!svg) return;\n\t\n\tconst zoom = getZoom();\n\tconst transform = getCurrentTransform();\n\t\n\tconst state = {\n\t\tzoom,\n\t\ttransform: { x: transform.x, y: transform.y }\n\t};\n\t\n\tviewStateCache.set(graphId, state);\n}\n\n// Restore view state if it exists\nexport function restoreViewState(graphId: string): boolean {\n\tif (!svg || !viewStateCache.has(graphId)) {\n\t\treturn false;\n\t}\n\t\n\tconst state = viewStateCache.get(graphId);\n\tif (!state) {\n\t\treturn false;\n\t}\n\t\n\t// Restore zoom and transform\n\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement;\n\tif (zoomGroup) {\n\t\tzoomGroup.setAttribute('transform', `scale(${state.zoom}) translate(${state.transform.x}, ${state.transform.y})`);\n\t\tupdatePanning();\n\t}\n\t\n\treturn true;\n}\n\n// Clear view state for a graph\nexport function clearViewState(graphId: string) {\n\tviewStateCache.delete(graphId);\n}","// This file is the only owner of automatic positions. It gives ELK the exact\n// measured sizes, reads ELK's complete result, and does not repair that result.\n\nimport type {\n\tElkExtendedEdge,\n\tElkLabel,\n\tElkNode,\n\tElkPoint,\n} from \"elkjs/lib/elk-api\";\n\nimport {\n\tMeasuredDiagram,\n\tMeasuredEdge,\n\tMeasuredGroup,\n\tMeasuredNode,\n\tPoint,\n\tResolvedEdge,\n\tResolvedGroup,\n\tResolvedLayout,\n\tResolvedNode,\n} from \"./geometry\";\n\n/** AutomaticLayoutOptions are the choices a caller may make before layout. */\nexport interface AutomaticLayoutOptions {\n\tdirection: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\";\n\tnodeSpacing: number;\n\tlayerSpacing: number;\n}\n\nconst defaultOptions: AutomaticLayoutOptions = {\n\tdirection: \"DOWN\",\n\tnodeSpacing: 80,\n\tlayerSpacing: 80,\n};\n\n/**\n * resolveAutomatic runs one compound ELK layout with exact measured sizes.\n * ELK errors and incomplete results are returned to the caller unchanged.\n */\nexport async function resolveAutomatic(\n\tdiagram: MeasuredDiagram,\n\toptions: Partial = {},\n): Promise {\n\tconst settings = {...defaultOptions, ...options};\n\tconst graph = buildLayoutTree(diagram, settings);\n\tconst ELK = await import(\"elkjs/lib/elk.bundled.js\").then(module => module.default);\n\tconst elk = new ELK();\n\tconst result = await elk.layout(graph);\n\treturn readResolvedLayout(diagram, result);\n}\n\n/**\n * buildLayoutTree creates one ELK tree. Nodes belong to their declared group,\n * groups belong to their declared parent, and each edge belongs to the nearest\n * group shared by both connected nodes.\n */\nfunction buildLayoutTree(\n\tdiagram: MeasuredDiagram,\n\toptions: AutomaticLayoutOptions,\n): ElkNode {\n\tconst nodes = new Map(diagram.nodes.map(node => [node.id, node]));\n\tconst groups = new Map(diagram.groups.map(group => [group.id, group]));\n\tcheckMeasuredLinks(diagram, nodes, groups);\n\n\tconst childrenByParent = new Map();\n\tfor (const node of diagram.nodes) {\n\t\tappend(childrenByParent, node.parentId, node);\n\t}\n\tconst groupsByParent = new Map();\n\tfor (const group of diagram.groups) {\n\t\tappend(groupsByParent, group.parentId, group);\n\t}\n\n\tconst elkGroups = new Map();\n\tconst buildGroup = (group: MeasuredGroup): ElkNode => {\n\t\tconst child: ElkNode = {\n\t\t\tid: group.id,\n\t\t\tchildren: [\n\t\t\t\t...(groupsByParent.get(group.id) ?? []).map(buildGroup),\n\t\t\t\t...(childrenByParent.get(group.id) ?? []).map(buildNode),\n\t\t\t],\n\t\t\tedges: [],\n\t\t\tlabels: [{\n\t\t\t\ttext: group.id,\n\t\t\t\twidth: group.titleSize.width,\n\t\t\t\theight: group.titleSize.height,\n\t\t\t}],\n\t\t\tlayoutOptions: groupLayoutOptions(options, group.titleSize.height),\n\t\t};\n\t\telkGroups.set(group.id, child);\n\t\treturn child;\n\t};\n\n\tconst root: ElkNode = {\n\t\tid: diagram.id,\n\t\tchildren: [\n\t\t\t...(groupsByParent.get(undefined) ?? []).map(buildGroup),\n\t\t\t...(childrenByParent.get(undefined) ?? []).map(buildNode),\n\t\t],\n\t\tedges: [],\n\t\tlayoutOptions: rootLayoutOptions(options),\n\t};\n\n\tfor (const edge of diagram.edges) {\n\t\tconst owner = lowestCommonGroup(edge, nodes, groups);\n\t\tconst container = owner ? elkGroups.get(owner) : root;\n\t\tif (!container) {\n\t\t\tthrow new Error(`edge ${edge.id} has missing layout group ${owner}`);\n\t\t}\n\t\tcontainer.edges?.push(buildEdge(edge));\n\t}\n\treturn root;\n}\n\n/** buildNode passes the measured node size to ELK without padding or guesses. */\nfunction buildNode(node: MeasuredNode): ElkNode {\n\treturn {\n\t\tid: node.id,\n\t\twidth: node.size.width,\n\t\theight: node.size.height,\n\t\tlayoutOptions: {\n\t\t\t\"elk.nodeSize.constraints\": \"FIXED_SIZE\",\n\t\t\t\"elk.portConstraints\": \"FREE\",\n\t\t},\n\t};\n}\n\n/** buildEdge passes the exact label size and stable endpoint IDs to ELK. */\nfunction buildEdge(edge: MeasuredEdge): ElkExtendedEdge {\n\treturn {\n\t\tid: edge.id,\n\t\tsources: [edge.sourceId],\n\t\ttargets: [edge.targetId],\n\t\tlabels: edge.labelSize ? [{\n\t\t\ttext: edge.id,\n\t\t\twidth: edge.labelSize.width,\n\t\t\theight: edge.labelSize.height,\n\t\t\tlayoutOptions: {\n\t\t\t\t\"elk.edgeLabels.inline\": \"false\",\n\t\t\t\t\"elk.edgeLabels.placement\": \"CENTER\",\n\t\t\t},\n\t\t}] : [],\n\t};\n}\n\n/** rootLayoutOptions set the rules shared by the full compound layout. */\nfunction rootLayoutOptions(options: AutomaticLayoutOptions): Record {\n\treturn {\n\t\t\"elk.algorithm\": \"layered\",\n\t\t\"elk.direction\": options.direction,\n\t\t\"elk.edgeRouting\": \"ORTHOGONAL\",\n\t\t\"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n\t\t\"elk.padding\": \"[top=40,left=40,bottom=40,right=40]\",\n\t\t\"elk.spacing.nodeNode\": String(options.nodeSpacing),\n\t\t\"elk.spacing.componentComponent\": String(options.nodeSpacing),\n\t\t\"elk.layered.spacing.nodeNodeBetweenLayers\": String(options.layerSpacing),\n\t\t\"elk.layered.spacing.edgeNodeBetweenLayers\": \"30\",\n\t\t\"elk.layered.spacing.edgeEdgeBetweenLayers\": \"20\",\n\t\t\"elk.layered.nodePlacement.strategy\": \"NETWORK_SIMPLEX\",\n\t\t\"elk.layered.nodePlacement.favorStraightEdges\": \"true\",\n\t\t\"elk.layered.crossingMinimization.strategy\": \"LAYER_SWEEP\",\n\t\t\"elk.layered.unnecessaryBendpoints\": \"true\",\n\t\t\"elk.edgeLabels.inline\": \"false\",\n\t\t\"elk.edgeLabels.avoidOverlap\": \"true\",\n\t\t\"elk.layered.edgeLabels.sideSelection\": \"SMART_DOWN\",\n\t\t\"elk.spacing.edgeLabel\": \"12\",\n\t};\n}\n\n/** groupLayoutOptions reserve title space and keep each group's children together. */\nfunction groupLayoutOptions(\n\toptions: AutomaticLayoutOptions,\n\ttitleHeight: number,\n): Record {\n\treturn {\n\t\t...rootLayoutOptions(options),\n\t\t\"elk.padding\": `[top=${titleHeight + 30},left=30,bottom=30,right=30]`,\n\t\t\"elk.nodeLabels.placement\": \"[H_LEFT,V_TOP,INSIDE]\",\n\t\t\"elk.spacing.labelNode\": \"10\",\n\t};\n}\n\n/**\n * checkMeasuredLinks rejects broken ownership and endpoints before calling ELK,\n * so ELK never has to guess what the model meant.\n */\nfunction checkMeasuredLinks(\n\tdiagram: MeasuredDiagram,\n\tnodes: Map,\n\tgroups: Map,\n): void {\n\tfor (const node of diagram.nodes) {\n\t\tif (node.parentId && !groups.has(node.parentId)) {\n\t\t\tthrow new Error(`node ${node.id} has missing parent group ${node.parentId}`);\n\t\t}\n\t}\n\tfor (const group of diagram.groups) {\n\t\tif (group.parentId && !groups.has(group.parentId)) {\n\t\t\tthrow new Error(`group ${group.id} has missing parent group ${group.parentId}`);\n\t\t}\n\t}\n\tfor (const edge of diagram.edges) {\n\t\tif (!nodes.has(edge.sourceId) || !nodes.has(edge.targetId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`edge ${edge.id} connects missing node ${edge.sourceId} or ${edge.targetId}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** lowestCommonGroup finds the nearest boundary that contains both endpoints. */\nfunction lowestCommonGroup(\n\tedge: MeasuredEdge,\n\tnodes: Map,\n\tgroups: Map,\n): string | undefined {\n\tconst sourceGroups = parentGroups(nodes.get(edge.sourceId)?.parentId, groups);\n\tconst targetGroups = new Set(parentGroups(nodes.get(edge.targetId)?.parentId, groups));\n\treturn sourceGroups.find(group => targetGroups.has(group));\n}\n\n/** parentGroups returns parent IDs from nearest to farthest. */\nfunction parentGroups(\n\tparentId: string | undefined,\n\tgroups: Map,\n): string[] {\n\tconst result: string[] = [];\n\tlet current = parentId;\n\twhile (current) {\n\t\tresult.push(current);\n\t\tcurrent = groups.get(current)?.parentId;\n\t}\n\treturn result;\n}\n\n/** readResolvedLayout changes ELK's parent-relative values into diagram positions. */\nfunction readResolvedLayout(\n\tdiagram: MeasuredDiagram,\n\troot: ElkNode,\n): ResolvedLayout {\n\tif (root.width === undefined || root.height === undefined) {\n\t\tthrow new Error(`ELK did not size diagram ${diagram.id}`);\n\t}\n\tconst nodes: ResolvedNode[] = [];\n\tconst groups: ResolvedGroup[] = [];\n\tconst edges: ResolvedEdge[] = [];\n\tconst measuredNodes = new Map(diagram.nodes.map(node => [node.id, node]));\n\tconst measuredGroups = new Map(diagram.groups.map(group => [group.id, group]));\n\n\treadContainer(\n\t\troot,\n\t\t0,\n\t\t0,\n\t\tmeasuredNodes,\n\t\tmeasuredGroups,\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t);\n\treturn {\n\t\tdiagramId: diagram.id,\n\t\tbounds: {x: 0, y: 0, width: root.width, height: root.height},\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t};\n}\n\n/**\n * readContainer walks one ELK group, adding its parent's position exactly once\n * to each child, edge point, and label box.\n */\nfunction readContainer(\n\tcontainer: ElkNode,\n\tparentX: number,\n\tparentY: number,\n\tmeasuredNodes: Map,\n\tmeasuredGroups: Map,\n\tnodes: ResolvedNode[],\n\tgroups: ResolvedGroup[],\n\tedges: ResolvedEdge[],\n): void {\n\tconst containerX = parentX + (container.x ?? 0);\n\tconst containerY = parentY + (container.y ?? 0);\n\tfor (const child of container.children ?? []) {\n\t\tconst childX = containerX + requiredNumber(child.x, child.id, \"x\");\n\t\tconst childY = containerY + requiredNumber(child.y, child.id, \"y\");\n\t\tconst width = requiredNumber(child.width, child.id, \"width\");\n\t\tconst height = requiredNumber(child.height, child.id, \"height\");\n\t\tconst measuredNode = measuredNodes.get(child.id);\n\t\tif (measuredNode) {\n\t\t\tnodes.push({\n\t\t\t\tid: child.id,\n\t\t\t\tparentId: measuredNode.parentId,\n\t\t\t\tbounds: {x: childX, y: childY, width, height},\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconst measuredGroup = measuredGroups.get(child.id);\n\t\tif (!measuredGroup) {\n\t\t\tthrow new Error(`ELK returned unknown node ${child.id}`);\n\t\t}\n\t\tconst label = requiredLabel(child.labels?.[0], child.id);\n\t\tgroups.push({\n\t\t\tid: child.id,\n\t\t\tparentId: measuredGroup.parentId,\n\t\t\tbounds: {x: childX, y: childY, width, height},\n\t\t\ttitleBounds: {\n\t\t\t\tx: childX + requiredNumber(label.x, child.id, \"title x\"),\n\t\t\t\ty: childY + requiredNumber(label.y, child.id, \"title y\"),\n\t\t\t\twidth: requiredNumber(label.width, child.id, \"title width\"),\n\t\t\t\theight: requiredNumber(label.height, child.id, \"title height\"),\n\t\t\t},\n\t\t});\n\t\treadContainer(\n\t\t\tchild,\n\t\t\tcontainerX,\n\t\t\tcontainerY,\n\t\t\tmeasuredNodes,\n\t\t\tmeasuredGroups,\n\t\t\tnodes,\n\t\t\tgroups,\n\t\t\tedges,\n\t\t);\n\t}\n\tfor (const edge of container.edges ?? []) {\n\t\tedges.push(readEdge(edge, containerX, containerY));\n\t}\n}\n\n/** readEdge copies every ELK section and label without choosing new positions. */\nfunction readEdge(edge: ElkExtendedEdge, offsetX: number, offsetY: number): ResolvedEdge {\n\tconst sections = (edge.sections ?? []).map(section => [\n\t\taddOffset(section.startPoint, offsetX, offsetY),\n\t\t...(section.bendPoints ?? []).map(point => addOffset(point, offsetX, offsetY)),\n\t\taddOffset(section.endPoint, offsetX, offsetY),\n\t]);\n\tconst label = edge.labels?.[0];\n\treturn {\n\t\tid: edge.id,\n\t\tsourceId: requiredEndpoint(edge.sources, edge.id, \"source\"),\n\t\ttargetId: requiredEndpoint(edge.targets, edge.id, \"target\"),\n\t\tsections,\n\t\tlabelBounds: label ? {\n\t\t\tx: offsetX + requiredNumber(label.x, edge.id, \"label x\"),\n\t\t\ty: offsetY + requiredNumber(label.y, edge.id, \"label y\"),\n\t\t\twidth: requiredNumber(label.width, edge.id, \"label width\"),\n\t\t\theight: requiredNumber(label.height, edge.id, \"label height\"),\n\t\t} : undefined,\n\t};\n}\n\n/** addOffset changes one parent-relative ELK point into a diagram point. */\nfunction addOffset(point: ElkPoint, offsetX: number, offsetY: number): Point {\n\treturn {x: offsetX + point.x, y: offsetY + point.y};\n}\n\n/** requiredNumber rejects incomplete ELK output instead of filling in zero. */\nfunction requiredNumber(\n\tvalue: number | undefined,\n\telementId: string,\n\tfield: string,\n): number {\n\tif (value === undefined) {\n\t\tthrow new Error(`ELK omitted ${field} for ${elementId}`);\n\t}\n\treturn value;\n}\n\n/** requiredEndpoint requires each MDL relationship to have exactly one endpoint. */\nfunction requiredEndpoint(\n\tendpoints: string[],\n\tedgeId: string,\n\tfield: string,\n): string {\n\tif (endpoints.length !== 1) {\n\t\tthrow new Error(`ELK returned ${endpoints.length} ${field}s for edge ${edgeId}`);\n\t}\n\treturn endpoints[0];\n}\n\n/** requiredLabel rejects a group whose measured title was not placed by ELK. */\nfunction requiredLabel(label: ElkLabel | undefined, groupId: string): ElkLabel {\n\tif (!label) {\n\t\tthrow new Error(`ELK omitted title for group ${groupId}`);\n\t}\n\treturn label;\n}\n\n/** append adds a child to one parent entry while keeping model order. */\nfunction append(\n\titems: Map,\n\tparentId: string | undefined,\n\titem: T,\n): void {\n\tconst siblings = items.get(parentId) ?? [];\n\tsiblings.push(item);\n\titems.set(parentId, siblings);\n}\n","// This file connects the editable graph to the layout pipeline. It measures\n// the current SVG once, asks ELK for one complete answer, validates that answer,\n// and returns positions without changing them.\n\nimport {resolveAutomatic} from \"./automatic-layout\";\nimport {\n\tMeasuredDiagram,\n\tMeasuredEdge,\n\tMeasuredGroup,\n\tMeasuredNode,\n\tRect,\n\tResolvedLayout,\n\tValidatedLayout,\n\tvalidateResolvedLayout,\n} from \"./geometry\";\nimport {Edge, GraphData, Group, LayoutDirection, Node} from \"./graph\";\nimport {calculateEdgeVertices} from \"./edge-utils\";\nimport {resolveManual} from \"./manual-layout\";\n\n/** LayoutOptions are the explicit choices available to editor and CLI callers. */\nexport interface LayoutOptions {\n\tdirection?: LayoutDirection;\n\tnodeSpacing?: number;\n\tlayerSpacing?: number;\n\tcompactLayout?: boolean;\n}\n\n/** GraphLayout carries checked positions back to the editable graph. */\nexport interface GraphLayout {\n\tvalidated: ValidatedLayout;\n\tnodes: Array<{id: string; x: number; y: number}>;\n\tgroups: Array<{id: string; bounds: Rect; titleBounds: Rect}>;\n\tedges: Array<{\n\t\tid: string;\n\t\tsections: Array>;\n\t\tlabelBounds?: Rect;\n\t}>;\n}\n\n/**\n * autoLayout measures, lays out, and validates a graph. It does not catch\n * errors because callers must not save a diagram after any failed step.\n */\nexport async function autoLayout(\n\tgraph: GraphData,\n\toptions: LayoutOptions = {},\n): Promise {\n\tawait document.fonts.ready;\n\tconst measured = measureGraph(graph);\n\tconst spacing = options.compactLayout ? 56 : 80;\n\tconst resolved = await resolveAutomatic(measured, {\n\t\tdirection: options.direction ?? graph.layoutDirection ?? \"DOWN\",\n\t\tnodeSpacing: options.nodeSpacing ?? spacing,\n\t\tlayerSpacing: options.layerSpacing ?? spacing,\n\t});\n\tconst validated = validateResolvedLayout(measured, resolved);\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\treturn {\n\t\tvalidated,\n\t\tnodes: validated.nodes.map(node => {\n\t\t\tconst measuredNode = measuredNodes.get(node.id);\n\t\t\tif (!measuredNode) {\n\t\t\t\tthrow new Error(`validated layout contains unknown node ${node.id}`);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tid: node.id,\n\t\t\t\tx: node.bounds.x + measuredNode.centerOffset.x,\n\t\t\t\ty: node.bounds.y + measuredNode.centerOffset.y,\n\t\t\t};\n\t\t}),\n\t\tgroups: validated.groups.map(group => ({\n\t\t\tid: group.id,\n\t\t\tbounds: group.bounds,\n\t\t\ttitleBounds: group.titleBounds,\n\t\t})),\n\t\tedges: validated.edges.map(edge => ({\n\t\t\tid: edge.id,\n\t\t\tsections: edge.sections,\n\t\t\tlabelBounds: edge.labelBounds,\n\t\t})),\n\t};\n}\n\n/**\n * validateCurrentLayout adapts the fully drawn editor state into the manual\n * layout contract. It never reads automatic positions to fill missing data.\n */\nexport function validateCurrentLayout(graph: GraphData): ValidatedLayout {\n\tconst measured = measureGraph(graph);\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\tconst nodes = graph.nodes().map(node => {\n\t\tconst measuredNode = measuredNodes.get(node.id);\n\t\tif (!measuredNode) {\n\t\t\tthrow new Error(`measured diagram omitted node ${node.id}`);\n\t\t}\n\t\treturn {\n\t\t\tid: node.id,\n\t\t\tparentId: measuredNode.parentId,\n\t\t\tbounds: {\n\t\t\t\tx: node.x - measuredNode.centerOffset.x,\n\t\t\t\ty: node.y - measuredNode.centerOffset.y,\n\t\t\t\t...measuredNode.size,\n\t\t\t},\n\t\t};\n\t});\n\tconst groups = Array.from(graph.groupsMap.values()).map(group => {\n\t\tif (!group.ref?.isConnected) {\n\t\t\tthrow new Error(`group ${group.id} must be drawn before saving`);\n\t\t}\n\t\tconst boundary = group.ref.querySelector(\"rect\");\n\t\tconst title = group.ref.querySelector(\"text\");\n\t\tif (!(boundary instanceof SVGGraphicsElement) || !(title instanceof SVGGraphicsElement)) {\n\t\t\tthrow new Error(`group ${group.id} is missing its boundary or title`);\n\t\t}\n\t\treturn {\n\t\t\tid: group.id,\n\t\t\tparentId: measured.groups.find(candidate => candidate.id === group.id)?.parentId,\n\t\t\tbounds: copyBox(boundary.getBBox()),\n\t\t\ttitleBounds: copyBox(title.getBBox()),\n\t\t};\n\t});\n\tconst edges = graph.edges.map(edge => {\n\t\tconst sections = edge.layoutSections ?? [calculateEdgeVertices(edge)];\n\t\tconst labelBounds = edge.layoutLabelBounds ?? edge.labelBounds;\n\t\tif (edge.label.trim().length > 0 && !labelBounds) {\n\t\t\tthrow new Error(`edge ${edge.id} is missing its label position`);\n\t\t}\n\t\treturn {\n\t\t\tid: edge.id,\n\t\t\tsourceId: edge.from.id,\n\t\t\ttargetId: edge.to.id,\n\t\t\tsections,\n\t\t\tlabelBounds: labelBounds ? {...labelBounds} : undefined,\n\t\t};\n\t});\n\tconst snapshot: ResolvedLayout = {\n\t\tdiagramId: graph.id,\n\t\tbounds: contentBounds(nodes.map(node => node.bounds), groups, edges),\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t};\n\treturn resolveManual(measured, snapshot);\n}\n\n/**\n * measureGraph reads the exact node and label boxes already drawn by the\n * browser. Parent IDs come only from the model's declared groups.\n */\nfunction measureGraph(graph: GraphData): MeasuredDiagram {\n\tconst {nodeParents, groupParents} = declaredParents(graph);\n\treturn {\n\t\tid: graph.id,\n\t\tnodes: graph.nodes().map(node => measureNode(node, nodeParents.get(node.id))),\n\t\tgroups: Array.from(graph.groupsMap.values()).map(group =>\n\t\t\tmeasureGroup(group, groupParents.get(group.id)),\n\t\t),\n\t\tedges: graph.edges.map(measureEdge),\n\t};\n}\n\n/** measureNode returns the complete box drawn for one node and its center. */\nfunction measureNode(node: Node, parentId: string | undefined): MeasuredNode {\n\tif (!node.ref?.isConnected) {\n\t\tthrow new Error(`node ${node.id} must be drawn before layout`);\n\t}\n\tconst bounds = node.ref.getBBox();\n\tcheckMeasuredBox(node.id, bounds);\n\treturn {\n\t\tid: node.id,\n\t\tparentId,\n\t\tsize: {width: bounds.width, height: bounds.height},\n\t\tcontentSize: {width: bounds.width, height: bounds.height},\n\t\tcenterOffset: {x: -bounds.x, y: -bounds.y},\n\t\tshape: node.style.shape ?? \"Box\",\n\t};\n}\n\n/** measureGroup reads the title box already drawn by the browser. */\nfunction measureGroup(group: Group, parentId: string | undefined): MeasuredGroup {\n\tif (!group.ref?.isConnected) {\n\t\tthrow new Error(`group ${group.id} must be drawn before layout`);\n\t}\n\tconst title = group.ref.querySelector(\"text\");\n\tif (!(title instanceof SVGGraphicsElement)) {\n\t\tthrow new Error(`group ${group.id} is missing its drawn title`);\n\t}\n\tconst bounds = title.getBBox();\n\tcheckMeasuredBox(group.id, bounds);\n\treturn {\n\t\tid: group.id,\n\t\tparentId,\n\t\ttitleSize: {width: bounds.width, height: bounds.height},\n\t};\n}\n\n/** measureEdge uses the label background that the current SVG already drew. */\nfunction measureEdge(edge: Edge): MeasuredEdge {\n\tconst hasLabel = edge.label.trim().length > 0;\n\tif (hasLabel && !edge.labelBounds) {\n\t\tthrow new Error(`edge ${edge.id} must be drawn before layout`);\n\t}\n\treturn {\n\t\tid: edge.id,\n\t\tsourceId: edge.from.id,\n\t\ttargetId: edge.to.id,\n\t\tlabelSize: hasLabel ? {\n\t\t\twidth: edge.labelBounds?.width ?? 0,\n\t\t\theight: edge.labelBounds?.height ?? 0,\n\t\t} : undefined,\n\t};\n}\n\n/**\n * declaredParents records model ownership and rejects elements that appear in\n * more than one group. Visual position never decides ownership.\n */\nfunction declaredParents(graph: GraphData): {\n\tnodeParents: Map;\n\tgroupParents: Map;\n} {\n\tconst nodeParents = new Map();\n\tconst groupParents = new Map();\n\tfor (const group of graph.groupsMap.values()) {\n\t\tfor (const member of group.nodes) {\n\t\t\tconst parents = isGroup(member) ? groupParents : nodeParents;\n\t\t\tconst existing = parents.get(member.id);\n\t\t\tif (existing && existing !== group.id) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`${isGroup(member) ? \"group\" : \"node\"} ${member.id} belongs to both ` +\n\t\t\t\t\t`${existing} and ${group.id}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tparents.set(member.id, group.id);\n\t\t}\n\t}\n\treturn {nodeParents, groupParents};\n}\n\n/** checkMeasuredBox rejects missing or invalid browser measurements. */\nfunction checkMeasuredBox(id: string, box: Rect): void {\n\tif (\n\t\t!Number.isFinite(box.x) ||\n\t\t!Number.isFinite(box.y) ||\n\t\t!Number.isFinite(box.width) ||\n\t\t!Number.isFinite(box.height) ||\n\t\tbox.width <= 0 ||\n\t\tbox.height <= 0\n\t) {\n\t\tthrow new Error(`browser returned invalid measured box for ${id}`);\n\t}\n}\n\n/** contentBounds returns the smallest box that contains all drawn geometry. */\nfunction contentBounds(\n\tnodes: Rect[],\n\tgroups: Array<{bounds: Rect; titleBounds: Rect}>,\n\tedges: Array<{sections: Array>; labelBounds?: Rect}>,\n): Rect {\n\tconst boxes = [\n\t\t...nodes,\n\t\t...groups.flatMap(group => [group.bounds, group.titleBounds]),\n\t\t...edges.flatMap(edge => edge.labelBounds ? [edge.labelBounds] : []),\n\t];\n\tconst points = edges.flatMap(edge => edge.sections.flat());\n\tif (boxes.length === 0 && points.length === 0) {\n\t\treturn {x: 0, y: 0, width: 0, height: 0};\n\t}\n\tconst left = Math.min(\n\t\t...boxes.map(box => box.x),\n\t\t...points.map(point => point.x),\n\t);\n\tconst top = Math.min(\n\t\t...boxes.map(box => box.y),\n\t\t...points.map(point => point.y),\n\t);\n\tconst right = Math.max(\n\t\t...boxes.map(box => box.x + box.width),\n\t\t...points.map(point => point.x),\n\t);\n\tconst bottom = Math.max(\n\t\t...boxes.map(box => box.y + box.height),\n\t\t...points.map(point => point.y),\n\t);\n\treturn {x: left, y: top, width: right - left, height: bottom - top};\n}\n\n/** copyBox copies the browser's read-only SVG box into the layout contract. */\nfunction copyBox(box: DOMRect): Rect {\n\treturn {x: box.x, y: box.y, width: box.width, height: box.height};\n}\n\n/** isGroup tells group boundaries from nodes without using display names. */\nfunction isGroup(member: Node | Group): member is Group {\n\treturn \"nodes\" in member;\n}\n","// Manual layout has its own entry point. It never fills missing positions from\n// automatic layout and never changes the positions a user authored.\n\nimport {\n\tMeasuredDiagram,\n\tResolvedLayout,\n\tValidatedLayout,\n\tvalidateResolvedLayout,\n} from \"./geometry\";\n\n/**\n * resolveManual checks one complete saved layout against the current measured\n * diagram. Missing or old IDs, incomplete routes, and collisions are errors.\n */\nexport function resolveManual(\n\tmeasured: MeasuredDiagram,\n\tcompleteSnapshot: ResolvedLayout,\n): ValidatedLayout {\n\treturn validateResolvedLayout(measured, completeSnapshot);\n}\n","import {GraphData, LayoutDirection, NodeLink} from \"./graph-view/graph\";\n\n\ninterface Model {\n\tname: string\n\tdescription: string\n\tversion: string\n\tmodel: {\n\t\tenterprise: {\n\t\t\tname: string\n\t\t}\n\t\tpeople: Element[]\n\t\tsoftwareSystems: Element[]\n\t\tdeploymentNodes: Element[]\n\t}\n\tviews: {\n\t\tsystemLandscapeViews: View[]\n\t\tcontainerViews: View[]\n\t\tcomponentViews: View[]\n\t\tdynamicViews: View[]\n\t\tdeploymentViews: View[]\n\t\tstyles: {\n\t\t\telements: {\n\t\t\t\t[key: string]: string\n\t\t\t}[];\n\t\t\trelationships: {\n\t\t\t\t[key: string]: string\n\t\t\t}[]\n\t\t}\n\t}\n}\n\ninterface Layouts {\n\t[key: string]: { // keyed by view key\n\t\t[key: string]: { x: number; y: number } // keyed by element id\n\t}\n}\n\ninterface Element {\n\tid: string;\n\tname: string;\n\ttechnology?: string;\n\tdescription?: string;\n\turl?: string;\n\tparent?: Element;\n\ttags?: string;\n\tlocation?: string;\n\tcontainers?: Element[];\n\tcomponents?: Element[];\n\trelationships?: Relation[];\n\tproperties?: { [key: string]: string }\n\tchildren?: Element[];\n\tinfrastructureNodes?: Element[];\n}\n\ninterface Relation {\n\tid: string;\n\tdescription: string;\n\ttags: string;\n\tsourceId: string;\n\tdestinationId: string;\n\ttechnology: string;\n\tinteractionStyle: string;\n}\n\ntype RankDirection = 'TopBottom' | 'BottomTop' | 'LeftRight' | 'RightLeft';\n\ninterface View {\n\tkey: string;\n\ttitle: string;\n\tdescription: string\n\tautomaticLayout?: {\n\t\trankDirection?: RankDirection;\n\t};\n\telements: {\n\t\tid: string\n\t}[];\n\trelationships: {\n\t\tid: string;\n\t\tvertices: { x: number; y: number }[];\n\t\trouting: string; // takes priority over style\n\t}[];\n\tsoftwareSystemId: string;\n}\n\nconst layoutDirections: Record = {\n\tTopBottom: 'DOWN',\n\tBottomTop: 'UP',\n\tLeftRight: 'RIGHT',\n\tRightLeft: 'LEFT',\n};\n\ninterface Metadata {\n\tname: string\n\tdescription: string\n\tversion: string\n\telements: {\n\t\tid: string\n\t\ttags?: string;\n\t\tlocation?: string;\n\t\tproperties?: { [key: string]: string };\n\t\telementViewKey?: string;\n\t\ttechnology?: string;\n\t\turl?: string;\n\t}[]\n}\n\nexport type ViewsList = {\n\tkey: string;\n\ttitle: string;\n\tsection: string;\n}[]\n\nexport const parseView = (model: Model, layouts: Layouts, viewKey: string) => {\n\n\tconst elements = new Map();\n\tconst relations = new Map();\n\n\tconst collectRels = (el: Element) => {\n\t\tif (Array.isArray(el.relationships)) {\n\t\t\tel.relationships.forEach(rel => {\n\t\t\t\trelations.set(rel.id, rel)\n\t\t\t})\n\t\t}\n\t}\n\n\t// People\n\tmodel.model.people && model.model.people.forEach((el: Element) => {\n\t\telements.set(el.id, el)\n\t\tif (Array.isArray(el.relationships)) {\n\t\t\tel.relationships.forEach(rel => {\n\t\t\t\trelations.set(rel.id, rel)\n\t\t\t})\n\t\t}\n\t})\n\t// Software Systems\n\tmodel.model.softwareSystems && model.model.softwareSystems.forEach((el: Element) => {\n\t\telements.set(el.id, el)\n\t\tcollectRels(el)\n\n\t\tif (Array.isArray(el.containers)) {\n\t\t\tel.containers.forEach((el1: Element) => {\n\t\t\t\tel1.parent = el;\n\t\t\t\telements.set(el1.id, el1)\n\t\t\t\tcollectRels(el1)\n\t\t\t\tif (Array.isArray(el1.components)) {\n\t\t\t\t\tel1.components.forEach((el2: Element) => {\n\t\t\t\t\t\tel2.parent = el1;\n\t\t\t\t\t\telements.set(el2.id, el2)\n\t\t\t\t\t\tcollectRels(el2)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\t// Deployment Nodes\n\tif (model.model.deploymentNodes) {\n\t\tconst containerInstances = (el: any) => {\n\t\t\tel.containerInstances && el.containerInstances.forEach((item: any) => {\n\t\t\t\tconst el1 = {...elements.get(item.containerId), id: item.id}\n\t\t\t\telements.set(el1.id, el1)\n\t\t\t\tel1.parent = el\n\t\t\t\tcollectRels(item)\n\t\t\t})\n\t\t}\n\n\t\tconst recAddNodes = (el: Element, parent: Element) => {\n\t\t\tel.parent = parent;\n\t\t\telements.set(el.id, el)\n\t\t\tcollectRels(el)\n\t\t\tcontainerInstances(el)\n\t\t\tel.children && el.children.forEach((el1: Element) => recAddNodes(el1, el))\n\t\t\tel.infrastructureNodes && el.infrastructureNodes.forEach((el1: Element) => recAddNodes(el1, el))\n\t\t}\n\n\t\tmodel.model.deploymentNodes.forEach((el: Element) => recAddNodes(el, null))\n\t}\n\n\t// Create graph from selected view\n\tconst {view, section} = getView(model, viewKey)\n\n\tif (!view) return null\n\n\tconst graph = new GraphData(view.key, view.title || view.key)\n\tconst rankDirection = view.automaticLayout?.rankDirection\n\tgraph.layoutDirection = rankDirection ? layoutDirections[rankDirection] : undefined\n\tconst metadata: Metadata = {name: graph.name, description: view.description, version: model.version, elements: []}\n\tgraph.metadata = metadata\n\n\tif (!view.elements) return graph\n\n\t//grouping rules - elements that are groups will not be nodes\n\tconst groupingIDs: { [key: string]: boolean } = {}\n\tif (section == 'deploymentViews' || section == 'containerViews') {\n\t\tview.elements.forEach(ref => {\n\t\t\tconst el = elements.get(ref.id)\n\t\t\tif (el?.parent) {\n\t\t\t\tgroupingIDs[el.parent.id] = true\n\t\t\t}\n\t\t})\n\t} else if (view.softwareSystemId) {\n\t\t//don't show grouping if the element is listed in the view\n\t\tif (!view.elements.find(ref => ref.id == view.softwareSystemId))\n\t\t\tgroupingIDs[view.softwareSystemId] = true\n\t} else if (section == 'systemLandscapeViews') {\n\t\t// create a virtual parent element from enterprise\n\t\tconst p: Element = {id: '__enterprise__', ...model.model.enterprise}\n\t\telements.set(p.id, p)\n\t\tif (model.model.people) model.model.people.filter(el => el.location != 'External').forEach(el => el.parent = p)\n\t\tif (model.model.softwareSystems) model.model.softwareSystems.filter(el => el.location != 'External').forEach(el => el.parent = p)\n\t\tgroupingIDs[p.id] = true\n\t}\n\n\tconst styles = model.views.styles\n\n\t// Build color-to-variable mapping for CSS custom properties theming\n\tconst cssClassName = (tag: string) => tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n\t\n\tif (styles?.elements) {\n\t\tstyles.elements.forEach(s => {\n\t\t\tif (s.tag) {\n\t\t\t\tconst varPrefix = `--mdl-${cssClassName(s.tag)}`\n\t\t\t\tif (s.background) graph.colorToVarMap.set(s.background as string, `${varPrefix}-bg`)\n\t\t\t\tif (s.color) graph.colorToVarMap.set(s.color as string, `${varPrefix}-color`)\n\t\t\t\tif (s.stroke) graph.colorToVarMap.set(s.stroke as string, `${varPrefix}-stroke`)\n\t\t\t}\n\t\t})\n\t}\n\n\tif (styles?.relationships) {\n\t\tstyles.relationships.forEach(s => {\n\t\t\tif (s.tag) {\n\t\t\t\tconst varPrefix = `--mdl-rel-${cssClassName(s.tag)}`\n\t\t\t\tif (s.color) graph.colorToVarMap.set(s.color as string, `${varPrefix}-color`)\n\t\t\t}\n\t\t})\n\t}\n\n\t//nodes\n\tview.elements.forEach((ref) => {\n\t\t// except grouping elements\n\t\tif (groupingIDs[ref.id]) return\n\n\t\tconst el = elements.get(ref.id)\n\t\tconst elementViewKey = el ? lookupContainerViewKey(model, el.id) : undefined\n\n\t\tlet sub = ''\n\t\tlet style = {}\n\t\tif (el) {\n\t\t\tconst tags = el.tags.split(',')\n\t\t\tsub = tags[tags.length - 1] // subtitle is []\n\t\t\tif (el.technology)\n\t\t\t\tsub += ': ' + el.technology // or [: ]\n\n\t\t\ttags.forEach(tag => {\n\t\t\t\tconst s = styles && styles.elements && styles.elements.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t}\n\n\t\tgraph.addNode(\n\t\t\tref.id,\n\t\t\tel ? (el.name || ref.id) : ref.id,\n\t\t\tsub,\n\t\t\t(el && el.description) ? el.description : '',\n\t\t\tstyle,\n\t\t\tnodeLink(el, elementViewKey)\n\t\t)\n\t\tel && metadata.elements.push({\n\t\t\tid: el.id,\n\t\t\ttags: el.tags,\n\t\t\tlocation: el.location,\n\t\t\tproperties: el.properties,\n\t\t\telementViewKey,\n\t\t\ttechnology: el.technology,\n\t\t\turl: el.url\n\t\t})\n\t})\n\t//edges\n\tif (Array.isArray(view.relationships)) {\n\t\tview.relationships.forEach(ref => {\n\t\t\tconst rel = relations.get(ref.id)\n\t\t\tif (!rel) return;\n\n\t\t\tif (!graph.nodesMap.has(rel.sourceId)) {\n\t\t\t\tif (elements.has(rel.sourceId)) {\n\t\t\t\t\tconst el = elements.get(rel.sourceId)\n\t\t\t\t\tconsole.warn('Element not found in this view: ', el.id, el.name)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('Element not found: ', rel.sourceId)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!graph.nodesMap.has(rel.destinationId)) {\n\t\t\t\tif (elements.has(rel.destinationId)) {\n\t\t\t\t\tconst el = elements.get(rel.destinationId)\n\t\t\t\t\tconsole.warn('Element not found in this view: ', el.id, el.name)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('Element not found: ', rel.destinationId)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet style: any = {}\n\t\t\trel.tags.split(',').forEach(tag => {\n\t\t\t\tconst s = styles && styles.relationships && styles.relationships.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t\tif (ref.routing) style.routing = ref.routing\n\n\t\t\tgraph.addEdge(rel.id, rel.sourceId, rel.destinationId, rel.description, ref.vertices, style)\n\t\t})\n\t}\n\n\t//groups\n\t//sort by depth to solve dependency\n\tconst level = (el: Element) => {\n\t\tlet i = 0\n\t\tfor (let p = el.parent; p; p = p.parent) i++;\n\t\treturn i\n\t}\n\tconst gElements = Object.keys(groupingIDs)\n\t\t.map(id => elements.get(id))\n\t\t.sort((a, b) => level(a) > level(b) ? -1 : 1)\n\n\tgElements.forEach(parent => {\n\t\tlet style = {}\n\t\tif (section == 'deploymentViews') {\n\t\t\tconst el = elements.get(parent.id)\n\t\t\tconst tags = el.tags.split(',')\n\t\t\ttags.forEach(tag => {\n\t\t\t\tconst s = styles && styles.elements && styles.elements.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t}\n\t\t\n\t\t// Filter group members more carefully to respect boundaries\n\t\tconst groupMembers = view.elements\n\t\t\t.map(ref => elements.get(ref.id))\n\t\t\t.filter(el => {\n\t\t\t\tif (!el || el.parent !== parent) return false;\n\t\t\t\t\n\t\t\t\t// For system landscape views, respect the location-based grouping\n\t\t\t\tif (section === 'systemLandscapeViews' && parent.id === '__enterprise__') {\n\t\t\t\t\t// Only include elements that are explicitly non-external\n\t\t\t\t\treturn el.location !== 'External';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// For other view types, include if parent matches\n\t\t\t\treturn true;\n\t\t\t})\n\t\t\t.map(el => el.id);\n\t\t\n\t\t// Only create group if it has members\n\t\tif (groupMembers.length > 0) {\n\t\t\tgraph.addGroup(\n\t\t\t\tparent.id,\n\t\t\t\tparent.name,\n\t\t\t\tgroupMembers,\n\t\t\t\tstyle\n\t\t\t)\n\t\t}\n\t})\n\n\t//layout if any and init graph\n\tgraph.init(layouts[graph.id])\n\treturn graph\n}\n\nfunction nodeLink(element: Element | undefined, elementViewKey: string | undefined): NodeLink | undefined {\n\tif (elementViewKey) {\n\t\tconst encodedViewKey = encodeURIComponent(elementViewKey)\n\t\treturn {\n\t\t\thref: `?id=${encodedViewKey}`,\n\t\t\texportHref: `${encodedViewKey}.svg`,\n\t\t}\n\t}\n\tif (element?.url) {\n\t\treturn {\n\t\t\thref: element.url,\n\t\t\texportHref: element.url,\n\t\t}\n\t}\n\treturn undefined\n}\n\n// lookup the view in all Views sections in the model. return the view and the section\nfunction getView(model: Model, viewKey: string) {\n\tlet view: View = null, section: string = ''\n\tObject.keys(model.views).filter(s => s.endsWith('Views')).some((s: string) => {\n\t\treturn ((model.views as any)[s]).some((v: View) => {\n\t\t\tif (v.key == viewKey) {\n\t\t\t\tview = v\n\t\t\t\tsection = s\n\t\t\t\treturn true\n\t\t\t}\n\t\t})\n\t})\n\treturn {view, section}\n}\n\n\nfunction lookupContainerViewKey(model: Model, softwareSystemId: string) {\n\tconst view = model.views.containerViews?.find(candidate => candidate.softwareSystemId == softwareSystemId)\n\treturn view?.key\n}\n\nexport const listViews = (model: any) => {\n\tconst viewsList: ViewsList = []\n\tconst sections = Object.keys(model.views).filter(section => section.endsWith('Views'))\n\tsections.forEach(s => {\n\t\tmodel.views[s].forEach((v: View) => {\n\t\t\tviewsList.push({key: v.key, title: v.title || v.key, section: s})\n\t\t})\n\t})\n\treturn viewsList;\n}\n","import React, {FC} from \"react\";\nimport { isMac, getModifierKeyName, getModifierKeyProperty } from './utils/platform';\n\ninterface Combination {\n\tctrl?: boolean;\n\tshift?: boolean;\n\talt?: boolean;\n\twheel?: boolean\n\tkey?: string;\n\tclick?: boolean;\n}\n\ninterface Shortcut {\n\tid: string,\n\thelp: string,\n\tcombinations: Combination[]\n}\n\nexport const SAVE = 'save'\n\nexport const UNDO = 'undo'\nexport const REDO = 'redo'\nexport const ADD_VERTEX = 'add-vertex'\nexport const ADD_LABEL_VERTEX = 'add-label-vertex'\nexport const DEL_VERTEX = 'del-vertex'\n\nexport const ZOOM_IN = 'zoom-in'\nexport const ZOOM_OUT = 'zoom-out'\nexport const ZOOM_FIT = 'zoom-fit'\nexport const ZOOM_100 = 'zoom-100'\n\nexport const SELECT_ALL = 'select-all'\nexport const DESELECT = 'deselect'\n\nexport const MOVE_LEFT = 'move-left'\nexport const MOVE_RIGHT = 'move-right'\nexport const MOVE_UP = 'move-up'\nexport const MOVE_DOWN = 'move-down'\nexport const MOVE_LEFT_FINE = 'move-left-fine'\nexport const MOVE_RIGHT_FINE = 'move-right-fine'\nexport const MOVE_UP_FINE = 'move-up-fine'\nexport const MOVE_DOWN_FINE = 'move-down-fine'\n\nexport const PAN_VIEW = 'pan-view'\nexport const SELECT_ELEMENT = 'select-element'\nexport const MULTI_SELECT = 'multi-select'\nexport const BOX_SELECT = 'box-select'\nexport const MOVE_ELEMENTS = 'move-elements'\n\nexport const HELP = 'help'\n\n// New shortcuts for toolbar buttons\nexport const TOGGLE_DRAG_MODE = 'toggle_drag_mode'\nexport const ALIGN_HORIZONTAL = 'align_horizontal'\nexport const ALIGN_VERTICAL = 'align_vertical'\nexport const DISTRIBUTE_HORIZONTAL = 'distribute_horizontal'\nexport const DISTRIBUTE_VERTICAL = 'distribute_vertical'\nexport const AUTO_LAYOUT = 'auto_layout'\nexport const RESET_POSITION = 'reset_position'\nexport const TOGGLE_GRID = 'toggle_grid'\nexport const TOGGLE_SNAP_TO_GRID = 'toggle_snap_to_grid'\nexport const SNAP_ALL_TO_GRID = 'snap_all_to_grid'\n\nconst shortcuts: { name: string; list: Shortcut[] }[] = [\n\t{\n\t\tname: 'Help',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: HELP,\n\t\t\t\thelp: 'Show/hide this help',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{key: '?', shift: true},\n\t\t\t\t\t{key: 'F1', shift: true}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'File',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: SAVE,\n\t\t\t\thelp: 'Save',\n\t\t\t\tcombinations: [{key: 's', ctrl: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'History',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: UNDO,\n\t\t\t\thelp: 'Undo',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: 'z'},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: REDO,\n\t\t\t\thelp: 'Redo',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, shift: true, key: 'z'},\n\t\t\t\t\t{ctrl: true, key: 'y'},\n\t\t\t\t]\n\t\t\t}\n\n\t\t],\n\t},\n\t{\n\t\tname: 'Relationship editing',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ADD_VERTEX,\n\t\t\t\thelp: 'Add relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{alt: true, click: true},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ADD_LABEL_VERTEX,\n\t\t\t\thelp: 'Add label anchor relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{alt: true, shift: true, click: true},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DEL_VERTEX,\n\t\t\t\thelp: 'Remove relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{key: 'DELETE'},\n\t\t\t\t\t{key: 'BACKSPACE'}\n\t\t\t\t]\n\t\t\t},\n\t\t]\n\t},\n\t{\n\t\tname: 'Zoom',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ZOOM_IN,\n\t\t\t\thelp: 'Zoom in',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: '='}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_OUT,\n\t\t\t\thelp: 'Zoom out',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: '-'}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_FIT,\n\t\t\t\thelp: 'Zoom - fit',\n\t\t\t\tcombinations: [{ctrl: true, key: '9'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_100,\n\t\t\t\thelp: 'Zoom 100%',\n\t\t\t\tcombinations: [{ctrl: true, key: '0'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'wheel_zoom',\n\t\t\t\thelp: 'Zoom in/out with mouse wheel',\n\t\t\t\tcombinations: [{wheel: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Mouse Interactions',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: PAN_VIEW,\n\t\t\t\thelp: 'Pan view (drag empty space)',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: SELECT_ELEMENT,\n\t\t\t\thelp: 'Select element',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MULTI_SELECT,\n\t\t\t\thelp: 'Add/remove from selection',\n\t\t\t\tcombinations: [{shift: true, click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: BOX_SELECT,\n\t\t\t\thelp: 'Box selection (drag empty space)',\n\t\t\t\tcombinations: [{shift: true, click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_ELEMENTS,\n\t\t\t\thelp: 'Move selected elements',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Select',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: SELECT_ALL,\n\t\t\t\thelp: 'Select All',\n\t\t\t\tcombinations: [{ctrl: true, key: 'a'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DESELECT,\n\t\t\t\thelp: 'Deselect',\n\t\t\t\tcombinations: [{key: 'ESC'}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Move',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: MOVE_UP,\n\t\t\t\thelp: 'Move up (grid increment)',\n\t\t\t\tcombinations: [{key: 'UP'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_UP_FINE,\n\t\t\t\thelp: 'Move up (1 pixel)',\n\t\t\t\tcombinations: [{key: 'UP', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_RIGHT,\n\t\t\t\thelp: 'Move right (grid increment)',\n\t\t\t\tcombinations: [{key: 'RIGHT'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_RIGHT_FINE,\n\t\t\t\thelp: 'Move right (1 pixel)',\n\t\t\t\tcombinations: [{key: 'RIGHT', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_DOWN,\n\t\t\t\thelp: 'Move down (grid increment)',\n\t\t\t\tcombinations: [{key: 'DOWN'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_DOWN_FINE,\n\t\t\t\thelp: 'Move down (1 pixel)',\n\t\t\t\tcombinations: [{key: 'DOWN', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_LEFT,\n\t\t\t\thelp: 'Move left (grid increment)',\n\t\t\t\tcombinations: [{key: 'LEFT'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_LEFT_FINE,\n\t\t\t\thelp: 'Move left (1 pixel)',\n\t\t\t\tcombinations: [{key: 'LEFT', shift: true}]\n\t\t\t},\n\t\t]\n\t},\n\t\t\t{\n\t\t\tname: 'View',\n\t\t\tlist: [\n\t\t\t\t{\n\t\t\t\t\tid: TOGGLE_DRAG_MODE,\n\t\t\t\t\thelp: 'Toggle between pan and select mode',\n\t\t\t\t\tcombinations: [{key: 't'}]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: RESET_POSITION,\n\t\t\t\t\thelp: 'Reset position and view',\n\t\t\t\t\tcombinations: [{key: 'Home', ctrl: true}]\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t{\n\t\tname: 'Alignment',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ALIGN_HORIZONTAL,\n\t\t\t\thelp: 'Align selected elements horizontally',\n\t\t\t\tcombinations: [{key: 'h', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ALIGN_VERTICAL,\n\t\t\t\thelp: 'Align selected elements vertically',\n\t\t\t\tcombinations: [{key: 'a', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DISTRIBUTE_HORIZONTAL,\n\t\t\t\thelp: 'Distribute selected elements horizontally',\n\t\t\t\tcombinations: [{key: 'h', ctrl: true, alt: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DISTRIBUTE_VERTICAL,\n\t\t\t\thelp: 'Distribute selected elements vertically',\n\t\t\t\tcombinations: [{key: 'v', ctrl: true, alt: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Layout',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: AUTO_LAYOUT,\n\t\t\t\thelp: 'Auto layout all elements',\n\t\t\t\tcombinations: [{key: 'l', ctrl: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Grid',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: TOGGLE_GRID,\n\t\t\t\thelp: 'Toggle grid visibility',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: TOGGLE_SNAP_TO_GRID,\n\t\t\t\thelp: 'Toggle snap to grid',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: SNAP_ALL_TO_GRID,\n\t\t\t\thelp: 'Snap all elements to grid',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true, alt: true}]\n\t\t\t}\n\t\t]\n\t}\n]\n\nconst shortcutMap = shortcuts\n\t.reduce((lst, s) => lst.concat(s.list), [] as Shortcut[])\n\t.reduce<{ [k: string]: Shortcut }>((map, s) => {\n\t\tmap[s.id] = s;\n\t\treturn map\n\t}, {})\n\nconst checkKey = (e: KeyboardEvent | MouseEvent, shortcut: Shortcut, click: boolean, wheel: boolean) => {\n\treturn shortcut.combinations.some(c => {\n\t\tif (Boolean(c.shift) != e.shiftKey) return false\n\t\t// Use platform-appropriate modifier key\n\t\tif (c.ctrl) {\n\t\t\tconst modifierKey = getModifierKeyProperty(e as KeyboardEvent);\n\t\t\tif (!modifierKey) return false;\n\t\t}\n\t\tif (Boolean(c.alt) != e.altKey) return false\n\t\tif (click) return c.click\n\t\tif (wheel) return c.wheel\n\t\tif (c.key) {\n\t\t\tconst ke = e as KeyboardEvent\n\t\t\tif (c.key == 'DELETE') return ke.key == 'Delete'\n\t\t\tif (c.key == 'BACKSPACE') return ke.key == 'Backspace'\n\t\t\tif (c.key == 'ESC') return ke.key == 'Escape'\n\t\t\tif (c.key == 'UP') return ke.key == 'ArrowUp'\n\t\t\tif (c.key == 'DOWN') return ke.key == 'ArrowDown'\n\t\t\tif (c.key == 'LEFT') return ke.key == 'ArrowLeft'\n\t\t\tif (c.key == 'RIGHT') return ke.key == 'ArrowRight'\n\t\t\treturn c.key && ke.key && c.key.toLowerCase() == ke.key.toLowerCase()\n\t\t}\n\t\treturn false\n\t})\n}\n\nexport const findShortcut = (e: KeyboardEvent | MouseEvent, click = false, wheel = false) => {\n\t// Find all matching shortcuts\n\tconst matches = Object.keys(shortcutMap).filter(k => checkKey(e, shortcutMap[k], click, wheel))\n\t\n\tif (matches.length === 0) return undefined\n\tif (matches.length === 1) return matches[0]\n\t\n\t// If multiple matches, prefer the one with more specific modifiers\n\t// Sort by number of modifiers (shift, ctrl, alt) in descending order\n\tconst sortedMatches = matches.sort((a, b) => {\n\t\tconst aShortcut = shortcutMap[a]\n\t\tconst bShortcut = shortcutMap[b]\n\t\t\n\t\tconst aModifiers = aShortcut.combinations[0]\n\t\tconst bModifiers = bShortcut.combinations[0]\n\t\t\n\t\tconst aCount = (aModifiers.shift ? 1 : 0) + (aModifiers.ctrl ? 1 : 0) + (aModifiers.alt ? 1 : 0)\n\t\tconst bCount = (bModifiers.shift ? 1 : 0) + (bModifiers.ctrl ? 1 : 0) + (bModifiers.alt ? 1 : 0)\n\t\t\n\t\treturn bCount - aCount // Descending order (more modifiers first)\n\t})\n\t\n\treturn sortedMatches[0]\n}\n\nconst comboText = (c: Combination) => {\n\treturn [\n\t\tc.ctrl && getModifierKeyName().toUpperCase(),\n\t\tc.shift && 'SHIFT',\n\t\tc.alt && 'ALT',\n\t\tc.key && (c.key.length > 1 ? c.key : `\"${c.key.toUpperCase()}\"`),\n\t\tc.click && 'CLICK',\n\t\tc.wheel && 'WHEEL'\n\t].filter(Boolean).join(' + ')\n}\n\nexport const Help: FC = () => {\n\treturn
\n\t\t

Shortcuts

\n\t\t\n\t\t\t\n\t\t\t{\n\t\t\t\tshortcuts.map(section => <>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{section.list.map(item => \n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t)\n\t\t\t}\n\t\t\t\n\t\t
{section.name}
{item.combinations.map(comboText).join(', ')}{item.help}
\n\t
\n}\n","/**\n * Robust platform detection utilities\n */\n\n/**\n * Detects if the user is on a Mac platform using multiple detection methods\n * for maximum compatibility across browsers and future-proofing.\n */\nexport const isMac = (): boolean => {\n if (typeof navigator === 'undefined') return false;\n \n // Method 1: Check userAgentData (modern browsers, most reliable)\n if ('userAgentData' in navigator && (navigator as any).userAgentData) {\n const platform = (navigator as any).userAgentData.platform;\n if (platform && platform.toLowerCase().includes('mac')) {\n return true;\n }\n }\n \n // Method 2: Check userAgent string (widely supported)\n const userAgent = navigator.userAgent.toLowerCase();\n if (userAgent.includes('mac os') || userAgent.includes('macintosh')) {\n return true;\n }\n \n // Method 3: Check platform (fallback, deprecated but still widely supported)\n if (navigator.platform) {\n const platform = navigator.platform.toLowerCase();\n if (platform.includes('mac') || platform.includes('darwin')) {\n return true;\n }\n }\n \n // Method 4: Check for Mac-specific features as additional validation\n try {\n const testEvent = new KeyboardEvent('keydown', { metaKey: true });\n if (testEvent.metaKey !== undefined) {\n // Additional heuristic: Mac typically has different key layouts\n return /mac|darwin|os x/i.test(navigator.userAgent);\n }\n } catch (e) {\n // Ignore errors in older browsers\n }\n \n return false;\n};\n\n/**\n * Gets the appropriate modifier key name for the current platform\n */\nexport const getModifierKeyName = (): string => {\n return isMac() ? 'Cmd' : 'Ctrl';\n};\n\n/**\n * Gets the appropriate modifier key property for keyboard events\n */\nexport const getModifierKeyProperty = (event: KeyboardEvent): boolean => {\n return isMac() ? event.metaKey : event.ctrlKey;\n};\n\n/**\n * Gets the appropriate Alt key name for the current platform\n */\nexport const getAltKeyName = (): string => {\n return isMac() ? 'Option' : 'Alt';\n}; ","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/api.js\";\nimport ___CSS_LOADER_GET_URL_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/getUrl.js\";\nvar ___CSS_LOADER_URL_IMPORT_0___ = new URL(\"@fontsource/inter/files/inter-latin-400-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_1___ = new URL(\"@fontsource/inter/files/inter-latin-500-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_2___ = new URL(\"@fontsource/inter/files/inter-latin-600-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_3___ = new URL(\"@fontsource/inter/files/inter-latin-700-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\nvar ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);\nvar ___CSS_LOADER_URL_REPLACEMENT_1___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_1___);\nvar ___CSS_LOADER_URL_REPLACEMENT_2___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_2___);\nvar ___CSS_LOADER_URL_REPLACEMENT_3___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_3___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_0___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_1___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_2___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_3___}) format(\"woff2\");\n}\n`, \"\",{\"version\":3,\"sources\":[\"webpack://./src/fonts.css\"],\"names\":[],\"mappings\":\"AAAA,uFAAuF;AACvF;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF\",\"sourcesContent\":[\"/* Local Inter files keep browser text measurement identical without internet access. */\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 400;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-400-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 500;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-500-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 600;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-600-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 700;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-700-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}`, \"\",{\"version\":3,\"sources\":[\"webpack://./src/style.css\"],\"names\":[],\"mappings\":\"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\",\"sourcesContent\":[\"html, body, #root {\\n height: 100%;\\n}\\n\\nbody {\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\\n color: #666;\\n margin: 0;\\n}\\n\\n#root {\\n display: flex;\\n flex-direction: column;\\n}\\n#root > div.graph {\\n flex: 1;\\n overflow: auto;\\n position: relative;\\n}\\n\\n.toolbar {\\n display: flex;\\n justify-content: space-between;\\n align-items: center;\\n padding: 4px 10px;\\n background-color: #f0f0f0;\\n border-bottom: 1px solid #cccccc;\\n}\\n\\n.toolbar > div {\\n display: flex;\\n align-items: center;\\n}\\n\\n.toolbar button {\\n padding: 5px 8px;\\n margin: 0 2px;\\n cursor: pointer;\\n}\\n\\n.toolbar button:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n\\n/* Drag mode toggle button styles */\\n.toolbar button.mode-toggle {\\n position: relative;\\n border: 1px solid #8f9fc9;\\n width: 40px;\\n min-height: 28px;\\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\\n border-color: #8f9fc9;\\n color: white;\\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\\n}\\n\\n.toolbar button.mode-toggle:hover {\\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\\n border-color: #abb8db;\\n}\\n\\n.toolbar button.mode-toggle.select-mode:active {\\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\\n}\\n\\n/* Pan mode and active toggle - darker blue */\\n.toolbar button.mode-toggle.pan-mode,\\n.toolbar button.active-toggle {\\n background: linear-gradient(to bottom, #4a90e2, #357abd);\\n border-color: #2968a3;\\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\\n}\\n\\n.toolbar button.mode-toggle.pan-mode:hover,\\n.toolbar button.active-toggle:hover {\\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\\n}\\n\\n.toolbar button.mode-toggle.pan-mode:active {\\n background: linear-gradient(to bottom, #357abd, #2968a3);\\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\\n}\\n\\n/* Toggle buttons when inactive - gray styling */\\n.toolbar button.inactive-toggle {\\n background: #c1c1c1;\\n color: #999;\\n border-color: #c1c1c1;\\n box-shadow: none;\\n}\\n\\n.toolbar button.inactive-toggle:hover {\\n background: #b1b1b1;\\n}\\n\\n.toolbar button.inactive-toggle:active {\\n background: #a1a1a1;\\n}\\n\\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\\n background: #c1c1c1;\\n color: #999;\\n border-color: #c1c1c1;\\n box-shadow: none;\\n}\\n\\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\\n.toolbar button .fas {\\n font-size: 1em;\\n vertical-align: middle;\\n}\\n\\n/* Zoom percentage display */\\n.toolbar button.zoom-display {\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\\n font-size: 11px;\\n font-weight: 600;\\n font-variant-numeric: tabular-nums;\\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\\n padding: 6px 8px;\\n text-align: center;\\n}\\n\\n.toolbar-group {\\n display: flex;\\n align-items: center;\\n margin-right: 25px; /* Large space between groups */\\n}\\n\\n.toolbar-group:last-child {\\n margin-right: 0; /* Remove right margin from the last group (help button) */\\n}\\n\\nbutton {\\n border: none;\\n background: linear-gradient(to bottom, #4a90e2, #357abd);\\n border-color: #2968a3;\\n border-radius: 3px;\\n padding: 6px 10px;\\n color: #fff;\\n outline: none;\\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\\n margin-right: 5px;\\n min-width: 32px;\\n min-height: 28px;\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n font-size: 14px;\\n line-height: 1;\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\\n position: relative;\\n}\\n\\nbutton:hover {\\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\\n}\\n\\nbutton:active {\\n background: linear-gradient(to bottom, #357abd, #2968a3);\\n}\\n\\nbutton:last-child {\\n margin-right: 0;\\n}\\n\\n/* Save button when no changes - gray */\\nbutton.action {\\n background: #c1c1c1;\\n color: #999;\\n}\\n\\nbutton.action:active {\\n background: #b1b1b1;\\n}\\n\\n/* Save button when there are changes - orange */\\nbutton.grp {\\n background: linear-gradient(to bottom, #ee9564, #de7d48);\\n margin-right: 0;\\n}\\n\\nbutton.grp:active {\\n background: #d67540;\\n}\\n\\n/* Auto-arrange button with AI purple to blue gradient */\\nbutton.auto-arrange {\\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\\n border-color: #4a4c93;\\n}\\n\\nbutton.auto-arrange:hover {\\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\\n border-color: #5a5ca3;\\n}\\n\\nbutton.auto-arrange:active {\\n background: linear-gradient(135deg, #593B83, #357abd);\\n border-color: #3a3c83;\\n}\\n\\nselect {\\n border: 1px solid #ccc;\\n background: white;\\n border-radius: 3px;\\n padding: 3px 7px;\\n color: #666;\\n outline: none;\\n margin-right: 5px;\\n font-size: 12px;\\n}\\n\\nselect:disabled {\\n background: #f5f5f5;\\n color: #999;\\n}\\n\\nbutton:disabled {\\n background: #c1c1c1;\\n color: #999;\\n}\\n\\n#root > div > svg {\\n position: absolute;\\n user-select: none;\\n}\\n\\n\\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\\n stroke: #29c229;\\n}\\n.edge .v-dot {\\n fill: transparent;\\n stroke: transparent;\\n stroke-width: 3px;\\n cursor: pointer;\\n transition: stroke 0.15s ease;\\n}\\n.edge .v-dot:hover {\\n stroke: #999;\\n fill: rgba(153, 153, 153, 0.1);\\n}\\n.edge .v-dot.selected {\\n stroke: #29c229;\\n fill: rgba(41, 194, 41, 0.1);\\n}\\n.edge .v-dot.selected:hover {\\n stroke: #29c229;\\n fill: rgba(41, 194, 41, 0.2);\\n}\\n.edge .v-dot.auto.selected {\\n stroke: #777;\\n fill: rgba(119, 119, 119, 0.1);\\n}\\n.edge .v-dot.auto.selected:hover {\\n stroke: #777;\\n fill: rgba(119, 119, 119, 0.2);\\n}\\ncircle#prj {\\n fill: none;\\n stroke: #777;\\n}\\n\\n.nodeShadow {\\n fill: none;\\n stroke-width: 4px;\\n stroke: rgba(0, 0, 0, 0.13);\\n}\\n\\ng.node {\\n user-select: none;\\n cursor: default;\\n}\\n\\ng.node.linked {\\n cursor: pointer;\\n}\\n\\ng.node text {\\n pointer-events: none;\\n}\\n\\n.icon {\\n fill: #aaa;\\n stroke: #fff;\\n}\\n#icon-cube {\\n fill: #aaa;\\n}\\n\\n/* Ensure all button icons are uncolored */\\nbutton .icon,\\nbutton svg,\\nbutton path {\\n fill: currentColor !important;\\n stroke: none !important;\\n}\\n\\n/* Font Awesome icon styling in buttons */\\nbutton i {\\n font-size: 12px;\\n color: inherit;\\n}\\n\\nrect.elastic {\\n pointer-events: none;\\n stroke: none;\\n fill: #3bd8281f;\\n display: none;\\n}\\nrect.elastic.on {\\n display: block;\\n}\\n\\n.popover {\\n position: absolute;\\n top: 50px;\\n bottom: 10px;\\n overflow: auto;\\n right: 10px;\\n background: ghostwhite;\\n padding: 30px;\\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\\n border: solid 1px #eee;\\n}\\n\\n.popover th {\\n text-align: left;\\n padding: 20px 0px 10px;\\n}\\n.popover td {\\n padding-right: 20px;\\n font-size: 14px;\\n}\\n\\n/* Simple tooltip system with smart positioning */\\n[data-tooltip] {\\n position: relative;\\n}\\n\\n[data-tooltip]:hover::after {\\n content: attr(data-tooltip);\\n position: absolute;\\n top: 100%;\\n left: 50%;\\n transform: translateX(-50%);\\n background: rgba(0, 0, 0, 0.9);\\n color: white;\\n padding: 6px 12px;\\n border-radius: 4px;\\n font-size: 12px !important;\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\\n font-weight: normal !important;\\n white-space: nowrap;\\n z-index: 1000;\\n pointer-events: none;\\n margin-top: 5px;\\n animation: tooltip-appear 0.1s ease-out;\\n min-width: 120px;\\n max-width: calc(100vw - 20px);\\n box-sizing: border-box;\\n}\\n\\n[data-tooltip]:hover::before {\\n content: '';\\n position: absolute;\\n top: 100%;\\n left: 50%;\\n transform: translateX(-50%);\\n border: 4px solid transparent;\\n border-bottom-color: rgba(0, 0, 0, 0.9);\\n z-index: 1000;\\n pointer-events: none;\\n margin-top: 1px;\\n animation: tooltip-appear 0.1s ease-out;\\n}\\n\\n/* Special handling for rightmost elements that might overflow */\\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\\n /* Apply to last 2 toolbar groups (save and help buttons) */\\n left: auto;\\n right: 0;\\n transform: none;\\n}\\n\\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\\n /* Apply to last 2 toolbar groups (save and help buttons) */\\n left: auto;\\n right: 16px;\\n transform: none;\\n}\\n\\n@keyframes tooltip-appear {\\n from {\\n opacity: 0;\\n transform: translateX(-50%) translateY(-5px);\\n }\\n to {\\n opacity: 1;\\n transform: translateX(-50%) translateY(0);\\n }\\n}\\n\\nselect {\\n position: relative;\\n}\\n\\n/* Robot shape internal elements - inherit stroke from parent node */\\n.node .robot-eye-socket {\\n fill: none;\\n stroke: inherit;\\n stroke-width: 2;\\n}\\n\\n.node .robot-eye {\\n fill: currentColor;\\n stroke: none;\\n}\\n\\n.node .robot-mouth {\\n stroke: inherit;\\n}\\n\\n.node .robot-antenna {\\n stroke: inherit;\\n}\\n\\n.node .robot-antenna-ball {\\n fill: currentColor;\\n stroke: inherit;\\n stroke-width: 1.5;\\n}\\n\\n.node .robot-panel {\\n stroke: inherit;\\n}\\n\\n.node .robot-indicator {\\n fill: currentColor;\\n stroke: none;\\n}\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","\n import API from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./fonts.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./fonts.css\";\n export default content && content.locals ? content.locals : undefined;\n","\n import API from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./style.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./style.css\";\n export default content && content.locals ? content.locals : undefined;\n"],"names":["el","style","Object","keys","forEach","key","value","setProperty","toString","nodeBorder","fill","stroke","filter","nodeText","edgeText","edgeRect","groupRect","groupText","cursor","thickness","color","opacity","fontSize","dashed","p1","p2","Math","sqrt","x","y","width","height","background","shape","calculateEdgeVertices","edge","n1","from","n2","to","vertices","concat","unshift","push","interior","slice","firstRoutingVertex","find","vertex","label","lastRoutingVertex","index","length","intersect","Error","id","calculateLabelPlacement","position","defaultPoint","segment","point","labelIndex","findIndex","movable","labelVertex","auto","adjacentSegments","p","q","reduce","longest","candidate","_constants__WEBPACK_IMPORTED_MODULE_0__","gd","undefined","targetLength","sum","traversed","segmentPosition","horizontalDistance","abs","verticalDistance","orientation","createEdgePath","path","Symbol","GeometryValidationError","issues","constructor","super","map","issue","message","join","this","name","validateResolvedLayout","measured","resolved","diagramId","checkUniqueIDs","nodes","node","groups","group","edges","checkCoverage","measuredNodes","Map","measuredGroups","resolvedNodes","resolvedGroups","checkFiniteRect","bounds","expected","get","nearlyEqual","size","contentSize","checkRectInBounds","titleBounds","contains","titleSize","forEachPair","first","second","overlaps","checkNodeOverlaps","checkGroupOverlaps","child","parentId","parent","checkOwnership","measuredEdges","labels","sourceId","targetId","sections","some","section","checkSectionConnections","isFinitePoint","pointInRect","values","routeIntersectsRect","inset","sourceInside","belongsToGroup","routeViolatesGroup","labelBounds","labelSize","checkEdges","ids","seen","Set","has","add","actual","expectedIDs","actualIDs","rect","Number","isFinite","isFiniteRect","samePoint","source","target","start","end","pointOnBorder","nodeId","groupId","shouldBeInside","dx","dy","minimum","maximum","origin","delta","lower","upper","EPSILON","max","min","segmentIntersectsRect","outer","inner","inclusive","amount","withinX","withinY","onVertical","onHorizontal","items","visit","code","elementIds","measuredText","measurementSVG","measureSVGText","text","attrs","attributes","entries","sort","localeCompare","JSON","stringify","measurementKey","cached","document","createElementNS","setAttribute","textContent","isConnected","visibility","pointerEvents","body","appendChild","getBBox","remove","set","svgTextWrap","maxW","lines","trim","split","flatMap","paragraph","paragraphLines","currentLine","word","parts","breakLongWord","part","nextLine","line","maxWidth","currentPart","character","nextPart","create","element","type","className","k","v","String","classList","use","setAttributeNS","d","t","textArea","bold","anchor","txt","i","span","append","r","rx","ry","icon","expand","expanded","g","transform","setPosition","insideBox","b","centeredBox","intersectRect","box","w","h","s","q1","q2","denominator","a","numerator1","numerator2","result","onLine1","onLine2","segmentIntersection","ret","intersectRectFull","intersectEllipse","ellCenter","nodeCenter","si","c","radicand_sqrt","pos","project","atob","len","dot","cabDistance","cylinderRadiusY","shapeLabelOffsetY","toLowerCase","D3Element","_el","attr","insert","el2","insertBefore","querySelector","bbox","rounded","shapeSvg","_ellipse","mobiledeviceportrait","shapes","roundedbox","component","cylinder","cy","person","circle","ellipse","hexagon","sz","n","folder","mobiledevicelandscape","mobiledevice","pipe","robot","headSize","headR","antennaH","antennaR","eyeR","eyeSpacing","earW","earH","bodyW","bodyTop","bodyH","headTop","eyeY","mouthY","mouthW","webbrowser","Undo","versions","lastSavedPos","exportDoc","importDoc","change","tmpPreviousState","func","timeout","context","clearTimeout","setTimeout","apply","debounce","saveNow","beforeChange","deepClone","currentState","splice","doc","structuredClone","parse","undo","redo","changed","setSaved","textBlock","gapAfter","field","wrapped","constants","M5","lineHeight","defaultEdgeStyle","Y$","defaultNodeStyle","nD","GraphData","nodesMap","edgeVertices","groupsMap","metadata","layoutDirection","validatedLayout","colorToVarMap","_undo","_gridVisible","_snapToGrid","_gridSize","_skipAutoFit","exportLayout","lo","importLayout","window","graph","init","layout","addNode","sub","description","link","nodeStyle","minimumHeight","contentLayout","title","subtitle","nodeWidth","textWidth","HORIZONTAL_PADDING","blocks","textHeight","block","VERTICAL_PADDING","layoutNodeContent","requiredHeight","Array","addEdge","fromNode","toNode","initVertex","edgeID","input","charCodeAt","imul","fnv1a36","stableVertexID","userDeletedVertices","addGroup","nodesOrGroups","console","error","Boolean","setNodeSelected","selected","ref","updateEdgesSel","e","clearResolvedLayout","layoutSections","layoutLabelBounds","layoutBounds","layoutTitleBounds","moveNode","disableSnap","skipUndo","snapped","snapToGrid","redrawEdges","redrawGroups","moveEdgeVertex","redrawEdge","moveSelected","insertEdgeVertex","isLabel","deleteEdgeVertex","indexOf","delete","alignTopLeft","contentBounds","calculateContentBounds","offsetX","offsetY","resetPanTransform","currentZoom","getZoom","zoomGroup","svg","graphData","bb","zoom","parentElement","clientWidth","clientHeight","updatePanningOptimized","clearViewState","shouldSkipAutoFit","resetView","updatePanning","removeChild","buildEdge","buildGroup","exportSVG","U","originalSvg","exportSvg","cloneNode","querySelectorAll","getAttribute","removeAttribute","exportElastic","exportWidth","padding","exportHeight","exportZoomGroup","convertStylesToCustomProperties","script","createElement","replace","firstChild","outerHTML","minX","Infinity","minY","maxX","maxY","left","right","top","bottom","centerX","centerY","approxLabelSize","full","lst","rerender","requiredSavedPoint","endsWith","edgeId","startsWith","isArray","saved","autoLayout","options","placedNode","placedGroup","placedEdge","interiorRoutePoints","validated","fitToView","alignSelectionV","alignSelectionH","distributeSelectionH","selectedNodes","selectedVertices","allElements","spacing","newX","distributeSelectionV","newY","setEdgeSelected","viewportWidth","viewportHeight","zoomX","zoomY","optimalZoom","finalZoom","translateX","translateY","saveViewState","saveLayoutState","restoreLayoutState","state","isGridVisible","isSnapToGrid","getGridSize","toggleGrid","updateGridDisplay","dispatchEvent","CustomEvent","toggleSnapToGrid","snapAllToGrid","snappedX","round","snappedY","existingGrid","existingGridRect","defs","pattern","route","previous","clickListener","addEventListener","selectListener","dragging","buildGraph","data","onNodeSelect","dragMode","innerHTML","__data","_buildGraph","elasticEl","addCursorInteraction","setZoom","buildGraphView","zoomG","nodesG","edgesG","groupsG","gdata","href","exportHref","content","shapeType","AV","styles","setBorderStyle","border","tg","buildNodeContent","buildNode","completeRoutePoints","edge_utils","l","bg","buildPlacedEdgeLabel","placement","buildEdgeLabel","IW","Zr","cx","p0","topExtension","bottomExtension","hexHeight","pad","findClosestSegment","fnd","dst","POSITIVE_INFINITY","prj","pts","mouseToDrawing","getBoundingClientRect","z","currentTransform","getCurrentTransform","clientX","clientY","addCustomCursorInteraction","conn","ini","elastic","isPanning","panStartX","panStartY","initialTransform","pendingSelectionChange","pendingNavigation","hasDragged","suppressLinkClick","eventListeners","onClick","Element","closest","preventDefault","stopPropagation","md","convertEvent","changedTouches","onMouseMoveHandler","getSelection","setSelection","setTransform","drawingDx","drawingDy","item","setDragging","update","onMouseMove","ex","ey","onMouseUpHandler","removeEventListener","navigation","boxSelection","shiftKey","location","onMouseUp","onMouseDownHandler","nodeFromEvent","effectiveMode","isSelected","translateMatch","match","parseFloat","getCurrentTransformLocal","startDrawingX","startDrawingY","pt","currentPt","currentDrawingX","currentDrawingY","createElastic","onMouseDown","event","handler","addDnd","existingCleanup","__cursorInteractionCleanup","getData","beforeUnloadHandler","returnValue","setDotSelected","dotEl","mouseMoveHandler","altKey","removePrjDot","keyUpHandler","shortcuts","Yp","Zj","_s","clickHandler","wheelHandler","sign","deltaY","newZoom","setZoomCentered","keyDownHandler","shortcut","bl","Ur","hU","i1","mD","F","Gn","customInteractionCleanup","handles","handle","b1","b2","scaleMatch","oldZoom","container","newTranslateX","newTranslateY","viewStateCache","graphId","restoreViewState","defaultOptions","direction","nodeSpacing","layerSpacing","async","resolveAutomatic","diagram","checkMeasuredLinks","childrenByParent","groupsByParent","elkGroups","children","layoutOptions","groupLayoutOptions","root","rootLayoutOptions","owner","lowestCommonGroup","buildLayoutTree","elk","__webpack_require__","then","bind","module","default","readContainer","readResolvedLayout","sources","targets","titleHeight","sourceGroups","parentGroups","targetGroups","current","parentX","parentY","containerX","containerY","childX","requiredNumber","childY","measuredNode","measuredGroup","requiredLabel","readEdge","addOffset","startPoint","bendPoints","endPoint","requiredEndpoint","elementId","endpoints","siblings","fonts","ready","measureGraph","compactLayout","geometry","centerOffset","validateCurrentLayout","boundary","SVGGraphicsElement","copyBox","snapshot","completeSnapshot","resolveManual","nodeParents","groupParents","member","parents","isGroup","existing","declaredParents","checkMeasuredBox","measureNode","measureGroup","measureEdge","hasLabel","boxes","points","flat","layoutDirections","TopBottom","BottomTop","LeftRight","RightLeft","model","viewsList","views","layouts","viewKey","elements","relations","collectRels","relationships","rel","people","softwareSystems","containers","el1","components","deploymentNodes","containerInstances","containerId","recAddNodes","infrastructureNodes","view","getView","_graph_view_graph__WEBPACK_IMPORTED_MODULE_0__","jg","rankDirection","automaticLayout","version","groupingIDs","softwareSystemId","enterprise","cssClassName","tag","varPrefix","elementViewKey","containerViews","lookupContainerViewKey","tags","technology","encodedViewKey","encodeURIComponent","url","nodeLink","properties","warn","destinationId","routing","level","groupMembers","ADD_VERTEX","ADD_LABEL_VERTEX","DEL_VERTEX","ZOOM_IN","ZOOM_OUT","ZOOM_FIT","ZOOM_100","SELECT_ALL","DESELECT","MOVE_LEFT","MOVE_RIGHT","MOVE_UP","MOVE_DOWN","MOVE_LEFT_FINE","MOVE_RIGHT_FINE","MOVE_UP_FINE","MOVE_DOWN_FINE","TOGGLE_DRAG_MODE","ALIGN_HORIZONTAL","ALIGN_VERTICAL","DISTRIBUTE_HORIZONTAL","DISTRIBUTE_VERTICAL","AUTO_LAYOUT","RESET_POSITION","TOGGLE_GRID","TOGGLE_SNAP_TO_GRID","SNAP_ALL_TO_GRID","list","help","combinations","shift","ctrl","alt","click","wheel","shortcutMap","comboText","_utils_platform__WEBPACK_IMPORTED_MODULE_1__","sy","toUpperCase","react_jsx_runtime__WEBPACK_IMPORTED_MODULE_2__","jsxs","jsx","Fragment","colSpan","matches","SA","ke","checkKey","aShortcut","bShortcut","aModifiers","bModifiers","aCount","isMac","navigator","userAgentData","platform","includes","userAgent","KeyboardEvent","metaKey","test","ctrlKey","___CSS_LOADER_URL_IMPORT_0___","URL","___CSS_LOADER_URL_IMPORT_1___","___CSS_LOADER_URL_IMPORT_2___","___CSS_LOADER_URL_IMPORT_3___","___CSS_LOADER_EXPORT___","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default","___CSS_LOADER_URL_REPLACEMENT_0___","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default","___CSS_LOADER_URL_REPLACEMENT_1___","___CSS_LOADER_URL_REPLACEMENT_2___","___CSS_LOADER_URL_REPLACEMENT_3___","names","mappings","sourcesContent","sourceRoot","__WEBPACK_DEFAULT_EXPORT__","styleTagTransform","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default","setAttributes","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default","domAPI","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default","insertStyleElement","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_cjs_js_fonts_css__WEBPACK_IMPORTED_MODULE_6__","A","locals","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"286.js","mappings":"mGAuH0B,CAACA,EAAgBC,KAC1CC,OAAOC,KAAKF,GAAOG,QAAQC,IAC1B,MAAMC,EAAQL,EAAMI,GACC,iBAAVC,EACVN,EAAGC,MAAMM,YAAYF,EAAKC,EAAME,YAEhCR,EAAGC,MAAMM,YAAYF,EAAKC,aA9CH,CACzBG,WAAY,CACXC,KAAM,4BACNC,OAAQ,OACRC,OAAQ,gBAETC,SAAU,CACT,cAAe,uDACfF,OAAQ,QAETG,SAAU,CACT,cAAe,uDACfH,OAAQ,QAETI,SAAU,CACTL,KAAM,OACNC,OAAQ,QAETK,UAAW,CACVN,KAAM,sBACNC,OAAQ,OACR,eAAgB,EAChB,mBAAoB,GAErBM,UAAW,CACVP,KAAM,OACN,YAAa,GACb,cAAe,MACf,cAAe,uDACfQ,OAAQ,mBAjDmC,CAC5CC,UAAW,EACXC,MAAO,OACPC,QAAS,EACTC,SAAU,GACVC,QAAQ,UAmEwB,CAACC,EAAWC,IACrCC,KAAKC,MAAMF,EAAGG,EAAIJ,EAAGI,IAAMH,EAAGG,EAAIJ,EAAGI,IAAMH,EAAGI,EAAIL,EAAGK,IAAMJ,EAAGI,EAAIL,EAAGK,WAjEhC,CAC5CC,MAAO,IACPC,OAAQ,IACRC,WAAY,0BACZZ,MAAO,OACPC,QAAS,GACTV,OAAQ,OACRW,SAAU,GACVW,MAAO,qECZD,SAASC,EAAsBC,GACrC,MAAMC,EAAKD,EAAKE,KAAMC,EAAKH,EAAKI,GAIhC,IAAIC,EAAoBL,EAAKK,SAAWL,EAAKK,SAASC,SAAW,GAIjED,EAASE,QAAQN,GACjBI,EAASG,KAAKL,GAEd,MAAMM,EAAWJ,EAASK,MAAM,GAAI,GAC9BC,EAAqBF,EAASG,KAAKC,IAAWA,EAAOC,QAAUX,EACrE,IAAIY,EAA2Bd,EAC/B,IAAK,IAAIe,EAAQP,EAASQ,OAAS,EAAGD,GAAS,EAAGA,IACjD,IAAKP,EAASO,GAAOF,MAAO,CAC3BC,EAAoBN,EAASO,GAC7B,KACD,CAED,IAAKf,EAAGiB,YAAcf,EAAGe,UACxB,MAAM,IAAIC,MAAM,QAAQnB,EAAKoB,yCAK9B,OAHAf,EAAS,GAAKJ,EAAGiB,UAAUP,GAC3BN,EAASA,EAASY,OAAS,GAAKd,EAAGe,UAAUH,GAEtCV,CACR,CAOO,SAASgB,EACfhB,EACAiB,EACAC,GAEA,IACIC,EADAC,EAAQ,CAAChC,EAAG8B,EAAa9B,EAAGC,EAAG6B,EAAa7B,GAEhD,MAAMgC,EAAarB,EAASsB,UAAUd,GAAWA,EAAsBC,OACvE,IAAIc,GAAU,EAEd,GAAIF,GAAc,EAAG,CACpB,MAAMG,EAAcxB,EAASqB,GAC7BD,EAAQI,EACRD,GAA+B,IAArBC,EAAYC,KACtB,MAAMC,EAA8B,GAChCL,EAAa,GAChBK,EAAiBvB,KAAK,CAACwB,EAAG3B,EAASqB,EAAa,GAAIO,EAAGR,IAEpDC,EAAarB,EAASY,OAAS,GAClCc,EAAiBvB,KAAK,CAACwB,EAAGP,EAAOQ,EAAG5B,EAASqB,EAAa,KAE3DF,EAAUO,EAAiBG,OAA4B,CAACC,EAASC,IAC3DD,GAGE,EAAAE,EAAAC,IAAkBF,EAAUJ,EAAGI,EAAUH,IAC/C,EAAAI,EAAAC,IAAkBH,EAAQH,EAAGG,EAAQF,GACnCG,EACAD,EALKC,OAMNG,EACJ,KAAO,CACN,MAIMC,EAJcnC,EAASK,MAAM,GAAGwB,OACrC,CAACO,EAAK5B,EAAQG,IAAUyB,GAAM,EAAAJ,EAAAC,IAAkBjC,EAASW,GAAQH,GACjE,GAEkCS,EACnC,IAAIoB,EAAY,EAChB,IAAK,IAAI1B,EAAQ,EAAGA,EAAQX,EAASY,OAAQD,IAAS,CACrD,MAAMoB,EAAY,CAACJ,EAAG3B,EAASW,EAAQ,GAAIiB,EAAG5B,EAASW,IACjDC,GAAS,EAAAoB,EAAAC,IAAkBF,EAAUJ,EAAGI,EAAUH,GACxD,GAAIhB,EAAS,GAAKyB,EAAYzB,GAAUuB,EAAc,CACrD,MAAMG,GAAmBH,EAAeE,GAAazB,EACrDQ,EAAQ,CACPhC,EAAG2C,EAAUJ,EAAEvC,GAAK2C,EAAUH,EAAExC,EAAI2C,EAAUJ,EAAEvC,GAAKkD,EACrDjD,EAAG0C,EAAUJ,EAAEtC,GAAK0C,EAAUH,EAAEvC,EAAI0C,EAAUJ,EAAEtC,GAAKiD,GAEtDnB,EAAUY,EACV,KACD,CACAM,GAAazB,CACd,CACD,CAEA,MAAM2B,EAAqBpB,EAAUjC,KAAKsD,IAAIrB,EAAQS,EAAExC,EAAI+B,EAAQQ,EAAEvC,GAAK,EACrEqD,EAAmBtB,EAAUjC,KAAKsD,IAAIrB,EAAQS,EAAEvC,EAAI8B,EAAQQ,EAAEtC,GAAK,EACzE,MAAO,IACH+B,EACHsB,YAAaD,EAAmBF,EAAqB,WAAa,aAClEpB,UACAI,UAEF,CAGO,SAASoB,EAAe3C,GAC9B,GAAIA,EAASY,OAAS,EACrB,MAAM,IAAIE,MAAM,+CAEjB,OAAOd,EAASK,MAAM,GAAGwB,OACxB,CAACe,EAAMxB,IAAU,GAAGwB,MAASxB,EAAMhC,KAAKgC,EAAM/B,IAC9C,IAAIW,EAAS,GAAGZ,KAAKY,EAAS,GAAGX,IAEnC,8BCjD4CwD,OAAO,mBAW5C,MAAMC,UAAgChC,MACnCiC,OAET,WAAAC,CAAYD,GACXE,MAAMF,EAAOG,IAAIC,GAASA,EAAMC,SAASC,KAAK,OAC9CC,KAAKC,KAAO,0BACZD,KAAKP,OAASA,CACf,EAQM,SAASS,EACfC,EACAC,GAEA,MAAMX,EAA0B,GAC5BU,EAAS1C,KAAO2C,EAASC,WAC5BZ,EAAO5C,KAAKgD,EACX,kBACA,CAACM,EAAS1C,GAAI2C,EAASC,WACvB,UAAUD,EAASC,+CAA+CF,EAAS1C,OAI7E6C,EAAe,gBAAiBH,EAASI,MAAMX,IAAIY,GAAQA,EAAK/C,IAAKgC,GACrEa,EAAe,iBAAkBH,EAASM,OAAOb,IAAIc,GAASA,EAAMjD,IAAKgC,GACzEa,EAAe,gBAAiBH,EAASQ,MAAMf,IAAIvD,GAAQA,EAAKoB,IAAKgC,GACrEa,EAAe,gBAAiBF,EAASG,MAAMX,IAAIY,GAAQA,EAAK/C,IAAKgC,GACrEa,EAAe,iBAAkBF,EAASK,OAAOb,IAAIc,GAASA,EAAMjD,IAAKgC,GACzEa,EAAe,gBAAiBF,EAASO,MAAMf,IAAIvD,GAAQA,EAAKoB,IAAKgC,GAErEmB,EACC,OACAT,EAASI,MAAMX,IAAIY,GAAQA,EAAK/C,IAChC2C,EAASG,MAAMX,IAAIY,GAAQA,EAAK/C,IAChCgC,GAEDmB,EACC,QACAT,EAASM,OAAOb,IAAIc,GAASA,EAAMjD,IACnC2C,EAASK,OAAOb,IAAIc,GAASA,EAAMjD,IACnCgC,GAEDmB,EACC,OACAT,EAASQ,MAAMf,IAAIvD,GAAQA,EAAKoB,IAChC2C,EAASO,MAAMf,IAAIvD,GAAQA,EAAKoB,IAChCgC,GAGD,MAAMoB,EAAgB,IAAIC,IAAIX,EAASI,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KAC7DO,EAAiB,IAAID,IAAIX,EAASM,OAAOb,IAAIc,GAAS,CAACA,EAAMjD,GAAIiD,KACjEM,EAAgB,IAAIF,IAAIV,EAASG,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KAC7DS,EAAiB,IAAIH,IAAIV,EAASK,OAAOb,IAAIc,GAAS,CAACA,EAAMjD,GAAIiD,KAEvEQ,EAAgB,UAAWd,EAASC,UAAWD,EAASe,OAAQ1B,GAChE,IAAK,MAAMe,KAAQJ,EAASG,MAAO,CAClCW,EAAgB,OAAQV,EAAK/C,GAAI+C,EAAKW,OAAQ1B,GAC9C,MAAM2B,EAAWP,EAAcQ,IAAIb,EAAK/C,KACpC2D,GACFE,EAAYF,EAASG,KAAKvF,MAAOwE,EAAKW,OAAOnF,QAC7CsF,EAAYF,EAASG,KAAKtF,OAAQuE,EAAKW,OAAOlF,SAE/CwD,EAAO5C,KAAKgD,EACX,gBACA,CAACW,EAAK/C,IACN,QAAQ+C,EAAK/C,sCAGX2D,IACHA,EAASI,YAAYxF,MAAQwE,EAAKW,OAAOnF,OACzCoF,EAASI,YAAYvF,OAASuE,EAAKW,OAAOlF,SAE1CwD,EAAO5C,KAAKgD,EACX,mBACA,CAACW,EAAK/C,IACN,QAAQ+C,EAAK/C,2CAGfgE,EAAkB,OAAQjB,EAAK/C,GAAI+C,EAAKW,OAAQf,EAASe,OAAQ1B,EAClE,CACA,IAAK,MAAMiB,KAASN,EAASK,OAAQ,CACpCS,EAAgB,QAASR,EAAMjD,GAAIiD,EAAMS,OAAQ1B,GACjDyB,EAAgB,cAAeR,EAAMjD,GAAIiD,EAAMgB,YAAajC,GAC5DgC,EAAkB,QAASf,EAAMjD,GAAIiD,EAAMS,OAAQf,EAASe,OAAQ1B,GAC/DkC,EAASjB,EAAMS,OAAQT,EAAMgB,cACjCjC,EAAO5C,KAAKgD,EACX,oBACA,CAACa,EAAMjD,IACP,SAASiD,EAAMjD,kCAGjB,MAAM2D,EAAWL,EAAeM,IAAIX,EAAMjD,KACtC2D,GACFE,EAAYF,EAASQ,UAAU5F,MAAO0E,EAAMgB,YAAY1F,QACxDsF,EAAYF,EAASQ,UAAU3F,OAAQyE,EAAMgB,YAAYzF,SAE1DwD,EAAO5C,KAAKgD,EACX,gBACA,CAACa,EAAMjD,IACP,SAASiD,EAAMjD,2CAGlB,CAqBA,GAgED,SAA2B8C,EAAuBd,GACjDoC,EAAYtB,EAAO,CAACuB,EAAOC,KACtBC,EAASF,EAAMX,OAAQY,EAAOZ,SACjC1B,EAAO5C,KAAKgD,EACX,eACA,CAACiC,EAAMrE,GAAIsE,EAAOtE,IAClB,SAASqE,EAAMrE,UAAUsE,EAAOtE,gBAIpC,CA7FCwE,CAAkB7B,EAASG,MAAOd,GAmGnC,SAA4BgB,EAAyBhB,GACpDoC,EAAYpB,EAAQ,CAACqB,EAAOC,MAE1BC,EAASF,EAAMX,OAAQY,EAAOZ,SAC7BQ,EAASG,EAAMX,OAAQY,EAAOZ,SAC9BQ,EAASI,EAAOZ,OAAQW,EAAMX,SAE/B1B,EAAO5C,KAAKgD,EACX,gBACA,CAACiC,EAAMrE,GAAIsE,EAAOtE,IAClB,UAAUqE,EAAMrE,UAAUsE,EAAOtE,0BAIrC,CAhHCyE,CAAmB9B,EAASK,OAAQhB,GAsHrC,SACCc,EACAE,EACAO,EACAC,EACAxB,GAEA,IAAK,MAAMe,KAAQD,EAAO,CACzB,MAAM4B,EAAQnB,EAAcK,IAAIb,EAAK/C,IAQrC,GAPI0E,GAASA,EAAMC,WAAa5B,EAAK4B,UACpC3C,EAAO5C,KAAKgD,EACX,oBACA,CAACW,EAAK/C,IACN,QAAQ+C,EAAK/C,gCAGV+C,EAAK4B,SACT,SAED,MAAMC,EAASpB,EAAeI,IAAIb,EAAK4B,UACnCD,GAASE,IAAWV,EAASU,EAAOlB,OAAQgB,EAAMhB,SACrD1B,EAAO5C,KAAKgD,EACX,oBACA,CAACW,EAAK/C,GAAI+C,EAAK4B,UACf,QAAQ5B,EAAK/C,gCAAgC+C,EAAK4B,YAGrD,CACA,IAAK,MAAM1B,KAASD,EAAQ,CAC3B,MAAM0B,EAAQlB,EAAeI,IAAIX,EAAMjD,IAQvC,GAPI0E,GAASA,EAAMC,WAAa1B,EAAM0B,UACrC3C,EAAO5C,KAAKgD,EACX,oBACA,CAACa,EAAMjD,IACP,SAASiD,EAAMjD,gCAGZiD,EAAM0B,SACV,SAED,MAAMC,EAASpB,EAAeI,IAAIX,EAAM0B,UACpCD,GAASE,IAAWV,EAASU,EAAOlB,OAAQgB,EAAMhB,SACrD1B,EAAO5C,KAAKgD,EACX,oBACA,CAACa,EAAMjD,GAAIiD,EAAM0B,UACjB,SAAS1B,EAAMjD,gCAAgCiD,EAAM0B,YAGxD,CACD,CAtKCE,CACCnC,EAASI,MACTJ,EAASM,OACTO,EACAC,EACAxB,GAuKF,SACCU,EACAC,EACAG,EACAE,EACAI,EACAE,EACAtB,GAEA,MAAM8C,EAAgB,IAAIzB,IAAIX,EAASQ,MAAMf,IAAIvD,GAAQ,CAACA,EAAKoB,GAAIpB,KAC7DmG,EAA4C,GAClD,IAAK,MAAMnG,KAAQ+D,EAASO,MAAO,CAClC,MAAMS,EAAWmB,EAAclB,IAAIhF,EAAKoB,IACxC,GAAK2D,EAaL,GATC/E,EAAKoG,WAAarB,EAASqB,UAC3BpG,EAAKqG,WAAatB,EAASsB,UAE3BjD,EAAO5C,KAAKgD,EACX,oBACA,CAACxD,EAAKoB,GAAIpB,EAAKoG,SAAUpG,EAAKqG,UAC9B,QAAQrG,EAAKoB,oDAGc,IAAzBpB,EAAKsG,SAASrF,QAAgBjB,EAAKsG,SAASC,KAAKC,GAAWA,EAAQvF,OAAS,GAChFmC,EAAO5C,KAAKgD,EACX,oBACA,CAACxD,EAAKoB,IACN,QAAQpB,EAAKoB,yCAJf,CAQAqF,EAAwBzG,EAAMkE,EAAOd,GACrC,IAAK,MAAMoD,KAAWxG,EAAKsG,SAC1B,IAAK,MAAM7E,KAAS+E,EACnB,IAAKE,EAAcjF,KAAWkF,EAAYlF,EAAOsC,EAASe,QAAQ,GAAO,CACxE1B,EAAO5C,KAAKgD,EACX,gBACA,CAACxD,EAAKoB,IACN,QAAQpB,EAAKoB,mDAEd,KACD,CAGF,IAAK,MAAM+C,KAAQD,EAAM0C,SACpBzC,EAAK/C,KAAOpB,EAAKoG,UAAYjC,EAAK/C,KAAOpB,EAAKqG,UAG9CQ,EAAoB7G,EAAKsG,SAAUQ,EAAM3C,EAAKW,OAAQ,MACzD1B,EAAO5C,KAAKgD,EACX,oBACA,CAACxD,EAAKoB,GAAI+C,EAAK/C,IACf,QAAQpB,EAAKoB,6BAA6B+C,EAAK/C,OAIlD,IAAK,MAAMiD,KAASD,EAAOwC,SAAU,CACpC,MAAMG,EAAeC,EACpBhH,EAAKoG,SACL/B,EAAMjD,GACNoD,EACAE,GASAqC,IAPoBC,EACpBhH,EAAKqG,SACLhC,EAAMjD,GACNoD,EACAE,IAIAuC,EAAmBjH,EAAKsG,SAAUjC,EAAMS,OAAQiC,IAEhD3D,EAAO5C,KAAKgD,EACX,qBACA,CAACxD,EAAKoB,GAAIiD,EAAMjD,IAChB,QAAQpB,EAAKoB,qBAAqBiD,EAAMjD,eAG3C,CACA,GAAIpB,EAAKkH,YAAa,CACrBrC,EAAgB,aAAc7E,EAAKoB,GAAIpB,EAAKkH,YAAa9D,GACzDgC,EAAkB,aAAcpF,EAAKoB,GAAIpB,EAAKkH,YAAanD,EAASe,OAAQ1B,IACxE2B,EAASoC,WACXlC,EAAYF,EAASoC,UAAUxH,MAAOK,EAAKkH,YAAYvH,QACvDsF,EAAYF,EAASoC,UAAUvH,OAAQI,EAAKkH,YAAYtH,SAEzDwD,EAAO5C,KAAKgD,EACX,gBACA,CAACxD,EAAKoB,IACN,QAAQpB,EAAKoB,4CAGf,IAAK,MAAM+C,KAAQD,EAAM0C,SACpBjB,EAAS3F,EAAKkH,YAAa/C,EAAKW,SACnC1B,EAAO5C,KAAKgD,EACX,qBACA,CAACxD,EAAKoB,GAAI+C,EAAK/C,IACf,QAAQpB,EAAKoB,0BAA0B+C,EAAK/C,OAI/C,IAAK,MAAMiD,KAASD,EAAOwC,SACtBjB,EAAS3F,EAAKkH,YAAa7C,EAAMgB,cACpCjC,EAAO5C,KAAKgD,EACX,sBACA,CAACxD,EAAKoB,GAAIiD,EAAMjD,IAChB,QAAQpB,EAAKoB,2BAA2BiD,EAAMjD,aAIjD+E,EAAO3F,KAAK,CAACY,GAAIpB,EAAKoB,GAAI0D,OAAQ9E,EAAKkH,aACxC,MAAWnC,EAASoC,WACnB/D,EAAO5C,KAAKgD,EACX,kBACA,CAACxD,EAAKoB,IACN,QAAQpB,EAAKoB,oCAtFf,CAyFD,CACAoE,EAAYW,EAAQ,CAACV,EAAOC,KACvBC,EAASF,EAAMX,OAAQY,EAAOZ,SACjC1B,EAAO5C,KAAKgD,EACX,sBACA,CAACiC,EAAMrE,GAAIsE,EAAOtE,IAClB,eAAeqE,EAAMrE,UAAUsE,EAAOtE,iBAIzC,IAAK,MAAMN,KAASqF,EACnB,IAAK,MAAMnG,KAAQ+D,EAASO,MACvBuC,EAAoB7G,EAAKsG,SAAUQ,EAAMhG,EAAMgE,OAAQ,MAC1D1B,EAAO5C,KAAKgD,EACX,qBACA,CAAC1C,EAAMM,GAAIpB,EAAKoB,IAChB,QAAQpB,EAAKoB,oBAAoBN,EAAMM,MAK5C,CApTCgG,CACCtD,EACAC,EACAY,EACAC,EACAJ,EACAE,EACAtB,GAGGA,EAAOnC,OAAS,EACnB,MAAM,IAAIkC,EAAwBC,GAEnC,OAAOW,CACR,CAGA,SAASE,EAAenD,EAAeuG,EAAejE,GACrD,MAAMkE,EAAO,IAAIC,IACjB,IAAK,MAAMnG,KAAMiG,EACZC,EAAKE,IAAIpG,IACZgC,EAAO5C,KAAKgD,EAAM,eAAgB,CAACpC,GAAK,GAAGN,KAASM,oBAErDkG,EAAKG,IAAIrG,EAEX,CAGA,SAASmD,EACRzD,EACAiE,EACA2C,EACAtE,GAEA,MAAMuE,EAAc,IAAIJ,IAAIxC,GACtB6C,EAAY,IAAIL,IAAIG,GAC1B,IAAK,MAAMtG,KAAMuG,EACXC,EAAUJ,IAAIpG,IAClBgC,EAAO5C,KAAKgD,EAAM,kBAAmB,CAACpC,GAAK,GAAGN,KAASM,sCAGzD,IAAK,MAAMA,KAAMwG,EACXD,EAAYH,IAAIpG,IACpBgC,EAAO5C,KAAKgD,EAAM,qBAAsB,CAACpC,GAAK,GAAGN,KAASM,oCAG7D,CAGA,SAASyD,EACR/D,EACAM,EACAyG,EACAzE,KA4aD,SAAsByE,GACrB,OAAOnB,EAAcmB,IAASC,OAAOC,SAASF,EAAKlI,QAAUmI,OAAOC,SAASF,EAAKjI,OACnF,CA5aMoI,CAAaH,IAASA,EAAKlI,MAAQ,GAAKkI,EAAKjI,OAAS,IAC1DwD,EAAO5C,KAAKgD,EAAM,aAAc,CAACpC,GAAK,GAAGN,KAASM,0BAEpD,CAGA,SAASgE,EACRtE,EACAM,EACAyG,EACA/C,EACA1B,GAEKkC,EAASR,EAAQ+C,IACrBzE,EAAO5C,KAAKgD,EAAM,gBAAiB,CAACpC,GAAK,GAAGN,KAASM,2CAEvD,CAmPA,SAASqF,EACRzG,EACAkE,EACAd,GAEA,IAAK,IAAIpC,EAAQ,EAAGA,EAAQhB,EAAKsG,SAASrF,OAAQD,IAGjD,IAAKiH,EAFYjI,EAAKsG,SAAStF,EAAQ,GAAGN,OAAO,GAAG,GACpCV,EAAKsG,SAAStF,GAAO,IAOpC,YALAoC,EAAO5C,KAAKgD,EACX,oBACA,CAACxD,EAAKoB,IACN,QAAQpB,EAAKoB,uCAKhB,MAAM8G,EAAShE,EAAMc,IAAIhF,EAAKoG,UACxB+B,EAASjE,EAAMc,IAAIhF,EAAKqG,UACxB+B,EAAQpI,EAAKsG,SAAS,GAAG,GACzB+B,EAAMrI,EAAKsG,SAAS5F,OAAO,GAAG,GAAGA,OAAO,GAAG,GAE/CwH,GACAC,GACAG,EAAcF,EAAOF,EAAOpD,SAC5BwD,EAAcD,EAAKF,EAAOrD,SAE3B1B,EAAO5C,KAAKgD,EACX,oBACA,CAACxD,EAAKoB,GAAIpB,EAAKoG,SAAUpG,EAAKqG,UAC9B,QAAQrG,EAAKoB,yCAGhB,CAGA,SAAS4F,EACRuB,EACAC,EACAtE,EACAE,GAEA,IAAI4B,EAAS9B,EAAMc,IAAIuD,IAASxC,SAChC,KAAOC,GAAQ,CACd,GAAIA,IAAWwC,EACd,OAAO,EAERxC,EAAS5B,EAAOY,IAAIgB,IAASD,QAC9B,CACA,OAAO,CACR,CAOA,SAASkB,EACRX,EACAjC,EACAoE,GAEA,IAAK,MAAMjC,KAAWF,EACrB,IAAK,IAAItF,EAAQ,EAAGA,EAAQwF,EAAQvF,OAAQD,IAK3C,GAAI2F,EAJa,CAChBlH,GAAI+G,EAAQxF,EAAQ,GAAGvB,EAAI+G,EAAQxF,GAAOvB,GAAK,EAC/CC,GAAI8G,EAAQxF,EAAQ,GAAGtB,EAAI8G,EAAQxF,GAAOtB,GAAK,GAEtB2E,GAAO,KAAWoE,EAC3C,OAAO,EAIV,OAAO,CACR,CAGA,SAAS5B,EAAoBP,EAAqBuB,GACjD,OAAOvB,EAASC,KAAKC,GAAWA,EAAQD,KAAK,CAAC9E,EAAOT,IACpDA,EAAQ,GAQV,SAA+BoH,EAAcC,EAAYR,GACxD,MAAMa,EAAKL,EAAI5I,EAAI2I,EAAM3I,EACnBkJ,EAAKN,EAAI3I,EAAI0I,EAAM1I,EACzB,IAAIkJ,EAAU,EACVC,EAAU,EACd,IAAK,MAAOC,EAAQC,EAAOC,EAAOC,IAAU,CAC3C,CAACb,EAAM3I,EAAGiJ,EAAIb,EAAKpI,EAAGoI,EAAKpI,EAAIoI,EAAKlI,OACpC,CAACyI,EAAM1I,EAAGiJ,EAAId,EAAKnI,EAAGmI,EAAKnI,EAAImI,EAAKjI,SACzB,CACX,GAAIL,KAAKsD,IAAIkG,GAASjB,OAAOoB,QAAS,CACrC,GAAIJ,GAAUE,GAASF,GAAUG,EAChC,OAAO,EAER,QACD,CACA,MAAMxD,GAASuD,EAAQF,GAAUC,EAC3BrD,GAAUuD,EAAQH,GAAUC,EAGlC,GAFAH,EAAUrJ,KAAK4J,IAAIP,EAASrJ,KAAK6J,IAAI3D,EAAOC,IAC5CmD,EAAUtJ,KAAK6J,IAAIP,EAAStJ,KAAK4J,IAAI1D,EAAOC,IACxCkD,GAAWC,EACd,OAAO,CAET,CACA,OAAOA,EAAU,GAAKD,EAAU,CACjC,CAhCeS,CAAsB7C,EAAQxF,EAAQ,GAAIS,EAAOoG,IAEhE,CAiCA,SAASlC,EAASF,EAAaC,GAC9B,OACCD,EAAMhG,EAAIiG,EAAOjG,EAAIiG,EAAO/F,OAC5B+F,EAAOjG,EAAIgG,EAAMhG,EAAIgG,EAAM9F,OAC3B8F,EAAM/F,EAAIgG,EAAOhG,EAAIgG,EAAO9F,QAC5B8F,EAAOhG,EAAI+F,EAAM/F,EAAI+F,EAAM7F,MAE7B,CAGA,SAAS0F,EAASgE,EAAaC,GAC9B,OACCD,EAAM7J,GAAK8J,EAAM9J,GACjB6J,EAAM5J,GAAK6J,EAAM7J,GACjB4J,EAAM7J,EAAI6J,EAAM3J,OAAS4J,EAAM9J,EAAI8J,EAAM5J,OACzC2J,EAAM5J,EAAI4J,EAAM1J,QAAU2J,EAAM7J,EAAI6J,EAAM3J,MAE5C,CAGA,SAAS+G,EAAYlF,EAAcoG,EAAY2B,GAC9C,OAAIA,EAEF/H,EAAMhC,GAAKoI,EAAKpI,GAChBgC,EAAM/B,GAAKmI,EAAKnI,GAChB+B,EAAMhC,GAAKoI,EAAKpI,EAAIoI,EAAKlI,OACzB8B,EAAM/B,GAAKmI,EAAKnI,EAAImI,EAAKjI,OAI1B6B,EAAMhC,EAAIoI,EAAKpI,GACfgC,EAAM/B,EAAImI,EAAKnI,GACf+B,EAAMhC,EAAIoI,EAAKpI,EAAIoI,EAAKlI,OACxB8B,EAAM/B,EAAImI,EAAKnI,EAAImI,EAAKjI,MAE1B,CAGA,SAASkH,EAAMe,EAAY4B,GAC1B,MAAO,CACNhK,EAAGoI,EAAKpI,EAAIgK,EACZ/J,EAAGmI,EAAKnI,EAAI+J,EACZ9J,MAAOJ,KAAK4J,IAAI,EAAGtB,EAAKlI,MAAiB,EAAT8J,GAChC7J,OAAQL,KAAK4J,IAAI,EAAGtB,EAAKjI,OAAkB,EAAT6J,GAEpC,CAGA,SAAS/C,EAAcjF,GACtB,OAAOqG,OAAOC,SAAStG,EAAMhC,IAAMqI,OAAOC,SAAStG,EAAM/B,EAC1D,CAQA,SAASuI,EAAUxC,EAAcC,GAChC,OAAOT,EAAYQ,EAAMhG,EAAGiG,EAAOjG,IAAMwF,EAAYQ,EAAM/F,EAAGgG,EAAOhG,EACtE,CAGA,SAAS4I,EAAc7G,EAAcoG,GACpC,MAAM6B,EAAUjI,EAAMhC,GAAKoI,EAAKpI,EAAI,IAAOgC,EAAMhC,GAAKoI,EAAKpI,EAAIoI,EAAKlI,MAAQ,GACtEgK,EAAUlI,EAAM/B,GAAKmI,EAAKnI,EAAI,IAAO+B,EAAM/B,GAAKmI,EAAKnI,EAAImI,EAAKjI,OAAS,GACvEgK,EAAa3E,EAAYxD,EAAMhC,EAAGoI,EAAKpI,IAC5CwF,EAAYxD,EAAMhC,EAAGoI,EAAKpI,EAAIoI,EAAKlI,OAC9BkK,EAAe5E,EAAYxD,EAAM/B,EAAGmI,EAAKnI,IAC9CuF,EAAYxD,EAAM/B,EAAGmI,EAAKnI,EAAImI,EAAKjI,QACpC,OAAQ+J,GAAWC,GAAgBF,GAAWG,CAC/C,CAGA,SAAS5E,EAAYQ,EAAeC,GACnC,OAAOnG,KAAKsD,IAAI4C,EAAQC,IAAW,EACpC,CAGA,SAASF,EAAesE,EAAYC,GACnC,IAAK,IAAItE,EAAQ,EAAGA,EAAQqE,EAAM7I,OAAQwE,IACzC,IAAK,IAAIC,EAASD,EAAQ,EAAGC,EAASoE,EAAM7I,OAAQyE,IACnDqE,EAAMD,EAAMrE,GAAQqE,EAAMpE,GAG7B,CAGA,SAASlC,EACRwG,EACAC,EACAxG,GAEA,MAAO,CAACuG,OAAMC,aAAYxG,UAC3B,iGCpwBO,MCaDyG,EAAe,IAAIzF,IACzB,IAAI0F,EAMG,SAASC,EAAeC,EAAcC,GAC5C,MAAMpM,EAsFP,SAAwBmM,EAAcC,GACrC,MAAMC,EAAaxM,OAAOyM,QAAQF,GAAOG,KAAK,EAAEhF,IAASC,KACxDD,EAAMiF,cAAchF,IAErB,OAAOiF,KAAKC,UAAU,CAACP,EAAME,GAC9B,CA3FaM,CAAeR,EAAMC,GAC3BQ,EAASZ,EAAalF,IAAI9G,GAChC,GAAI4M,EACH,OAAOA,EAGR,MAAM3G,EAAO4G,SAASC,gBAAgB,6BAA8B,QACpE7G,EAAK8G,aAAa,IAAK,KACvB9G,EAAK8G,aAAa,IAAK,KACvB,IAAK,MAAOrH,EAAMzF,KAAUJ,OAAOyM,QAAQF,GAC1CnG,EAAK8G,aAAarH,EAAMzF,GAEzBgG,EAAK+G,YAAcb,GA6DfF,GAAgBgB,cAGpBhB,EAAiBY,SAASC,gBAAgB,6BAA8B,OACxEb,EAAec,aAAa,cAAe,QAC3Cd,EAAerM,MAAMwD,SAAW,WAChC6I,EAAerM,MAAMsN,WAAa,SAClCjB,EAAerM,MAAMuN,cAAgB,OACrCN,SAASO,KAAKC,YAAYpB,IAPlBA,GA3DJoB,YAAYpH,GAChB,MAAMW,EAASX,EAAKqH,UACpBrH,EAAKsH,SACL,MAAMvG,EAAO,CAACvF,MAAOmF,EAAOnF,MAAOC,OAAQkF,EAAOlF,QAElD,OADAsK,EAAawB,IAAIxN,EAAKgH,GACfA,CACR,CAMO,SAASyG,EACftB,EACA1K,EACA2K,GAEA,IAAIsB,EAAO,EAoCX,MAAO,CAACC,MAnCMxB,EAAKyB,OAAOC,MAAM,MAAMC,QAAQC,IAC7C,MAAMC,EAA2B,GACjC,IAAIC,EAAwB,GAC5B,IAAK,MAAMC,KAAQH,EAAUH,OAAOC,MAAM,OAAQ,CACjD,GAAI3B,EAAegC,EAAM9B,GAAO3K,MAAQA,EAAO,CAC1CwM,EAAYlL,OAAS,IACxBiL,EAAe1L,KAAK2L,EAAYzI,KAAK,MACrCyI,EAAc,IAEf,MAAME,EAAQC,EAAcF,EAAMzM,EAAO2K,GACzC,IAAK,MAAMiC,KAAQF,EAAM3L,MAAM,GAAI,GAClCwL,EAAe1L,KAAK+L,GACpBX,EAAOrM,KAAK4J,IAAIyC,EAAMxB,EAAemC,EAAMjC,GAAO3K,OAEnDwM,EAAcE,EAAM3L,OAAO,GAC3B,QACD,CAEA,MAAM8L,EAAW,IAAIL,EAAaC,GAC5BlH,EAAOkF,EAAeoC,EAAS9I,KAAK,KAAM4G,GAC5CpF,EAAKvF,MAAQA,GAASwM,EAAYlL,OAAS,GAC9CiL,EAAe1L,KAAK2L,EAAYzI,KAAK,MACrCyI,EAAc,CAACC,KAEfR,EAAOrM,KAAK4J,IAAIyC,EAAM1G,EAAKvF,OAC3BwM,EAAcK,EAEhB,CACA,GAAIL,EAAYlL,OAAS,EAAG,CAC3B,MAAMwL,EAAON,EAAYzI,KAAK,KAC9BwI,EAAe1L,KAAKiM,GACpBb,EAAOrM,KAAK4J,IAAIyC,EAAMxB,EAAeqC,EAAMnC,GAAO3K,MACnD,CACA,OAAOuM,IAEON,OAChB,CAyBA,SAASU,EACRF,EACAM,EACApC,GAEA,MAAM+B,EAAkB,GACxB,IAAIM,EAAc,GAClB,IAAK,MAAMC,KAAaR,EAAM,CAC7B,MAAMS,EAAWF,EAAcC,EAC3BxC,EAAeyC,EAAUvC,GAAO3K,MAAQ+M,GAAYC,EAAY1L,OAAS,GAC5EoL,EAAM7L,KAAKmM,GACXA,EAAcC,GAEdD,EAAcE,CAEhB,CAIA,OAHIF,EAAY1L,OAAS,GACxBoL,EAAM7L,KAAKmM,GAELN,CACR,CCrIO,MAAMS,EAAS,CACrB,OAAAC,CAAQC,EAAc1C,EAAyC,CAAC,EAAG2C,GAClE,MAAMpP,EAAKkN,SAASC,gBAAgB,6BAA8BgC,GAGlE,OAFAjP,OAAOyM,QAAQF,GAAOrM,QAAQ,EAAEiP,EAAGC,KAAOtP,EAAGoN,aAAaiC,EAAGE,OAAOD,KAChEF,GAAWpP,EAAGwP,UAAU5F,IAAIwF,GACzBpP,CACR,EAEA,GAAAyP,CAAIlM,EAAYkJ,EAAyC,CAAC,GACzD,MAAMzM,EAAK8F,KAAKoJ,QAAQ,MAAOzC,GAE/B,OADAzM,EAAG0P,eAAe,+BAAgC,aAAc,IAAMnM,GAC/DvD,CACR,EAEA,IAAAoF,CAAKA,EAAcqH,EAAyC,CAAC,EAAG2C,GAE/D,OADUtJ,KAAKoJ,QAAQ,OAAQ,IAAIzC,EAAOkD,EAAGvK,GAAOgK,EAErD,EAEA,IAAA5C,CAAKA,EAAcC,EAAyC,CAAC,GAC5D,MAAMmD,EAAI9J,KAAKoJ,QAAQ,OAAQzC,GAE/B,OADID,IAAMoD,EAAEvC,YAAcb,GACnBoD,CACR,EAEA,QAAAC,CAASrD,EAAc1K,EAAeR,EAAkBwO,EAAelO,EAAI,EAAGC,EAAI,EAAGkO,EAAS,IAC7F,MAAMtD,EAAgC,CACrC,YAAa,GAAGnL,MAChB,cAAewO,EAAO,OAAS,WAE1B9B,MAACA,EAAKD,KAAEA,GAAQD,EAAYtB,EAAM1K,EAAO2K,GACzCuD,EAAMlK,KAAK0G,KAAK,GAAI,CAAC5K,EAAG,EAAGC,IAAG,cAAekO,QAAUrL,IAQ7D,OANAsJ,EAAM5N,QAAQ,CAACwO,EAAMqB,KACpB,MAAMC,EAAOpK,KAAKoJ,QAAQ,QAAS,CAACtN,IAAGkJ,GAAI,GAAGxJ,EAAW,SAAUmL,IACnEyD,EAAK7C,YAAcuB,EACnBoB,EAAIG,OAAOD,KAGL,CAACF,MAAKlF,IAAKkD,EAAM5K,OAAS,IAAM9B,EAAW,GAAIyM,OACvD,EAEA,IAAA/D,CAAKlI,EAAeC,EAAgBH,EAAI,EAAGC,EAAI,EAAGuO,EAAI,EAAGhB,GACxD,OAAOtJ,KAAKoJ,QAAQ,OAAQ,CAACtN,IAAGC,IAAGwO,GAAID,EAAGE,GAAIF,EAAGtO,QAAOC,UAASqN,EAClE,EAEA,IAAAmB,CAAKA,EAAc3O,EAAI,EAAGC,EAAI,GAC7B,OAAOiE,KAAK2J,IAAIc,EAAM,CAAC3O,IAAGC,KAC3B,EAEA,MAAA2O,CAAO5O,EAAWC,EAAW4O,GAC5B,MAAMC,EAAI5K,KAAKoJ,QAAQ,IAAK,CAACyB,UAAW,aAAa/O,KAAKC,MAAO,UAKjE,OAJA6O,EAAEP,OACDrK,KAAKkE,KAAK,GAAI,GAAI,EAAG,EAAG,GACxBlE,KAAK0G,KAAKiE,EAAW,IAAM,IAAK,CAAC7O,EAAG,GAAIC,EAAG,GAAI,cAAe,YAExD6O,CACR,GAGM,SAASE,EAAYF,EAAgB9O,EAAWC,GACtD6O,EAAEtD,aAAa,YAAa,aAAaxL,KAAKC,KAC/C,CCrDO,SAASgP,EAAU1M,EAAU2M,EAASC,GAAc,GAC1D,OAAOA,EACL5M,EAAEvC,EAAIkP,EAAElP,EAAIkP,EAAEhP,MAAQ,GAAKqC,EAAEvC,EAAIkP,EAAElP,EAAIkP,EAAEhP,MAAQ,GAAKqC,EAAEtC,EAAIiP,EAAEjP,EAAIiP,EAAE/O,OAAS,GAAKoC,EAAEtC,EAAIiP,EAAEjP,EAAIiP,EAAE/O,OAAS,EACzGoC,EAAEvC,EAAIkP,EAAElP,GAAKuC,EAAEvC,EAAIkP,EAAElP,EAAIkP,EAAEhP,OAASqC,EAAEtC,EAAIiP,EAAEjP,GAAKsC,EAAEtC,EAAIiP,EAAEjP,EAAIiP,EAAE/O,MAClE,CAkEO,SAASiP,EAAcC,EAAW9M,GACxC,OAAI0M,EAAU1M,EAAG8M,GAAa,CAACrP,EAAGqP,EAAIrP,EAAGC,EAAGoP,EAAIpP,GAd1C,SAA2BL,EAAWC,EAAWwP,GACvD,MAAMC,EAAID,EAAInP,MAAQ,EAChBqP,EAAIF,EAAIlP,OAAS,EAOvB,MANuC,CACtC,CAACoC,EAAG,CAACvC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,GAAI/M,EAAG,CAACxC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,IAC/D,CAAChN,EAAG,CAACvC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,GAAI/M,EAAG,CAACxC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,IAC/D,CAAChN,EAAG,CAACvC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,GAAI/M,EAAG,CAACxC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,IAC/D,CAAChN,EAAG,CAACvC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,GAAI/M,EAAG,CAACxC,EAAGqP,EAAIrP,EAAIsP,EAAGrP,EAAGoP,EAAIpP,EAAIsP,KAEpDzL,IAAI0L,GA7CjB,SAA6B5P,EAAW6P,EAAW5P,EAAW6P,GAC7D,IAAIC,EAAaC,EAAGV,EAAGW,EAAYC,EAClCC,EAAuE,CACtE/P,EAAG,KACHC,EAAG,KACH+P,SAAS,EACTC,SAAS,GAGX,OADAN,GAAeD,EAAGzP,EAAIJ,EAAGI,IAAMwP,EAAGzP,EAAIJ,EAAGI,IAAM0P,EAAG1P,EAAIH,EAAGG,IAAMyP,EAAGxP,EAAIL,EAAGK,GACtD,GAAf0P,IAGJC,EAAIhQ,EAAGK,EAAIJ,EAAGI,EACdiP,EAAItP,EAAGI,EAAIH,EAAGG,EACd6P,GAAeH,EAAG1P,EAAIH,EAAGG,GAAK4P,GAAOF,EAAGzP,EAAIJ,EAAGI,GAAKiP,EACpDY,GAAeL,EAAGzP,EAAIJ,EAAGI,GAAK4P,GAAOH,EAAGxP,EAAIL,EAAGK,GAAKiP,EACpDU,EAAIC,EAAaF,EACjBT,EAAIY,EAAaH,EAGjBI,EAAO/P,EAAIJ,EAAGI,EAAK4P,GAAKH,EAAGzP,EAAIJ,EAAGI,GAClC+P,EAAO9P,EAAIL,EAAGK,EAAK2P,GAAKH,EAAGxP,EAAIL,EAAGK,GAG9B2P,EAAI,GAAKA,EAAI,IAChBG,EAAOC,SAAU,GAGdd,GAAK,GAAKA,GAAK,IAClBa,EAAOE,SAAU,IAnBVF,CAuBT,CAYsBG,CAAoBtQ,EAAIC,EAAI2P,EAAEjN,EAAGiN,EAAEhN,IAAIxD,OAAOmR,GAAOA,EAAIH,SAAWG,EAAIF,QAC9F,CAKQG,CAAkBf,EAAK9M,EAAG8M,GAAK,IAAM,CAACrP,EAAGqP,EAAIrP,EAAGC,EAAGoP,EAAIpP,EAC/D,CAEO,SAASoQ,EAAiBC,EAAkB7B,EAAYC,EAAY6B,EAAmBvO,GAG7F,MAAMpC,EAAK,CAACI,EAAGgC,EAAMhC,EAAIsQ,EAAUtQ,EAAGC,EAAG+B,EAAM/B,EAAIqQ,EAAUrQ,GACvDJ,EAAS0Q,EAAWvQ,EAAIsQ,EAAUtQ,EAAlCH,EAAwC0Q,EAAWtQ,EAAIqQ,EAAUrQ,EAEnEJ,GAAQD,EAAGI,IACdJ,EAAGI,GAAK,MAGT,MAAMwP,GAAK3P,EAAOD,EAAGK,IAAMJ,EAAOD,EAAGI,GAC/BwQ,EAAK3Q,EAAQ2P,EAAI3P,EACjB+P,EAAKlB,EAAKA,EAAOD,EAAKA,EAAKe,EAAIA,EAC/BN,EAAI,EAAIT,EAAKA,EAAK+B,EAAKhB,EACvBiB,EAAIhC,EAAKA,EAAK+B,EAAKA,EAAK/B,EAAKA,EAAKC,EAAKA,EAEvCgC,EAAgB5Q,KAAKC,KAAMmP,EAAIA,EAAM,EAAIU,EAAIa,GAC7CzQ,EAAIJ,EAAGI,EAAIH,IACdqP,EAAIwB,IAAkB,EAAId,KAC1BV,EAAIwB,IAAkB,EAAId,GACvBe,EAAM,CACX3Q,EAAGA,EACHC,EAAGuP,EAAIxP,EAAIwQ,GAMZ,OAHAG,EAAI3Q,GAAKsQ,EAAUtQ,EACnB2Q,EAAI1Q,GAAKqQ,EAAUrQ,EAEZ0Q,CACR,CAuCO,SAASC,EAAQrO,EAAUqN,EAAUV,GAC3C,IAAI2B,EAAW3B,EAAElP,EAAI4P,EAAE5P,EAAnB6Q,EAAyB3B,EAAEjP,EAAI2P,EAAE3P,EAEjC6Q,EAAMD,EAASA,EAASA,EAASA,EACjCE,GAFWxO,EAAEvC,EAAI4P,EAAE5P,GAEJ6Q,GAFUtO,EAAEtC,EAAI2P,EAAE3P,GAEA4Q,EACjC7C,EAAIlO,KAAK6J,IAAI,EAAG7J,KAAK4J,IAAI,EAAGqH,EAAMD,IACtC,MAAO,CACN9Q,EAAG4P,EAAE5P,EAAI6Q,EAAS7C,EAClB/N,EAAG2P,EAAE3P,EAAI4Q,EAAS7C,EAEpB,CAEO,SAASgD,EAAYpR,EAAWC,GACtC,OAAOC,KAAKsD,IAAIvD,EAAGG,EAAIJ,EAAGI,GAAKF,KAAKsD,IAAIvD,EAAGI,EAAIL,EAAGK,EACnD,CCxJA,SAASgR,EAAgB/Q,GACxB,OAAOA,EAAQ,GAAK,IAAMA,EAAQ,GACnC,CAEO,SAASgR,EAAkB7Q,EAAeH,EAAeC,GAC/D,OAAQE,EAAM8Q,eACb,IAAK,WACJ,OAAO,EAAIF,EAAgB/Q,GAC5B,IAAK,SACJ,MAAgB,GAATC,EACR,IAAK,SACJ,OAAOD,EAAQ,GAChB,IAAK,QACJ,MAAgB,IAATC,EACR,IAAK,aACJ,OAAOA,EAAS,EACjB,QACC,OAAO,EAEV,CAEA,MAAMiR,EACYC,IAEjB,WAAAzN,CAAYxF,GACX8F,KAAKmN,IAAMjT,CACZ,CAEA,IAAAsG,GACC,OAAOR,KAAKmN,GACb,CAEA,IAAAC,CAAKnN,EAAczF,GAElB,OADAwF,KAAKmN,IAAI7F,aAAarH,EAAMwJ,OAAOjP,IAC5BwF,IACR,CAEA,MAAAqN,CAAOhE,EAAcoD,GACpB,MAAMvS,EAAKkN,SAASC,gBAAgB,6BAA8BgC,GAC5DiE,EAAMtN,KAAKmN,IAAII,aAAarT,EAAI8F,KAAKmN,IAAIK,cAAcf,IAC7D,OAAO,IAAIS,EAAUI,EACtB,EAID,SAASpJ,EAAK7B,EAAmBoL,EAAYjN,EAAckN,GAAU,GACpE,MAAMC,EAAWtL,EAAOgL,OAAO,OAAQ,gBACrCD,KAAK,KAAMM,EAAUlN,EAAKxE,MAAQ,EAAI,GACtCoR,KAAK,KAAMM,EAAUlN,EAAKxE,MAAQ,EAAI,GACtCoR,KAAK,KAAMK,EAAKzR,MAAQ,GACxBoR,KAAK,KAAMK,EAAKxR,OAAS,GACzBmR,KAAK,QAASK,EAAKzR,OACnBoR,KAAK,SAAUK,EAAKxR,QAMtB,OAJAuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc1K,EAAM1C,EAC5B,EAEO6P,CACR,CA0DA,SAASC,EAASvL,EAAmBoL,EAAYjN,EAAc+J,EAAYC,GAC1E,MAAMmD,EAAWtL,EAAOgL,OAAO,UAAW,gBACxCD,KAAK,KAAM,GACXA,KAAK,KAAM,GACXA,KAAK,KAAM7C,GACX6C,KAAK,KAAM5C,GACX4C,KAAK,QAAS5M,EAAKxE,OACnBoR,KAAK,SAAU5M,EAAKvE,QAKtB,OAHAuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOqO,EAAiB3L,EAAM+J,EAAIC,EAAIhK,EAAM1C,EAC7C,EACO6P,CACR,CA0GA,SAASE,EAAqBxL,EAAmBoL,EAAYjN,GAC5D,MAAMwE,EAAKxE,EAAKxE,MAAQ,EAClBsO,EAAI9J,EAAKxE,MAAQ,GACjB2R,EAAWtL,EAAOgL,OAAO,IAAK,gBAwBpC,OAvBAM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,IAAK,KAAK5M,EAAKxE,MAAQ,MAAMwE,EAAKvE,OAAS,MAAMuE,EAAKxE,aAAawE,EAAKxE,MAAQ,KAAKwE,EAAKvE,OAAS,MAAMuE,EAAKxE,WACrH2R,EAASN,OAAO,SAAU,gBACxBD,KAAK,KAAM,GACXA,KAAK,KAAM5M,EAAKvE,OAAS,EAAI+I,EAAK,GAClCoI,KAAK,IAAS,GAAJ9C,GACZqD,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM9C,GACX8C,KAAK,KAAM5M,EAAKvE,OAAS,EAAI+I,EAAK,EAAQ,GAAJsF,GACtC8C,KAAK,QAAa,EAAJ9C,GACd8C,KAAK,SAAc,GAAJ9C,GACjBqD,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM9C,GACX8C,KAAK,KAAM9C,GACX8C,KAAK,KAAMK,EAAKzR,MAAQ,GACxBoR,KAAK,KAAMK,EAAKxR,OAAS,EAAI+I,GAC7BoI,KAAK,QAASK,EAAKzR,OACnBoR,KAAK,SAAUK,EAAKxR,OAAS,EAAI+I,GAEnCxE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc,CAACpP,EAAG0E,EAAK1E,EAAGC,EAAGyE,EAAKzE,EAAGC,MAAOwE,EAAKxE,MAAOC,OAAQuE,EAAKvE,OAAS,EAAI+I,GAAKlH,EAC/F,EAEO6P,CACR,CAmKO,MAAMG,EAA8E,CAC1F3C,IAAK,CAAC9I,EAAoB7B,IAAiB0D,EAAK,IAAIgJ,EAAU7K,GAAS7B,EAAMA,GAAMA,OACnFuN,WAAY,CAAC1L,EAAoB7B,IAAiB0D,EAAK,IAAIgJ,EAAU7K,GAAS7B,EAAMA,GAAM,GAAMA,OAChGwN,UAAW,CAAC3L,EAAoB7B,IAlRjC,SAAmB6B,EAAmBoL,EAAYjN,GACjD,MAAMuE,EAAKvE,EAAKxE,MAAQ,GAClB2R,EAAWtL,EAAOgL,OAAO,IAAK,gBAwBpC,OAvBAM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAM5M,EAAKxE,MAAQ,EAAI+I,GAC5BqI,KAAK,KAAM5M,EAAKvE,OAAS,EAAI8I,GAC7BqI,KAAK,QAAc,EAALrI,GACdqI,KAAK,SAAUrI,GACjB4I,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAM5M,EAAKxE,MAAQ,EAAI+I,GAC5BqI,KAAK,KAAM5M,EAAKvE,OAAS,EAAS,IAAL8I,GAC7BqI,KAAK,QAAc,EAALrI,GACdqI,KAAK,SAAUrI,GACjB4I,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAM5M,EAAKxE,MAAQ,GACxBoR,KAAK,KAAM5M,EAAKvE,OAAS,GACzBmR,KAAK,QAAS5M,EAAKxE,OACnBoR,KAAK,SAAU5M,EAAKvE,QAEtBuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc,CAACpP,EAAG0E,EAAK1E,EAAIiJ,EAAK,EAAGhJ,EAAGyE,EAAKzE,EAAGC,MAAOwE,EAAKxE,MAAQ+I,EAAI9I,OAAQuE,EAAKvE,QAAS6B,EACpG,EAEO6P,CACR,CAuPkDK,CAAU,IAAId,EAAU7K,GAAS7B,EAAMA,GAAMA,OAC9FyN,SAAU,CAAC5L,EAAoB7B,IAjXhC,SAAkB6B,EAAmBoL,EAAYjN,GAChD,MAAM4K,EAAIqC,EAAKzR,MACTuO,EAAKa,EAAI,EACTZ,EAAKuC,EAAgB3B,GACrBC,EAAIoC,EAAKxR,OAETE,EACL,OAAOqO,MAAOD,KAAMC,WAAYY,SAASb,KAAMC,YAAaY,WAAWC,EAAI,EAAIb,OAAQD,KAAMC,WAAYY,WAAgB,EAAIZ,EAARa,IAEhHsC,EAAWtL,EACf+K,KAAK,iBAAkBJ,EAAkB,WAAY5B,EAAGC,IACxDgC,OAAO,OAAQ,gBACfD,KAAK,IAAKjR,GACViR,KAAK,YAAa,cAAgBhC,EAAI,EAAI,KAAQC,EAAI,EAAK,KAe7D,OAbA7K,EAAKjD,UAAY,SAAUO,GAC1B,MAAM2O,EAAMvB,EAAc1K,EAAM1C,GAChC,IAAIoQ,EAAK1N,EAAKzE,EAAIyE,EAAKvE,OAAS,EAAIuO,EACpC,OAAIiC,EAAI1Q,EAAImS,EACJ/B,EAAiB,CAACrQ,EAAG0E,EAAK1E,EAAGC,EAAGmS,GAAK3D,EAAIC,EAAIhK,EAAM1C,IAE3DoQ,EAAK1N,EAAKzE,EAAIyE,EAAKvE,OAAS,EAAIuO,EAC5BiC,EAAI1Q,EAAImS,EACJ/B,EAAiB,CAACrQ,EAAG0E,EAAK1E,EAAGC,EAAGmS,GAAK3D,EAAIC,EAAIhK,EAAM1C,GAEpD2O,EACR,EAEOkB,CACR,CAoViDM,CAAS,IAAIf,EAAU7K,GAAS7B,EAAMA,GAAMA,OAC5F2N,OAAQ,CAAC9L,EAAoB7B,IAnV9B,SAAgB6B,EAAmBoL,EAAYjN,GAC9C,MAAM4K,EAAIqC,EAAKzR,MACTqP,EAAIoC,EAAKxR,OAETE,EACL,KAAK,IAAMiP,KAAKC,EAAI,MAAMD,EAAI,KAAKC,EAAI,aAAaA,EAAI,WACrDD,EAAI,MAAMC,MAAMD,EAAIA,EAAI,MAAMC,MAAMD,KAAKC,EAAI,WAC7CD,EAAI,KAAKC,EAAI,WAAWD,EAAI,IAAMA,KAAKC,EAAI,YAC3CD,EAAI,KAAKA,EAAI,WAAW,IAAMA,KAAKC,EAAI,IAErCsC,EAAWtL,EACf+K,KAAK,iBAAkBJ,EAAkB,SAAU5B,EAAGC,IACtDgC,OAAO,OAAQ,gBACfD,KAAK,IAAKjR,GACViR,KAAK,YAAa,cAAgBhC,EAAI,EAAI,KAAQC,EAAI,EAAK,KAO7D,OALA7K,EAAKjD,UAAY,SAAUO,GAE1B,OADYoN,EAAc1K,EAAM1C,EAEjC,EAEO6P,CACR,CA6T+CQ,CAAO,IAAIjB,EAAU7K,GAAS7B,EAAMA,GAAMA,OACxF4N,OAAQ,CAAC/L,EAAoB7B,IA7S9B,SAAgB6B,EAAmBoL,EAAYjN,GAC9C,OAAOoN,EAASvL,EAAQoL,EAAMjN,EAAMA,EAAKxE,MAAQ,EAAGwE,EAAKxE,MAAQ,EAClE,CA2S+CoS,CAAO,IAAIlB,EAAU7K,GAAS7B,EAAMA,GAAMA,OACxF6N,QAAS,CAAChM,EAAoB7B,IA1S/B,SAAiB6B,EAAmBoL,EAAYjN,GAC/C,OAAOoN,EAASvL,EAAQoL,EAAMjN,EAAmB,IAAbA,EAAKxE,MAA0B,IAAbwE,EAAKxE,MAC5D,CAwSgDqS,CAAQ,IAAInB,EAAU7K,GAAS7B,EAAMA,GAAMA,OAC1F8N,QAAS,CAACjM,EAAoB7B,IAvS/B,SAAiB6B,EAAmBoL,EAAYjN,GAC/C,MAAM+N,EAAK/N,EAAKxE,MAAQ,EAGlB2R,EAAWtL,EAAOgL,OAAO,UAAW,gBACxCD,KAAK,SACL,CAAC,GAAQ,KAAQ,EAAQ,EAAQ,IAAS,MAAS,IAAS,MAAS,GAAS,GAAS,GAAQ,KAAQ,GAAQ,MAAQxN,IAAI4O,GAAKA,EAAID,GAAIxO,KAAK,MAC7IqN,KAAK,QAAS5M,EAAKxE,OACnBoR,KAAK,SAAU5M,EAAKvE,QAKtB,OAHAuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOqO,EAAiB3L,EAAMA,EAAKxE,MAAQ,EAAGwE,EAAKxE,MAAQ,EAAGwE,EAAM1C,EACrE,EACO6P,CACR,CAyRgDW,CAAQ,IAAIpB,EAAU7K,GAAS7B,EAAMA,GAAMA,OAC1FiO,OAAQ,CAACpM,EAAoB7B,IA3P9B,SAAgB6B,EAAmBoL,EAAYjN,GAC9C,MAAMwE,EAAKxE,EAAKxE,MAAQ,GAClB2R,EAAWtL,EACf+K,KAAK,iBAAkBJ,EAAkB,SAAUxM,EAAKxE,MAAOwE,EAAKvE,SACpEoR,OAAO,IAAK,gBAcd,OAbAM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAM5M,EAAKxE,MAAQ,GACxBoR,KAAK,KAAM5M,EAAKvE,OAAS,EAAS,EAAL+I,GAC7BoI,KAAK,QAAS5M,EAAKxE,OACnBoR,KAAK,SAAU5M,EAAKvE,OAAc,EAAL+I,GAC/B2I,EAASN,OAAO,OAAQ,gBACtBD,KAAK,IAAK,OAAO5M,EAAKvE,OAAS,EAAI,EAAI+I,MAAOA,MAAO,EAAIA,MAAOxE,EAAKxE,MAAQ,EAAS,EAALgJ,MAAgB,EAALA,KAE9FxE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc,CAACpP,EAAG0E,EAAK1E,EAAGC,EAAGyE,EAAKzE,EAAIiJ,EAAK,EAAGhJ,MAAOwE,EAAKxE,MAAOC,OAAQuE,EAAKvE,OAAS+I,GAAKlH,EACpG,EAEO6P,CACR,CAwO+Cc,CAAO,IAAIvB,EAAU7K,GAAS7B,EAAMA,GAAMA,OACxFkO,sBAAuB,CAACrM,EAAoB7B,IAvO7C,SAA+B6B,EAAmBoL,EAAYjN,GAC7D,MAAMuE,EAAKvE,EAAKxE,MAAQ,EAClBsO,EAAI9J,EAAKxE,MAAQ,GACjB2R,EAAWtL,EAAOgL,OAAO,IAAK,gBAwBpC,OAvBAM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,IAAK,KAAK5M,EAAKxE,MAAQ,MAAMwE,EAAKvE,OAAS,QAAQuE,EAAKvE,WAAWuE,EAAKxE,MAAQ,MAAMwE,EAAKvE,OAAS,QAAQuE,EAAKvE,UACxH0R,EAASN,OAAO,SAAU,gBACxBD,KAAK,MAAO5M,EAAKxE,MAAQ,EAAI+I,EAAK,GAClCqI,KAAK,KAAM,GACXA,KAAK,IAAS,GAAJ9C,GACZqD,EAASN,OAAO,OAAQ,gBACtBD,KAAK,IAAK5M,EAAKxE,MAAQ,EAAI+I,EAAK,EAAQ,GAAJuF,GACpC8C,KAAK,KAAM9C,GACX8C,KAAK,QAAa,GAAJ9C,GACd8C,KAAK,SAAc,EAAJ9C,GACjBqD,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM9C,GACX8C,KAAK,KAAM9C,GACX8C,KAAK,KAAMK,EAAKzR,MAAQ,EAAI+I,GAC5BqI,KAAK,KAAMK,EAAKxR,OAAS,GACzBmR,KAAK,QAASK,EAAKzR,MAAQ,EAAI+I,GAC/BqI,KAAK,SAAUK,EAAKxR,QAEtBuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc,CAACpP,EAAG0E,EAAK1E,EAAGC,EAAGyE,EAAKzE,EAAGC,MAAOwE,EAAKxE,MAAQ,EAAI+I,EAAI9I,OAAQuE,EAAKvE,QAAS6B,EAC/F,EAEO6P,CACR,CA2M8De,CAAsB,IAAIxB,EAAU7K,GAAS7B,EAAMA,GAAMA,OACtHqN,qBAAsB,CAACxL,EAAoB7B,IAAiBqN,EAAqB,IAAIX,EAAU7K,GAAS7B,EAAMA,GAAMA,OACpHmO,aAAc,CAACtM,EAAoB7B,IAAiBqN,EAAqB,IAAIX,EAAU7K,GAAS7B,EAAMA,GAAMA,OAC5GoO,KAAM,CAACvM,EAAoB7B,IA9K5B,SAAc6B,EAAmBoL,EAAYjN,GAC5C,MAAM4K,EAAI5K,EAAKxE,MACTqP,EAAI7K,EAAKvE,OACTuO,EAAKa,EAAI,EACTd,EAAKC,GAAM,IAAMY,EAAI,IAErBjP,EACL,KAAKoO,aACFA,KAAMC,aAAca,WACpBd,KAAMC,cAAea,WACrBD,aACAb,KAAMC,aAAca,YACnBD,MAECuC,EAAWtL,EACfgL,OAAO,OAAQ,gBACfD,KAAK,IAAKjR,GACViR,KAAK,YAAa,cAAgBhC,EAAI,EAAI,KAAQC,EAAI,EAAK,KAM7D,OAJA7K,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc,CAACpP,EAAG0E,EAAK1E,EAAIyO,EAAIxO,EAAGyE,EAAKzE,EAAGC,MAAOwE,EAAKxE,MAAQ,EAAIuO,EAAItO,OAAQuE,EAAKvE,QAAS6B,EACpG,EAEO6P,CACR,CAsJ6CiB,CAAK,IAAI1B,EAAU7K,GAAS7B,EAAMA,GAAMA,OACpFqO,MAAO,CAACxM,EAAoB7B,IArJ7B,SAAe6B,EAAmBoL,EAAYjN,GAC7C,MAAM4K,EAAI5K,EAAKxE,MACTqP,EAAI7K,EAAKvE,OAGT6S,EAAWlT,KAAK6J,IAAQ,IAAJ2F,EAAc,IAAJC,GAC9B0D,EAAmB,GAAXD,EACRE,EAAsB,IAAXF,EACXG,EAAsB,IAAXH,EAGXI,EAAkB,IAAXJ,EACPK,EAAwB,IAAXL,EAGbM,EAAkB,IAAXN,EACPO,EAAkB,GAAXP,EAGPQ,EAAQlE,EACRmE,GAAWlE,EAAI,EAAI2D,EAAWF,EAC9BU,EAAQnE,EAAI2D,EAAWF,EAGvBnB,EAAWtL,EACf+K,KAAK,iBAAkBJ,EAAkB,QAAS5B,EAAGC,IACrDgC,OAAO,IAAK,gBAGdM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KARO,GASZA,KAAK,KATO,GAUZA,KAAK,KAAMkC,EAAQ,GACnBlC,KAAK,IAAKmC,GACVnC,KAAK,QAASkC,GACdlC,KAAK,SAAUoC,GAGjB,MAAMC,GAAWpE,EAAI,EAAI2D,EACzBrB,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM2B,GACX3B,KAAK,KAAM2B,GACX3B,KAAK,KAAM0B,EAAW,GACtB1B,KAAK,IAAKqC,GACVrC,KAAK,QAAS0B,GACd1B,KAAK,SAAU0B,GAGjBnB,EAASN,OAAO,OAAQ,gBACtBD,KAAK,QAAS,iBACdA,KAAK,KAAM,GACXA,KAAK,KAAMqC,GACXrC,KAAK,KAAM,GACXA,KAAK,MAAO/B,EAAI,EAAe,EAAX4D,GACpB7B,KAAK,eAA2B,GAAX6B,GACrB7B,KAAK,iBAAkB,SAGzBO,EAASN,OAAO,SAAU,gBACxBD,KAAK,QAAS,sBACdA,KAAK,KAAM,GACXA,KAAK,MAAO/B,EAAI,EAAe,EAAX4D,GACpB7B,KAAK,IAAgB,IAAX6B,GAGZ,MAAMS,EAAOD,EAAqB,GAAXX,EACvBnB,EAASN,OAAO,SAAU,gBACxBD,KAAK,QAAS,aACdA,KAAK,MAAO+B,GACZ/B,KAAK,KAAMsC,GACXtC,KAAK,IAAK8B,GACZvB,EAASN,OAAO,SAAU,gBACxBD,KAAK,QAAS,aACdA,KAAK,KAAM+B,GACX/B,KAAK,KAAMsC,GACXtC,KAAK,IAAK8B,GAGZ,MAAMS,EAASF,EAAqB,GAAXX,EACnBc,EAAoB,IAAXd,EA4Bf,OA3BAnB,EAASN,OAAO,OAAQ,gBACtBD,KAAK,QAAS,eACdA,KAAK,IAAK,KAAKwC,EAAS,KAAKD,QAAaA,EAAkB,GAATC,KAAgBA,EAAS,KAAKD,KACjFvC,KAAK,OAAQ,QACbA,KAAK,eAA2B,GAAX6B,GACrB7B,KAAK,iBAAkB,SAGzBO,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAa,IAAPgC,GACXhC,KAAK,KAAa,IAAPgC,GACXhC,KAAK,KAAM0B,EAAW,EAAIM,EAAO,GACjChC,KAAK,IAAKsC,EAAOL,EAAO,GACxBjC,KAAK,QAASgC,GACdhC,KAAK,SAAUiC,GACjB1B,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAa,IAAPgC,GACXhC,KAAK,KAAa,IAAPgC,GACXhC,KAAK,IAAK0B,EAAW,EAAI,GACzB1B,KAAK,IAAKsC,EAAOL,EAAO,GACxBjC,KAAK,QAASgC,GACdhC,KAAK,SAAUiC,GAEjB7O,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc1K,EAAM1C,EAC5B,EAEO6P,CACR,CAyC8CkB,CAAM,IAAI3B,EAAU7K,GAAS7B,EAAMA,GAAMA,OACtFqP,WAAY,CAACxN,EAAoB7B,IAxClC,SAAoB6B,EAAmBoL,EAAYjN,GAClD,MAAMwE,EAAKxE,EAAKvE,OAAS,EACnB0R,EAAWtL,EACf+K,KAAK,iBAAkBJ,EAAkB,aAAcxM,EAAKxE,MAAOwE,EAAKvE,SACxEoR,OAAO,IAAK,gBAkBd,OAjBAM,EAASN,OAAO,OAAQ,gBACtBD,KAAK,IAAK,aACN5M,EAAKxE,MAAQ,MAAMwE,EAAKvE,OAAS,EAAI+I,MAAOxE,EAAKxE,kBACjDwE,EAAKxE,MAAQ,EAAIgJ,EAAK,MAAMxE,EAAKvE,OAAS,EAAI+I,EAAK,MAAMA,EAAK,MAAMA,EAAK,OAAOA,EAAK,gBACrFxE,EAAKxE,MAAQ,EAAIgJ,MAAOxE,EAAKvE,OAAS,EAAI+I,EAAK,MAAMxE,EAAKxE,MAAQgJ,EAAKA,EAAK,MAAMA,EAAK,OAAOxE,EAAKxE,MAAQgJ,EAAKA,EAAK,aAE3H2I,EAASN,OAAO,OAAQ,gBACtBD,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAM5M,EAAKxE,MAAQ,GACxBoR,KAAK,KAAM5M,EAAKvE,OAAS,GACzBmR,KAAK,QAAS5M,EAAKxE,OACnBoR,KAAK,SAAU5M,EAAKvE,QAEtBuE,EAAKjD,UAAY,SAAUO,GAC1B,OAAOoN,EAAc1K,EAAM1C,EAC5B,EAEO6P,CACR,CAiBmDkC,CAAW,IAAI3C,EAAU7K,GAAS7B,EAAMA,GAAMA,qBCnc1F,MAAMsP,EACKC,SAAkB,GAC3BtD,IAAc,EACduD,aAAuB,EACdC,UACAC,UACjBC,OACQC,iBAA+B,KAEvC,WAAA1Q,CAAYjC,EAAYwS,EAAsBC,GAC7ClQ,KAAKiQ,UAAYA,EACjBjQ,KAAKkQ,UAAYA,EACjBlQ,KAAKmQ,OA+DP,SAAkBE,GACjB,IAAIC,EACJ,OAAO,WACN,MAAMC,EAAUvQ,KAKhBwQ,aAAaF,GACbA,EAAUG,WALI,WACbH,EAAU,KACVD,EAAKK,MAAMH,EACZ,EAtE6C,IAyE9C,CACD,CA1EgBI,CAAS,IAAM3Q,KAAK4Q,UACnC,CAGA,YAAAC,GACM7Q,KAAKoQ,mBACTpQ,KAAKoQ,iBAAmBpQ,KAAK8Q,UAAU9Q,KAAKiQ,aAE9C,CAEA,MAAA3S,GACC,OAAO0C,KAAK+P,SAASzS,MACtB,CAEA,YAAAyT,GACC,OAAO/Q,KAAK8Q,UAAU9Q,KAAK+P,SAAS/P,KAAKyM,IAAM,GAChD,CAEQ,OAAAmE,GACP,IAAK5Q,KAAKoQ,iBACT,MAAM5S,MAAM,4EAGbwC,KAAK+P,SAAS/P,KAAKyM,KAAOzM,KAAK8Q,UAAU9Q,KAAKiQ,aAC9CjQ,KAAK+P,SAAS/P,KAAKyM,IAAM,GAAKzM,KAAKoQ,iBACnCpQ,KAAKoQ,iBAAmB,KACxBpQ,KAAKyM,KAAO,EAGZzM,KAAK+P,SAASiB,OAAOhR,KAAKyM,IAC3B,CAEQ,SAAAqE,CAAUG,GAEjB,MAA+B,oBAApBC,gBACHA,gBAAgBD,GAEjBjK,KAAKmK,MAAMnK,KAAKC,UAAUgK,GAClC,CAEA,IAAAG,GACC,GAAIpR,KAAKyM,IAAM,EAAG,OAClBzM,KAAKyM,KAAO,EACZ,MAAMwE,EAAMjR,KAAK+P,SAAS/P,KAAKyM,IAAM,GACrCzM,KAAKkQ,UAAUlQ,KAAK8Q,UAAUG,GAC/B,CAEA,IAAAI,GACC,GAAIrR,KAAKyM,IAAMzM,KAAK+P,SAASzS,OAAS,EAAG,OACzC,MAAM2T,EAAMjR,KAAK+P,SAAS/P,KAAKyM,KAC/BzM,KAAKkQ,UAAUlQ,KAAK8Q,UAAUG,IAC9BjR,KAAKyM,KAAO,CACb,CAEA,OAAA6E,GACC,OAAOtR,KAAKyM,MAAQzM,KAAKgQ,YAC1B,CAEA,QAAAuB,GACCvR,KAAKgQ,aAAehQ,KAAKyM,GAC1B,iCCvDD,SAAS+E,EACR9K,EACA1K,EACAR,EACAwO,EACAyH,EACAC,GAEA,MAKMC,EAAU3J,EAAYtB,EAAM1K,EALpB,CACb,cAAeyN,OAAOmI,EAAAC,GAAW9W,SAAS,gBAC1C,YAAa,GAAGS,MAChB,cAAewO,EAAO,OAAS,WAKhC,MAAO,CACN9B,MAHayJ,EAAQzJ,MAAM5K,OAAS,EAAIqU,EAAQzJ,MAAQ,CAAC,IAIzD1M,WACAsW,WAAYtW,EAAW,EACvBwO,OACA0H,QACAD,WAEF,OCgDA,MAAMM,EAAmBH,EAAAI,GACnBC,EAAmBL,EAAAM,GAsClB,MAAMC,EACZ1U,GACAwC,KACAmS,SACAzR,MACA0R,aACAC,UACAC,SACAC,gBACAC,gBACAC,cAAqC,IAAI5R,IACjC6R,MACAC,cAAwB,EACxBC,aAAuB,EACvBC,UAAoB,GACpBC,cAAwB,EAEhC,WAAArT,CAAYjC,EAAawC,GACxBD,KAAKvC,GAAKA,EACVuC,KAAKC,KAAOA,EAEZD,KAAKW,MAAQ,GACbX,KAAKqS,aAAe,IAAIvR,IACxBd,KAAKoS,SAAW,IAAItR,IACpBd,KAAKsS,UAAY,IAAIxR,IAErBd,KAAK2S,MAAQ,IAAI7C,EAChB9P,KAAKvC,GACL,IAAMuC,KAAKgT,cAAa,GACvBC,GAAOjT,KAAKkT,aAAaD,GAAI,IAI/BE,OAAOC,MAAQpT,IAChB,CAGA,IAAAqT,CAAKC,GACJA,GAAUtT,KAAKkT,aAAaI,GAC5BtT,KAAK2S,MAAQ,IAAI7C,EAChB9P,KAAKvC,GACL,IAAMuC,KAAKgT,cAAa,GACvBC,GAAOjT,KAAKkT,aAAaD,GAAI,IAE3BjT,KAAK2S,MAAMrV,UACd0C,KAAKkT,aAAalT,KAAK2S,MAAM5B,gBAI9B/Q,KAAK2S,MAAM9B,eACX7Q,KAAK2S,MAAMxC,QACZ,CAEA,OAAAoD,CAAQ9V,EAAYN,EAAeqW,EAAaC,EAAqBtZ,EAAkBuZ,GACtF,GAAI1T,KAAKoS,SAASvO,IAAIpG,GAAK,MAAMD,MAAM,mBAAqBC,GAC5D,MAAMkW,EAAY,IAAI1B,KAAqB9X,GACrCgC,GAASwX,EAAUxX,OAAS,OAAO8Q,cAEnC2G,EAA0B,WAAVzX,EAAqB,IAAM,IAC3CH,EAAQJ,KAAK4J,IAFE,IAEgBmO,EAAU3X,OAAS,GAElD6X,EDlJD,SACNC,EACAC,EACAN,EACAO,EACAxY,GAEA,MAAMyY,EAAYrY,KAAK4J,IAAIwO,EAAYE,GAAwB,IACzDC,EAAS,CACd3C,EAAUsC,EAAOG,EAAWzY,GAAU,EAtCtB,EAsCuC,QACvDgW,EAAU,IAAIuC,KAAaE,EAAsB,IAAXzY,GAAiB,EAtCpC,IAuCnBgW,EAAUiC,EAAaQ,EAAWrY,KAAK6J,IAAe,GAAXjK,EAAgB,KAAK,EAAO,EAAG,gBAErE4Y,EAAaD,EAAO5V,OACzB,CAACtC,EAAQoY,IAAUpY,EAASoY,EAAMnM,MAAM5K,OAAS+W,EAAMvC,WAAauC,EAAM5C,SAC1E,GAGD,MAAO,CACN0C,SACAC,aACAR,cAAeQ,EAAaE,GAE9B,CC2HwBC,CAAkBpX,EAAOqW,EAAKC,EAAazX,EADhD2X,EAAUnY,UAAY,IAEvC,IAAIS,EAASL,KAAK4J,IAAIoO,EAAeD,EAAU1X,QAAU,EAAG4X,EAAcD,eAI1E,IAAK,IAAIzJ,EAAI,EAAGA,EAAI,GAAIA,IAAK,CAC5B,MAAMqK,EAAiBX,EAAcD,cACpChY,KAAKsD,IAAI8N,EAAkB7Q,EAAOH,EAAOC,IAC1C,GAAIuY,GAAkBvY,EAAS,GAC9B,MAEDA,EAASuY,CACV,CAEA,MAAMhG,EAAU,CACf/Q,KAAIqW,MAAO3W,EAAOqW,MAAKC,cAAatZ,MAAOwZ,EAC3C7X,EAAG,EAAGC,EAAG,EAAGC,QAAOC,SAAQsB,UAAW,KAAMmW,OAAMG,iBAEnD7T,KAAKoS,SAASrK,IAAIyG,EAAE/Q,GAAI+Q,EACzB,CAEA,KAAAjO,GACC,OAAOkU,MAAMlY,KAAKyD,KAAKoS,SAASnP,SACjC,CAEA,OAAAyR,CAAQjX,EAAYkX,EAAkBC,EAAgBzX,EAAeT,EAAmBvC,GACvFuC,GAAYA,EAASpC,QAAQ,CAAC+D,EAAG8L,KAChC,MAAMX,EAAInL,EACVmL,EAAE/L,GAAK,KAAKA,KAAM0M,IAClBnK,KAAKqS,aAAatK,IAAIyB,EAAE/L,GAAI+L,KAO7B,MAsBMnN,EAAO,CACZoB,KACAlB,KAAMyD,KAAKoS,SAAS/Q,IAAIsT,GACxBlY,GAAIuD,KAAKoS,SAAS/Q,IAAIuT,GACtBzX,QACAT,SAAU,KACVvC,MAAO,IAAI4X,KAAqB5X,GAChC0a,WAhBmBxW,IACnB,MAAMmL,EAAInL,EAMV,OALKmL,EAAE/L,KACN+L,EAAE/L,GARmB,EAACqX,EAAgBzW,IAGhC,KAAKyW,OAXIC,KAChB,IAAI1J,EAAI,WACR,IAAK,IAAIlB,EAAI,EAAGA,EAAI4K,EAAMzX,OAAQ6M,IACjCkB,GAAK0J,EAAMC,WAAW7K,GACtBkB,EAAIzP,KAAKqZ,KAAK5J,EAAG,UAElB,OAAQA,IAAM,GAAG3Q,SAAS,KAKFwa,CAAQ,GAAGJ,KAFxBzW,EAAUvC,KACVuC,EAAUtC,OAMboZ,CAAe9Y,EAAKoB,GAAIY,GAC/B2B,KAAKqS,aAAatK,IAAIyB,EAAE/L,GAAI+L,IAE7BA,EAAEnN,KAAOA,EACFgC,GAUP+W,qBAAqB,GAEtBpV,KAAKW,MAAM9D,KAAKR,GACZK,IACHL,EAAKK,SAAWA,EAASkD,IAAIvB,GAAKhC,EAAKwY,WAAWxW,IAEpD,CAEA,QAAAgX,CAAS5X,EAAYwC,EAAcqV,EAAyBnb,GAC3D,GAAI6F,KAAKsS,UAAUzO,IAAIpG,GAEtB,YADA8X,QAAQC,MAAM,iBAAiB/X,KAAMwC,KAGtC,MAAMS,EAAe,CACpBjD,KAAIwC,OAAMnE,EAAG,KAAMC,EAAG,KAAMC,MAAO,KAAMC,OAAQ,KACjDsE,MAAO+U,EAAc1V,IAAI2J,IACxB,MAAMiF,EAAIxO,KAAKoS,SAAS/Q,IAAIkI,IAAMvJ,KAAKsS,UAAUjR,IAAIkI,GAErD,OADKiF,GAAG+G,QAAQC,MAAM,iBAAiBjM,yBAAyB9L,MAAOwC,MAChEuO,IACL1T,OAAO2a,SACVtb,SAED6F,KAAKsS,UAAUvK,IAAItK,EAAIiD,EACxB,CAWA,eAAAgV,CAAgBlV,EAAYmV,GAC3BnV,EAAKmV,SAAWA,EAChBA,EACCnV,EAAKoV,IAAIlM,UAAU5F,IAAI,YACvBtD,EAAKoV,IAAIlM,UAAU5B,OAAO,YAC3B9H,KAAK6V,gBACN,CAEQ,cAAAA,GACP7V,KAAKW,MAAMrG,QAAQwb,IACdA,EAAErZ,GAAGkZ,UAAYG,EAAEvZ,KAAKoZ,SAC3BG,EAAEF,IAAIlM,UAAU5F,IAAI,YAEpBgS,EAAEF,IAAIlM,UAAU5B,OAAO,aAG1B,CAGQ,mBAAAiO,GACP/V,KAAKyS,qBAAkB7T,EACvBoB,KAAKW,MAAMrG,QAAQ+B,IAClBA,EAAK2Z,oBAAiBpX,EACtBvC,EAAK4Z,uBAAoBrX,IAE1BoB,KAAKsS,UAAUhY,QAAQoG,IACtBA,EAAMwV,kBAAetX,EACrB8B,EAAMyV,uBAAoBvX,GAE5B,CAEA,QAAAwX,CAAS5H,EAAS1S,EAAWC,EAAWsa,GAAuB,EAAOC,GAAoB,GACzF,GAAK9H,EAAL,CAGA,GAAIxO,KAAK6S,cAAgBwD,EAAa,CACrC,MAAME,EAAUvW,KAAKwW,WAAW1a,EAAGC,GACnCD,EAAIya,EAAQza,EACZC,EAAIwa,EAAQxa,CACb,CAEIyS,EAAE1S,GAAKA,GAAK0S,EAAEzS,GAAKA,IAElBua,GACJtW,KAAK2S,MAAM9B,eAEZ7Q,KAAK+V,sBACLvH,EAAE1S,EAAIA,EACN0S,EAAEzS,EAAIA,EACN+O,EAAY0D,EAAEoH,IAAK9Z,EAAGC,GACtBiE,KAAKyW,YAAYjI,GACjBxO,KAAK0W,aAAalI,GACb8H,GACJtW,KAAK2S,MAAMxC,SArBJ,CAuBT,CAEA,cAAAwG,CAAenN,EAAe1N,EAAWC,EAAWsa,GAAuB,EAAOC,GAAoB,GAErG,GAAItW,KAAK6S,cAAgBwD,EAAa,CACrC,MAAME,EAAUvW,KAAKwW,WAAW1a,EAAGC,GACnCD,EAAIya,EAAQza,EACZC,EAAIwa,EAAQxa,CACb,CAGIyN,EAAE1N,GAAKA,GAAK0N,EAAEzN,GAAKA,IAClBua,GACJtW,KAAK2S,MAAM9B,eAEZ7Q,KAAK+V,sBACLvM,EAAE1N,EAAIA,EACN0N,EAAEzN,EAAIA,EACNiE,KAAK4W,WAAWpN,EAAEnN,MACbia,GACJtW,KAAK2S,MAAMxC,SAEb,CAEA,YAAA0G,CAAa9R,EAAYC,EAAYqR,GAAuB,GAC3DrW,KAAKO,QAAQjG,QAAQkU,GAAKA,EAAEmH,UAAY3V,KAAKoW,SAAS5H,EAAGA,EAAE1S,EAAIiJ,EAAIyJ,EAAEzS,EAAIiJ,EAAIqR,GAAa,IAC1FrW,KAAKqS,aAAa/X,QAAQkP,GAAKA,EAAEmM,UAAY3V,KAAK2W,eAAenN,EAAGA,EAAE1N,EAAIiJ,EAAIyE,EAAEzN,EAAIiJ,EAAIqR,GAAa,GACtG,CAEA,gBAAAS,CAAiBza,EAAYgC,EAAUoO,EAAasK,GACnD/W,KAAK2S,MAAM9B,eACX7Q,KAAK+V,sBACL,MAAMvM,EAAInN,EAAKwY,WAAWxW,GAC1BmL,EAAEmM,UAAW,EACToB,IACH1a,EAAKK,SAASpC,QAAQkP,GAAKA,EAAErM,OAAQ,GACrCqM,EAAErM,OAAQ,GAEXd,EAAKK,SAASsU,OAAOvE,EAAM,EAAG,EAAGjD,GACjCxJ,KAAK4W,WAAWva,GAChB2D,KAAK2S,MAAMxC,QACZ,CAEA,gBAAA6G,CAAiBxN,GAChBxJ,KAAK2S,MAAM9B,eACX7Q,KAAK+V,sBAEL,MAAM1Y,EAAQmM,EAAEnN,KAAKK,SAASua,QAAQzN,GAClCnM,GAAS,IACZmM,EAAEnN,KAAKK,SAASsU,OAAO3T,EAAO,GAC9B2C,KAAKqS,aAAa6E,OAAO1N,EAAE/L,IAG3B+L,EAAEnN,KAAK+Y,qBAAsB,GAG9BpV,KAAK4W,WAAWpN,EAAEnN,MAClB2D,KAAK2S,MAAMxC,QACZ,CAEA,OAAAmB,GACC,OAAOtR,KAAK2S,MAAMrB,SACnB,CAEA,IAAAF,GACCpR,KAAK2S,MAAMvB,MACZ,CAEA,IAAAC,GACCrR,KAAK2S,MAAMtB,MACZ,CAIA,YAAA8F,GACC,MAAMC,EAAgBpX,KAAKqX,yBAGrBC,EAFU,IAECF,EAActb,EACzByb,EAHU,IAGCH,EAAcrb,EAG/BiE,KAAK+S,cAAe,EAEpB/S,KAAK2S,MAAM9B,eAEX7Q,KAAKoS,SAAS9X,QAAQkG,IACrBR,KAAKoW,SAAS5V,EAAMA,EAAK1E,EAAIwb,EAAS9W,EAAKzE,EAAIwb,GAAS,GAAM,KAG/DvX,KAAKqS,aAAa/X,QAAQ4C,IACzB8C,KAAK2W,eAAezZ,EAAQA,EAAOpB,EAAIwb,EAASpa,EAAOnB,EAAIwb,GAAS,GAAM,KAG3EvX,KAAK2S,MAAMxC,QAGZ,CAGA,iBAAAqH,GACC,MAAMC,EAAcC,IACdC,EAAYC,EAAIpK,cAAc,UAChCmK,IACHA,EAAUrQ,aAAa,YAAa,SAASmQ,sBAqtDhD,SAAgCI,GAC/B,MAAMC,EAAKD,EAAUR,yBACfU,EAAOL,IACPtM,EAAIxP,KAAK4J,IAAIoS,EAAII,cAAcC,YAAcF,EAAMD,EAAGhc,EAAIgc,EAAG9b,MAAQ,IACrEqP,EAAIzP,KAAK4J,IAAIoS,EAAII,cAAcE,aAAeH,EAAMD,EAAG/b,EAAI+b,EAAG7b,OAAS,IAC7E2b,EAAItQ,aAAa,QAASmC,OAAO2B,EAAI2M,IACrCH,EAAItQ,aAAa,SAAUmC,OAAO4B,EAAI0M,GACvC,CA3tDGI,CAAuBnY,OAIxBoY,GAAepY,KAAKvC,IAGpBuC,KAAK+S,cAAe,CACrB,CAGA,iBAAAsF,GACC,OAAOrY,KAAK+S,YACb,CAGA,SAAAuF,GACC,MAAMX,EAAYC,EAAIpK,cAAc,UAChCmK,IAEHA,EAAUrQ,aAAa,YAAa,4BACpCiR,KAIDH,GAAepY,KAAKvC,GACrB,CAGQ,WAAAgZ,CAAYjI,GACnBxO,KAAKW,MAAMrG,QAAQwb,IAAMtH,GAAKsH,EAAEvZ,MAAQiS,GAAKsH,EAAErZ,KAAOuD,KAAK4W,WAAWd,IACtE9V,KAAK6V,gBACN,CAEA,UAAAe,CAAWd,GACV,MAAMzX,EAAIyX,EAAEF,IAAIoC,cAChB3Z,EAAEma,YAAY1C,EAAEF,KAChBE,EAAEF,IAAM6C,EAAU3C,GAClBzX,EAAEgM,OAAOyL,EAAEF,IACZ,CAEQ,YAAAc,CAAalW,GACpBR,KAAKsS,UAAUhY,QAAQoG,IAEtB,MAAMrC,EAAIqC,EAAMkV,IAAIoC,cACpB3Z,EAAEma,YAAY9X,EAAMkV,KACpB8C,EAAWhY,GACXrC,EAAEgM,OAAO3J,EAAMkV,MAEjB,CAEA,SAAA+C,GACM3Y,KAAKyS,kBACTzS,KAAKyS,iBAAkB,EAAAa,EAAAsF,GAAsB5Y,OAG9C,MAAM6Y,EAA6BzR,SAASoG,cAAc,aAIpDsL,GAHUD,EAAYrL,cAAc,gBAGxBqL,EAAYE,WAAU,IAGxCD,EAAUE,iBAAiB,gCAAgC1e,QAAQoZ,IAClEA,EAAKpM,aAAa,OAAQoM,EAAKuF,aAAa,qBAAuB,IACnEvF,EAAKwF,gBAAgB,sBAItB,MAAMC,EAAgBL,EAAUtL,cAAc,gBAC1C2L,GACHA,EAAcrR,SAKf,MAAMsP,EAAgBpX,KAAKyS,gBAAgBtR,OAMrCiY,EAAchC,EAAcpb,MAASqd,IACrCC,EAAelC,EAAcnb,OAAUod,IAGvC/B,EAPU,GAOCF,EAActb,EACzByb,EARU,GAQCH,EAAcrb,EAGzBwd,EAAkBT,EAAUtL,cAAc,UAC5C+L,GAEHA,EAAgBjS,aAAa,YAAa,sBAAsBgQ,MAAYC,MAI7EuB,EAAUxR,aAAa,UAAW,OAAO8R,KAAeE,KACxDR,EAAUxR,aAAa,QAASmC,OAAO2P,IACvCN,EAAUxR,aAAa,SAAUmC,OAAO6P,IAGxCR,EAAUxR,aAAa,QAAS,8BAGhCtH,KAAKwZ,gCAAgCV,GAGrC,MAAMW,EAASrS,SAASsS,cAAc,UAUtC,OATAD,EAAOnS,aAAa,OAAQ,oBAC5BtH,KAAKuS,SAASe,OAAStT,KAAKgT,eAC5ByG,EAAOpP,OAAO,YAA0BrD,KAAKC,UAAUjH,KAAKuS,SAAU,KAAM,GAshBjEoH,QAAQ,OAAQ,mBAthBuD,OAClFb,EAAUvL,aAAakM,EAAQX,EAAUc,YAG7Bd,EAAUe,SAIvB,CAIQ,+BAAAL,CAAgC5B,GACP,IAA5B5X,KAAK0S,cAAcnR,OAGvBqW,EAAIoB,iBAAiB,UAAU1e,QAAQJ,IACtC,MAAMU,EAAOV,EAAG+e,aAAa,QACzBre,GAAQoF,KAAK0S,cAAc7O,IAAIjJ,IAClCV,EAAGoN,aAAa,OAAQ,OAAOtH,KAAK0S,cAAcrR,IAAIzG,OAAUA,QAKlEgd,EAAIoB,iBAAiB,YAAY1e,QAAQJ,IACxC,MAAMW,EAASX,EAAG+e,aAAa,UAC3Bpe,GAAUmF,KAAK0S,cAAc7O,IAAIhJ,IACpCX,EAAGoN,aAAa,SAAU,OAAOtH,KAAK0S,cAAcrR,IAAIxG,OAAYA,QAGvE,CAGA,sBAAAwc,GACC,IAAIyC,EAAOC,IAAUC,EAAOD,IAAUE,GAAO,IAAWC,GAAO,IAqE/D,OAlEAla,KAAKO,QAAQjG,QAAQkG,IACpB,MAAM2Z,EAAO3Z,EAAK1E,EAAI0E,EAAKxE,MAAQ,EAC7Boe,EAAQ5Z,EAAK1E,EAAI0E,EAAKxE,MAAQ,EAC9Bqe,EAAM7Z,EAAKzE,EAAIyE,EAAKvE,OAAS,EAC7Bqe,EAAS9Z,EAAKzE,EAAIyE,EAAKvE,OAAS,EAEtC6d,EAAOle,KAAK6J,IAAIqU,EAAMK,GACtBF,EAAOre,KAAK4J,IAAIyU,EAAMG,GACtBJ,EAAOpe,KAAK6J,IAAIuU,EAAMK,GACtBH,EAAOte,KAAK4J,IAAI0U,EAAMI,KAIvBta,KAAKqS,aAAa/X,QAAQ4C,IACzB4c,EAAOle,KAAK6J,IAAIqU,EAAM5c,EAAOpB,EAAI,GACjCme,EAAOre,KAAK4J,IAAIyU,EAAM/c,EAAOpB,EAAI,GACjCke,EAAOpe,KAAK6J,IAAIuU,EAAM9c,EAAOnB,EAAI,GACjCme,EAAOte,KAAK4J,IAAI0U,EAAMhd,EAAOnB,EAAI,KAIlCiE,KAAKsS,UAAUhY,QAAQoG,IACtB,MAAMyZ,EAAOzZ,EAAM5E,EAAI4E,EAAM1E,MAAQ,EAC/Boe,EAAQ1Z,EAAM5E,EAAI4E,EAAM1E,MAAQ,EAChCqe,EAAM3Z,EAAM3E,EAAI2E,EAAMzE,OAAS,EAC/Bqe,EAAS5Z,EAAM3E,EAAI2E,EAAMzE,OAAS,EAExC6d,EAAOle,KAAK6J,IAAIqU,EAAMK,GACtBF,EAAOre,KAAK4J,IAAIyU,EAAMG,GACtBJ,EAAOpe,KAAK6J,IAAIuU,EAAMK,GACtBH,EAAOte,KAAK4J,IAAI0U,EAAMI,KAIvBta,KAAKW,MAAMrG,QAAQ+B,IAkBlB,GAhBAyd,EAAOle,KAAK6J,IAAIqU,EAAMzd,EAAKE,KAAKT,EAAI,GAAIO,EAAKI,GAAGX,EAAI,IACpDme,EAAOre,KAAK4J,IAAIyU,EAAM5d,EAAKE,KAAKT,EAAI,GAAIO,EAAKI,GAAGX,EAAI,IACpDke,EAAOpe,KAAK6J,IAAIuU,EAAM3d,EAAKE,KAAKR,EAAI,GAAIM,EAAKI,GAAGV,EAAI,IACpDme,EAAOte,KAAK4J,IAAI0U,EAAM7d,EAAKE,KAAKR,EAAI,GAAIM,EAAKI,GAAGV,EAAI,IAGhDM,EAAKK,UACRL,EAAKK,SAASpC,QAAQ4C,IACrB4c,EAAOle,KAAK6J,IAAIqU,EAAM5c,EAAOpB,EAAI,IACjCme,EAAOre,KAAK4J,IAAIyU,EAAM/c,EAAOpB,EAAI,IACjCke,EAAOpe,KAAK6J,IAAIuU,EAAM9c,EAAOnB,EAAI,IACjCme,EAAOte,KAAK4J,IAAI0U,EAAMhd,EAAOnB,EAAI,MAK/BM,EAAKc,OAASd,EAAKc,MAAMgL,OAAQ,CAEpC,MAAMoS,GAAWle,EAAKE,KAAKT,EAAIO,EAAKI,GAAGX,GAAK,EACtC0e,GAAWne,EAAKE,KAAKR,EAAIM,EAAKI,GAAGV,GAAK,EACtC0e,EAAsC,GAApBpe,EAAKc,MAAMG,OAAc,GAEjDwc,EAAOle,KAAK6J,IAAIqU,EAAMS,EAAUE,GAChCR,EAAOre,KAAK4J,IAAIyU,EAAMM,EAAUE,GAChCT,EAAOpe,KAAK6J,IAAIuU,EAAMQ,EAAU,IAChCN,EAAOte,KAAK4J,IAAI0U,EAAMM,EAAU,GACjC,IAIGV,IAASC,IACL,CAAEje,EAAG,EAAGC,EAAG,EAAGC,MAAO,IAAKC,OAAQ,KAGnC,CACNH,EAAGge,EACH/d,EAAGie,EACHhe,MAAOie,EAAOH,EACd7d,OAAQie,EAAOF,EAEjB,CAQA,YAAAhH,CAAa0H,GAAO,GACnB,MAAMzO,EAAc,CAAC,EAmBrB,OAlBAjM,KAAKO,QAAQjG,QAAQkU,GAAKvC,EAAIuC,EAAE/Q,IAAM,CAAC3B,EAAG0S,EAAE1S,EAAGC,EAAGyS,EAAEzS,IACpDiE,KAAKW,MAAMrG,QAAQwb,IAClB,IAAKA,EAAEpZ,SAAU,OAEjB,MAAMie,EAAM7E,EAAEpZ,SAASkD,IAAI4J,IAAC,CAC3B1N,EAAG0N,EAAE1N,EACLC,EAAGyN,EAAEzN,EACLoB,MAAOqM,EAAErM,MACTgB,KAAMqL,EAAErL,SAELwc,EAAIrd,QAAUod,KACjBzO,EAAI,KAAK6J,EAAErY,MAAQkd,GAGhB7E,EAAEV,sBACLnJ,EAAI,KAAK6J,EAAErY,eAAgB,KAGtBwO,CACR,CAEA,QAAAsF,GACCvR,KAAK2S,MAAMpB,UACZ,CAEA,YAAA2B,CAAaI,EAAgBsH,GAAW,GACvC5a,KAAK+V,sBACL,IAAK,MAAOxM,EAAGC,KAAMpP,OAAOyM,QAAQyM,GAAS,CAC5C,MAAM9E,EAAIxO,KAAKoS,SAAS/Q,IAAIkI,GAC5B,GAAIiF,EAAG,CACN,MAAM1Q,EAAQ+c,EAAmBtR,EAAGC,GACpCgF,EAAE1S,EAAIgC,EAAMhC,EACZ0S,EAAEzS,EAAI+B,EAAM/B,EACZ,QACD,CACA,GAAIwN,EAAEuR,SAAS,YAAa,CAC3B,MAAMC,EAASxR,EAAExM,MAAM,GAAI,GACrBV,EAAO2D,KAAKW,MAAM1D,KAAKwB,GAAaA,EAAUhB,KAAOsd,GAC3D,IAAK1e,IAAc,IAANmN,EACZ,MAAM,IAAIhM,MAAM,qCAAqC+L,KAEtDlN,EAAK+Y,qBAAsB,EAC3B,QACD,CACA,GAAI7L,EAAEyR,WAAW,MAAO,CACvB,MAAM3e,EAAO2D,KAAKW,MAAM1D,KAAKwB,GAAaA,EAAUhB,KAAO8L,EAAExM,MAAM,IACnE,IAAKV,IAASoY,MAAMwG,QAAQzR,GAC3B,MAAM,IAAIhM,MAAM,6BAA6B+L,KAE9C,IAAK,MAAMrM,KAAUb,EAAKK,UAAY,GACrCsD,KAAKqS,aAAa6E,OAAOha,EAAOO,IAEjCpB,EAAKK,SAAW8M,EAAE5J,IAAI,CAACsb,EAAO7d,KAC7B,MAAMS,EAAQ+c,EAAmB,GAAGtR,KAAKlM,KAAU6d,GAC7Che,EAASb,EAAKwY,WAAW/W,GAG/B,OAFAZ,EAAOC,MAAQ+d,EAAM/d,MACrBD,EAAOiB,KAAO+c,EAAM/c,KACbjB,IAER,QACD,CACA,MAAM,IAAIM,MAAM,mCAAmC+L,IACpD,CACA,IAAK,MAAM/I,KAAQR,KAAKO,QACvB,KAAMC,EAAK/C,MAAM6V,GAChB,MAAM,IAAI9V,MAAM,0BAA0BgD,EAAK/C,MAG7Cmd,IACH5a,KAAKO,QAAQjG,QAAQkU,GAAK1D,EAAY0D,EAAEoH,IAAKpH,EAAE1S,EAAG0S,EAAEzS,IACpDiE,KAAKW,MAAMrG,QAAQwb,GAAK9V,KAAK4W,WAAWd,IACxC9V,KAAK6V,iBACL7V,KAAK0W,aAAa,MAEpB,CAEA,gBAAMyE,CAAWC,GAChB,MAAMjd,QAAa,EAAAmV,EAAA/J,GAAWvJ,KAAMob,GACpCpb,KAAK2S,MAAM9B,eAEX,IAAK,MAAMwK,KAAcld,EAAKoC,MAAO,CACpC,MAAMC,EAAOR,KAAKoS,SAAS/Q,IAAIga,EAAW5d,IAC1C,IAAK+C,EACJ,MAAM,IAAIhD,MAAM,gCAAgC6d,EAAW5d,MAE5D+C,EAAK1E,EAAIuf,EAAWvf,EACpB0E,EAAKzE,EAAIsf,EAAWtf,EACpB+O,EAAYtK,EAAKoV,IAAKpV,EAAK1E,EAAG0E,EAAKzE,EACpC,CAEA,IAAK,MAAMuf,KAAend,EAAKsC,OAAQ,CACtC,MAAMC,EAAQV,KAAKsS,UAAUjR,IAAIia,EAAY7d,IAC7C,IAAKiD,EACJ,MAAM,IAAIlD,MAAM,iCAAiC8d,EAAY7d,MAE9DiD,EAAMwV,aAAeoF,EAAYna,OACjCT,EAAMyV,kBAAoBmF,EAAY5Z,YACtChB,EAAM5E,EAAIwf,EAAYna,OAAOrF,EAAIwf,EAAYna,OAAOnF,MAAQ,EAC5D0E,EAAM3E,EAAIuf,EAAYna,OAAOpF,EAAIuf,EAAYna,OAAOlF,OAAS,EAC7DyE,EAAM1E,MAAQsf,EAAYna,OAAOnF,MACjC0E,EAAMzE,OAASqf,EAAYna,OAAOlF,MACnC,CAEA,IAAK,MAAMsf,KAAcpd,EAAKwC,MAAO,CACpC,MAAMtE,EAAO2D,KAAKW,MAAM1D,KAAKwB,GAAaA,EAAUhB,KAAO8d,EAAW9d,IACtE,IAAKpB,EACJ,MAAM,IAAImB,MAAM,gCAAgC+d,EAAW9d,MAE5D,IAAK,MAAMP,KAAUb,EAAKK,UAAY,GACrCsD,KAAKqS,aAAa6E,OAAOha,EAAOO,IAEjCpB,EAAK2Z,eAAiBuF,EAAW5Y,SACjCtG,EAAK4Z,kBAAoBsF,EAAWhY,YACpClH,EAAKK,SAAW8e,EAAoBD,EAAW5Y,UAAU/C,IAAI9B,IAC5D,MAAMZ,EAASb,EAAKwY,WAAW/W,GAE/B,OADAZ,EAAOiB,MAAO,EACPjB,IAERb,EAAK+Y,qBAAsB,CAC5B,CAEApV,KAAKyS,gBAAkBtU,EAAKsd,UAC5Bzb,KAAKW,MAAMrG,QAAQ+B,GAAQ2D,KAAK4W,WAAWva,IAC3C2D,KAAK0W,aAAa,MAClB1W,KAAK0b,YACL1b,KAAK2S,MAAMxC,QACZ,CAEA,eAAAwL,GACC,MAAMhB,EAAe3a,KAAKO,QAAQzF,OAAO0T,GAAKA,EAAEmH,UAChDgF,EAAI9d,QAAQ4X,MAAMlY,KAAKyD,KAAKqS,aAAapP,UAAUnI,OAAO0O,GAAKA,EAAEmM,WACjE,IAAIqE,EAAOpe,KAAK6J,OAAOkV,EAAI/a,IAAIvB,GAAKA,EAAEtC,IACtCiE,KAAKoS,SAAS9X,QAAQkU,GAAKA,EAAEmH,UAAY3V,KAAKoW,SAAS5H,EAAGA,EAAE1S,EAAGke,GAAM,GAAO,IAC5Eha,KAAKqS,aAAa/X,QAAQkP,GAAKA,EAAEmM,UAAY3V,KAAK2W,eAAenN,EAAGA,EAAE1N,EAAGke,GAAM,GAAO,GACvF,CAEA,eAAA4B,GACC,MAAMjB,EAAe3a,KAAKO,QAAQzF,OAAO0T,GAAKA,EAAEmH,UAChDgF,EAAI9d,QAAQ4X,MAAMlY,KAAKyD,KAAKqS,aAAapP,UAAUnI,OAAO0O,GAAKA,EAAEmM,WACjE,IAAImE,EAAOle,KAAK6J,OAAOkV,EAAI/a,IAAIvB,GAAKA,EAAEvC,IACtCkE,KAAKoS,SAAS9X,QAAQkU,GAAKA,EAAEmH,UAAY3V,KAAKoW,SAAS5H,EAAGsL,EAAMtL,EAAEzS,GAAG,GAAO,IAC5EiE,KAAKqS,aAAa/X,QAAQkP,GAAKA,EAAEmM,UAAY3V,KAAK2W,eAAenN,EAAGsQ,EAAMtQ,EAAEzN,GAAG,GAAO,GACvF,CAEA,oBAAA8f,GACC,MAAMC,EAAgB9b,KAAKO,QAAQzF,OAAO0T,GAAKA,EAAEmH,UAC3CoG,EAAmBtH,MAAMlY,KAAKyD,KAAKqS,aAAapP,UAAUnI,OAAO0O,GAAKA,EAAEmM,UAE9E,GAAImG,EAAcxe,OAASye,EAAiBze,OAAS,EAAG,OAExD0C,KAAK2S,MAAM9B,eAGX,MAAMmL,EAAc,IAAIF,KAAkBC,GAC1CC,EAAYlV,KAAK,CAAC4E,EAAGV,IAAMU,EAAE5P,EAAIkP,EAAElP,GAEnC,MAAMge,EAAOkC,EAAY,GAAGlgB,EAEtBmgB,GADOD,EAAYA,EAAY1e,OAAS,GAAGxB,EACzBge,IAASkC,EAAY1e,OAAS,GAGtD0e,EAAY1hB,QAAQ,CAAC8O,EAAS/L,KAC7B,MAAM6e,EAAOpC,EAAQzc,EAAQ4e,EACzB,UAAW7S,EAEdpJ,KAAKoW,SAAShN,EAAiB8S,EAAM9S,EAAQrN,GAAG,GAAO,GAGvDiE,KAAK2W,eAAevN,EAAuB8S,EAAM9S,EAAQrN,GAAG,GAAO,KAIrEiE,KAAK2S,MAAMxC,QACZ,CAEA,oBAAAgM,GACC,MAAML,EAAgB9b,KAAKO,QAAQzF,OAAO0T,GAAKA,EAAEmH,UAC3CoG,EAAmBtH,MAAMlY,KAAKyD,KAAKqS,aAAapP,UAAUnI,OAAO0O,GAAKA,EAAEmM,UAE9E,GAAImG,EAAcxe,OAASye,EAAiBze,OAAS,EAAG,OAExD0C,KAAK2S,MAAM9B,eAGX,MAAMmL,EAAc,IAAIF,KAAkBC,GAC1CC,EAAYlV,KAAK,CAAC4E,EAAGV,IAAMU,EAAE3P,EAAIiP,EAAEjP,GAEnC,MAAMie,EAAOgC,EAAY,GAAGjgB,EAEtBkgB,GADOD,EAAYA,EAAY1e,OAAS,GAAGvB,EACzBie,IAASgC,EAAY1e,OAAS,GAGtD0e,EAAY1hB,QAAQ,CAAC8O,EAAS/L,KAC7B,MAAM+e,EAAOpC,EAAQ3c,EAAQ4e,EACzB,UAAW7S,EAEdpJ,KAAKoW,SAAShN,EAAiBA,EAAQtN,EAAGsgB,GAAM,GAAO,GAGvDpc,KAAK2W,eAAevN,EAAuBA,EAAQtN,EAAGsgB,GAAM,GAAO,KAIrEpc,KAAK2S,MAAMxC,QACZ,CAGA,eAAAkM,CAAgBhgB,EAAYsZ,GAEvBA,IACH3V,KAAK0V,gBAAgBrZ,EAAKE,MAAM,GAChCyD,KAAK0V,gBAAgBrZ,EAAKI,IAAI,IAG/BuD,KAAK6V,gBACN,CAGA,SAAA6F,GACC,MAAMtE,EAAgBpX,KAAKqX,yBAG3B,GAA4B,IAAxBD,EAAcpb,OAAwC,IAAzBob,EAAcnb,OAC9C,OAID,MAAMqgB,EAAgB1E,EAAII,eAAeC,aAAe,IAClDsE,EAAiB3E,EAAII,eAAeE,cAAgB,IAMpDsE,GAASF,EAAgBjD,IAAejC,EAAcpb,MACtDygB,GAASF,EAAiBlD,IAAejC,EAAcnb,OACvDygB,EAAc9gB,KAAK6J,IAAI+W,EAAOC,GAG9BE,EAAY/gB,KAAK4J,IAAI5J,KAAK6J,IAAIiX,EAAa,GAAI,IAY/CE,EALkBN,EAAgB,GAJjBlF,EAActb,EAAIsb,EAAcpb,MAAQ,GASR2gB,EACjDE,EALkBN,EAAiB,GAJlBnF,EAAcrb,EAAIqb,EAAcnb,OAAS,GAST0gB,EAGjDhF,EAAYC,EAAIpK,cAAc,UAChCmK,GACHA,EAAUrQ,aAAa,YAAa,aAAasV,MAAeC,YAAqBF,MAItFpE,IAGAuE,GAAc9c,KAAKvC,GACpB,CAGQ,eAAAsf,GACP,OAAO/c,KAAKgT,cAAa,EAC1B,CAGQ,kBAAAgK,CAAmBC,GAC1Bjd,KAAKkT,aAAa+J,GAAO,EAC1B,CAGA,aAAAC,GACC,OAAOld,KAAK4S,YACb,CAEA,YAAAuK,GACC,OAAOnd,KAAK6S,WACb,CAEA,WAAAuK,GACC,OAAOpd,KAAK8S,SACb,CAEA,UAAAuK,GACCrd,KAAK4S,cAAgB5S,KAAK4S,aAC1B5S,KAAKsd,oBAELnK,OAAOoK,cAAc,IAAIC,YAAY,oBACtC,CAEA,gBAAAC,GACCzd,KAAK6S,aAAe7S,KAAK6S,YAEzBM,OAAOoK,cAAc,IAAIC,YAAY,oBACtC,CAEA,aAAAE,GACM1d,KAAK6S,cAEV7S,KAAK2S,MAAM9B,eACX7Q,KAAKO,QAAQjG,QAAQkG,IACpB,MAAMmd,EAAW/hB,KAAKgiB,MAAMpd,EAAK1E,EAAIkE,KAAK8S,WAAa9S,KAAK8S,UACtD+K,EAAWjiB,KAAKgiB,MAAMpd,EAAKzE,EAAIiE,KAAK8S,WAAa9S,KAAK8S,UAC5D9S,KAAKoW,SAAS5V,EAAMmd,EAAUE,GAAU,GAAO,KAEhD7d,KAAK2S,MAAMxC,SACZ,CAGQ,UAAAqG,CAAW1a,EAAWC,GAC7B,MAAO,CACND,EAAGF,KAAKgiB,MAAM9hB,EAAIkE,KAAK8S,WAAa9S,KAAK8S,UACzC/W,EAAGH,KAAKgiB,MAAM7hB,EAAIiE,KAAK8S,WAAa9S,KAAK8S,UAE3C,CAEA,iBAAAwK,GACC,IAAK1F,EAAK,OAGV,MAAMkG,EAAelG,EAAIpK,cAAc,iBACnCsQ,GACHA,EAAahW,SAGd,MAAMiW,EAAmBnG,EAAIpK,cAAc,oBAK3C,GAJIuQ,GACHA,EAAiBjW,UAGb9H,KAAK4S,aAAc,OAGxB,IAAIoL,EAAOpG,EAAIpK,cAAc,QACxBwQ,IACJA,EAAO5W,SAASC,gBAAgB,6BAA8B,QAC9DuQ,EAAIrK,aAAayQ,EAAMpG,EAAIgC,aAG5B,MAAMqE,EAAU7W,SAASC,gBAAgB,6BAA8B,WACvE4W,EAAQxgB,GAAK,eACbwgB,EAAQ3W,aAAa,QAAStH,KAAK8S,UAAUpY,YAC7CujB,EAAQ3W,aAAa,SAAUtH,KAAK8S,UAAUpY,YAC9CujB,EAAQ3W,aAAa,eAAgB,kBAErC,MAAMhI,EAAO8H,SAASC,gBAAgB,6BAA8B,QACpE/H,EAAKgI,aAAa,IAAK,KAAKtH,KAAK8S,uBAAuB9S,KAAK8S,aAC7DxT,EAAKgI,aAAa,OAAQ,QAC1BhI,EAAKgI,aAAa,SAAU,WAC5BhI,EAAKgI,aAAa,eAAgB,KAClChI,EAAKgI,aAAa,UAAW,OAE7B2W,EAAQrW,YAAYtI,GACpB0e,EAAKpW,YAAYqW,GAGjB,MAAM/Z,EAAOkD,SAASC,gBAAgB,6BAA8B,QACpEnD,EAAKzG,GAAK,kBACVyG,EAAKoD,aAAa,IAAK,UACvBpD,EAAKoD,aAAa,IAAK,UACvBpD,EAAKoD,aAAa,QAAS,SAC3BpD,EAAKoD,aAAa,SAAU,SAC5BpD,EAAKoD,aAAa,OAAQ,sBAC1BpD,EAAKoD,aAAa,iBAAkB,QAGpC,MAAMqQ,EAAYC,EAAIpK,cAAc,UAChCmK,GACHA,EAAUpK,aAAarJ,EAAMyT,EAAUiC,WAEzC,EAID,SAAS4B,EAAoB7Y,GAC5B,MAAMub,EAAiB,GACvB,IAAK,MAAMrb,KAAWF,EACrB,IAAK,MAAM7E,KAAS+E,EAAS,CAC5B,MAAMsb,EAAWD,EAAMA,EAAM5gB,OAAS,GACjC6gB,GAAYA,EAASriB,IAAMgC,EAAMhC,GAAKqiB,EAASpiB,IAAM+B,EAAM/B,GAC/DmiB,EAAMrhB,KAAKiB,EAEb,CAED,OAAOogB,EAAMnhB,MAAM,GAAI,EACxB,CAGA,SAAS8d,EACRpd,EACAjD,GAEA,GACkB,iBAAVA,GACPia,MAAMwG,QAAQzgB,KACb2J,OAAOC,SAAS5J,EAAMsB,KACtBqI,OAAOC,SAAS5J,EAAMuB,GAEvB,MAAM,IAAIyB,MAAM,gBAAgBC,4CAEjC,OAAOjD,CACR,CAQA,IAUI4jB,EAVAxG,EAAqBxQ,SAASoG,cAAc,aAC3CoK,IACJA,EAAMxQ,SAASC,gBAAgB,6BAA8B,OAC7DuQ,EAAItQ,aAAa,KAAM,SACvBsQ,EAAIyG,iBAAiB,QAASvI,GAAKsI,EAActI,KAGlD8B,EAAItQ,aAAa,QAAS,QAC1BsQ,EAAItQ,aAAa,SAAU,QAG3B,IACIgX,EADAC,GAAW,EAIR,MAAMC,EAAa,CAACC,EAAiBC,EAAiCC,KAE5E/G,EAAIgH,UPnmCe,uqBOomCnBxX,SAASO,KAAK0C,OAAOuN,GAErBA,EAAIiH,OAASJ,EAEbH,EAAiBI,EAGjBN,EAAgBtI,MAOhBgJ,EAAYL,GACZ,MAAMM,EAAY5V,EAAOjF,KAAK,IAAK,IAAK,GAAI,GAAI,EAAG,WASnD,OARA0T,EAAIvN,OAAO0U,GAGXN,EAAKnB,oBAGL0B,EAAqBpH,EAAK+G,GAEnB,CACN/G,MACAqH,YAIWC,EAAkBT,IAC9B7G,EAAMxQ,SAASC,gBAAgB,6BAA8B,OAC7DuQ,EAAItQ,aAAa,KAAM,SACvBwX,EAAYL,GACL7G,GAGFkH,EAAeL,IAEpB,MAAMU,EAAQhW,EAAOC,QAAQ,IAAK,CAAC,EAAG,QAChCgW,EAASjW,EAAOC,QAAQ,IAAK,CAAC,EAAG,SACjCiW,EAASlW,EAAOC,QAAQ,IAAK,CAAC,EAAG,SACjCkW,EAAUnW,EAAOC,QAAQ,IAAK,CAAC,EAAG,UACxC+V,EAAM9U,OAAOiV,EAASD,EAAQD,GAG9BX,EAAKrM,SAAS9X,QAASkU,KAgJxB,SAAmBA,EAASiQ,GAE3BtL,OAAOoM,MAAQd,EAEf,MAAM7T,EAAIzB,EAAOC,QAAQ,IAAK,CAAC,EAAG,QAClCwB,EAAEtD,aAAa,KAAMkH,EAAE/Q,IACvB+Q,EAAEmH,UAAY/K,EAAElB,UAAU5F,IAAI,YAC9BgH,EAAYF,EAAG4D,EAAE1S,EAAG0S,EAAEzS,GACtB,MAAM2X,EAAOlF,EAAEkF,KACZvK,EAAOC,QAAQ,IAAK,CACrBoW,KAAMhR,EAAEkF,KAAK8L,KACb,mBAAoBhR,EAAEkF,KAAK+L,WAC3B,aAAc,QAAQjR,EAAEsF,SACtB,YACD,KACG4L,EAAUhM,GAAQ9I,EACpB8I,IACH9I,EAAElB,UAAU5F,IAAI,UAChB8G,EAAEP,OAAOqJ,IAIV,MAAMiM,EAAYnR,EAAErU,MAAMgC,OAAS,MAE7BA,GADU2R,EAAO6R,EAAU1S,gBAAkBa,EAAO3C,KACxBuU,EAASlR,GAE3CrS,EAAMuN,UAAU5F,IAAI,eAGpB,EAAA8N,EAAAgO,IAAWzjB,EAAO0jB,EAAOllB,YAEzBwB,EAAMmL,aAAa,OAAQkH,EAAErU,MAAM+B,YACnCC,EAAMmL,aAAa,SAAUkH,EAAErU,MAAMU,QAErCsB,EAAMmL,aAAa,eAAgB,KACnCnL,EAAMmL,aAAa,UAAWmC,OAAO+E,EAAErU,MAAMoB,UAC7CukB,EAAe3jB,EAAOqS,EAAErU,MAAM4lB,QAE9B,MAAMC,ED7vCA,SAA0B1M,EAA2BhY,GAC3D,MAAMoF,EAAQyI,EAAOC,QAAQ,KAC7B,IAAIiR,GAAO/G,EAAOc,WAAa,EA2B/B,OAzBAd,EAAOa,OAAO7Z,QAAS+Z,IACtB,MAAM3N,EAAOyC,EAAOzC,KAAK,GAAI,CAAC,cAAe,YAC7C,EAAAkL,EAAAgO,IAAWlZ,EAAMkL,EAAAC,GAAW9W,UACxBO,GACHoL,EAAKY,aAAa,OAAQhM,GAEvB+Y,EAAM3C,OACThL,EAAKY,aAAa,aAAc+M,EAAM3C,OAGvC2C,EAAMnM,MAAM5N,QAAQ,CAACwO,EAAMzL,KAC1B,MAAM+M,EAAOjB,EAAOC,QAAQ,QAAS,CACpCtN,EAAG,EACHC,EAAGse,EAAMhG,EAAM7Y,SAAW6B,EAAQgX,EAAMvC,WACxC,YAAa,GAAGuC,EAAM7Y,aACtB,cAAe6Y,EAAMrK,KAAO,OAAS,WAEtCI,EAAK7C,YAAcuB,EACnBpC,EAAK2D,OAAOD,KAGb1J,EAAM2J,OAAO3D,GACb2T,GAAOhG,EAAMnM,MAAM5K,OAAS+W,EAAMvC,WAAauC,EAAM5C,WAG/C/Q,CACR,CC+tCYuf,CAAiBzR,EAAEqF,cAAerF,EAAErU,MAAMmB,OAErDwP,EAAYkV,EAAI,GADK7b,OAAOub,EAAQzG,aAAa,oBAAsB,GACrC,GAClCyG,EAAQrV,OAAO2V,GAGfpV,EAAEiU,OAASrQ,EACXA,EAAEoH,IAAMhL,CAGT,CA/LEsV,CAAU1R,EAAGiQ,GACbW,EAAO/U,OAAOmE,EAAEoH,OAGjB6I,EAAK9d,MAAMrG,QAAQ+B,IAClBA,EAAKkH,iBAAc3E,IAEpB6f,EAAK9d,MAAMrG,QAAQwb,IAClB2C,EAAU3C,GACVuJ,EAAOhV,OAAOyL,EAAEF,OAGjB6I,EAAKnM,UAAUhY,QAASoG,IACvBgY,EAAWhY,GACX4e,EAAQjV,OAAO3J,EAAMkV,OAGtBgC,EAAIvN,OAAO8U,IAGZ,SAAS1G,EAAUpc,GAClB,MAAMC,EAAKD,EAAKE,KAEVqO,EAAIzB,EAAOC,QAAQ,IAAK,CAAC,EAAG,QAClCwB,EAAEtD,aAAa,KAAMjL,EAAKoB,IAC1BmN,EAAEtD,aAAa,YAAajL,EAAKE,KAAKkB,IACtCmN,EAAEtD,aAAa,UAAWjL,EAAKI,GAAGgB,IAElC,MAAME,GAAYtB,EAAKlC,MAAMwD,UAAY,IAAM,IAEzCjB,EAAWL,EAAK2Z,eAkCvB,SAA6BrT,GAC5B,MAAMub,EAAiB,GACvB,IAAK,MAAMrb,KAAWF,EACrB,IAAK,MAAM7E,KAAS+E,EAAS,CAC5B,MAAMsb,EAAWD,EAAMA,EAAM5gB,OAAS,GACjC6gB,GAAYA,EAASriB,IAAMgC,EAAMhC,GAAKqiB,EAASpiB,IAAM+B,EAAM/B,GAC/DmiB,EAAMrhB,KAAKiB,EAEb,CAED,OAAOogB,CACR,CA5CIiC,CAAoB9jB,EAAK2Z,iBACzB,EAAAoK,EAAAC,GAAsBhkB,IACnBikB,GAACA,EAAEpW,IAAEA,GAAO7N,EAAK4Z,kBA6CxB,SAA8B9U,EAAc9E,GAC3C,MAAMke,EAAUpZ,EAAOrF,EAAIqF,EAAOnF,MAAQ,GAEpCkO,IAACA,IADS/I,EAAOpF,EAAIoF,EAAOlF,OACpBkN,EAAOY,SACpB1N,EAAKc,MACL,IACAd,EAAKlC,MAAMqB,UACX,EACA+e,EACApZ,EAAOpF,EACP,YAED,EAAA6V,EAAAgO,IAAW1V,EAAK2V,EAAO7kB,UACvBkP,EAAI5C,aAAa,SAAU,QAC3B4C,EAAI5C,aAAa,YAAamC,OAAOpN,EAAKlC,MAAMqB,WAChD0O,EAAI5C,aAAa,OAAQjL,EAAKlC,MAAMmB,OACpC4O,EAAI5C,aAAa,aAAc,SAC/B,MAAMgZ,EAAKnX,EAAOjF,KAAK/C,EAAOnF,MAAOmF,EAAOlF,OAAQkF,EAAOrF,EAAGqF,EAAOpF,GAErE,OADA,EAAA6V,EAAAgO,IAAWU,EAAIT,EAAO5kB,UACf,CACNqlB,KACApW,MAEF,CAnEIqW,CAAqBlkB,EAAK4Z,kBAAmB5Z,GAyEjD,SAAwBmkB,EAA+BnkB,GACtD,MACMb,EAAWa,EAAKlC,MAAMqB,SAC5B,IAAI0O,IAACA,EAAGlF,GAAEA,EAAEiD,KAAEA,GAAQkB,EAAOY,SAAS1N,EAAKc,MAAO,IAAK3B,GAAU,EAAOglB,EAAU1kB,EAAG0kB,EAAUzkB,EAAG,UAClGiJ,GAAMxJ,EAAW,EACjByM,GAAQzM,EACR,MAAM+e,EAAoC,aAA1BiG,EAAUphB,YACvBohB,EAAU1kB,EAAImM,EAAO,EANP,GAOduY,EAAU1kB,EACP0e,EAAoC,aAA1BgG,EAAUphB,YACvBohB,EAAUzkB,EACVykB,EAAUzkB,EAAIiJ,EAAK,EAVL,GAWX7D,EAAS,CACdrF,EAAGye,EAAUtS,EAAO,EACpBlM,EAAGye,EAAUxV,EAAK,EAClBhJ,MAAOiM,EACPhM,OAAQ+I,GAETkF,EAAI8O,iBAAiB,SAAS1e,QAAS8P,IACtCA,EAAK9C,aAAa,IAAKmC,OAAO8Q,MAE/BrQ,EAAI5C,aAAa,IAAKmC,OAAO+Q,EAAUxV,EAAK,KAE5C,EAAA4M,EAAAgO,IAAW1V,EAAK2V,EAAO7kB,UACvBkP,EAAI5C,aAAa,SAAU,QAC3B4C,EAAI5C,aAAa,YAAamC,OAAOpN,EAAKlC,MAAMqB,WAChD0O,EAAI5C,aAAa,OAAQjL,EAAKlC,MAAMmB,OAEpC,MAAMglB,EAAKnX,EAAOjF,KAAK/C,EAAOnF,MAAOmF,EAAOlF,OAAQkF,EAAOrF,EAAGqF,EAAOpF,GAIrE,OAHA,EAAA6V,EAAAgO,IAAWU,EAAIT,EAAO5kB,UACtBiP,EAAI5C,aAAa,aAAc,SAC/BjL,EAAKkH,YAAcpC,EACZ,CAACmf,KAAIpW,MACb,CAzGIuW,EAAe,EAAAL,EAAAM,IAAwBhkB,EAAUiB,EAAUrB,GAAKD,GACnEuO,EAAEP,OAAOiW,EAAIpW,GAEb,MAAM5K,GAAO,EAAA8gB,EAAAO,IAAejkB,GAEtB2B,EAAI8K,EAAO7J,KAAKA,EAAM,CAAC,aAAc,eAAgB,QAC3DjB,EAAEiJ,aAAa,OAAQ,QACvBjJ,EAAEiJ,aAAa,SAAUjL,EAAKlC,MAAMmB,OACpC+C,EAAEiJ,aAAa,eAAgBmC,OAAOpN,EAAKlC,MAAMkB,YACjDgD,EAAEiJ,aAAa,iBAAkB,SACjCjL,EAAKlC,MAAMsB,QAAU4C,EAAEiJ,aAAa,mBAAoB,KACxDsD,EAAEP,OAAOhM,GAKT,IAAK,MAAMA,KAAKhC,EAAKK,UAAY,GAAI,CACpC,MAAM8M,EAAInL,EACVmL,EAAEoM,IAAMzM,EAAOC,QAAQ,SAAU,CAAC3L,GAAI+L,EAAE/L,GAAImjB,GAAIviB,EAAEvC,EAAGoS,GAAI7P,EAAEtC,EAAGuO,EAAG,EAAG1P,KAAM,QAAS,SACnF4O,EAAEmM,UAAYnM,EAAEoM,IAAIlM,UAAU5F,IAAI,YAClC0F,EAAErL,MAAQqL,EAAEoM,IAAIlM,UAAU5F,IAAI,QAC9B8G,EAAEP,OAAOb,EAAEoM,IACZ,CAGA,OADAvZ,EAAKuZ,IAAMhL,EACJA,CACR,CAqIA,SAAS8N,EAAWhY,GACnB,GAA0B,GAAtBA,EAAMH,MAAMjD,OACf,OAED,MAAMsN,EAAIzB,EAAOC,QAAQ,IAAK,CAAC,EAAG,SAClC,GAAI1I,EAAMwV,cAAgBxV,EAAMyV,kBAAmB,CAClD,MAAMhV,EAAST,EAAMwV,aACfpC,EAAQpT,EAAMyV,kBACdjS,EAAOiF,EAAOjF,KAAK/C,EAAOnF,MAAOmF,EAAOlF,OAAQkF,EAAOrF,EAAGqF,EAAOpF,IACvE,EAAA6V,EAAAgO,IAAW1b,EAAM2b,EAAO3kB,WACxBwF,EAAMvG,MAAMU,QAAUqJ,EAAKoD,aAAa,SAAU5G,EAAMvG,MAAMU,QAC9D6F,EAAMvG,MAAM+B,YAAcgI,EAAKoD,aAAa,OAAQ5G,EAAMvG,MAAM+B,YAChE,MAAMwK,EAAOyC,EAAOzC,KAAKhG,EAAMT,KAAM,CACpCnE,EAAGgY,EAAMhY,EACTC,EAAG+X,EAAM/X,EACT,oBAAqB,YAMtB,OAJA,EAAA6V,EAAAgO,IAAWlZ,EAAMmZ,EAAO1kB,WACxBuF,EAAMvG,MAAMmB,OAASoL,EAAKY,aAAa,OAAQ5G,EAAMvG,MAAMmB,OAC3DsP,EAAEP,OAAOnG,EAAMwC,QACfhG,EAAMkV,IAAMhL,EAEb,CAEA,IAAIiW,EAAY,CAAC/kB,EAAG,MAAOC,EAAG,OAAQL,EAAY,CAACI,EAAG,EAAGC,EAAG,GAC5D2E,EAAMH,MAAMjG,QAAQkU,IAEnB,MAAMrS,EAASqS,EAAWrU,OAAOgC,OAAO8Q,eAAiB,MAEzD,IAAI6T,EAAetS,EAAEvS,OAAS,EAC1B8kB,EAAkBvS,EAAEvS,OAAS,EAEjC,GAAc,UAAVE,EAAmB,CAGtB,MAAM6S,EAAsB,IAAXR,EAAEvS,OACnB6kB,EAAetS,EAAEvS,OAAS,EAAI+S,EAC9B+R,EAAkBvS,EAAEvS,OAAS,CAC9B,MAAO,GAAc,YAAVE,EAAqB,CAG/B,MAAM6kB,EAAYxS,EAAExS,MAAQ,EAAI,KAChC8kB,EAAeE,EACfD,EAAkBC,CACnB,CAEA,MAAMhW,EAAI,CACTlP,EAAG0S,EAAE1S,EAAI0S,EAAExS,MAAQ,EACnBD,EAAGyS,EAAEzS,EAAI+kB,EACT9kB,MAAOwS,EAAExS,MACTC,OAAQ6kB,EAAeC,GAExBF,EAAG/kB,EAAIF,KAAK6J,IAAIob,EAAG/kB,EAAGkP,EAAElP,GACxB+kB,EAAG9kB,EAAIH,KAAK6J,IAAIob,EAAG9kB,EAAGiP,EAAEjP,GACxBL,EAAGI,EAAIF,KAAK4J,IAAI9J,EAAGI,EAAGkP,EAAElP,EAAIkP,EAAEhP,OAC9BN,EAAGK,EAAIH,KAAK4J,IAAI9J,EAAGK,EAAGiP,EAAEjP,EAAIiP,EAAE/O,UAE/B,MAEMmP,EAAIxP,KAAK4J,IAAI9J,EAAGI,EAAI+kB,EAAG/kB,EAAG,KAC1BuP,EAAI3P,EAAGK,EAAI8kB,EAAG9kB,EACd+b,EAAK,CACVhc,EAAG+kB,EAAG/kB,EALK,GAMXC,EAAG8kB,EAAG9kB,EANK,GAOXC,MAAOoP,EAAI6V,GACXhlB,OAAQoP,EAAI4V,GAPO,IASd3W,EAAInB,EAAOjF,KAAK4T,EAAG9b,MAAO8b,EAAG7b,OAAQ6b,EAAGhc,EAAGgc,EAAG/b,GACpD2E,EAAM5E,EAAIgc,EAAGhc,EAAIgc,EAAG9b,MAAQ,EAC5B0E,EAAM3E,EAAI+b,EAAG/b,EAAI+b,EAAG7b,OAAS,EAC7ByE,EAAM1E,MAAQ8b,EAAG9b,MACjB0E,EAAMzE,OAAS6b,EAAG7b,QAClB,EAAA2V,EAAAgO,IAAWtV,EAAGuV,EAAO3kB,WACrBwF,EAAMvG,MAAMU,QAAUyP,EAAEhD,aAAa,SAAU5G,EAAMvG,MAAMU,QAC3D6F,EAAMvG,MAAM+B,YAAcoO,EAAEhD,aAAa,OAAQ5G,EAAMvG,MAAM+B,YAE7D,MAAMgO,EAAMf,EAAOzC,KAAKhG,EAAMT,KAAM,CAACnE,EAAG+kB,EAAG/kB,EAAGC,EAAG+b,EAAG/b,EAAI+b,EAAG7b,OAAS4jB,EAAO1kB,UAAU,gBACrF,EAAAyW,EAAAgO,IAAW1V,EAAK2V,EAAO1kB,WACvBuF,EAAMvG,MAAMmB,OAAS4O,EAAI5C,aAAa,OAAQ5G,EAAMvG,MAAMmB,OAE1DsP,EAAEP,OAAOC,EAAGJ,GACZxJ,EAAMkV,IAAMhL,CACb,CAEA,SAASsW,EAAmB9N,EAAkB/U,GAE7C,IAAI8iB,EAAM,CAACC,IAAKjd,OAAOkd,kBAAmB5U,KAAM,EAAGpQ,KAAM,KAAcilB,IAAK,MAa5E,OAZAlO,EAAMzS,MAAMrG,QAAQ+B,IACnB,MAAMK,EAAWL,EAAKK,UAAY,GAC5B6kB,EAAM,CAACllB,EAAKE,QAASG,EAAUL,EAAKI,IAC1C,IAAK,IAAI0N,EAAI,EAAGA,EAAIoX,EAAIjkB,OAAQ6M,IAAK,CACpC,MAAMmX,EAAM5U,EAAQrO,EAAGkjB,EAAIpX,EAAI,GAAIoX,EAAIpX,IACjCiX,EAAMtU,EAAYzO,EAAGijB,GACvBF,EAAM,IACNA,EAAMD,EAAIC,MACbD,EAAM,CAACC,MAAK3U,IAAKtC,EAAGmX,MAAKjlB,QAE3B,IAEM8kB,EAAI9kB,KAAO8kB,EAAM,IACzB,CAEA,SAASK,EAAe1L,GAEvB,MAAM9K,EAAI4M,EAAI6J,wBACRC,EAAIhK,IAGJiK,EAAmBC,IAIzB,MAAO,CACN9lB,GAAIga,EAAE+L,QAAU7W,EAAElP,EAAI6lB,EAAiB7lB,GAAK4lB,EAC5C3lB,GAAI+Z,EAAEgM,QAAU9W,EAAEjP,EAAI4lB,EAAiB5lB,GAAK2lB,EAE9C,CASA,SAASK,EAA2BnK,EAAoBoK,EAUrDrD,GACF,IAAIsD,EAA6C,GAC7CC,EAAe,KACfC,GAAY,EACZC,EAAY,EACZC,EAAY,EACZC,EAAmB,CAAExmB,EAAG,EAAGC,EAAG,GAC9BwmB,EAAqE,KACrEC,EAAmC,KACnCC,GAAa,EACbC,GAAoB,EAGxB,MAAMC,EAA8F,GAgRpG,SAASC,EAAQ9M,GAChB,MAAMtR,EAASsR,EAAEtR,OACXA,aAAkBqe,SAAare,EAAOse,QAAQ,gBACpDhN,EAAEiN,iBACGL,IACL5M,EAAEkN,kBACFN,GAAoB,GACrB,CAyDA,OAtDA,SAAgBtZ,GACf,IAAI6Z,EAAwC,KAE5C,SAASC,EAAapN,GACrB,MAAI,mBAAoBA,GAAKA,EAAEqN,eACvBrN,EAAEqN,eAAe,GAElBrN,CACR,CAEA,SAASsN,EAAmBtN,GACtBmN,GAjHP,SAAqBnN,EAAe/Q,EAAYC,GAG/C,IAAKyd,IAAe7mB,KAAKsD,IAAI6F,GADP,GAC8BnJ,KAAKsD,IAAI8F,GADvC,KAErByd,GAAa,EACbD,EAAoB,KAGhBD,GAAwB,CAC3B,MAAMhiB,EAAQyhB,EAAKqB,eACnB9iB,EAAMjD,OAAS,EACfiD,EAAM1D,KAAK0lB,EAAuB/hB,MAClCwhB,EAAKsB,aAAa/iB,GAElB0hB,EAAM,CAAC,CAAEnmB,EAAGymB,EAAuB/hB,KAAK1E,EAAGC,EAAGwmB,EAAuB/hB,KAAKzE,EAAGyS,EAAG+T,EAAuB/hB,OACvG+hB,EAAyB,IAC1B,CAGD,GAAIJ,GA9GL,SAAsBrmB,EAAWC,GAChC,MAAM4b,EAAYC,EAAIpK,cAAc,UACpC,IAAKmK,EAAW,OAEhB,MAAMI,EAAOL,IACbC,EAAUrQ,aAAa,YAAa,aAAaxL,MAAMC,YAAYgc,KACpE,CA6GEwL,CAFajB,EAAiBxmB,EAAIiJ,EACrBud,EAAiBvmB,EAAIiJ,QAE5B,GAAIid,EAAI3kB,OAAS,GAAKmlB,EAAY,CAGxC,MAAM1K,EAAOiK,EAAKtK,UACZ8L,EAAYze,EAAKgT,EACjB0L,EAAYze,EAAK+S,EACvBkK,EAAI3nB,QAAQopB,IAGX1B,EAAK5L,SAASsN,EAAKlV,EAAGkV,EAAK5nB,EAAI0nB,EAAWE,EAAK3nB,EAAI0nB,KAEpDzB,EAAK2B,aAAY,EAClB,MAAWzB,IAEVA,EAAQ0B,OAAO9N,GACfkM,EAAK2B,aAAY,GAEnB,CAyEEE,CADA/N,EAAIoN,EAAapN,GACFA,EAAE+L,QAAUoB,EAAGa,GAAIhO,EAAEgM,QAAUmB,EAAGc,GAClD,CASA,SAASC,EAAiBlO,GANzB1O,SAAS6c,oBAAoB,YAAab,GAC1Chc,SAAS6c,oBAAoB,YAAab,GAC1Chc,SAAS6c,oBAAoB,UAAWD,GACxC5c,SAAS6c,oBAAoB,WAAYD,GA9E3C,SAAmBlO,GAClBkM,EAAK2B,aAAY,GACjB,MAAMO,EAAazB,EAAa,KAAOD,EASvC,GARIC,IACHC,GAAoB,EACpBvP,OAAO1C,WAAW,KACjBiS,GAAoB,GAClB,IAIAH,IAA2BE,EAAY,CAC1C,MAAMliB,EAAQyhB,EAAKqB,eACnB9iB,EAAMjD,OAAS,EACfiD,EAAM1D,KAAK0lB,EAAuB/hB,MAClCwhB,EAAKsB,aAAa/iB,EACnB,CAEA,GAAI2hB,EAAS,CACZ,MAAM/W,EAAM+W,EAAQxd,MAChByG,EACH6W,EAAKmC,aAAahZ,EAAK2K,EAAEsO,UACdnC,EAAI3kB,QAEf0kB,EAAKsB,aAAa,IAEnBpB,EAAU,IACX,CAGA,GAAIC,GAAaM,EAAY,CAC5B,MAAM5K,EAAaD,EAAYiH,OAC3BhH,GAAaA,EAAUpa,IAC1Bqf,GAAcjF,EAAUpa,GAE1B,CAGA8kB,EAAyB,KACzBC,EAAoB,KACpBC,GAAa,EACbN,GAAY,EACZH,EAAKzJ,gBACD2L,IACH/Q,OAAOkR,SAAS7E,KAAO0E,EAEzB,CAqCEI,CAAUpB,EAAapN,IACvBmN,EAAK,IACN,CAEA,SAASsB,EAAmBzO,GAC3BA,EAAIoN,EAAapN,GACjBmN,EAAK,CAAEa,GAAIhO,EAAE+L,QAASkC,GAAIjO,EAAEgM,SAxN9B,SAAqBhM,GACpBA,EAAEiN,iBACFN,GAAa,EACbF,EAAyB,KACzB,MAAM/d,EAASsR,EAAEtR,OACXkP,EAAOlP,aAAkBqe,QAAUre,EAAOse,QAAQ,cAAgB,KACxEN,EAAoB1M,EAAEsO,SAAW,KAAO1Q,GAAMuF,aAAa,SAAW,KAEtE,MAAMzY,EAAOwhB,EAAKwC,cAAc1O,GAG1B2O,EAAgB3O,EAAEsO,SAAyB,QAAbzF,EAAqB,SAAW,MAASA,EAE7E,GAAKne,EAsBL,GAAsB,QAAlBikB,EAAyB,CAE5BtC,GAAY,EACZD,EAAU,KAEV,MAAM3hB,EAAQyhB,EAAKqB,eACfrB,EAAK0C,WAAWlkB,GAEnByhB,EAAM1hB,EAAMX,IAAI4O,IAAC,CAAO1S,EAAG0S,EAAE1S,EAAGC,EAAGyS,EAAEzS,EAAGyS,QAIxCwT,EAAKsB,aAAa,CAAC9iB,IACnByhB,EAAM,CAAC,CAAEnmB,EAAG0E,EAAK1E,EAAGC,EAAGyE,EAAKzE,EAAGyS,EAAGhO,IAEpC,KAAO,CAEN2hB,GAAY,EACZD,EAAU,KACV,MAAM3hB,EAAQyhB,EAAKqB,eAEnB,GAAIvN,EAAEsO,UAAyB,WAAbzF,EAAuB,CAExC,GAAIqD,EAAK0C,WAAWlkB,GAAO,CAC1B,MAAMnD,EAAQkD,EAAMvC,UAAUwQ,GAAKA,EAAE/Q,KAAO+C,EAAK/C,IAC7CJ,GAAS,GAAGkD,EAAMyQ,OAAO3T,EAAO,EACrC,MACCkD,EAAM1D,KAAK2D,GAEZwhB,EAAKsB,aAAa/iB,GAClB0hB,EAAM1hB,EAAMX,IAAI4O,IAAC,CAAO1S,EAAG0S,EAAE1S,EAAGC,EAAGyS,EAAEzS,EAAGyS,MACzC,MAEKwT,EAAK0C,WAAWlkB,IAEnByhB,EAAM1hB,EAAMX,IAAI4O,IAAC,CAAO1S,EAAG0S,EAAE1S,EAAGC,EAAGyS,EAAEzS,EAAGyS,OAExC+T,EAAyB,OAGzBA,EAAyB,CAAE/hB,OAAM4jB,SAAUtO,EAAEsO,UAE7CnC,EAAM,CAAC,CAAEnmB,EAAG0E,EAAK1E,EAAGC,EAAGyE,EAAKzE,EAAGyS,EAAGhO,IAGrC,KAlEuB,QAAlBikB,GAEHtC,GAAY,EACZD,EAAU,KACVE,EAAYtM,EAAE+L,QACdQ,EAAYvM,EAAEgM,QACdQ,EA3CH,WACC,MAAM3K,EAAYC,EAAIpK,cAAc,UACpC,IAAKmK,EAAW,MAAO,CAAE7b,EAAG,EAAGC,EAAG,GAElC,MACM4oB,GADYhN,EAAUsB,aAAa,cAAgB,IACxB2L,MAAM,gCACvC,OAAID,EACI,CACN7oB,EAAG+oB,WAAWF,EAAe,KAAO,EACpC5oB,EAAG8oB,WAAWF,EAAe,KAAO,GAG/B,CAAE7oB,EAAG,EAAGC,EAAG,EACnB,CA8BsB+oB,GACnB7C,EAAM,GAEND,EAAKsB,aAAa,MAGlBnB,GAAY,EACZD,EAzHH,WACC,IAAI6C,EAAgB,EAAGC,EAAgB,EAAG9gB,EAA8B,KAExE,MAAO,CACN,GAAA+d,CAAInM,GAEH,MAAMmP,EAAKzD,EAAe1L,GAC1BiP,EAAgBE,EAAGnpB,EACnBkpB,EAAgBC,EAAGlpB,EAEnBmI,EAAOkD,SAASC,gBAAgB,6BAA8B,QAC9DnD,EAAKoD,aAAa,OAAQ,0BAC1BpD,EAAKoD,aAAa,SAAU,0BAC5BpD,EAAKoD,aAAa,eAAgB,KAClCpD,EAAKoD,aAAa,mBAAoB,OACtCpD,EAAKoD,aAAa,IAAKmC,OAAOsb,IAC9B7gB,EAAKoD,aAAa,IAAKmC,OAAOub,IAC9B9gB,EAAKoD,aAAa,QAAS,KAC3BpD,EAAKoD,aAAa,SAAU,KAG5B,MAAMqQ,EAAYC,EAAIpK,cAAc,UAChCmK,EACHA,EAAU/P,YAAY1D,GAEtB0T,EAAIhQ,YAAY1D,EAElB,EACA,MAAA0f,CAAO9N,GACN,IAAK5R,EAAM,OAGX,MAAMghB,EAAY1D,EAAe1L,GAC3BqP,EAAkBD,EAAUppB,EAC5BspB,EAAkBF,EAAUnpB,EAG5BD,EAAIF,KAAK6J,IAAIsf,EAAeI,GAC5BppB,EAAIH,KAAK6J,IAAIuf,EAAeI,GAC5BppB,EAAQJ,KAAKsD,IAAIimB,EAAkBJ,GACnC9oB,EAASL,KAAKsD,IAAIkmB,EAAkBJ,GAE1C9gB,EAAKoD,aAAa,IAAKmC,OAAO3N,IAC9BoI,EAAKoD,aAAa,IAAKmC,OAAO1N,IAC9BmI,EAAKoD,aAAa,QAASmC,OAAOzN,IAClCkI,EAAKoD,aAAa,SAAUmC,OAAOxN,GACpC,EACA,GAAAyI,GACC,IAAKR,EAAM,OAAO,KAGlB,MAAMpI,EAAI+oB,WAAW3gB,EAAK+U,aAAa,MAAQ,KACzCld,EAAI8oB,WAAW3gB,EAAK+U,aAAa,MAAQ,KACzCjd,EAAQ6oB,WAAW3gB,EAAK+U,aAAa,UAAY,KACjDhd,EAAS4oB,WAAW3gB,EAAK+U,aAAa,WAAa,KAMzD,OAJA/U,EAAK4D,SACL5D,EAAO,KAGHlI,EAAQ,GAAKC,EAAS,EAClB,CACNH,EAAGA,EAAGC,EAAGA,EAAGC,MAAOA,EAAOC,OAAQA,EAClCke,KAAMre,EAAGue,IAAKte,EAAGqe,MAAOte,EAAIE,EAAOse,OAAQve,EAAIE,GAG1C,IACR,EAEF,CAoDaopB,GACNnD,GAASA,EAAQD,IAAInM,GACzBmM,EAAM,GAoDT,CAwIEqD,CAAYxP,GACZ1O,SAASiX,iBAAiB,YAAa+E,GACvChc,SAASiX,iBAAiB,YAAa+E,GACvChc,SAASiX,iBAAiB,UAAW2F,GACrC5c,SAASiX,iBAAiB,WAAY2F,EACvC,CAEA5a,EAAQiV,iBAAiB,YAAakG,GACtCnb,EAAQiV,iBAAiB,aAAckG,GAGvC5B,EAAe9lB,KACd,CAAEuM,UAASmc,MAAO,YAAaC,QAASjB,GACxC,CAAEnb,UAASmc,MAAO,aAAcC,QAASjB,GAE3C,CAEAkB,CAAO7N,GACPA,EAAIyG,iBAAiB,QAASuE,GAC9BD,EAAe9lB,KAAK,CAAEuM,QAASwO,EAAK2N,MAAO,QAASC,QAAS5C,IAGtD,KACND,EAAeroB,QAAQ,EAAG8O,UAASmc,QAAOC,cACzCpc,EAAQ6a,oBAAoBsB,EAAOC,KAGtC,CAEO,SAASxG,EAAqBpH,EAAoB+G,GAExD,MAAM+G,EAAmB9N,EAAY+N,2BAKrC,SAASC,EAAQ1rB,GAEhB,OAAOA,EAAG2kB,MACX,CAPI6G,GACHA,IAQD,MAAM/mB,EAAK,IAAOinB,EAAQhO,GAGpB+K,EAA8F,GAE9FkD,EAAuB/P,IACvBnX,IAAK2S,YACVwE,EAAEiN,iBACFjN,EAAEgQ,YAAc,KAKjB,SAASC,EAAelc,EAAW8L,GAClC9L,EAAE8L,SAAWA,EACb,MAAMqQ,EAAQpO,EAAIpK,cAAc,IAAM3D,EAAEpM,IACxCoM,EAAE8L,SAAWqQ,EAAMtc,UAAU5F,IAAI,YAAckiB,EAAMtc,UAAU5B,OAAO,WACvE,CAPAqL,OAAOkL,iBAAiB,eAAgBwH,GACxClD,EAAe9lB,KAAK,CAAEuM,QAAS+J,OAAQoS,MAAO,eAAgBC,QAASK,IASvE,MAAMI,EAAoBnQ,IACzB,IAAKA,EAAEoQ,OAAQ,OACf,MAAM/E,EAAMD,EAAmBviB,IAAM6iB,EAAe1L,IACpD,GAAIqL,EAAK,CACR,MAAMG,IAACA,GAAOH,EACR9e,EAASuV,EAAIpK,cAAc,WACjC,IAAIX,EAAMxK,EAAOmL,cAAc,QAC1BX,IACJA,EAAM1D,EAAOC,QAAQ,SAAU,CAAC3L,GAAI,MAAOmjB,GAAIU,EAAIxlB,EAAGoS,GAAIoT,EAAIvlB,EAAGuO,EAAG,IACpEjI,EAAOgI,OAAOwC,IAEfA,EAAIvF,aAAa,KAAMmC,OAAO6X,EAAIxlB,IAClC+Q,EAAIvF,aAAa,KAAMmC,OAAO6X,EAAIvlB,GACnC,MACCoqB,KAGFvO,EAAIyG,iBAAiB,YAAa4H,GAClCtD,EAAe9lB,KAAK,CAAEuM,QAASwO,EAAK2N,MAAO,YAAaC,QAASS,IAEjE,MAAMG,EAAgBtQ,IACrB,MAAMvb,GAAM,EAAA8rB,EAAAC,IAAaxQ,GAAG,GACxBvb,GAAO8rB,EAAAE,IAAchsB,GAAO8rB,EAAAG,IAChCL,KAKD,SAASA,IACR,MAAMjsB,EAAK0d,EAAIpK,cAAc,gBAC7BtT,GAAMA,EAAG8d,cAAcQ,YAAYte,EACpC,CANAiZ,OAAOkL,iBAAiB,QAAS+H,GACjCzD,EAAe9lB,KAAK,CAAEuM,QAAS+J,OAAQoS,MAAO,QAASC,QAASY,IAOhE,MAAMK,EAAgB3Q,IACrB,MAAMvb,GAAM,EAAA8rB,EAAAC,IAAaxQ,GAAG,GAC5B,GAAIvb,GAAO8rB,EAAAG,IAAoBjsB,GAAO8rB,EAAAE,GAAY,OAClD,MAAMpF,EAAMD,EAAmBviB,IAAM6iB,EAAe1L,IACpD,GAAIqL,EAAK,CACR,MAAM9kB,KAACA,EAAIoQ,IAAEA,EAAG6U,IAAEA,GAAOH,EAEzBxiB,IAAKmY,iBAAiBza,EAAMilB,EAAK7U,EAAKlS,GAAO8rB,EAAAG,IAC7CL,GACD,GAEDvO,EAAIyG,iBAAiB,QAASoI,GAC9B9D,EAAe9lB,KAAK,CAAEuM,QAASwO,EAAK2N,MAAO,QAASC,QAASiB,IAE7D,MAAMC,EAAgB5Q,IAGrB,MAAM1Q,EAA8B,GAAtBxJ,KAAK+qB,KAAK7Q,EAAE8Q,QACpBnP,EAAcC,IACdmP,EAAUjrB,KAAK4J,IAAI,GAAK5J,KAAK6J,IAAI,EAAGgS,EAAcrS,IAExD,GAAIyhB,IAAYpP,EAAa,CAE5B,MAAMvT,EAAO0T,EAAI6J,wBAGjBqF,EAAgBD,EAFH/Q,EAAE+L,QAAU3d,EAAKiW,KACjBrE,EAAEgM,QAAU5d,EAAKmW,KAE9BvE,EAAEiN,iBAGF,MAAMlL,EAAaD,EAAYiH,OAC3BhH,GAAaA,EAAUpa,IAC1Bqf,GAAcjF,EAAUpa,GAE1B,GAEDma,EAAIyG,iBAAiB,QAASqI,GAC9B/D,EAAe9lB,KAAK,CAAEuM,QAASwO,EAAK2N,MAAO,QAASC,QAASkB,IAE7D,MAAMK,EAAkBjR,IACvB,MAAMkR,GAAW,EAAAX,EAAAC,IAAaxQ,GAO9B,OAJIkR,GACHlR,EAAEiN,iBAGKiE,GACP,KAAKX,EAAAY,GACqBxS,MAAMlY,KAAKoC,IAAK0T,aAAapP,UAAUnI,OAAO0O,GAAKA,EAAEmM,UAC7Drb,QAAQkP,IACxB7K,IAAKqY,iBAAiBxN,KAEvB,MACD,IAAK,OACJ7K,IAAKyS,OACL,MACD,IAAK,OACJzS,IAAK0S,OACL,MACD,KAAKgV,EAAAa,GAGJJ,EAFkBlrB,KAAK6J,IAAI,EAAe,IAAZiS,MAG9BoF,GAAcne,IAAKlB,IACnB,MACD,KAAK4oB,EAAAc,GAGJL,EAFmBlrB,KAAK4J,IAAI,GAAKkS,IAAY,MAG7CoF,GAAcne,IAAKlB,IACnB,MACD,KAAK4oB,EAAAe,GAEJN,EAAgB,GAChBhK,GAAcne,IAAKlB,IACnB,MACD,KAAK4oB,EAAAgB,GACJ1oB,IAAK+c,YAEL,MACD,KAAK2K,EAAAiB,EACJ3oB,IAAK4B,QAAQjG,QAAQkU,GAAK7P,IAAK+W,gBAAgBlH,GAAG,IAClD7P,IAAK0T,aAAa/X,QAAQkP,GAAKuc,EAAevc,GAAG,IACjD,MACD,KAAK6c,EAAAkB,GACJ5oB,IAAK4B,QAAQjG,QAAQkU,GAAK7P,IAAK+W,gBAAgBlH,GAAG,IAClD7P,IAAK0T,aAAa/X,QAAQkP,GAAKuc,EAAevc,GAAG,MAIpD2J,OAAOkL,iBAAiB,UAAW0I,GACnCpE,EAAe9lB,KAAK,CAAEuM,QAAS+J,OAAQoS,MAAO,UAAWC,QAASuB,IAGlE,MAAMS,EAA2BzF,EAA2BnK,EAAK,CAChE,aAAA4M,CAAc1O,GACbA,EAAEiN,iBAEF,IAAI7oB,EAAM4b,EAAEtR,OAAsBse,QAAQ,kBAC1C,OAAI5oB,EAAW0rB,EAAQ1rB,IAEvBA,EAAM4b,EAAEtR,OAAsBse,QAAQ,yBAClC5oB,EACIyE,IAAK0T,aAAahR,IAAInH,EAAGuD,IAE1B,KACR,EACA,YAAA6lB,CAAamE,GAEZ9oB,IAAK4B,QAAQjG,QAAQkU,GAAK7P,IAAK+W,gBAAgBlH,EAAGiZ,EAAQ7kB,KAAKyI,GAAKA,EAAE5N,IAAM+Q,EAAE/Q,MAE9EkB,IAAK0T,aAAa/X,QAAQuP,GAAKkc,EAAelc,EAAG4d,EAAQ7kB,KAAKyI,GAAKA,EAAE5N,IAAMoM,EAAEpM,MAC7E6gB,EAAe3f,IAAK4B,QAAQtD,KAAKuR,GAAKA,EAAEmH,UACzC,EACA,WAAAgO,CAAY9Z,GACX0U,EAAW1U,CACZ,EACA6a,WAAWgD,GACHA,EAAO/R,SAEf,YAAA0N,GACC,MAAMpX,EAAgBtN,IAAK4B,QAAQzF,OAAO0T,GAAKA,EAAEmH,UAEjD,OADAhX,IAAK0T,aAAa/X,QAAQuP,GAAKA,EAAE8L,UAAY1J,EAAIpP,KAAKgN,IAC/CoC,CACR,EACAyL,QAASA,EACT,QAAAtB,CAAS/K,EAAWvP,EAAWC,GAC1B4C,IAAKyT,SAASvO,IAAIwH,EAAE5N,IACvBkB,IAAKyX,SAAS/K,EAAWvP,EAAGC,IAE3BsP,EAAiBlN,MAAO,EACzBQ,IAAKgY,eAAetL,EAAiBvP,EAAGC,GAE1C,EACA,YAAAooB,CAAahZ,EAAcrH,GAG1BnF,IAAKyT,SAAS9X,QAAQkU,IJ1/DlB,IAAsBmZ,EAAUC,EAIX5c,GAJC2c,EAKrB,CAAC7rB,GADmBkP,EIu/DewD,GJt/D7B1S,EAAIkP,EAAEhP,MAAQ,EAAGD,EAAGiP,EAAEjP,EAAIiP,EAAE/O,OAAS,EAAGD,MAAOgP,EAAEhP,MAAOC,OAAQ+O,EAAE/O,SAJrEH,GAD4B8rB,EI2/DQzc,GJ1/D7BrP,EAAI8rB,EAAG5rB,OAAS2rB,EAAG5rB,EAAI6rB,EAAG7rB,EAAI6rB,EAAG3rB,QAAU0rB,EAAG7rB,EAAI6rB,EAAG3rB,MAAQ4rB,EAAG9rB,GAAK6rB,EAAG5rB,EAAI4rB,EAAG1rB,OAAS2rB,EAAG7rB,EI6/DxG4C,IAAK+W,gBAAgBlH,GAAIA,EAAEmH,UAChB7R,GAEXnF,IAAK+W,gBAAgBlH,GAAG,KAI1B7P,IAAK0T,aAAa/X,QAAQuP,IACXkB,EAAUlB,EAAGsB,GAAK,GAG/B4a,EAAelc,GAAIA,EAAE8L,UACV7R,GAEXiiB,EAAelc,GAAG,KAIpByU,EAAe3f,IAAK4B,QAAQtD,KAAKuR,GAAKA,EAAEmH,UACzC,EACA4C,cAAeA,GACboG,GAaD/G,EAAY+N,2BAVE,KACfhD,EAAeroB,QAAQ,EAAG8O,UAASmc,QAAOC,cACzCpc,EAAQ6a,oBAAoBsB,EAAOC,KAEhCgC,GACHA,IAMH,CAEO,SAAS9P,IACf,IAAKE,EAAK,OAAO,EACjB,MAAM1d,EAAK0d,EAAIpK,cAAc,UAC7B,IAAKtT,EAAI,OAAO,EAGhB,MACM2tB,GADY3tB,EAAG+e,aAAa,cAAgB,IACrB2L,MAAM,oBACnC,OAAIiD,GACIhD,WAAWgD,EAAW,KAEvB,CACR,CAIO,SAAS5I,EAAQlH,GACvB,IAAKH,EAAK,OACV,MAAM1d,EAAK0d,EAAIpK,cAAc,UAC7B,IAAKtT,EAAI,OAGT,MAAMynB,EAAmBC,IACzB1nB,EAAGoN,aAAa,YAAa,aAAaqa,EAAiB7lB,MAAM6lB,EAAiB5lB,YAAYgc,MAG9FQ,GACD,CAEO,SAASuO,EAAgBD,EAAiBtM,EAAkBC,GAClE,MAAMtgB,EAAK0d,EAAIpK,cAAc,UACvBsa,EAAUpQ,IAGhB,QAAgB9Y,IAAZ2b,QAAqC3b,IAAZ4b,EAAuB,CAGnD,MAAMuN,EAAYnQ,EAAII,cAClB+P,GACHxN,EAAUwN,EAAU9P,YAAc,EAClCuC,EAAUuN,EAAU7P,aAAe,IAGnCqC,EAAU3C,EAAIK,YAAc,EAC5BuC,EAAU5C,EAAIM,aAAe,EAE/B,CAGA,MAAMyJ,EAAmBC,IAYnBoG,EAAgBzN,GANJA,EAAUoH,EAAiB7lB,GAAKgsB,EAMNjB,EACtCoB,EAAgBzN,GANJA,EAAUmH,EAAiB5lB,GAAK+rB,EAMNjB,EAG5C3sB,EAAGoN,aAAa,YAAa,aAAa0gB,MAAkBC,YAAwBpB,MAGpFtO,GACD,CAEA,SAASqJ,IACR,IAAKhK,EAAK,MAAO,CAAE9b,EAAG,EAAGC,EAAG,GAC5B,MAAM7B,EAAK0d,EAAIpK,cAAc,UAC7B,IAAKtT,EAAI,MAAO,CAAE4B,EAAG,EAAGC,EAAG,GAE3B,MACM4oB,GADYzqB,EAAG+e,aAAa,cAAgB,IACjB2L,MAAM,gCACvC,OAAID,EACI,CACN7oB,EAAG+oB,WAAWF,EAAe,KAAO,EACpC5oB,EAAG8oB,WAAWF,EAAe,KAAO,GAG/B,CAAE7oB,EAAG,EAAGC,EAAG,EACnB,CAEA,SAASwc,IACR,IAAKX,EAAK,OACV,MAAM1d,EAAK0d,EAAIpK,cAAc,UAC7B,IAAKtT,EAAI,OACT,MAAM4d,EAAK5d,EAAG2N,UACRkQ,EAAOL,IACb,IAAKE,EAAII,cAAe,OACxB,MAAM5M,EAAIxP,KAAK4J,IAAIoS,EAAII,cAAcC,YAAcF,EAAMD,EAAGhc,EAAIgc,EAAG9b,MAAQ,IACrEqP,EAAIzP,KAAK4J,IAAIoS,EAAII,cAAcE,aAAeH,EAAMD,EAAG/b,EAAI+b,EAAG7b,OAAS,IAC7E2b,EAAItQ,aAAa,QAASmC,OAAO2B,EAAI2M,IACrCH,EAAItQ,aAAa,SAAUmC,OAAO4B,EAAI0M,GAIvC,CAYO,MAwBD+H,EAAiB,CAAC5lB,EAAgBC,KAC1B,UAATA,EAAmBD,EAAGoN,aAAa,mBAAoB,KACzC,UAATnN,GAAmBD,EAAGoN,aAAa,mBAAoB,MAG3DuY,EAAS,CAEdllB,WAAY,CAEXG,OAAQ,gBAETC,SAAU,CACT,cAAe,oBACfF,OAAQ,QAITG,SAAU,CACT,cAAe,oBACfH,OAAQ,QAGTI,SAAU,CACTL,KAAM,OACNC,OAAQ,QAITK,UAAW,CAEVN,KAAM,sBACNC,OAAQ,OACR,eAAgB,EAChB,mBAAoB,GAErBM,UAAW,CACV,cAAe,oBACfP,KAAM,OACN,YAAa,GACb,cAAe,OACfQ,OAAQ,YAKJ8sB,EAAiB,IAAIpnB,IAGpB,SAASgc,GAAcqL,GAC7B,IAAKvQ,EAAK,OAEV,MAAMG,EAAOL,IACP7M,EAAY+W,IAEZ3E,EAAQ,CACblF,OACAlN,UAAW,CAAE/O,EAAG+O,EAAU/O,EAAGC,EAAG8O,EAAU9O,IAG3CmsB,EAAengB,IAAIogB,EAASlL,EAC7B,CAGO,SAASmL,GAAiBD,GAChC,IAAKvQ,IAAQsQ,EAAerkB,IAAIskB,GAC/B,OAAO,EAGR,MAAMlL,EAAQiL,EAAe7mB,IAAI8mB,GACjC,IAAKlL,EACJ,OAAO,EAIR,MAAMtF,EAAYC,EAAIpK,cAAc,UAMpC,OALImK,IACHA,EAAUrQ,aAAa,YAAa,SAAS2V,EAAMlF,mBAAmBkF,EAAMpS,UAAU/O,MAAMmhB,EAAMpS,UAAU9O,MAC5Gwc,MAGM,CACR,CAGO,SAASH,GAAe+P,GAC9BD,EAAehR,OAAOiR,EACvB,mDCtvEA,MAAME,EAAyC,CAC9CC,UAAW,OACXC,YAAa,GACbC,aAAc,IAORC,eAAeC,EACrBC,EACAvN,EAA2C,CAAC,GAE5C,MACMhI,EAYP,SACCuV,EACAvN,GAEA,MAAM7a,EAAQ,IAAIO,IAAI6nB,EAAQpoB,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KACpDC,EAAS,IAAIK,IAAI6nB,EAAQloB,OAAOb,IAAIc,GAAS,CAACA,EAAMjD,GAAIiD,MA6H/D,SACCioB,EACApoB,EACAE,GAEA,IAAK,MAAMD,KAAQmoB,EAAQpoB,MAC1B,GAAIC,EAAK4B,WAAa3B,EAAOoD,IAAIrD,EAAK4B,UACrC,MAAM,IAAI5E,MAAM,QAAQgD,EAAK/C,+BAA+B+C,EAAK4B,YAGnE,IAAK,MAAM1B,KAASioB,EAAQloB,OAC3B,GAAIC,EAAM0B,WAAa3B,EAAOoD,IAAInD,EAAM0B,UACvC,MAAM,IAAI5E,MAAM,SAASkD,EAAMjD,+BAA+BiD,EAAM0B,YAGtE,IAAK,MAAM/F,KAAQssB,EAAQhoB,MAC1B,IAAKJ,EAAMsD,IAAIxH,EAAKoG,YAAclC,EAAMsD,IAAIxH,EAAKqG,UAChD,MAAM,IAAIlF,MACT,QAAQnB,EAAKoB,4BAA4BpB,EAAKoG,eAAepG,EAAKqG,WAItE,CAlJCkmB,CAAmBD,EAASpoB,EAAOE,GAEnC,MAAMooB,EAAmB,IAAI/nB,IAC7B,IAAK,MAAMN,KAAQmoB,EAAQpoB,MAC1B8J,EAAOwe,EAAkBroB,EAAK4B,SAAU5B,GAEzC,MAAMsoB,EAAiB,IAAIhoB,IAC3B,IAAK,MAAMJ,KAASioB,EAAQloB,OAC3B4J,EAAOye,EAAgBpoB,EAAM0B,SAAU1B,GAGxC,MAAMqoB,EAAY,IAAIjoB,IAChB4X,EAAchY,IACnB,MAAMyB,EAAiB,CACtB1E,GAAIiD,EAAMjD,GACVurB,SAAU,KACLF,EAAeznB,IAAIX,EAAMjD,KAAO,IAAImC,IAAI8Y,OACxCmQ,EAAiBxnB,IAAIX,EAAMjD,KAAO,IAAImC,IAAIsgB,IAE/Cvf,MAAO,GACP6B,OAAQ,CAAC,CACRkE,KAAMhG,EAAMjD,GACZzB,MAAO0E,EAAMkB,UAAU5F,MACvBC,OAAQyE,EAAMkB,UAAU3F,SAEzBgtB,cAAeC,EAAmB9N,EAAS1a,EAAMkB,UAAU3F,SAG5D,OADA8sB,EAAUhhB,IAAIrH,EAAMjD,GAAI0E,GACjBA,GAGFgnB,EAAgB,CACrB1rB,GAAIkrB,EAAQlrB,GACZurB,SAAU,KACLF,EAAeznB,SAAIzC,IAAc,IAAIgB,IAAI8Y,OACzCmQ,EAAiBxnB,SAAIzC,IAAc,IAAIgB,IAAIsgB,IAEhDvf,MAAO,GACPsoB,cAAeG,EAAkBhO,IAGlC,IAAK,MAAM/e,KAAQssB,EAAQhoB,MAAO,CACjC,MAAM0oB,EAAQC,EAAkBjtB,EAAMkE,EAAOE,GACvCsnB,EAAYsB,EAAQN,EAAU1nB,IAAIgoB,GAASF,EACjD,IAAKpB,EACJ,MAAM,IAAIvqB,MAAM,QAAQnB,EAAKoB,+BAA+B4rB,KAE7DtB,EAAUpnB,OAAO9D,KAAK4b,EAAUpc,GACjC,CACA,OAAO8sB,CACR,CApEeI,CAAgBZ,EADb,IAAIN,KAAmBjN,IAGlCoO,EAAM,UADMC,EAAA3T,EAAA,KAAA4T,KAAAD,EAAA3f,EAAA6f,KAAAF,EAAA,SAAmCC,KAAKE,GAAUA,EAAOC,UAG3E,OA4LD,SACClB,EACAQ,GAEA,QAAmBvqB,IAAfuqB,EAAKntB,YAAuC4C,IAAhBuqB,EAAKltB,OACpC,MAAM,IAAIuB,MAAM,4BAA4BmrB,EAAQlrB,MAErD,MAAM8C,EAAwB,GACxBE,EAA0B,GAC1BE,EAAwB,GAc9B,OAVAmpB,EACCX,EACA,EACA,EANqB,IAAIroB,IAAI6nB,EAAQpoB,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KAC3C,IAAIM,IAAI6nB,EAAQloB,OAAOb,IAAIc,GAAS,CAACA,EAAMjD,GAAIiD,KAQrEH,EACAE,EACAE,GAEM,CACNN,UAAWsoB,EAAQlrB,GACnB0D,OAAQ,CAACrF,EAAG,EAAGC,EAAG,EAAGC,MAAOmtB,EAAKntB,MAAOC,OAAQktB,EAAKltB,QACrDsE,QACAE,SACAE,QAEF,CA1NQopB,CAAmBpB,QADLa,EAAIlW,OAAOF,GAEjC,CAkEA,SAAS8M,EAAU1f,GAClB,MAAO,CACN/C,GAAI+C,EAAK/C,GACTzB,MAAOwE,EAAKe,KAAKvF,MACjBC,OAAQuE,EAAKe,KAAKtF,OAClBgtB,cAAe,CACd,2BAA4B,aAC5B,sBAAuB,QAG1B,CAGA,SAASxQ,EAAUpc,GAClB,MAAO,CACNoB,GAAIpB,EAAKoB,GACTusB,QAAS,CAAC3tB,EAAKoG,UACfwnB,QAAS,CAAC5tB,EAAKqG,UACfF,OAAQnG,EAAKmH,UAAY,CAAC,CACzBkD,KAAMrK,EAAKoB,GACXzB,MAAOK,EAAKmH,UAAUxH,MACtBC,OAAQI,EAAKmH,UAAUvH,OACvBgtB,cAAe,CACd,wBAAyB,QACzB,2BAA4B,YAEzB,GAEP,CAGA,SAASG,EAAkBhO,GAC1B,MAAO,CACN,gBAAiB,UACjB,gBAAiBA,EAAQkN,UACzB,kBAAmB,aACnB,wBAAyB,mBACzB,cAAe,sCACf,uBAAwB7e,OAAO2R,EAAQmN,aACvC,iCAAkC9e,OAAO2R,EAAQmN,aACjD,4CAA6C9e,OAAO2R,EAAQoN,cAC5D,4CAA6C,KAC7C,4CAA6C,KAC7C,qCAAsC,kBACtC,+CAAgD,OAChD,4CAA6C,cAC7C,oCAAqC,OACrC,wBAAyB,QACzB,8BAA+B,OAC/B,uCAAwC,aACxC,wBAAyB,KAE3B,CAGA,SAASU,EACR9N,EACA8O,GAEA,MAAO,IACHd,EAAkBhO,GACrB,cAAe,QAAQ8O,EAAc,iCACrC,2BAA4B,wBAC5B,wBAAyB,KAE3B,CA+BA,SAASZ,EACRjtB,EACAkE,EACAE,GAEA,MAAM0pB,EAAeC,EAAa7pB,EAAMc,IAAIhF,EAAKoG,WAAWL,SAAU3B,GAChE4pB,EAAe,IAAIzmB,IAAIwmB,EAAa7pB,EAAMc,IAAIhF,EAAKqG,WAAWN,SAAU3B,IAC9E,OAAO0pB,EAAaltB,KAAKyD,GAAS2pB,EAAaxmB,IAAInD,GACpD,CAGA,SAAS0pB,EACRhoB,EACA3B,GAEA,MAAMoL,EAAmB,GACzB,IAAIye,EAAUloB,EACd,KAAOkoB,GACNze,EAAOhP,KAAKytB,GACZA,EAAU7pB,EAAOY,IAAIipB,IAAUloB,SAEhC,OAAOyJ,CACR,CAuCA,SAASie,EACR/B,EACAwC,EACAC,EACA3pB,EACAE,EACAR,EACAE,EACAE,GAEA,MAAM8pB,EAAaF,GAAWxC,EAAUjsB,GAAK,GACvC4uB,EAAaF,GAAWzC,EAAUhsB,GAAK,GAC7C,IAAK,MAAMoG,KAAS4lB,EAAUiB,UAAY,GAAI,CAC7C,MAAM2B,EAASF,EAAaG,EAAezoB,EAAMrG,EAAGqG,EAAM1E,GAAI,KACxDotB,EAASH,EAAaE,EAAezoB,EAAMpG,EAAGoG,EAAM1E,GAAI,KACxDzB,EAAQ4uB,EAAezoB,EAAMnG,MAAOmG,EAAM1E,GAAI,SAC9CxB,EAAS2uB,EAAezoB,EAAMlG,OAAQkG,EAAM1E,GAAI,UAChDqtB,EAAejqB,EAAcQ,IAAIc,EAAM1E,IAC7C,GAAIqtB,EAAc,CACjBvqB,EAAM1D,KAAK,CACVY,GAAI0E,EAAM1E,GACV2E,SAAU0oB,EAAa1oB,SACvBjB,OAAQ,CAACrF,EAAG6uB,EAAQ5uB,EAAG8uB,EAAQ7uB,QAAOC,YAEvC,QACD,CACA,MAAM8uB,EAAgBhqB,EAAeM,IAAIc,EAAM1E,IAC/C,IAAKstB,EACJ,MAAM,IAAIvtB,MAAM,6BAA6B2E,EAAM1E,MAEpD,MAAMN,EAAQ6tB,EAAc7oB,EAAMK,SAAS,GAAIL,EAAM1E,IACrDgD,EAAO5D,KAAK,CACXY,GAAI0E,EAAM1E,GACV2E,SAAU2oB,EAAc3oB,SACxBjB,OAAQ,CAACrF,EAAG6uB,EAAQ5uB,EAAG8uB,EAAQ7uB,QAAOC,UACtCyF,YAAa,CACZ5F,EAAG6uB,EAASC,EAAeztB,EAAMrB,EAAGqG,EAAM1E,GAAI,WAC9C1B,EAAG8uB,EAASD,EAAeztB,EAAMpB,EAAGoG,EAAM1E,GAAI,WAC9CzB,MAAO4uB,EAAeztB,EAAMnB,MAAOmG,EAAM1E,GAAI,eAC7CxB,OAAQ2uB,EAAeztB,EAAMlB,OAAQkG,EAAM1E,GAAI,mBAGjDqsB,EACC3nB,EACAsoB,EACAC,EACA7pB,EACAE,EACAR,EACAE,EACAE,EAEF,CACA,IAAK,MAAMtE,KAAQ0rB,EAAUpnB,OAAS,GACrCA,EAAM9D,KAAKouB,EAAS5uB,EAAMouB,EAAYC,GAExC,CAGA,SAASO,EAAS5uB,EAAuBib,EAAiBC,GACzD,MAAM5U,GAAYtG,EAAKsG,UAAY,IAAI/C,IAAIiD,GAAW,CACrDqoB,EAAUroB,EAAQsoB,WAAY7T,EAASC,OACnC1U,EAAQuoB,YAAc,IAAIxrB,IAAI9B,GAASotB,EAAUptB,EAAOwZ,EAASC,IACrE2T,EAAUroB,EAAQwoB,SAAU/T,EAASC,KAEhCpa,EAAQd,EAAKmG,SAAS,GAC5B,MAAO,CACN/E,GAAIpB,EAAKoB,GACTgF,SAAU6oB,EAAiBjvB,EAAK2tB,QAAS3tB,EAAKoB,GAAI,UAClDiF,SAAU4oB,EAAiBjvB,EAAK4tB,QAAS5tB,EAAKoB,GAAI,UAClDkF,WACAY,YAAapG,EAAQ,CACpBrB,EAAGwb,EAAUsT,EAAeztB,EAAMrB,EAAGO,EAAKoB,GAAI,WAC9C1B,EAAGwb,EAAUqT,EAAeztB,EAAMpB,EAAGM,EAAKoB,GAAI,WAC9CzB,MAAO4uB,EAAeztB,EAAMnB,MAAOK,EAAKoB,GAAI,eAC5CxB,OAAQ2uB,EAAeztB,EAAMlB,OAAQI,EAAKoB,GAAI,sBAC3CmB,EAEN,CAGA,SAASssB,EAAUptB,EAAiBwZ,EAAiBC,GACpD,MAAO,CAACzb,EAAGwb,EAAUxZ,EAAMhC,EAAGC,EAAGwb,EAAUzZ,EAAM/B,EAClD,CAGA,SAAS6uB,EACRpwB,EACA+wB,EACA7Z,GAEA,QAAc9S,IAAVpE,EACH,MAAM,IAAIgD,MAAM,eAAekU,SAAa6Z,KAE7C,OAAO/wB,CACR,CAGA,SAAS8wB,EACRE,EACAzQ,EACArJ,GAEA,GAAyB,IAArB8Z,EAAUluB,OACb,MAAM,IAAIE,MAAM,gBAAgBguB,EAAUluB,UAAUoU,eAAmBqJ,KAExE,OAAOyQ,EAAU,EAClB,CAGA,SAASR,EAAc7tB,EAA6B0H,GACnD,IAAK1H,EACJ,MAAM,IAAIK,MAAM,+BAA+BqH,KAEhD,OAAO1H,CACR,CAGA,SAASkN,EACRlE,EACA/D,EACAshB,GAEA,MAAM+H,EAAWtlB,EAAM9E,IAAIe,IAAa,GACxCqpB,EAAS5uB,KAAK6mB,GACdvd,EAAM4B,IAAI3F,EAAUqpB,EACrB,qBCnWOhD,eAAetN,EACrB/H,EACAgI,EAAyB,CAAC,SAEpBhU,SAASskB,MAAMC,MACrB,MAAMxrB,EAAWyrB,EAAaxY,GACxB6I,EAAUb,EAAQyQ,cAAgB,GAAK,GACvCzrB,QAAiBsoB,EAAiBvoB,EAAU,CACjDmoB,UAAWlN,EAAQkN,WAAalV,EAAMZ,iBAAmB,OACzD+V,YAAanN,EAAQmN,aAAetM,EACpCuM,aAAcpN,EAAQoN,cAAgBvM,IAEjCR,GAAY,EAAAqQ,EAAAxhB,GAAuBnK,EAAUC,GAC7CS,EAAgB,IAAIC,IAAIX,EAASI,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KACnE,MAAO,CACNib,YACAlb,MAAOkb,EAAUlb,MAAMX,IAAIY,IAC1B,MAAMsqB,EAAejqB,EAAcQ,IAAIb,EAAK/C,IAC5C,IAAKqtB,EACJ,MAAM,IAAIttB,MAAM,0CAA0CgD,EAAK/C,MAEhE,MAAO,CACNA,GAAI+C,EAAK/C,GACT3B,EAAG0E,EAAKW,OAAOrF,EAAIgvB,EAAaiB,aAAajwB,EAC7CC,EAAGyE,EAAKW,OAAOpF,EAAI+uB,EAAaiB,aAAahwB,KAG/C0E,OAAQgb,EAAUhb,OAAOb,IAAIc,IAAK,CACjCjD,GAAIiD,EAAMjD,GACV0D,OAAQT,EAAMS,OACdO,YAAahB,EAAMgB,eAEpBf,MAAO8a,EAAU9a,MAAMf,IAAIvD,IAAI,CAC9BoB,GAAIpB,EAAKoB,GACTkF,SAAUtG,EAAKsG,SACfY,YAAalH,EAAKkH,eAGrB,CAMO,SAASyoB,EAAsB5Y,GACrC,MAAMjT,EAAWyrB,EAAaxY,GACxBvS,EAAgB,IAAIC,IAAIX,EAASI,MAAMX,IAAIY,GAAQ,CAACA,EAAK/C,GAAI+C,KAC7DD,EAAQ6S,EAAM7S,QAAQX,IAAIY,IAC/B,MAAMsqB,EAAejqB,EAAcQ,IAAIb,EAAK/C,IAC5C,IAAKqtB,EACJ,MAAM,IAAIttB,MAAM,iCAAiCgD,EAAK/C,MAEvD,MAAO,CACNA,GAAI+C,EAAK/C,GACT2E,SAAU0oB,EAAa1oB,SACvBjB,OAAQ,CACPrF,EAAG0E,EAAK1E,EAAIgvB,EAAaiB,aAAajwB,EACtCC,EAAGyE,EAAKzE,EAAI+uB,EAAaiB,aAAahwB,KACnC+uB,EAAavpB,SAIbd,EAASgU,MAAMlY,KAAK6W,EAAMd,UAAUrP,UAAUrD,IAAIc,IACvD,IAAKA,EAAMkV,KAAKpO,YACf,MAAM,IAAIhK,MAAM,SAASkD,EAAMjD,kCAEhC,MAAMwuB,EAAWvrB,EAAMkV,IAAIpI,cAAc,QACnCsG,EAAQpT,EAAMkV,IAAIpI,cAAc,QACtC,KAAMye,aAAoBC,oBAAyBpY,aAAiBoY,oBACnE,MAAM,IAAI1uB,MAAM,SAASkD,EAAMjD,uCAEhC,MAAO,CACNA,GAAIiD,EAAMjD,GACV2E,SAAUjC,EAASM,OAAOxD,KAAKwB,GAAaA,EAAUhB,KAAOiD,EAAMjD,KAAK2E,SACxEjB,OAAQgrB,EAAQF,EAASpkB,WACzBnG,YAAayqB,EAAQrY,EAAMjM,cAGvBlH,EAAQyS,EAAMzS,MAAMf,IAAIvD,IAC7B,MAAMsG,EAAWtG,EAAK2Z,gBAAkB,EAAC,EAAAoK,EAAAC,GAAsBhkB,IACzDkH,EAAclH,EAAK4Z,mBAAqB5Z,EAAKkH,YACnD,GAAIlH,EAAKc,MAAMgL,OAAO7K,OAAS,IAAMiG,EACpC,MAAM,IAAI/F,MAAM,QAAQnB,EAAKoB,oCAE9B,MAAO,CACNA,GAAIpB,EAAKoB,GACTgF,SAAUpG,EAAKE,KAAKkB,GACpBiF,SAAUrG,EAAKI,GAAGgB,GAClBkF,WACAY,YAAaA,EAAc,IAAIA,QAAe3E,KAG1CwtB,EAA2B,CAChC/rB,UAAW+S,EAAM3V,GACjB0D,OAAQiW,EAAc7W,EAAMX,IAAIY,GAAQA,EAAKW,QAASV,EAAQE,GAC9DJ,QACAE,SACAE,SAED,OChIM,SACNR,EACAksB,GAEA,OAAO,EAAAP,EAAAxhB,GAAuBnK,EAAUksB,EACzC,CD2HQC,CAAcnsB,EAAUisB,EAChC,CAMA,SAASR,EAAaxY,GACrB,MAAMmZ,YAACA,EAAWC,aAAEA,GAmErB,SAAyBpZ,GAIxB,MAAMmZ,EAAc,IAAIzrB,IAClB0rB,EAAe,IAAI1rB,IACzB,IAAK,MAAMJ,KAAS0S,EAAMd,UAAUrP,SACnC,IAAK,MAAMwpB,KAAU/rB,EAAMH,MAAO,CACjC,MAAMmsB,EAAUC,EAAQF,GAAUD,EAAeD,EAC3CK,EAAWF,EAAQrrB,IAAIorB,EAAOhvB,IACpC,GAAImvB,GAAYA,IAAalsB,EAAMjD,GAClC,MAAM,IAAID,MACT,GAAGmvB,EAAQF,GAAU,QAAU,UAAUA,EAAOhvB,sBAC7CmvB,SAAgBlsB,EAAMjD,MAG3BivB,EAAQ3kB,IAAI0kB,EAAOhvB,GAAIiD,EAAMjD,GAC9B,CAED,MAAO,CAAC8uB,cAAaC,eACtB,CAvFqCK,CAAgBzZ,GACpD,MAAO,CACN3V,GAAI2V,EAAM3V,GACV8C,MAAO6S,EAAM7S,QAAQX,IAAIY,GAS3B,SAAqBA,EAAY4B,GAChC,IAAK5B,EAAKoV,KAAKpO,YACd,MAAM,IAAIhK,MAAM,QAAQgD,EAAK/C,kCAE9B,MAAM0D,EAASX,EAAKoV,IAAI/N,UAExB,OADAilB,EAAiBtsB,EAAK/C,GAAI0D,GACnB,CACN1D,GAAI+C,EAAK/C,GACT2E,WACAb,KAAM,CAACvF,MAAOmF,EAAOnF,MAAOC,OAAQkF,EAAOlF,QAC3CuF,YAAa,CAACxF,MAAOmF,EAAOnF,MAAOC,OAAQkF,EAAOlF,QAClD8vB,aAAc,CAACjwB,GAAIqF,EAAOrF,EAAGC,GAAIoF,EAAOpF,GACxCI,MAAOqE,EAAKrG,MAAMgC,OAAS,MAE7B,CAvBmC4wB,CAAYvsB,EAAM+rB,EAAYlrB,IAAIb,EAAK/C,MACxEgD,OAAQgU,MAAMlY,KAAK6W,EAAMd,UAAUrP,UAAUrD,IAAIc,GAyBnD,SAAsBA,EAAc0B,GACnC,IAAK1B,EAAMkV,KAAKpO,YACf,MAAM,IAAIhK,MAAM,SAASkD,EAAMjD,kCAEhC,MAAMqW,EAAQpT,EAAMkV,IAAIpI,cAAc,QACtC,KAAMsG,aAAiBoY,oBACtB,MAAM,IAAI1uB,MAAM,SAASkD,EAAMjD,iCAEhC,MAAM0D,EAAS2S,EAAMjM,UAErB,OADAilB,EAAiBpsB,EAAMjD,GAAI0D,GACpB,CACN1D,GAAIiD,EAAMjD,GACV2E,WACAR,UAAW,CAAC5F,MAAOmF,EAAOnF,MAAOC,OAAQkF,EAAOlF,QAElD,CAvCG+wB,CAAatsB,EAAO8rB,EAAanrB,IAAIX,EAAMjD,MAE5CkD,MAAOyS,EAAMzS,MAAMf,IAAIqtB,GAEzB,CAsCA,SAASA,EAAY5wB,GACpB,MAAM6wB,EAAW7wB,EAAKc,MAAMgL,OAAO7K,OAAS,EAC5C,GAAI4vB,IAAa7wB,EAAKkH,YACrB,MAAM,IAAI/F,MAAM,QAAQnB,EAAKoB,kCAE9B,MAAO,CACNA,GAAIpB,EAAKoB,GACTgF,SAAUpG,EAAKE,KAAKkB,GACpBiF,SAAUrG,EAAKI,GAAGgB,GAClB+F,UAAW0pB,EAAW,CACrBlxB,MAAOK,EAAKkH,aAAavH,OAAS,EAClCC,OAAQI,EAAKkH,aAAatH,QAAU,QACjC2C,EAEN,CA6BA,SAASkuB,EAAiBrvB,EAAY0N,GACrC,IACEhH,OAAOC,SAAS+G,EAAIrP,KACpBqI,OAAOC,SAAS+G,EAAIpP,KACpBoI,OAAOC,SAAS+G,EAAInP,SACpBmI,OAAOC,SAAS+G,EAAIlP,SACrBkP,EAAInP,OAAS,GACbmP,EAAIlP,QAAU,EAEd,MAAM,IAAIuB,MAAM,6CAA6CC,IAE/D,CAGA,SAAS2Z,EACR7W,EACAE,EACAE,GAEA,MAAMwsB,EAAQ,IACV5sB,KACAE,EAAO4H,QAAQ3H,GAAS,CAACA,EAAMS,OAAQT,EAAMgB,iBAC7Cf,EAAM0H,QAAQhM,GAAQA,EAAKkH,YAAc,CAAClH,EAAKkH,aAAe,KAE5D6pB,EAASzsB,EAAM0H,QAAQhM,GAAQA,EAAKsG,SAAS0qB,QACnD,GAAqB,IAAjBF,EAAM7vB,QAAkC,IAAlB8vB,EAAO9vB,OAChC,MAAO,CAACxB,EAAG,EAAGC,EAAG,EAAGC,MAAO,EAAGC,OAAQ,GAEvC,MAAMke,EAAOve,KAAK6J,OACd0nB,EAAMvtB,IAAIuL,GAAOA,EAAIrP,MACrBsxB,EAAOxtB,IAAI9B,GAASA,EAAMhC,IAExBue,EAAMze,KAAK6J,OACb0nB,EAAMvtB,IAAIuL,GAAOA,EAAIpP,MACrBqxB,EAAOxtB,IAAI9B,GAASA,EAAM/B,IAU9B,MAAO,CAACD,EAAGqe,EAAMpe,EAAGse,EAAKre,MARXJ,KAAK4J,OACf2nB,EAAMvtB,IAAIuL,GAAOA,EAAIrP,EAAIqP,EAAInP,UAC7BoxB,EAAOxtB,IAAI9B,GAASA,EAAMhC,IAMUqe,EAAMle,OAJ/BL,KAAK4J,OAChB2nB,EAAMvtB,IAAIuL,GAAOA,EAAIpP,EAAIoP,EAAIlP,WAC7BmxB,EAAOxtB,IAAI9B,GAASA,EAAM/B,IAEiCse,EAChE,CAGA,SAAS8R,EAAQhhB,GAChB,MAAO,CAACrP,EAAGqP,EAAIrP,EAAGC,EAAGoP,EAAIpP,EAAGC,MAAOmP,EAAInP,MAAOC,OAAQkP,EAAIlP,OAC3D,CAGA,SAAS0wB,EAAQF,GAChB,MAAO,UAAWA,CACnB,0BElNA,MAAMa,EAA2D,CAChEC,UAAW,OACXC,UAAW,KACXC,UAAW,QACXC,UAAW,qBA8TcC,IACzB,MAAMC,EAAuB,GAO7B,OANiBxzB,OAAOC,KAAKszB,EAAME,OAAO/yB,OAAO+H,GAAWA,EAAQiY,SAAS,UACpExgB,QAAQgR,IAChBqiB,EAAME,MAAMviB,GAAGhR,QAASkP,IACvBokB,EAAU/wB,KAAK,CAACtC,IAAKiP,EAAEjP,IAAKuZ,MAAOtK,EAAEsK,OAAStK,EAAEjP,IAAKsI,QAASyI,QAGzDsiB,SA9SiB,CAACD,EAAcG,EAAkBC,KAEzD,MAAMC,EAAW,IAAIltB,IACfmtB,EAAY,IAAIntB,IAEhBotB,EAAeh0B,IAChBua,MAAMwG,QAAQ/gB,EAAGi0B,gBACpBj0B,EAAGi0B,cAAc7zB,QAAQ8zB,IACxBH,EAAUlmB,IAAIqmB,EAAI3wB,GAAI2wB,MAoCzB,GA9BAT,EAAMA,MAAMU,QAAUV,EAAMA,MAAMU,OAAO/zB,QAASJ,IACjD8zB,EAASjmB,IAAI7N,EAAGuD,GAAIvD,GAChBua,MAAMwG,QAAQ/gB,EAAGi0B,gBACpBj0B,EAAGi0B,cAAc7zB,QAAQ8zB,IACxBH,EAAUlmB,IAAIqmB,EAAI3wB,GAAI2wB,OAKzBT,EAAMA,MAAMW,iBAAmBX,EAAMA,MAAMW,gBAAgBh0B,QAASJ,IACnE8zB,EAASjmB,IAAI7N,EAAGuD,GAAIvD,GACpBg0B,EAAYh0B,GAERua,MAAMwG,QAAQ/gB,EAAGq0B,aACpBr0B,EAAGq0B,WAAWj0B,QAASk0B,IACtBA,EAAInsB,OAASnI,EACb8zB,EAASjmB,IAAIymB,EAAI/wB,GAAI+wB,GACrBN,EAAYM,GACR/Z,MAAMwG,QAAQuT,EAAIC,aACrBD,EAAIC,WAAWn0B,QAASgT,IACvBA,EAAIjL,OAASmsB,EACbR,EAASjmB,IAAIuF,EAAI7P,GAAI6P,GACrB4gB,EAAY5gB,SAQbqgB,EAAMA,MAAMe,gBAAiB,CAChC,MAAMC,EAAsBz0B,IAC3BA,EAAGy0B,oBAAsBz0B,EAAGy0B,mBAAmBr0B,QAASopB,IACvD,MAAM8K,EAAM,IAAIR,EAAS3sB,IAAIqiB,EAAKkL,aAAcnxB,GAAIimB,EAAKjmB,IACzDuwB,EAASjmB,IAAIymB,EAAI/wB,GAAI+wB,GACrBA,EAAInsB,OAASnI,EACbg0B,EAAYxK,MAIRmL,EAAc,CAAC30B,EAAamI,KACjCnI,EAAGmI,OAASA,EACZ2rB,EAASjmB,IAAI7N,EAAGuD,GAAIvD,GACpBg0B,EAAYh0B,GACZy0B,EAAmBz0B,GACnBA,EAAG8uB,UAAY9uB,EAAG8uB,SAAS1uB,QAASk0B,GAAiBK,EAAYL,EAAKt0B,IACtEA,EAAG40B,qBAAuB50B,EAAG40B,oBAAoBx0B,QAASk0B,GAAiBK,EAAYL,EAAKt0B,KAG7FyzB,EAAMA,MAAMe,gBAAgBp0B,QAASJ,GAAgB20B,EAAY30B,EAAI,MACtE,CAGA,MAAM60B,KAACA,EAAIlsB,QAAEA,GA+Md,SAAiB8qB,EAAcI,GAC9B,IAAIgB,EAAa,KAAMlsB,EAAkB,GAUzC,OATAzI,OAAOC,KAAKszB,EAAME,OAAO/yB,OAAOwQ,GAAKA,EAAEwP,SAAS,UAAUlY,KAAM0I,GACtDqiB,EAAME,MAAcviB,GAAI1I,KAAM4G,IACtC,GAAIA,EAAEjP,KAAOwzB,EAGZ,OAFAgB,EAAOvlB,EACP3G,EAAUyI,GACH,KAIH,CAACyjB,OAAMlsB,UACf,CA3NyBmsB,CAAQrB,EAAOI,GAEvC,IAAKgB,EAAM,OAAO,KAElB,MAAM3b,EAAQ,IAAI6b,EAAAC,GAAUH,EAAKx0B,IAAKw0B,EAAKjb,OAASib,EAAKx0B,KACnD40B,EAAgBJ,EAAKK,iBAAiBD,cAC5C/b,EAAMZ,gBAAkB2c,EAAgB7B,EAAiB6B,QAAiBvwB,EAC1E,MAAM2T,EAAqB,CAACtS,KAAMmT,EAAMnT,KAAMwT,YAAasb,EAAKtb,YAAa4b,QAAS1B,EAAM0B,QAASrB,SAAU,IAG/G,GAFA5a,EAAMb,SAAWA,GAEZwc,EAAKf,SAAU,OAAO5a,EAG3B,MAAMkc,EAA0C,CAAC,EACjD,GAAe,mBAAXzsB,GAA2C,kBAAXA,EACnCksB,EAAKf,SAAS1zB,QAAQsb,IACrB,MAAM1b,EAAK8zB,EAAS3sB,IAAIuU,EAAInY,IACxBvD,GAAImI,SACPitB,EAAYp1B,EAAGmI,OAAO5E,KAAM,UAGxB,GAAIsxB,EAAKQ,iBAEVR,EAAKf,SAAS/wB,KAAK2Y,GAAOA,EAAInY,IAAMsxB,EAAKQ,oBAC7CD,EAAYP,EAAKQ,mBAAoB,QAChC,GAAe,wBAAX1sB,EAAmC,CAE7C,MAAMxE,EAAa,CAACZ,GAAI,oBAAqBkwB,EAAMA,MAAM6B,YACzDxB,EAASjmB,IAAI1J,EAAEZ,GAAIY,GACfsvB,EAAMA,MAAMU,QAAQV,EAAMA,MAAMU,OAAOvzB,OAAOZ,GAAqB,YAAfA,EAAGmqB,UAAwB/pB,QAAQJ,GAAMA,EAAGmI,OAAShE,GACzGsvB,EAAMA,MAAMW,iBAAiBX,EAAMA,MAAMW,gBAAgBxzB,OAAOZ,GAAqB,YAAfA,EAAGmqB,UAAwB/pB,QAAQJ,GAAMA,EAAGmI,OAAShE,GAC/HixB,EAAYjxB,EAAEZ,KAAM,CACrB,CAEA,MAAMoiB,EAAS8N,EAAME,MAAMhO,OAGrB4P,EAAgBC,GAAgBA,EAAIziB,cAAc0M,QAAQ,cAAe,KAE3EkG,GAAQmO,UACXnO,EAAOmO,SAAS1zB,QAAQgR,IACvB,GAAIA,EAAEokB,IAAK,CACV,MAAMC,EAAY,SAASF,EAAankB,EAAEokB,OACtCpkB,EAAEpP,YAAYkX,EAAMV,cAAc3K,IAAIuD,EAAEpP,WAAsB,GAAGyzB,QACjErkB,EAAEhQ,OAAO8X,EAAMV,cAAc3K,IAAIuD,EAAEhQ,MAAiB,GAAGq0B,WACvDrkB,EAAEzQ,QAAQuY,EAAMV,cAAc3K,IAAIuD,EAAEzQ,OAAkB,GAAG80B,WAC9D,IAIE9P,GAAQsO,eACXtO,EAAOsO,cAAc7zB,QAAQgR,IAC5B,GAAIA,EAAEokB,IAAK,CACV,MAAMC,EAAY,aAAaF,EAAankB,EAAEokB,OAC1CpkB,EAAEhQ,OAAO8X,EAAMV,cAAc3K,IAAIuD,EAAEhQ,MAAiB,GAAGq0B,UAC5D,IAKFZ,EAAKf,SAAS1zB,QAASsb,IAEtB,GAAI0Z,EAAY1Z,EAAInY,IAAK,OAEzB,MAAMvD,EAAK8zB,EAAS3sB,IAAIuU,EAAInY,IACtBmyB,EAAiB11B,EA6JzB,SAAgCyzB,EAAc4B,GAC7C,MAAMR,EAAOpB,EAAME,MAAMgC,gBAAgB5yB,KAAKwB,GAAaA,EAAU8wB,kBAAoBA,GACzF,OAAOR,GAAMx0B,GACd,CAhK8Bu1B,CAAuBnC,EAAOzzB,EAAGuD,SAAMmB,EAEnE,IAAI4U,EAAM,GACNrZ,EAAQ,CAAC,EACb,GAAID,EAAI,CACP,MAAM61B,EAAO71B,EAAG61B,KAAK3nB,MAAM,KAC3BoL,EAAMuc,EAAKA,EAAKzyB,OAAS,GACrBpD,EAAG81B,aACNxc,GAAO,KAAOtZ,EAAG81B,YAElBD,EAAKz1B,QAAQo1B,IACZ,MAAMpkB,EAAIuU,GAAUA,EAAOmO,UAAYnO,EAAOmO,SAAS/wB,KAAKqO,GAAKA,EAAEokB,KAAOA,GAC1EpkB,IAAMnR,EAAQ,IAAIA,KAAUmR,KAE9B,CAEA8H,EAAMG,QACLqC,EAAInY,GACJvD,GAAMA,EAAG+F,MAAkB2V,EAAInY,GAC/B+V,EACCtZ,GAAMA,EAAGuZ,YAAevZ,EAAGuZ,YAAc,GAC1CtZ,EAuGH,SAAkBiP,EAA8BwmB,GAC/C,GAAIA,EAAgB,CACnB,MAAMK,EAAiBC,mBAAmBN,GAC1C,MAAO,CACNpQ,KAAM,OAAOyQ,IACbxQ,WAAY,GAAGwQ,QAEjB,CACA,GAAI7mB,GAAS+mB,IACZ,MAAO,CACN3Q,KAAMpW,EAAQ+mB,IACd1Q,WAAYrW,EAAQ+mB,IAIvB,CArHGC,CAASl2B,EAAI01B,IAEd11B,GAAMqY,EAASyb,SAASnxB,KAAK,CAC5BY,GAAIvD,EAAGuD,GACPsyB,KAAM71B,EAAG61B,KACT1L,SAAUnqB,EAAGmqB,SACbgM,WAAYn2B,EAAGm2B,WACfT,iBACAI,WAAY91B,EAAG81B,WACfG,IAAKj2B,EAAGi2B,QAIN1b,MAAMwG,QAAQ8T,EAAKZ,gBACtBY,EAAKZ,cAAc7zB,QAAQsb,IAC1B,MAAMwY,EAAMH,EAAU5sB,IAAIuU,EAAInY,IAC9B,IAAK2wB,EAAK,OAEV,IAAKhb,EAAMhB,SAASvO,IAAIuqB,EAAI3rB,UAAW,CACtC,GAAIurB,EAASnqB,IAAIuqB,EAAI3rB,UAAW,CAC/B,MAAMvI,EAAK8zB,EAAS3sB,IAAI+sB,EAAI3rB,UAC5B8S,QAAQ+a,KAAK,mCAAoCp2B,EAAGuD,GAAIvD,EAAG+F,KAC5D,MACCsV,QAAQ+a,KAAK,sBAAuBlC,EAAI3rB,UAEzC,MACD,CACA,IAAK2Q,EAAMhB,SAASvO,IAAIuqB,EAAImC,eAAgB,CAC3C,GAAIvC,EAASnqB,IAAIuqB,EAAImC,eAAgB,CACpC,MAAMr2B,EAAK8zB,EAAS3sB,IAAI+sB,EAAImC,eAC5Bhb,QAAQ+a,KAAK,mCAAoCp2B,EAAGuD,GAAIvD,EAAG+F,KAC5D,MACCsV,QAAQ+a,KAAK,sBAAuBlC,EAAImC,eAEzC,MACD,CACA,IAAIp2B,EAAa,CAAC,EAClBi0B,EAAI2B,KAAK3nB,MAAM,KAAK9N,QAAQo1B,IAC3B,MAAMpkB,EAAIuU,GAAUA,EAAOsO,eAAiBtO,EAAOsO,cAAclxB,KAAKqO,GAAKA,EAAEokB,KAAOA,GACpFpkB,IAAMnR,EAAQ,IAAIA,KAAUmR,MAEzBsK,EAAI4a,UAASr2B,EAAMq2B,QAAU5a,EAAI4a,SAErCpd,EAAMsB,QAAQ0Z,EAAI3wB,GAAI2wB,EAAI3rB,SAAU2rB,EAAImC,cAAenC,EAAI3a,YAAamC,EAAIlZ,SAAUvC,KAMxF,MAAMs2B,EAASv2B,IACd,IAAIiQ,EAAI,EACR,IAAK,IAAI9L,EAAInE,EAAGmI,OAAQhE,EAAGA,EAAIA,EAAEgE,OAAQ8H,IACzC,OAAOA,GA+CR,OA7CkB/P,OAAOC,KAAKi1B,GAC5B1vB,IAAInC,GAAMuwB,EAAS3sB,IAAI5D,IACvBqJ,KAAK,CAAC4E,EAAGV,IAAMylB,EAAM/kB,GAAK+kB,EAAMzlB,IAAM,EAAI,GAElC1Q,QAAQ+H,IACjB,IAAIlI,EAAQ,CAAC,EACE,mBAAX0I,GACQmrB,EAAS3sB,IAAIgB,EAAO5E,IACfsyB,KAAK3nB,MAAM,KACtB9N,QAAQo1B,IACZ,MAAMpkB,EAAIuU,GAAUA,EAAOmO,UAAYnO,EAAOmO,SAAS/wB,KAAKqO,GAAKA,EAAEokB,KAAOA,GAC1EpkB,IAAMnR,EAAQ,IAAIA,KAAUmR,MAK9B,MAAMolB,EAAe3B,EAAKf,SACxBpuB,IAAIgW,GAAOoY,EAAS3sB,IAAIuU,EAAInY,KAC5B3C,OAAOZ,MACFA,GAAMA,EAAGmI,SAAWA,GAGT,yBAAZQ,GAAoD,mBAAdR,EAAO5E,IAEzB,aAAhBvD,EAAGmqB,WAMXzkB,IAAI1F,GAAMA,EAAGuD,IAGXizB,EAAapzB,OAAS,GACzB8V,EAAMiC,SACLhT,EAAO5E,GACP4E,EAAOpC,KACPywB,EACAv2B,KAMHiZ,EAAMC,KAAKya,EAAQ1a,EAAM3V,KAClB2V,8CC5VD,MAIMud,EAAa,aACbC,EAAmB,mBACnBC,EAAa,aAEbC,EAAU,UACVC,EAAW,WACXC,EAAW,WACXC,EAAW,WAEXC,EAAa,aACbC,EAAW,WAEXC,EAAY,YACZC,EAAa,aACbC,EAAU,UACVC,EAAY,YACZC,EAAiB,iBACjBC,EAAkB,kBAClBC,EAAe,eACfC,EAAiB,iBAWjBC,EAAmB,mBACnBC,EAAmB,mBACnBC,EAAiB,iBACjBC,EAAwB,wBACxBC,EAAsB,sBACtBC,EAAc,cACdC,EAAiB,iBACjBC,EAAc,cACdC,EAAsB,sBACtBC,EAAmB,mBAE1BhM,EAAkD,CACvD,CACCpmB,KAAM,OACNqyB,KAAM,CACL,CACC70B,GAnBgB,OAoBhB80B,KAAM,sBACNC,aAAc,CACb,CAACj4B,IAAK,IAAKk4B,OAAO,GAClB,CAACl4B,IAAK,KAAMk4B,OAAO,OAKvB,CACCxyB,KAAM,OACNqyB,KAAM,CACL,CACC70B,GA/DgB,OAgEhB80B,KAAM,OACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,OAInC,CACCzyB,KAAM,UACNqyB,KAAM,CACL,CACC70B,GAvEgB,OAwEhB80B,KAAM,OACNC,aAAc,CACb,CAACE,MAAM,EAAMn4B,IAAK,OAGpB,CACCkD,GA7EgB,OA8EhB80B,KAAM,OACNC,aAAc,CACb,CAACE,MAAM,EAAMD,OAAO,EAAMl4B,IAAK,KAC/B,CAACm4B,MAAM,EAAMn4B,IAAK,SAMtB,CACC0F,KAAM,uBACNqyB,KAAM,CACL,CACC70B,GAAIkzB,EACJ4B,KAAM,0BACNC,aAAc,CACb,CAACG,KAAK,EAAMC,OAAO,KAGrB,CACCn1B,GAAImzB,EACJ2B,KAAM,uCACNC,aAAc,CACb,CAACG,KAAK,EAAMF,OAAO,EAAMG,OAAO,KAGlC,CACCn1B,GAAIozB,EACJ0B,KAAM,6BACNC,aAAc,CACb,CAACj4B,IAAK,UACN,CAACA,IAAK,iBAKV,CACC0F,KAAM,OACNqyB,KAAM,CACL,CACC70B,GAAIqzB,EACJyB,KAAM,UACNC,aAAc,CACb,CAACE,MAAM,EAAMn4B,IAAK,OAGpB,CACCkD,GAAIszB,EACJwB,KAAM,WACNC,aAAc,CACb,CAACE,MAAM,EAAMn4B,IAAK,OAGpB,CACCkD,GAAIuzB,EACJuB,KAAM,aACNC,aAAc,CAAC,CAACE,MAAM,EAAMn4B,IAAK,OAElC,CACCkD,GAAIwzB,EACJsB,KAAM,YACNC,aAAc,CAAC,CAACE,MAAM,EAAMn4B,IAAK,OAElC,CACCkD,GAAI,aACJ80B,KAAM,+BACNC,aAAc,CAAC,CAACK,OAAO,OAI1B,CACC5yB,KAAM,qBACNqyB,KAAM,CACL,CACC70B,GAlIoB,WAmIpB80B,KAAM,8BACNC,aAAc,CAAC,CAACI,OAAO,KAExB,CACCn1B,GAtI0B,iBAuI1B80B,KAAM,iBACNC,aAAc,CAAC,CAACI,OAAO,KAExB,CACCn1B,GA1IwB,eA2IxB80B,KAAM,4BACNC,aAAc,CAAC,CAACC,OAAO,EAAMG,OAAO,KAErC,CACCn1B,GA9IsB,aA+ItB80B,KAAM,mCACNC,aAAc,CAAC,CAACC,OAAO,EAAMG,OAAO,KAErC,CACCn1B,GAlJyB,gBAmJzB80B,KAAM,yBACNC,aAAc,CAAC,CAACI,OAAO,OAI1B,CACC3yB,KAAM,SACNqyB,KAAM,CACL,CACC70B,GAAIyzB,EACJqB,KAAM,aACNC,aAAc,CAAC,CAACE,MAAM,EAAMn4B,IAAK,OAElC,CACCkD,GAAI0zB,EACJoB,KAAM,WACNC,aAAc,CAAC,CAACj4B,IAAK,WAIxB,CACC0F,KAAM,OACNqyB,KAAM,CACL,CACC70B,GAAI6zB,EACJiB,KAAM,2BACNC,aAAc,CAAC,CAACj4B,IAAK,QAEtB,CACCkD,GAAIi0B,EACJa,KAAM,oBACNC,aAAc,CAAC,CAACj4B,IAAK,KAAMk4B,OAAO,KAEnC,CACCh1B,GAAI4zB,EACJkB,KAAM,8BACNC,aAAc,CAAC,CAACj4B,IAAK,WAEtB,CACCkD,GAAIg0B,EACJc,KAAM,uBACNC,aAAc,CAAC,CAACj4B,IAAK,QAASk4B,OAAO,KAEtC,CACCh1B,GAAI8zB,EACJgB,KAAM,6BACNC,aAAc,CAAC,CAACj4B,IAAK,UAEtB,CACCkD,GAAIk0B,EACJY,KAAM,sBACNC,aAAc,CAAC,CAACj4B,IAAK,OAAQk4B,OAAO,KAErC,CACCh1B,GAAI2zB,EACJmB,KAAM,6BACNC,aAAc,CAAC,CAACj4B,IAAK,UAEtB,CACCkD,GAAI+zB,EACJe,KAAM,sBACNC,aAAc,CAAC,CAACj4B,IAAK,OAAQk4B,OAAO,OAIrC,CACAxyB,KAAM,OACNqyB,KAAM,CACL,CACC70B,GAAIm0B,EACJW,KAAM,qCACNC,aAAc,CAAC,CAACj4B,IAAK,OAEtB,CACCkD,GAAIy0B,EACJK,KAAM,0BACNC,aAAc,CAAC,CAACj4B,IAAK,OAAQm4B,MAAM,OAIvC,CACCzyB,KAAM,YACNqyB,KAAM,CACL,CACC70B,GAAIo0B,EACJU,KAAM,uCACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMD,OAAO,KAE9C,CACCh1B,GAAIq0B,EACJS,KAAM,qCACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMD,OAAO,KAE9C,CACCh1B,GAAIs0B,EACJQ,KAAM,4CACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMC,KAAK,KAE5C,CACCl1B,GAAIu0B,EACJO,KAAM,0CACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMC,KAAK,OAI9C,CACC1yB,KAAM,SACNqyB,KAAM,CACL,CACC70B,GAAIw0B,EACJM,KAAM,2BACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,OAInC,CACCzyB,KAAM,OACNqyB,KAAM,CACL,CACC70B,GAAI00B,EACJI,KAAM,yBACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,KAEjC,CACCj1B,GAAI20B,EACJG,KAAM,sBACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMD,OAAO,KAE9C,CACCh1B,GAAI40B,EACJE,KAAM,4BACNC,aAAc,CAAC,CAACj4B,IAAK,IAAKm4B,MAAM,EAAMC,KAAK,QAMzCG,EAAczM,EAClB9nB,OAAO,CAACoc,EAAKrP,IAAMqP,EAAIhe,OAAO2O,EAAEgnB,MAAO,IACvC/zB,OAAkC,CAACqB,EAAK0L,KACxC1L,EAAI0L,EAAE7N,IAAM6N,EACL1L,GACL,CAAC,GAqDCmzB,EAAaxmB,GACX,CACNA,EAAEmmB,OAAQ,EAAAM,EAAAC,MAAqBC,cAC/B3mB,EAAEkmB,OAAS,QACXlmB,EAAEomB,KAAO,MACTpmB,EAAEhS,MAAQgS,EAAEhS,IAAI+C,OAAS,EAAIiP,EAAEhS,IAAM,IAAIgS,EAAEhS,IAAI24B,kBAC/C3mB,EAAEqmB,OAAS,QACXrmB,EAAEsmB,OAAS,SACV/3B,OAAO2a,SAAS1V,KAAK,mEAGA,KAChB,EAAAozB,EAAAC,MAAA,OAAK9pB,UAAU,UAAS0f,SAAA,EAC9B,EAAAmK,EAAAE,KAAA,MAAArK,SAAI,eACJ,EAAAmK,EAAAE,KAAA,SAAArK,UACC,EAAAmK,EAAAE,KAAA,SAAArK,SAEC3C,EAAUzmB,IAAIiD,IAAW,EAAAswB,EAAAC,MAAAD,EAAAG,SAAA,CAAAtK,SAAA,EACxB,EAAAmK,EAAAE,KAAA,MAAArK,UACC,EAAAmK,EAAAE,KAAA,MAAIE,QAAS,EAAEvK,SAAEnmB,EAAQ5C,SAEzB4C,EAAQyvB,KAAK1yB,IAAI8jB,IAAQ,EAAAyP,EAAAC,MAAA,MAAApK,SAAA,EACzB,EAAAmK,EAAAE,KAAA,MAAArK,SAAKtF,EAAK8O,aAAa5yB,IAAImzB,GAAWhzB,KAAK,SAC3C,EAAAozB,EAAAE,KAAA,MAAArK,SAAKtF,EAAK6O,mFAhDY,CAACzc,EAA+B8c,GAAQ,EAAOC,GAAQ,KAElF,MAAMW,EAAUp5B,OAAOC,KAAKy4B,GAAah4B,OAAOyO,GA5BhC,EAACuM,EAA+BkR,EAAoB4L,EAAgBC,IAC7E7L,EAASwL,aAAa5vB,KAAK2J,IACjC,GAAIkJ,QAAQlJ,EAAEkmB,QAAU3c,EAAEsO,SAAU,OAAO,EAE3C,GAAI7X,EAAEmmB,QACe,EAAAM,EAAAS,IAAuB3d,GACzB,OAAO,EAE1B,GAAIL,QAAQlJ,EAAEomB,MAAQ7c,EAAEoQ,OAAQ,OAAO,EACvC,GAAI0M,EAAO,OAAOrmB,EAAEqmB,MACpB,GAAIC,EAAO,OAAOtmB,EAAEsmB,MACpB,GAAItmB,EAAEhS,IAAK,CACV,MAAMm5B,EAAK5d,EACX,MAAa,UAATvJ,EAAEhS,IAAkC,UAAVm5B,EAAGn5B,IACpB,aAATgS,EAAEhS,IAAqC,aAAVm5B,EAAGn5B,IACvB,OAATgS,EAAEhS,IAA+B,UAAVm5B,EAAGn5B,IACjB,MAATgS,EAAEhS,IAA8B,WAAVm5B,EAAGn5B,IAChB,QAATgS,EAAEhS,IAAgC,aAAVm5B,EAAGn5B,IAClB,QAATgS,EAAEhS,IAAgC,aAAVm5B,EAAGn5B,IAClB,SAATgS,EAAEhS,IAAiC,cAAVm5B,EAAGn5B,IACzBgS,EAAEhS,KAAOm5B,EAAGn5B,KAAOgS,EAAEhS,IAAI0S,eAAiBymB,EAAGn5B,IAAI0S,aACzD,CACA,OAAO,IAM6C0mB,CAAS7d,EAAGgd,EAAYvpB,GAAIqpB,EAAOC,IAExF,GAAuB,IAAnBW,EAAQl2B,OACZ,OAAuB,IAAnBk2B,EAAQl2B,OAAqBk2B,EAAQ,GAInBA,EAAQ1sB,KAAK,CAAC4E,EAAGV,KACtC,MAAM4oB,EAAYd,EAAYpnB,GACxBmoB,EAAYf,EAAY9nB,GAExB8oB,EAAaF,EAAUpB,aAAa,GACpCuB,EAAaF,EAAUrB,aAAa,GAEpCwB,GAAUF,EAAWrB,MAAQ,EAAI,IAAMqB,EAAWpB,KAAO,EAAI,IAAMoB,EAAWnB,IAAM,EAAI,GAG9F,OAFgBoB,EAAWtB,MAAQ,EAAI,IAAMsB,EAAWrB,KAAO,EAAI,IAAMqB,EAAWpB,IAAM,EAAI,GAE9EqB,IAGI,kKC1Xf,MAAMC,EAAQ,KACnB,GAAyB,oBAAdC,UAA2B,OAAO,EAG7C,GAAI,kBAAmBA,WAAcA,UAAkBC,cAAe,CACpE,MAAMC,EAAYF,UAAkBC,cAAcC,SAClD,GAAIA,GAAYA,EAASnnB,cAAconB,SAAS,OAC9C,OAAO,CAEX,CAGA,MAAMC,EAAYJ,UAAUI,UAAUrnB,cACtC,GAAIqnB,EAAUD,SAAS,WAAaC,EAAUD,SAAS,aACrD,OAAO,EAIT,GAAIH,UAAUE,SAAU,CACtB,MAAMA,EAAWF,UAAUE,SAASnnB,cACpC,GAAImnB,EAASC,SAAS,QAAUD,EAASC,SAAS,UAChD,OAAO,CAEX,CAGA,IAEE,QAA0Bz1B,IADR,IAAI21B,cAAc,UAAW,CAAEC,SAAS,IAC5CA,QAEZ,MAAO,mBAAmBC,KAAKP,UAAUI,UAE7C,CAAE,MAAOxe,GACP,CAGF,OAAO,iBAa8ByP,GAC9B0O,IAAU1O,EAAMiP,QAAUjP,EAAMmP,eARP,IACzBT,IAAU,MAAQ,+EC/C3BU,EAAA,IAAAC,IAA4CnL,EAAA,KAAAA,EAAAze,GAC5C6pB,EAAA,IAAAD,IAA4CnL,EAAA,KAAAA,EAAAze,GAC5C8pB,EAAA,IAAAF,IAA4CnL,EAAA,KAAAA,EAAAze,GAC5C+pB,EAAA,IAAAH,IAA4CnL,EAAA,KAAAA,EAAAze,GAC5CgqB,EAA8BC,IAA4BC,KAC1DC,EAAyCC,IAA+BT,GACxEU,EAAyCD,IAA+BP,GACxES,EAAyCF,IAA+BN,GACxES,EAAyCH,IAA+BL,GAExEC,EAAAn4B,KAAA,CAAA+sB,EAAAnsB,GAAA,kNAMY03B,kJAQAE,kJAQAC,kJAQAC,2BAEZ,IAAOlG,QAAA,EAAArF,QAAA,8BAAAwL,MAAA,GAAAC,SAAA,8TAAiYC,eAAA,62BAAw5BC,WAAA,MAEhyC,MAAAC,EAAA,8DC7CAZ,QAA8BC,GAA4BC,KAE1DF,EAAAn4B,KAAA,CAAA+sB,EAAAnsB,GAAA,42SA4bC,IAAO4xB,QAAA,EAAArF,QAAA,8BAAAwL,MAAA,GAAAC,SAAA,uhHAA0lHC,eAAA,82SAAm4SC,WAAA,MAEr+Z,MAAAC,EAAA,wJCxbAxa,EAAA,GAEAA,EAAAya,kBAA4BC,IAC5B1a,EAAA2a,cAAwBC,IACxB5a,EAAA/N,OAAiB4oB,IAAAtM,KAAa,aAC9BvO,EAAA8a,OAAiBC,IACjB/a,EAAAgb,mBAA6BC,IAEhBC,IAAIC,EAAAC,EAAOpb,GAKFmb,EAAAC,GAAWD,EAAAC,EAAOC,QAAUF,EAAAC,EAAOC,4ICbzDrb,EAAA,GAEAA,EAAAya,kBAA4BC,IAC5B1a,EAAA2a,cAAwBC,IACxB5a,EAAA/N,OAAiB4oB,IAAAtM,KAAa,aAC9BvO,EAAA8a,OAAiBC,IACjB/a,EAAAgb,mBAA6BC,IAEhBC,IAAII,EAAAF,EAAOpb,GAKFsb,EAAAF,GAAWE,EAAAF,EAAOC,QAAUC,EAAAF,EAAOC","sources":["webpack://app/./src/graph-view/constants.ts","webpack://app/./src/graph-view/edge-utils.ts","webpack://app/./src/graph-view/geometry.ts","webpack://app/./src/graph-view/defs.ts","webpack://app/./src/graph-view/svg-text.ts","webpack://app/./src/graph-view/svg-create.ts","webpack://app/./src/graph-view/intersect.ts","webpack://app/./src/graph-view/shapes.ts","webpack://app/./src/graph-view/undo.ts","webpack://app/./src/graph-view/node-content.ts","webpack://app/./src/graph-view/graph.ts","webpack://app/./src/graph-view/automatic-layout.ts","webpack://app/./src/graph-view/layout.ts","webpack://app/./src/graph-view/manual-layout.ts","webpack://app/./src/parseModel.ts","webpack://app/./src/shortcuts.tsx","webpack://app/./src/utils/platform.ts","webpack://app/./src/fonts.css","webpack://app/./src/style.css","webpack://app/./src/fonts.css?a158","webpack://app/./src/style.css?dd02"],"sourcesContent":["// Constants and default configurations for the graph view\n\nexport interface Point {\n\tx: number;\n\ty: number;\n}\n\nexport interface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\nexport interface NodeStyle {\n\t// Width of element, in pixels.\n\twidth?: number\n\t// Height of element, in pixels.\n\theight?: number\n\t// Background color of element as HTML RGB hex string (e.g. \"#ffffff\")\n\tbackground?: string\n\t// Stroke color of element as HTML RGB hex string (e.g. \"#000000\")\n\tstroke?: string\n\t// Foreground (text) color of element as HTML RGB hex string (e.g. \"#ffffff\")\n\tcolor?: string\n\t// Standard font size used to render text, in pixels.\n\tfontSize?: number\n\t// Shape used to render element.\n\tshape?: string\n\t// URL of PNG/JPG/GIF file or Base64 data URI representation.\n\ticon?: string\n\t// Type of border used to render element.\n\tborder?: string\n\t// Opacity used to render element; 0-100.\n\topacity?: number\n\t// Whether element metadata should be shown.\n\tmetadata?: boolean\n\t// Whether element description should be shown.\n\tdescription?: boolean\n}\n\nexport interface EdgeStyle {\n\t// Thickness of line, in pixels.\n\tthickness?: number\n\t// Color of line as HTML RGB hex string (e.g. \"#ffffff\").\n\tcolor?: string\n\t// Standard font size used to render relationship annotation, in pixels.\n\tfontSize?: number\n\t// Width of relationship annotation, in pixels.\n\twidth?: number\n\t// Whether line is dashed.\n\tdashed?: boolean\n\t// Position of label along edge (0-100).\n\tposition?: number\n\t// Opacity used to render relationship; 0-100.\n\topacity?: number\n\t// Arrow style for the edge.\n\tarrowStyle?: 'normal' | 'large' | 'small' | 'none'\n}\n\n// Default styles\nexport const DEFAULT_EDGE_STYLE: EdgeStyle = {\n\tthickness: 3,\n\tcolor: '#999',\n\topacity: 1,\n\tfontSize: 22,\n\tdashed: true,\n};\n\nexport const DEFAULT_NODE_STYLE: NodeStyle = {\n\twidth: 280,\n\theight: 180,\n\tbackground: 'rgba(255, 255, 255, .9)',\n\tcolor: '#666',\n\topacity: .9,\n\tstroke: '#999',\n\tfontSize: 22,\n\tshape: 'Box'\n};\n\n// SVG styles\nexport const SVG_STYLES = {\n\tnodeBorder: {\n\t\tfill: \"rgba(255, 255, 255, 0.86)\",\n\t\tstroke: \"#aaa\",\n\t\tfilter: 'url(#shadow)',\n\t},\n\tnodeText: {\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\tedgeText: {\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\tedgeRect: {\n\t\tfill: \"none\",\n\t\tstroke: \"none\",\n\t},\n\tgroupRect: {\n\t\tfill: \"rgba(0, 0, 0, 0.02)\",\n\t\tstroke: \"#666\",\n\t\t'stroke-width': 3,\n\t\t\"stroke-dasharray\": 4,\n\t},\n\tgroupText: {\n\t\tfill: \"#666\",\n\t\t\"font-size\": 22,\n\t\t\"font-weight\": \"500\",\n\t\t'font-family': 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',\n\t\tcursor: \"default\"\n\t}\n};\n\n// Configuration constants\nexport const SVG_PADDING = 20;\nexport const DEFAULT_GRID_SIZE = 25;\nexport const EDGE_SPREAD_DISTANCE = 70;\nexport const EDGE_SPREAD_DISTANCE_X = 200;\n\n// Utility function to apply styles to SVG elements\nexport const applyStyle = (el: SVGElement, style: { [key: string]: string | number }) => {\n\tObject.keys(style).forEach(key => {\n\t\tconst value = style[key];\n\t\tif (typeof value === 'number') {\n\t\t\tel.style.setProperty(key, value.toString());\n\t\t} else {\n\t\t\tel.style.setProperty(key, value);\n\t\t}\n\t});\n};\n\n// Utility function to calculate distance between two points\nexport const calculateDistance = (p1: Point, p2: Point): number => {\n\treturn Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));\n};","import { Point, calculateDistance } from './constants';\nimport { Segment } from './intersect';\n\n// Define interfaces locally since they're not exported from graph.ts\ninterface NodeStyle {\n\tbackground?: string;\n\tstroke?: string;\n\topacity?: number;\n\tfontSize?: number;\n\tshape?: string;\n\tborder?: string;\n}\n\ninterface Node extends Point {\n\tid: string;\n\ttitle: string;\n\tsub: string;\n\tdescription: string;\n\twidth: number;\n\theight: number;\n\tref?: SVGGElement;\n\tselected?: boolean;\n\tintersect: (p: Point) => Point;\n\tstyle: NodeStyle;\n}\n\ninterface EdgeVertex extends Point {\n\tid: string;\n\tselected?: boolean;\n\tref?: SVGElement;\n\tlabel?: boolean;\n\tauto?: boolean;\n}\n\ninterface EdgeStyle {\n\tcolor?: string;\n\tthickness?: number;\n\tfontSize?: number;\n\tposition?: number;\n\tdashed?: boolean;\n}\n\ninterface Edge {\n\tid: string;\n\tlabel: string;\n\tfrom: Node;\n\tto: Node;\n\tvertices?: EdgeVertex[];\n\tref?: SVGGElement;\n\tstyle: EdgeStyle;\n\tinitVertex: (p: Point) => EdgeVertex;\n\tuserDeletedVertices?: boolean; // Track if user explicitly deleted vertices\n}\n\nexport interface EdgeLabelPlacement extends Point {\n\torientation: 'horizontal' | 'vertical';\n\tsegment?: Segment;\n\tmovable: boolean;\n}\n\n/**\n * Calculate edge vertices, handling multi-edge scenarios and auto-vertices\n */\nexport function calculateEdgeVertices(edge: Edge): Point[] {\n\tconst n1 = edge.from, n2 = edge.to;\n\t\n\t\n\t// if vertices exists, follow them\n\tlet vertices: Point[] = edge.vertices ? edge.vertices.concat() : [];\n\t// Don't remove label vertices - they should be preserved for rendering\n\t// (The autoLayout process handles replacing old ones with new ones)\n\n\tvertices.unshift(n1)\n\tvertices.push(n2)\n\n\tconst interior = vertices.slice(1, -1) as EdgeVertex[];\n\tconst firstRoutingVertex = interior.find(vertex => !vertex.label) ?? n2;\n\tlet lastRoutingVertex: Point = n1;\n\tfor (let index = interior.length - 1; index >= 0; index--) {\n\t\tif (!interior[index].label) {\n\t\t\tlastRoutingVertex = interior[index];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!n1.intersect || !n2.intersect) {\n\t\tthrow new Error(`edge ${edge.id} nodes must be drawn before routing`);\n\t}\n\tvertices[0] = n1.intersect(firstRoutingVertex);\n\tvertices[vertices.length - 1] = n2.intersect(lastRoutingVertex);\n\t\n\treturn vertices;\n}\n\n/**\n * Calculate the label anchor and the orientation of the path segment that owns\n * it. Renderers use the orientation to place text beside the relationship line\n * instead of centering text over vertical segments.\n */\nexport function calculateLabelPlacement(\n\tvertices: Point[],\n\tposition: number,\n\tdefaultPoint: Point,\n): EdgeLabelPlacement {\n\tlet point = {x: defaultPoint.x, y: defaultPoint.y};\n\tlet segment: Segment | undefined;\n\tconst labelIndex = vertices.findIndex(vertex => (vertex as EdgeVertex).label);\n\tlet movable = true;\n\n\tif (labelIndex >= 0) {\n\t\tconst labelVertex = vertices[labelIndex] as EdgeVertex;\n\t\tpoint = labelVertex;\n\t\tmovable = labelVertex.auto === true;\n\t\tconst adjacentSegments: Segment[] = [];\n\t\tif (labelIndex > 0) {\n\t\t\tadjacentSegments.push({p: vertices[labelIndex - 1], q: point});\n\t\t}\n\t\tif (labelIndex < vertices.length - 1) {\n\t\t\tadjacentSegments.push({p: point, q: vertices[labelIndex + 1]});\n\t\t}\n\t\tsegment = adjacentSegments.reduce((longest, candidate) => {\n\t\t\tif (!longest) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\treturn calculateDistance(candidate.p, candidate.q) >\n\t\t\t\tcalculateDistance(longest.p, longest.q)\n\t\t\t\t? candidate\n\t\t\t\t: longest;\n\t\t}, undefined);\n\t} else {\n\t\tconst totalLength = vertices.slice(1).reduce(\n\t\t\t(sum, vertex, index) => sum + calculateDistance(vertices[index], vertex),\n\t\t\t0,\n\t\t);\n\t\tconst targetLength = totalLength * position;\n\t\tlet traversed = 0;\n\t\tfor (let index = 1; index < vertices.length; index++) {\n\t\t\tconst candidate = {p: vertices[index - 1], q: vertices[index]};\n\t\t\tconst length = calculateDistance(candidate.p, candidate.q);\n\t\t\tif (length > 0 && traversed + length >= targetLength) {\n\t\t\t\tconst segmentPosition = (targetLength - traversed) / length;\n\t\t\t\tpoint = {\n\t\t\t\t\tx: candidate.p.x + (candidate.q.x - candidate.p.x) * segmentPosition,\n\t\t\t\t\ty: candidate.p.y + (candidate.q.y - candidate.p.y) * segmentPosition,\n\t\t\t\t};\n\t\t\t\tsegment = candidate;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttraversed += length;\n\t\t}\n\t}\n\n\tconst horizontalDistance = segment ? Math.abs(segment.q.x - segment.p.x) : 0;\n\tconst verticalDistance = segment ? Math.abs(segment.q.y - segment.p.y) : 0;\n\treturn {\n\t\t...point,\n\t\torientation: verticalDistance > horizontalDistance ? 'vertical' : 'horizontal',\n\t\tsegment,\n\t\tmovable,\n\t};\n}\n\n/** createEdgePath draws the given points without cutting or moving the route. */\nexport function createEdgePath(vertices: Point[]): string {\n\tif (vertices.length < 2) {\n\t\tthrow new Error(\"edge route must contain at least two points\");\n\t}\n\treturn vertices.slice(1).reduce(\n\t\t(path, point) => `${path} L${point.x},${point.y}`,\n\t\t`M${vertices[0].x},${vertices[0].y}`,\n\t);\n}","// This file defines the diagram sizes and positions shared by layout and SVG\n// drawing. Automatic and manual layout both produce ResolvedLayout. The SVG\n// drawing code accepts a layout only after this file has checked it.\n\n/** Point is one position in the diagram. */\nexport interface Point {\n\tx: number;\n\ty: number;\n}\n\n/** Size is a width and height. Both values must be zero or greater. */\nexport interface Size {\n\twidth: number;\n\theight: number;\n}\n\n/** Rect is a box that does not rotate. */\nexport interface Rect extends Point, Size {}\n\n/** MeasuredNode holds the exact box and text sizes needed before layout. */\nexport interface MeasuredNode {\n\tid: string;\n\tparentId?: string;\n\tsize: Size;\n\tcontentSize: Size;\n\tcenterOffset: Point;\n\tshape: string;\n}\n\n/** MeasuredGroup describes a boundary that must contain its children and title. */\nexport interface MeasuredGroup {\n\tid: string;\n\tparentId?: string;\n\ttitleSize: Size;\n}\n\n/** MeasuredEdge names the two connected nodes and the exact label size. */\nexport interface MeasuredEdge {\n\tid: string;\n\tsourceId: string;\n\ttargetId: string;\n\tlabelSize?: Size;\n}\n\n/**\n * MeasuredDiagram is the complete input to automatic or manual layout.\n * It contains sizes, but no positions.\n */\nexport interface MeasuredDiagram {\n\tid: string;\n\tnodes: MeasuredNode[];\n\tgroups: MeasuredGroup[];\n\tedges: MeasuredEdge[];\n}\n\n/** ResolvedNode is one measured node placed in the diagram. */\nexport interface ResolvedNode {\n\tid: string;\n\tparentId?: string;\n\tbounds: Rect;\n}\n\n/** ResolvedGroup is one placed boundary and its title box. */\nexport interface ResolvedGroup {\n\tid: string;\n\tparentId?: string;\n\tbounds: Rect;\n\ttitleBounds: Rect;\n}\n\n/**\n * ResolvedEdge contains a complete route. Each section is an ordered list of\n * points. labelBounds is the final label box chosen by layout.\n */\nexport interface ResolvedEdge {\n\tid: string;\n\tsourceId: string;\n\ttargetId: string;\n\tsections: Point[][];\n\tlabelBounds?: Rect;\n}\n\n/**\n * ResolvedLayout is the unchecked result of automatic or manual layout.\n * Drawing code must not use it directly.\n */\nexport interface ResolvedLayout {\n\tdiagramId: string;\n\tbounds: Rect;\n\tnodes: ResolvedNode[];\n\tgroups: ResolvedGroup[];\n\tedges: ResolvedEdge[];\n}\n\n/** GeometryIssueCode names one rule that the layout broke. */\nexport type GeometryIssueCode =\n\t| \"duplicate_id\"\n\t| \"missing_element\"\n\t| \"unexpected_element\"\n\t| \"non_finite\"\n\t| \"size_mismatch\"\n\t| \"content_overflow\"\n\t| \"node_overlap\"\n\t| \"group_overlap\"\n\t| \"false_containment\"\n\t| \"edge_through_node\"\n\t| \"edge_through_group\"\n\t| \"label_node_overlap\"\n\t| \"label_label_overlap\"\n\t| \"label_edge_overlap\"\n\t| \"label_title_overlap\"\n\t| \"out_of_bounds\"\n\t| \"disconnected_edge\";\n\n/** GeometryIssue reports the stable IDs of the affected diagram elements. */\nexport interface GeometryIssue {\n\tcode: GeometryIssueCode;\n\telementIds: string[];\n\tmessage: string;\n}\n\nconst validatedLayoutBrand: unique symbol = Symbol(\"ValidatedLayout\");\n\n/**\n * ValidatedLayout is the only layout the SVG drawing code accepts. The private\n * marker prevents other code from creating one without running the checks.\n */\nexport type ValidatedLayout = ResolvedLayout & {\n\treadonly [validatedLayoutBrand]: true;\n};\n\n/** GeometryValidationError reports every layout problem found in one check. */\nexport class GeometryValidationError extends Error {\n\treadonly issues: GeometryIssue[];\n\n\tconstructor(issues: GeometryIssue[]) {\n\t\tsuper(issues.map(issue => issue.message).join(\"\\n\"));\n\t\tthis.name = \"GeometryValidationError\";\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * validateResolvedLayout checks that every element is present, every number is\n * valid, boundaries show true ownership, elements do not collide, and all\n * content stays inside the diagram.\n */\nexport function validateResolvedLayout(\n\tmeasured: MeasuredDiagram,\n\tresolved: ResolvedLayout,\n): ValidatedLayout {\n\tconst issues: GeometryIssue[] = [];\n\tif (measured.id !== resolved.diagramId) {\n\t\tissues.push(issue(\n\t\t\t\"missing_element\",\n\t\t\t[measured.id, resolved.diagramId],\n\t\t\t`layout ${resolved.diagramId} does not resolve measured diagram ${measured.id}`,\n\t\t));\n\t}\n\n\tcheckUniqueIDs(\"measured node\", measured.nodes.map(node => node.id), issues);\n\tcheckUniqueIDs(\"measured group\", measured.groups.map(group => group.id), issues);\n\tcheckUniqueIDs(\"measured edge\", measured.edges.map(edge => edge.id), issues);\n\tcheckUniqueIDs(\"resolved node\", resolved.nodes.map(node => node.id), issues);\n\tcheckUniqueIDs(\"resolved group\", resolved.groups.map(group => group.id), issues);\n\tcheckUniqueIDs(\"resolved edge\", resolved.edges.map(edge => edge.id), issues);\n\n\tcheckCoverage(\n\t\t\"node\",\n\t\tmeasured.nodes.map(node => node.id),\n\t\tresolved.nodes.map(node => node.id),\n\t\tissues,\n\t);\n\tcheckCoverage(\n\t\t\"group\",\n\t\tmeasured.groups.map(group => group.id),\n\t\tresolved.groups.map(group => group.id),\n\t\tissues,\n\t);\n\tcheckCoverage(\n\t\t\"edge\",\n\t\tmeasured.edges.map(edge => edge.id),\n\t\tresolved.edges.map(edge => edge.id),\n\t\tissues,\n\t);\n\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\tconst measuredGroups = new Map(measured.groups.map(group => [group.id, group]));\n\tconst resolvedNodes = new Map(resolved.nodes.map(node => [node.id, node]));\n\tconst resolvedGroups = new Map(resolved.groups.map(group => [group.id, group]));\n\n\tcheckFiniteRect(\"diagram\", resolved.diagramId, resolved.bounds, issues);\n\tfor (const node of resolved.nodes) {\n\t\tcheckFiniteRect(\"node\", node.id, node.bounds, issues);\n\t\tconst expected = measuredNodes.get(node.id);\n\t\tif (expected && (\n\t\t\t!nearlyEqual(expected.size.width, node.bounds.width) ||\n\t\t\t!nearlyEqual(expected.size.height, node.bounds.height)\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"size_mismatch\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} size changed after measurement`,\n\t\t\t));\n\t\t}\n\t\tif (expected && (\n\t\t\texpected.contentSize.width > node.bounds.width ||\n\t\t\texpected.contentSize.height > node.bounds.height\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"content_overflow\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} content exceeds its resolved bounds`,\n\t\t\t));\n\t\t}\n\t\tcheckRectInBounds(\"node\", node.id, node.bounds, resolved.bounds, issues);\n\t}\n\tfor (const group of resolved.groups) {\n\t\tcheckFiniteRect(\"group\", group.id, group.bounds, issues);\n\t\tcheckFiniteRect(\"group title\", group.id, group.titleBounds, issues);\n\t\tcheckRectInBounds(\"group\", group.id, group.bounds, resolved.bounds, issues);\n\t\tif (!contains(group.bounds, group.titleBounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} does not contain its title`,\n\t\t\t));\n\t\t}\n\t\tconst expected = measuredGroups.get(group.id);\n\t\tif (expected && (\n\t\t\t!nearlyEqual(expected.titleSize.width, group.titleBounds.width) ||\n\t\t\t!nearlyEqual(expected.titleSize.height, group.titleBounds.height)\n\t\t)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"size_mismatch\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} title size changed after measurement`,\n\t\t\t));\n\t\t}\n\t}\n\n\tcheckNodeOverlaps(resolved.nodes, issues);\n\tcheckGroupOverlaps(resolved.groups, issues);\n\tcheckOwnership(\n\t\tmeasured.nodes,\n\t\tmeasured.groups,\n\t\tresolvedNodes,\n\t\tresolvedGroups,\n\t\tissues,\n\t);\n\tcheckEdges(\n\t\tmeasured,\n\t\tresolved,\n\t\tresolvedNodes,\n\t\tresolvedGroups,\n\t\tmeasuredNodes,\n\t\tmeasuredGroups,\n\t\tissues,\n\t);\n\n\tif (issues.length > 0) {\n\t\tthrow new GeometryValidationError(issues);\n\t}\n\treturn resolved as ValidatedLayout;\n}\n\n/** checkUniqueIDs requires each model ID to appear only once. */\nfunction checkUniqueIDs(label: string, ids: string[], issues: GeometryIssue[]): void {\n\tconst seen = new Set();\n\tfor (const id of ids) {\n\t\tif (seen.has(id)) {\n\t\t\tissues.push(issue(\"duplicate_id\", [id], `${label} ${id} is duplicated`));\n\t\t}\n\t\tseen.add(id);\n\t}\n}\n\n/** checkCoverage rejects missing elements and old elements left in a layout. */\nfunction checkCoverage(\n\tlabel: string,\n\texpected: string[],\n\tactual: string[],\n\tissues: GeometryIssue[],\n): void {\n\tconst expectedIDs = new Set(expected);\n\tconst actualIDs = new Set(actual);\n\tfor (const id of expectedIDs) {\n\t\tif (!actualIDs.has(id)) {\n\t\t\tissues.push(issue(\"missing_element\", [id], `${label} ${id} is missing from resolved layout`));\n\t\t}\n\t}\n\tfor (const id of actualIDs) {\n\t\tif (!expectedIDs.has(id)) {\n\t\t\tissues.push(issue(\"unexpected_element\", [id], `${label} ${id} is not in the measured diagram`));\n\t\t}\n\t}\n}\n\n/** checkFiniteRect rejects invalid numbers and negative sizes. */\nfunction checkFiniteRect(\n\tlabel: string,\n\tid: string,\n\trect: Rect,\n\tissues: GeometryIssue[],\n): void {\n\tif (!isFiniteRect(rect) || rect.width < 0 || rect.height < 0) {\n\t\tissues.push(issue(\"non_finite\", [id], `${label} ${id} has invalid geometry`));\n\t}\n}\n\n/** checkRectInBounds requires the whole box to fit inside the diagram. */\nfunction checkRectInBounds(\n\tlabel: string,\n\tid: string,\n\trect: Rect,\n\tbounds: Rect,\n\tissues: GeometryIssue[],\n): void {\n\tif (!contains(bounds, rect)) {\n\t\tissues.push(issue(\"out_of_bounds\", [id], `${label} ${id} lies outside the resolved view bounds`));\n\t}\n}\n\n/** checkNodeOverlaps prevents two nodes from sharing the same area. */\nfunction checkNodeOverlaps(nodes: ResolvedNode[], issues: GeometryIssue[]): void {\n\tforEachPair(nodes, (first, second) => {\n\t\tif (overlaps(first.bounds, second.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"node_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`nodes ${first.id} and ${second.id} overlap`,\n\t\t\t));\n\t\t}\n\t});\n}\n\n/**\n * checkGroupOverlaps allows one real boundary inside another, but rejects\n * boundaries that partly cover each other and suggest false ownership.\n */\nfunction checkGroupOverlaps(groups: ResolvedGroup[], issues: GeometryIssue[]): void {\n\tforEachPair(groups, (first, second) => {\n\t\tif (\n\t\t\toverlaps(first.bounds, second.bounds) &&\n\t\t\t!contains(first.bounds, second.bounds) &&\n\t\t\t!contains(second.bounds, first.bounds)\n\t\t) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"group_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`groups ${first.id} and ${second.id} partially overlap`,\n\t\t\t));\n\t\t}\n\t});\n}\n\n/**\n * checkOwnership requires each child to be inside the parent named by the\n * model. Visual overlap never creates ownership.\n */\nfunction checkOwnership(\n\tnodes: MeasuredNode[],\n\tgroups: MeasuredGroup[],\n\tresolvedNodes: Map,\n\tresolvedGroups: Map,\n\tissues: GeometryIssue[],\n): void {\n\tfor (const node of nodes) {\n\t\tconst child = resolvedNodes.get(node.id);\n\t\tif (child && child.parentId !== node.parentId) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[node.id],\n\t\t\t\t`node ${node.id} has the wrong parent ID`,\n\t\t\t));\n\t\t}\n\t\tif (!node.parentId) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parent = resolvedGroups.get(node.parentId);\n\t\tif (child && parent && !contains(parent.bounds, child.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[node.id, node.parentId],\n\t\t\t\t`node ${node.id} lies outside owning group ${node.parentId}`,\n\t\t\t));\n\t\t}\n\t}\n\tfor (const group of groups) {\n\t\tconst child = resolvedGroups.get(group.id);\n\t\tif (child && child.parentId !== group.parentId) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id],\n\t\t\t\t`group ${group.id} has the wrong parent ID`,\n\t\t\t));\n\t\t}\n\t\tif (!group.parentId) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst parent = resolvedGroups.get(group.parentId);\n\t\tif (child && parent && !contains(parent.bounds, child.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"false_containment\",\n\t\t\t\t[group.id, group.parentId],\n\t\t\t\t`group ${group.id} lies outside owning group ${group.parentId}`,\n\t\t\t));\n\t\t}\n\t}\n}\n\n/**\n * checkEdges checks connected node IDs, complete routes, crossings, labels,\n * and boundary crossings for every relationship.\n */\nfunction checkEdges(\n\tmeasured: MeasuredDiagram,\n\tresolved: ResolvedLayout,\n\tnodes: Map,\n\tgroups: Map,\n\tmeasuredNodes: Map,\n\tmeasuredGroups: Map,\n\tissues: GeometryIssue[],\n): void {\n\tconst measuredEdges = new Map(measured.edges.map(edge => [edge.id, edge]));\n\tconst labels: Array<{id: string; bounds: Rect}> = [];\n\tfor (const edge of resolved.edges) {\n\t\tconst expected = measuredEdges.get(edge.id);\n\t\tif (!expected) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\tedge.sourceId !== expected.sourceId ||\n\t\t\tedge.targetId !== expected.targetId\n\t\t) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id, edge.sourceId, edge.targetId],\n\t\t\t\t`edge ${edge.id} endpoints do not match its measured contract`,\n\t\t\t));\n\t\t}\n\t\tif (edge.sections.length === 0 || edge.sections.some(section => section.length < 2)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} has no complete route section`,\n\t\t\t));\n\t\t\tcontinue;\n\t\t}\n\t\tcheckSectionConnections(edge, nodes, issues);\n\t\tfor (const section of edge.sections) {\n\t\t\tfor (const point of section) {\n\t\t\t\tif (!isFinitePoint(point) || !pointInRect(point, resolved.bounds, true)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"out_of_bounds\",\n\t\t\t\t\t\t[edge.id],\n\t\t\t\t\t\t`edge ${edge.id} has a route point outside the resolved view`,\n\t\t\t\t\t));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const node of nodes.values()) {\n\t\t\tif (node.id === edge.sourceId || node.id === edge.targetId) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (routeIntersectsRect(edge.sections, inset(node.bounds, 0.5))) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"edge_through_node\",\n\t\t\t\t\t[edge.id, node.id],\n\t\t\t\t\t`edge ${edge.id} crosses unrelated node ${node.id}`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\tfor (const group of groups.values()) {\n\t\t\tconst sourceInside = belongsToGroup(\n\t\t\t\tedge.sourceId,\n\t\t\t\tgroup.id,\n\t\t\t\tmeasuredNodes,\n\t\t\t\tmeasuredGroups,\n\t\t\t);\n\t\t\tconst targetInside = belongsToGroup(\n\t\t\t\tedge.targetId,\n\t\t\t\tgroup.id,\n\t\t\t\tmeasuredNodes,\n\t\t\t\tmeasuredGroups,\n\t\t\t);\n\t\t\tif (\n\t\t\t\tsourceInside === targetInside &&\n\t\t\t\trouteViolatesGroup(edge.sections, group.bounds, sourceInside)\n\t\t\t) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"edge_through_group\",\n\t\t\t\t\t[edge.id, group.id],\n\t\t\t\t\t`edge ${edge.id} violates group ${group.id} boundary`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t\tif (edge.labelBounds) {\n\t\t\tcheckFiniteRect(\"edge label\", edge.id, edge.labelBounds, issues);\n\t\t\tcheckRectInBounds(\"edge label\", edge.id, edge.labelBounds, resolved.bounds, issues);\n\t\t\tif (expected.labelSize && (\n\t\t\t\t!nearlyEqual(expected.labelSize.width, edge.labelBounds.width) ||\n\t\t\t\t!nearlyEqual(expected.labelSize.height, edge.labelBounds.height)\n\t\t\t)) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"size_mismatch\",\n\t\t\t\t\t[edge.id],\n\t\t\t\t\t`edge ${edge.id} label size changed after measurement`,\n\t\t\t\t));\n\t\t\t}\n\t\t\tfor (const node of nodes.values()) {\n\t\t\t\tif (overlaps(edge.labelBounds, node.bounds)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"label_node_overlap\",\n\t\t\t\t\t\t[edge.id, node.id],\n\t\t\t\t\t\t`edge ${edge.id} label overlaps node ${node.id}`,\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const group of groups.values()) {\n\t\t\t\tif (overlaps(edge.labelBounds, group.titleBounds)) {\n\t\t\t\t\tissues.push(issue(\n\t\t\t\t\t\t\"label_title_overlap\",\n\t\t\t\t\t\t[edge.id, group.id],\n\t\t\t\t\t\t`edge ${edge.id} label overlaps group ${group.id} title`,\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t\tlabels.push({id: edge.id, bounds: edge.labelBounds});\n\t\t} else if (expected.labelSize) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"missing_element\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} is missing its measured label`,\n\t\t\t));\n\t\t}\n\t}\n\tforEachPair(labels, (first, second) => {\n\t\tif (overlaps(first.bounds, second.bounds)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"label_label_overlap\",\n\t\t\t\t[first.id, second.id],\n\t\t\t\t`edge labels ${first.id} and ${second.id} overlap`,\n\t\t\t));\n\t\t}\n\t});\n\tfor (const label of labels) {\n\t\tfor (const edge of resolved.edges) {\n\t\t\tif (routeIntersectsRect(edge.sections, inset(label.bounds, 0.5))) {\n\t\t\t\tissues.push(issue(\n\t\t\t\t\t\"label_edge_overlap\",\n\t\t\t\t\t[label.id, edge.id],\n\t\t\t\t\t`edge ${edge.id} crosses label ${label.id}`,\n\t\t\t\t));\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * checkSectionConnections requires one ordered route from the source border to\n * the target border. Separate or reversed sections are not a complete route.\n */\nfunction checkSectionConnections(\n\tedge: ResolvedEdge,\n\tnodes: Map,\n\tissues: GeometryIssue[],\n): void {\n\tfor (let index = 1; index < edge.sections.length; index++) {\n\t\tconst previous = edge.sections[index - 1].slice(-1)[0];\n\t\tconst current = edge.sections[index][0];\n\t\tif (!samePoint(previous, current)) {\n\t\t\tissues.push(issue(\n\t\t\t\t\"disconnected_edge\",\n\t\t\t\t[edge.id],\n\t\t\t\t`edge ${edge.id} has disconnected route sections`,\n\t\t\t));\n\t\t\treturn;\n\t\t}\n\t}\n\tconst source = nodes.get(edge.sourceId);\n\tconst target = nodes.get(edge.targetId);\n\tconst start = edge.sections[0][0];\n\tconst end = edge.sections.slice(-1)[0].slice(-1)[0];\n\tif (\n\t\t!source ||\n\t\t!target ||\n\t\t!pointOnBorder(start, source.bounds) ||\n\t\t!pointOnBorder(end, target.bounds)\n\t) {\n\t\tissues.push(issue(\n\t\t\t\"disconnected_edge\",\n\t\t\t[edge.id, edge.sourceId, edge.targetId],\n\t\t\t`edge ${edge.id} does not connect both node borders`,\n\t\t));\n\t}\n}\n\n/** belongsToGroup follows parent IDs to decide whether a node belongs to a group. */\nfunction belongsToGroup(\n\tnodeId: string,\n\tgroupId: string,\n\tnodes: Map,\n\tgroups: Map,\n): boolean {\n\tlet parent = nodes.get(nodeId)?.parentId;\n\twhile (parent) {\n\t\tif (parent === groupId) {\n\t\t\treturn true;\n\t\t}\n\t\tparent = groups.get(parent)?.parentId;\n\t}\n\treturn false;\n}\n\n/**\n * routeViolatesGroup checks the middle of each line. When both connected nodes\n * are inside a group, the line must stay inside it. When both are outside, the\n * line must stay outside it.\n */\nfunction routeViolatesGroup(\n\tsections: Point[][],\n\tgroup: Rect,\n\tshouldBeInside: boolean,\n): boolean {\n\tfor (const section of sections) {\n\t\tfor (let index = 1; index < section.length; index++) {\n\t\t\tconst midpoint = {\n\t\t\t\tx: (section[index - 1].x + section[index].x) / 2,\n\t\t\t\ty: (section[index - 1].y + section[index].y) / 2,\n\t\t\t};\n\t\t\tif (pointInRect(midpoint, group, false) !== shouldBeInside) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/** routeIntersectsRect reports whether any part of a route enters a box. */\nfunction routeIntersectsRect(sections: Point[][], rect: Rect): boolean {\n\treturn sections.some(section => section.some((point, index) =>\n\t\tindex > 0 && segmentIntersectsRect(section[index - 1], point, rect),\n\t));\n}\n\n/**\n * segmentIntersectsRect checks both straight and diagonal lines. A line may\n * touch the box border, but it may not enter the box.\n */\nfunction segmentIntersectsRect(start: Point, end: Point, rect: Rect): boolean {\n\tconst dx = end.x - start.x;\n\tconst dy = end.y - start.y;\n\tlet minimum = 0;\n\tlet maximum = 1;\n\tfor (const [origin, delta, lower, upper] of [\n\t\t[start.x, dx, rect.x, rect.x + rect.width],\n\t\t[start.y, dy, rect.y, rect.y + rect.height],\n\t] as const) {\n\t\tif (Math.abs(delta) < Number.EPSILON) {\n\t\t\tif (origin <= lower || origin >= upper) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst first = (lower - origin) / delta;\n\t\tconst second = (upper - origin) / delta;\n\t\tminimum = Math.max(minimum, Math.min(first, second));\n\t\tmaximum = Math.min(maximum, Math.max(first, second));\n\t\tif (minimum >= maximum) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn maximum > 0 && minimum < 1;\n}\n\n/** overlaps treats touching borders as contact, not shared area. */\nfunction overlaps(first: Rect, second: Rect): boolean {\n\treturn (\n\t\tfirst.x < second.x + second.width &&\n\t\tsecond.x < first.x + first.width &&\n\t\tfirst.y < second.y + second.height &&\n\t\tsecond.y < first.y + first.height\n\t);\n}\n\n/** contains requires the whole inner box to be inside the outer box. */\nfunction contains(outer: Rect, inner: Rect): boolean {\n\treturn (\n\t\touter.x <= inner.x &&\n\t\touter.y <= inner.y &&\n\t\touter.x + outer.width >= inner.x + inner.width &&\n\t\touter.y + outer.height >= inner.y + inner.height\n\t);\n}\n\n/** pointInRect can include or exclude points that only touch the border. */\nfunction pointInRect(point: Point, rect: Rect, inclusive: boolean): boolean {\n\tif (inclusive) {\n\t\treturn (\n\t\t\tpoint.x >= rect.x &&\n\t\t\tpoint.y >= rect.y &&\n\t\t\tpoint.x <= rect.x + rect.width &&\n\t\t\tpoint.y <= rect.y + rect.height\n\t\t);\n\t}\n\treturn (\n\t\tpoint.x > rect.x &&\n\t\tpoint.y > rect.y &&\n\t\tpoint.x < rect.x + rect.width &&\n\t\tpoint.y < rect.y + rect.height\n\t);\n}\n\n/** inset makes a box smaller so touching its border does not count as crossing it. */\nfunction inset(rect: Rect, amount: number): Rect {\n\treturn {\n\t\tx: rect.x + amount,\n\t\ty: rect.y + amount,\n\t\twidth: Math.max(0, rect.width - amount * 2),\n\t\theight: Math.max(0, rect.height - amount * 2),\n\t};\n}\n\n/** isFinitePoint accepts only real numbers that SVG can draw. */\nfunction isFinitePoint(point: Point): boolean {\n\treturn Number.isFinite(point.x) && Number.isFinite(point.y);\n}\n\n/** isFiniteRect checks all four numbers in a box. */\nfunction isFiniteRect(rect: Rect): boolean {\n\treturn isFinitePoint(rect) && Number.isFinite(rect.width) && Number.isFinite(rect.height);\n}\n\n/** samePoint compares route points while allowing tiny browser number changes. */\nfunction samePoint(first: Point, second: Point): boolean {\n\treturn nearlyEqual(first.x, second.x) && nearlyEqual(first.y, second.y);\n}\n\n/** pointOnBorder checks that a route endpoint touches one side of a node box. */\nfunction pointOnBorder(point: Point, rect: Rect): boolean {\n\tconst withinX = point.x >= rect.x - 0.1 && point.x <= rect.x + rect.width + 0.1;\n\tconst withinY = point.y >= rect.y - 0.1 && point.y <= rect.y + rect.height + 0.1;\n\tconst onVertical = nearlyEqual(point.x, rect.x) ||\n\t\tnearlyEqual(point.x, rect.x + rect.width);\n\tconst onHorizontal = nearlyEqual(point.y, rect.y) ||\n\t\tnearlyEqual(point.y, rect.y + rect.height);\n\treturn (withinY && onVertical) || (withinX && onHorizontal);\n}\n\n/** nearlyEqual hides only browser rounding smaller than one tenth of a unit. */\nfunction nearlyEqual(first: number, second: number): boolean {\n\treturn Math.abs(first - second) <= 0.1;\n}\n\n/** forEachPair compares each pair once. */\nfunction forEachPair(items: T[], visit: (first: T, second: T) => void): void {\n\tfor (let first = 0; first < items.length; first++) {\n\t\tfor (let second = first + 1; second < items.length; second++) {\n\t\t\tvisit(items[first], items[second]);\n\t\t}\n\t}\n}\n\n/** issue creates one layout problem report. */\nfunction issue(\n\tcode: GeometryIssueCode,\n\telementIds: string[],\n\tmessage: string,\n): GeometryIssue {\n\treturn {code, elementIds, message};\n}\n","export const defs = `\n\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n\t\n\t\t\n\t\n`\n","type TextAttributes = Record;\n\ninterface TextSize {\n\twidth: number;\n\theight: number;\n}\n\n/** WrappedText contains the exact lines and widest rendered line. */\nexport interface WrappedText {\n\tlines: string[];\n\tmaxW: number;\n}\n\nconst measuredText = new Map();\nlet measurementSVG: SVGSVGElement | undefined;\n\n/**\n * measureSVGText returns the browser's rendered text size. Results are cached\n * because node and edge layout often measures the same words many times.\n */\nexport function measureSVGText(text: string, attrs: TextAttributes): TextSize {\n\tconst key = measurementKey(text, attrs);\n\tconst cached = measuredText.get(key);\n\tif (cached) {\n\t\treturn cached;\n\t}\n\n\tconst node = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n\tnode.setAttribute(\"x\", \"0\");\n\tnode.setAttribute(\"y\", \"0\");\n\tfor (const [name, value] of Object.entries(attrs)) {\n\t\tnode.setAttribute(name, value);\n\t}\n\tnode.textContent = text;\n\n\tconst svg = getMeasurementSVG();\n\tsvg.appendChild(node);\n\tconst bounds = node.getBBox();\n\tnode.remove();\n\tconst size = {width: bounds.width, height: bounds.height};\n\tmeasuredText.set(key, size);\n\treturn size;\n}\n\n/**\n * svgTextWrap splits text into lines that fit the given width. It uses the\n * same browser text sizes that SVG drawing uses.\n */\nexport function svgTextWrap(\n\ttext: string,\n\twidth: number,\n\tattrs: TextAttributes,\n): WrappedText {\n\tlet maxW = 0;\n\tconst lines = text.trim().split(\"\\n\").flatMap(paragraph => {\n\t\tconst paragraphLines: string[] = [];\n\t\tlet currentLine: string[] = [];\n\t\tfor (const word of paragraph.trim().split(/\\s+/)) {\n\t\t\tif (measureSVGText(word, attrs).width > width) {\n\t\t\t\tif (currentLine.length > 0) {\n\t\t\t\t\tparagraphLines.push(currentLine.join(\" \"));\n\t\t\t\t\tcurrentLine = [];\n\t\t\t\t}\n\t\t\t\tconst parts = breakLongWord(word, width, attrs);\n\t\t\t\tfor (const part of parts.slice(0, -1)) {\n\t\t\t\t\tparagraphLines.push(part);\n\t\t\t\t\tmaxW = Math.max(maxW, measureSVGText(part, attrs).width);\n\t\t\t\t}\n\t\t\t\tcurrentLine = parts.slice(-1);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst nextLine = [...currentLine, word];\n\t\t\tconst size = measureSVGText(nextLine.join(\" \"), attrs);\n\t\t\tif (size.width > width && currentLine.length > 0) {\n\t\t\t\tparagraphLines.push(currentLine.join(\" \"));\n\t\t\t\tcurrentLine = [word];\n\t\t\t} else {\n\t\t\t\tmaxW = Math.max(maxW, size.width);\n\t\t\t\tcurrentLine = nextLine;\n\t\t\t}\n\t\t}\n\t\tif (currentLine.length > 0) {\n\t\t\tconst line = currentLine.join(\" \");\n\t\t\tparagraphLines.push(line);\n\t\t\tmaxW = Math.max(maxW, measureSVGText(line, attrs).width);\n\t\t}\n\t\treturn paragraphLines;\n\t});\n\treturn {lines, maxW};\n}\n\n/** getMeasurementSVG creates the single hidden SVG used for text measurement. */\nfunction getMeasurementSVG(): SVGSVGElement {\n\tif (measurementSVG?.isConnected) {\n\t\treturn measurementSVG;\n\t}\n\tmeasurementSVG = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n\tmeasurementSVG.setAttribute(\"aria-hidden\", \"true\");\n\tmeasurementSVG.style.position = \"absolute\";\n\tmeasurementSVG.style.visibility = \"hidden\";\n\tmeasurementSVG.style.pointerEvents = \"none\";\n\tdocument.body.appendChild(measurementSVG);\n\treturn measurementSVG;\n}\n\n/** measurementKey gives equal text and font settings the same cache entry. */\nfunction measurementKey(text: string, attrs: TextAttributes): string {\n\tconst attributes = Object.entries(attrs).sort(([first], [second]) =>\n\t\tfirst.localeCompare(second),\n\t);\n\treturn JSON.stringify([text, attributes]);\n}\n\n/** breakLongWord splits one word only when it cannot fit on an empty line. */\nfunction breakLongWord(\n\tword: string,\n\tmaxWidth: number,\n\tattrs: TextAttributes,\n): string[] {\n\tconst parts: string[] = [];\n\tlet currentPart = \"\";\n\tfor (const character of word) {\n\t\tconst nextPart = currentPart + character;\n\t\tif (measureSVGText(nextPart, attrs).width > maxWidth && currentPart.length > 0) {\n\t\t\tparts.push(currentPart);\n\t\t\tcurrentPart = character;\n\t\t} else {\n\t\t\tcurrentPart = nextPart;\n\t\t}\n\t}\n\tif (currentPart.length > 0) {\n\t\tparts.push(currentPart);\n\t}\n\treturn parts;\n}\n\n\n","import {svgTextWrap} from \"./svg-text\";\n\nexport const create = {\n\telement(type: string, attrs: Record = {}, className?: string) {\n\t\tconst el = document.createElementNS('http://www.w3.org/2000/svg', type);\n\t\tObject.entries(attrs).forEach(([k, v]) => el.setAttribute(k, String(v)));\n\t\tif (className) el.classList.add(className);\n\t\treturn el;\n\t},\n\n\tuse(id: string, attrs: Record = {}) {\n\t\tconst el = this.element('use', attrs);\n\t\tel.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + id);\n\t\treturn el;\n\t},\n\n\tpath(path: string, attrs: Record = {}, className?: string) {\n\t\tconst p = this.element(\"path\", {...attrs, d: path}, className);\n\t\treturn p;\n\t},\n\n\ttext(text: string, attrs: Record = {}) {\n\t\tconst t = this.element('text', attrs) as SVGTextElement;\n\t\tif (text) t.textContent = text;\n\t\treturn t;\n\t},\n\n\ttextArea(text: string, width: number, fontSize: number, bold: boolean, x = 0, y = 0, anchor = '') {\n\t\tconst attrs: Record = {\n\t\t\t'font-size': `${fontSize}px`,\n\t\t\t'font-weight': bold ? 'bold' : 'normal'\n\t\t};\n\t\tconst {lines, maxW} = svgTextWrap(text, width, attrs);\n\t\tconst txt = this.text('', {x: 0, y, 'text-anchor': anchor || undefined});\n\t\t\n\t\tlines.forEach((line, i) => {\n\t\t\tconst span = this.element('tspan', {x, dy: `${fontSize + 2}px`, ...attrs});\n\t\t\tspan.textContent = line;\n\t\t\ttxt.append(span);\n\t\t});\n\t\t\n\t\treturn {txt, dy: (lines.length + 1) * (fontSize + 2), maxW};\n\t},\n\n\trect(width: number, height: number, x = 0, y = 0, r = 0, className?: string) {\n\t\treturn this.element('rect', {x, y, rx: r, ry: r, width, height}, className) as SVGRectElement;\n\t},\n\n\ticon(icon: string, x = 0, y = 0) {\n\t\treturn this.use(icon, {x, y});\n\t},\n\n\texpand(x: number, y: number, expanded: boolean) {\n\t\tconst g = this.element('g', {transform: `translate(${x},${y})`}, 'expand') as SVGGElement;\n\t\tg.append(\n\t\t\tthis.rect(19, 19, 0, 0, 1),\n\t\t\tthis.text(expanded ? '-' : '+', {x: 10, y: 14, 'text-anchor': 'middle'})\n\t\t);\n\t\treturn g;\n\t}\n};\n\nexport function setPosition(g: SVGGElement, x: number, y: number) {\n\tg.setAttribute('transform', `translate(${x},${y})`);\n}","interface Point {\n\tx: number;\n\ty: number;\n}\n\ninterface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\n\nexport function insideBox(p: Point, b: BBox, centeredBox = true): boolean {\n\treturn centeredBox ?\n\t\t(p.x > b.x - b.width / 2 && p.x < b.x + b.width / 2 && p.y > b.y - b.height / 2 && p.y < b.y + b.height / 2) :\n\t\t(p.x > b.x && p.x < b.x + b.width && p.y > b.y && p.y < b.y + b.height)\n}\n\nexport function boxesOverlap(b1: BBox, b2: BBox): boolean {\n\treturn b1.x < b2.x + b2.width && b1.y < b2.y + b2.height && b1.x + b1.width > b2.x && b1.y + b1.height > b2.y\n}\n\nexport function uncenterBox(b: BBox): BBox {\n\treturn {x: b.x - b.width / 2, y: b.y - b.height / 2, width: b.width, height: b.height}\n}\n\nexport function scaleBox(b: BBox, sc: number): BBox {\n\treturn {x: b.x * sc, y: b.y * sc, width: b.width * sc, height: b.height * sc}\n}\n\n// intersect 2 segments (p1->q1) with (p2, q2)\n// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite)\n// and booleans for whether line segment 1 or line segment 2 contain the point\nfunction segmentIntersection(p1: Point, q1: Point, p2: Point, q2: Point) {\n\tlet denominator, a, b, numerator1, numerator2,\n\t\tresult: { x: number, y: number, onLine1: boolean, onLine2: boolean } = {\n\t\t\tx: null,\n\t\t\ty: null,\n\t\t\tonLine1: false,\n\t\t\tonLine2: false\n\t\t};\n\tdenominator = (q2.y - p2.y) * (q1.x - p1.x) - (q2.x - p2.x) * (q1.y - p1.y);\n\tif (denominator == 0) {\n\t\treturn result;\n\t}\n\ta = p1.y - p2.y;\n\tb = p1.x - p2.x;\n\tnumerator1 = ((q2.x - p2.x) * a) - ((q2.y - p2.y) * b);\n\tnumerator2 = ((q1.x - p1.x) * a) - ((q1.y - p1.y) * b);\n\ta = numerator1 / denominator;\n\tb = numerator2 / denominator;\n\n\t// if we cast these lines infinitely in both directions, they intersect here:\n\tresult.x = p1.x + (a * (q1.x - p1.x));\n\tresult.y = p1.y + (a * (q1.y - p1.y));\n\n\t// if line1 is a segment and line2 is infinite, they intersect if:\n\tif (a > 0 && a < 1) {\n\t\tresult.onLine1 = true;\n\t}\n\t// if line2 is a segment and line1 is infinite, they intersect if:\n\tif (b >= 0 && b <= 1) {\n\t\tresult.onLine2 = true;\n\t}\n\t// if line1 and line2 are segments, they intersect if both of the above are true\n\treturn result;\n}\n\n// intersects a segment (p1->p2) with a box\nexport function intersectRectFull(p1: Point, p2: Point, box: BBox): Point[] {\n\tconst w = box.width / 2\n\tconst h = box.height / 2\n\tconst segs: { p: Point; q: Point }[] = [\n\t\t{p: {x: box.x - w, y: box.y - h}, q: {x: box.x - w, y: box.y + h}},\n\t\t{p: {x: box.x - w, y: box.y - h}, q: {x: box.x + w, y: box.y - h}},\n\t\t{p: {x: box.x + w, y: box.y - h}, q: {x: box.x + w, y: box.y + h}},\n\t\t{p: {x: box.x - w, y: box.y + h}, q: {x: box.x + w, y: box.y + h}},\n\t]\n\treturn segs.map(s => segmentIntersection(p1, p2, s.p, s.q)).filter(ret => ret.onLine1 && ret.onLine2)\n}\n\n// intersects a line that goes from p to the center of the box\nexport function intersectRect(box: BBox, p: Point): Point {\n\tif (insideBox(p, box)) return {x: box.x, y: box.y}\n\treturn intersectRectFull(box, p, box)[0] || {x: box.x, y: box.y}\n}\n\nexport function intersectEllipse(ellCenter: Point, rx: number, ry: number, nodeCenter: Point, point: Point) {\n\n\t//translate all to center ellipse\n\tconst p1 = {x: point.x - ellCenter.x, y: point.y - ellCenter.y}\n\tconst p2 = {x: nodeCenter.x - ellCenter.x, y: nodeCenter.y - ellCenter.y}\n\n\tif (p2.x == p1.x) { //hack to avoid singularity\n\t\tp1.x += .0000001\n\t}\n\n\tconst s = (p2.y - p1.y) / (p2.x - p1.x);\n\tconst si = p2.y - (s * p2.x);\n\tconst a = (ry * ry) + (rx * rx * s * s);\n\tconst b = 2 * rx * rx * si * s;\n\tconst c = rx * rx * si * si - rx * rx * ry * ry;\n\n\tconst radicand_sqrt = Math.sqrt((b * b) - (4 * a * c));\n\tconst x = p1.x > p2.x ?\n\t\t(-b + radicand_sqrt) / (2 * a) :\n\t\t(-b - radicand_sqrt) / (2 * a)\n\tconst pos = {\n\t\tx: x,\n\t\ty: s * x + si\n\t}\n\t//translate back\n\tpos.x += ellCenter.x;\n\tpos.y += ellCenter.y\n\n\treturn pos;\n}\n\nexport interface Segment {\n\tp: Point;\n\tq: Point;\n}\n\n// given a polyline as a list of segments, interrupt it over the box so no line is inside the box\nexport function intersectPolylineBox(segments: Segment[], box: BBox) {\n\tfor (let i = 0; i < segments.length; i++) {\n\t\tconst s = segments[i]\n\t\tif (insideBox(s.p, box)) {\n\t\t\tif (insideBox(s.q, box)) { // segment both ends inside box\n\t\t\t\tsegments.splice(i, 1)\n\t\t\t\ti -= 1\n\t\t\t} else { // segment start inside box\n\t\t\t\ts.p = intersectRectFull(s.p, s.q, box)[0]\n\t\t\t}\n\t\t} else {\n\t\t\tif (insideBox(s.q, box)) { // segment end inside box\n\t\t\t\ts.q = intersectRectFull(s.p, s.q, box)[0]\n\t\t\t} else { // both ends outside\n\t\t\t\tconst ret = intersectRectFull(s.p, s.q, box)\n\t\t\t\tif (ret.length == 2) { // intersects the box, splice segment\n\t\t\t\t\t// order the intersection points, closest first\n\t\t\t\t\tconst dst1 = Math.abs(ret[0].x - s.p.x) + Math.abs(ret[0].y - s.p.y)\n\t\t\t\t\tconst dst2 = Math.abs(ret[1].x - s.p.x) + Math.abs(ret[1].y - s.p.y)\n\t\t\t\t\tif (dst1 > dst2) ret.reverse()\n\t\t\t\t\t// split the segment in 2\n\t\t\t\t\tconst s2 = {p: ret[1], q: s.q}\n\t\t\t\t\ts.q = ret[0]\n\t\t\t\t\tsegments.splice(i + 1, 0, s2)\n\t\t\t\t\ti += 1\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function project(p: Point, a: Point, b: Point): Point {\n\tlet atob = {x: b.x - a.x, y: b.y - a.y};\n\tlet atop = {x: p.x - a.x, y: p.y - a.y};\n\tlet len = atob.x * atob.x + atob.y * atob.y;\n\tlet dot = atop.x * atob.x + atop.y * atob.y;\n\tlet t = Math.min(1, Math.max(0, dot / len));\n\treturn {\n\t\tx: a.x + atob.x * t,\n\t\ty: a.y + atob.y * t\n\t};\n}\n\nexport function cabDistance(p1: Point, p2: Point): number {\n\treturn Math.abs(p2.x - p1.x) + Math.abs(p2.y - p1.y)\n}","import {intersectEllipse, intersectRect} from \"./intersect\";\n\ninterface Point {\n\tx: number;\n\ty: number;\n}\n\ninterface BBox extends Point {\n\twidth: number;\n\theight: number;\n}\n\ninterface D3Node extends BBox {\n\tintersect: (p: Point) => Point\n}\n\nfunction cylinderRadiusY(width: number) {\n\treturn width / 2 / (5.5 + width / 70);\n}\n\nexport function shapeLabelOffsetY(shape: string, width: number, height: number) {\n\tswitch (shape.toLowerCase()) {\n\t\tcase \"cylinder\":\n\t\t\treturn 2 * cylinderRadiusY(width);\n\t\tcase \"person\":\n\t\t\treturn height * 0.4;\n\t\tcase \"folder\":\n\t\t\treturn width / 10;\n\t\tcase \"robot\":\n\t\t\treturn height * 0.35;\n\t\tcase \"webbrowser\":\n\t\t\treturn height / 8;\n\t\tdefault:\n\t\t\treturn 0;\n\t}\n}\n\nclass D3Element {\n\tprivate readonly _el: SVGElement\n\n\tconstructor(el: SVGElement) {\n\t\tthis._el = el;\n\t}\n\n\tnode() {\n\t\treturn this._el;\n\t}\n\n\tattr(name: string, value: string | number) {\n\t\tthis._el.setAttribute(name, String(value))\n\t\treturn this;\n\t}\n\n\tinsert(type: string, pos: string) {\n\t\tconst el = document.createElementNS('http://www.w3.org/2000/svg', type)\n\t\tconst el2 = this._el.insertBefore(el, this._el.querySelector(pos))\n\t\treturn new D3Element(el2)\n\t}\n}\n\n\nfunction rect(parent: D3Element, bbox: BBox, node: D3Node, rounded = false) {\n\tconst shapeSvg = parent.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", rounded ? node.width / 8 : 3)\n\t\t.attr(\"ry\", rounded ? node.width / 8 : 3)\n\t\t.attr(\"x\", -bbox.width / 2)\n\t\t.attr(\"y\", -bbox.height / 2)\n\t\t.attr(\"width\", bbox.width)\n\t\t.attr(\"height\", bbox.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\n\nfunction cylinder(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = bbox.width;\n\tconst rx = w / 2;\n\tconst ry = cylinderRadiusY(w);\n\tconst h = bbox.height;\n\n\tconst shape =\n\t\t`M 0,${ry} a${rx},${ry} 0,0,0 ${w} 0 a ${rx},${ry} 0,0,0 ${-w} 0 l 0,${h - 2 * ry} a ${rx},${ry} 0,0,0 ${w} 0 l 0,${-h + 2 * ry}`;\n\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"cylinder\", w, h))\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\tconst pos = intersectRect(node, point)\n\t\tlet cy = node.y + node.height / 2 - ry\n\t\tif (pos.y > cy)\n\t\t\treturn intersectEllipse({x: node.x, y: cy}, rx, ry, node, point)\n\n\t\tcy = node.y - node.height / 2 + ry\n\t\tif (pos.y < cy)\n\t\t\treturn intersectEllipse({x: node.x, y: cy}, rx, ry, node, point)\n\n\t\treturn pos;\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction person(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = bbox.width;\n\tconst h = bbox.height;\n\n\tconst shape =\n\t\t`M ${.38 * w},${h / 3} A${w / 2},${h / 2} 0,0,0 0 ${h / 2}\n\t\tL${w / 11},${h} L${w - w / 11},${h} L${w},${h / 2}\n\t\tA${w / 2},${h / 2} 0,0,0 ${w - .38 * w} ${h / 3} \n\t\tA${w / 6},${w / 6} 0,1,0 ${.38 * w} ${h / 3}`;\n\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"person\", w, h))\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\tconst pos = intersectRect(node, point)\n\t\treturn pos;\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction _ellipse(parent: D3Element, bbox: BBox, node: D3Node, rx: number, ry: number) {\n\tconst shapeSvg = parent.insert(\"ellipse\", \":first-child\")\n\t\t.attr(\"cx\", 0)\n\t\t.attr(\"cy\", 0)\n\t\t.attr('rx', rx)\n\t\t.attr('ry', ry)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectEllipse(node, rx, ry, node, point)\n\t};\n\treturn shapeSvg;\n}\n\nfunction circle(parent: D3Element, bbox: BBox, node: D3Node) {\n\treturn _ellipse(parent, bbox, node, node.width / 2, node.width / 2)\n}\n\nfunction ellipse(parent: D3Element, bbox: BBox, node: D3Node) {\n\treturn _ellipse(parent, bbox, node, node.width * .55, node.width * .45)\n}\n\nfunction hexagon(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst sz = node.width / 2\n\t// drawing a hexagon from polar coords\n\t// [0,1,2,3,4,5,6].map(i=>`${Math.sin(Math.PI/3*i+Math.PI/6).toFixed(4)},${Math.cos(Math.PI/3*i+Math.PI/6).toFixed(4)}`).join(',')\n\tconst shapeSvg = parent.insert(\"polygon\", \":first-child\")\n\t\t.attr(\"points\",\n\t\t\t[0.5000, 0.8660, 1.0000, 0.0000, 0.5000, -0.8660, -0.5000, -0.8660, -1.0000, -0.0000, -0.5000, 0.8660, 0.5000, 0.8660].map(n => n * sz).join(','))\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectEllipse(node, node.width / 2, node.width / 2, node, point)\n\t};\n\treturn shapeSvg;\n}\n\nfunction component(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dx = node.width / 10\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2 - dx)\n\t\t.attr(\"y\", -node.height / 2 + dx)\n\t\t.attr(\"width\", dx * 2)\n\t\t.attr(\"height\", dx);\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2 - dx)\n\t\t.attr(\"y\", -node.height / 2 + dx * 2.5)\n\t\t.attr(\"width\", dx * 2)\n\t\t.attr(\"height\", dx);\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x - dx / 2, y: node.y, width: node.width + dx, height: node.height}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction folder(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.width / 20\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"folder\", node.width, node.height))\n\t\t.insert('g', ':first-child')\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2 + dy * 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height - dy * 2);\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('d', `M0,${-node.height / 2 + 2 * dy} l${dy},${-2 * dy} h${node.width / 2 - dy * 2} v${dy * 2}`)\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y + dy / 2, width: node.width, height: node.height + dy}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction mobiledevicelandscape(parent: D3Element, bbox: BBox, node: D3Node, rounded = false) {\n\tconst dx = node.width / 8\n\tconst r = node.width / 14\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert('path', ':first-child')\n\t\t.attr('d', `M${-node.width / 2},${-node.height / 2} l0,${node.height} M${node.width / 2},${-node.height / 2} l0,${node.height}`)\n\tshapeSvg.insert('circle', ':first-child')\n\t\t.attr('cx', -node.width / 2 - dx / 2)\n\t\t.attr('cy', 0)\n\t\t.attr('r', r * .4)\n\tshapeSvg.insert('rect', ':first-child')\n\t\t.attr('x', node.width / 2 + dx / 2 - r * .2)\n\t\t.attr('y', -r)\n\t\t.attr('width', r * .4)\n\t\t.attr('height', r * 2)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", r)\n\t\t.attr(\"ry\", r)\n\t\t.attr(\"x\", -bbox.width / 2 - dx)\n\t\t.attr(\"y\", -bbox.height / 2)\n\t\t.attr(\"width\", bbox.width + 2 * dx)\n\t\t.attr(\"height\", bbox.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y, width: node.width + 2 * dx, height: node.height}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction mobiledeviceportrait(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.width / 8\n\tconst r = node.width / 14\n\tconst shapeSvg = parent.insert('g', ':first-child')\n\tshapeSvg.insert('path', ':first-child')\n\t\t.attr('d', `M${-node.width / 2},${-node.height / 2} l${node.width},0 M${-node.width / 2},${node.height / 2} l${node.width},0`)\n\tshapeSvg.insert('circle', ':first-child')\n\t\t.attr('cx', 0)\n\t\t.attr('cy', node.height / 2 + dy / 2)\n\t\t.attr('r', r * .4)\n\tshapeSvg.insert('rect', ':first-child')\n\t\t.attr('x', -r)\n\t\t.attr('y', -node.height / 2 - dy / 2 - r * .2)\n\t\t.attr('width', r * 2)\n\t\t.attr('height', r * .4)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", r)\n\t\t.attr(\"ry\", r)\n\t\t.attr(\"x\", -bbox.width / 2)\n\t\t.attr(\"y\", -bbox.height / 2 - dy)\n\t\t.attr(\"width\", bbox.width)\n\t\t.attr(\"height\", bbox.height + 2 * dy);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect({x: node.x, y: node.y, width: node.width, height: node.height + 2 * dy}, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction pipe(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = node.width;\n\tconst h = node.height;\n\tconst ry = h / 2;\n\tconst rx = ry / (2.5 + w / 70);\n\n\tconst shape =\n\t\t`M${-rx},0\n\t\ta${rx},${ry} 0,0,1 0,${h}\n\t\ta${rx},${ry} 0,0,1 0,${-h}\n\t\tl${w},0\n\t\ta${rx},${ry} 0,0,1 0,${h}\n\t\tl${-w},0`;\n\n\tconst shapeSvg = parent\n\t\t.insert('path', ':first-child')\n\t\t.attr('d', shape)\n\t\t.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2) + ')');\n\n\tnode.intersect = function (point: Point) {\n\t\treturn intersectRect({x: node.x - rx, y: node.y, width: node.width + 2 * rx, height: node.height}, point)\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction robot(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst w = node.width\n\tconst h = node.height\n\t\n\t// Small head at top (like person shape but robot-styled)\n\tconst headSize = Math.min(w * 0.28, h * 0.25)\n\tconst headR = headSize * 0.2\n\tconst antennaH = headSize * 0.25\n\tconst antennaR = headSize * 0.08\n\t\n\t// Eye dimensions\n\tconst eyeR = headSize * 0.12\n\tconst eyeSpacing = headSize * 0.22\n\t\n\t// Ear dimensions \n\tconst earW = headSize * 0.12\n\tconst earH = headSize * 0.3\n\t\n\t// Body fills most of the space for text\n\tconst bodyW = w\n\tconst bodyTop = -h / 2 + antennaH + headSize\n\tconst bodyH = h - antennaH - headSize\n\tconst bodyR = 3\n\t\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"robot\", w, h))\n\t\t.insert('g', ':first-child')\n\t\n\t// Body - main rectangle for text (draw first so it's behind)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", bodyR)\n\t\t.attr(\"ry\", bodyR)\n\t\t.attr('x', -bodyW / 2)\n\t\t.attr('y', bodyTop)\n\t\t.attr('width', bodyW)\n\t\t.attr('height', bodyH)\n\t\n\t// Head\n\tconst headTop = -h / 2 + antennaH\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", headR)\n\t\t.attr(\"ry\", headR)\n\t\t.attr('x', -headSize / 2)\n\t\t.attr('y', headTop)\n\t\t.attr('width', headSize)\n\t\t.attr('height', headSize)\n\t\n\t// Antenna\n\tshapeSvg.insert(\"line\", \":first-child\")\n\t\t.attr('class', 'robot-antenna')\n\t\t.attr('x1', 0)\n\t\t.attr('y1', headTop)\n\t\t.attr('x2', 0)\n\t\t.attr('y2', -h / 2 + antennaR * 2)\n\t\t.attr('stroke-width', antennaR * 0.6)\n\t\t.attr('stroke-linecap', 'round')\n\t\n\t// Antenna ball\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-antenna-ball')\n\t\t.attr('cx', 0)\n\t\t.attr('cy', -h / 2 + antennaR * 2)\n\t\t.attr('r', antennaR * 1.2)\n\t\n\t// Eyes\n\tconst eyeY = headTop + headSize * 0.4\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-eye')\n\t\t.attr('cx', -eyeSpacing)\n\t\t.attr('cy', eyeY)\n\t\t.attr('r', eyeR)\n\tshapeSvg.insert(\"circle\", \":first-child\")\n\t\t.attr('class', 'robot-eye')\n\t\t.attr('cx', eyeSpacing)\n\t\t.attr('cy', eyeY)\n\t\t.attr('r', eyeR)\n\t\n\t// Mouth - simple smile\n\tconst mouthY = headTop + headSize * 0.7\n\tconst mouthW = headSize * 0.28\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('class', 'robot-mouth')\n\t\t.attr('d', `M${-mouthW / 2},${mouthY} Q0,${mouthY + mouthW * 0.3} ${mouthW / 2},${mouthY}`)\n\t\t.attr('fill', 'none')\n\t\t.attr('stroke-width', antennaR * 0.5)\n\t\t.attr('stroke-linecap', 'round')\n\t\n\t// Ears\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", earW * 0.25)\n\t\t.attr(\"ry\", earW * 0.25)\n\t\t.attr('x', -headSize / 2 - earW - 1)\n\t\t.attr('y', eyeY - earH / 2)\n\t\t.attr('width', earW)\n\t\t.attr('height', earH)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", earW * 0.25)\n\t\t.attr(\"ry\", earW * 0.25)\n\t\t.attr('x', headSize / 2 + 1)\n\t\t.attr('y', eyeY - earH / 2)\n\t\t.attr('width', earW)\n\t\t.attr('height', earH)\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nfunction webbrowser(parent: D3Element, bbox: BBox, node: D3Node) {\n\tconst dy = node.height / 8\n\tconst shapeSvg = parent\n\t\t.attr('label-offset-y', shapeLabelOffsetY(\"webbrowser\", node.width, node.height))\n\t\t.insert('g', ':first-child')\n\tshapeSvg.insert(\"path\", \":first-child\")\n\t\t.attr('d', `\n\t\t\tM${-node.width / 2},${-node.height / 2 + dy} h${node.width}\n\t\t\tM${-node.width / 2 + dy / 4},${-node.height / 2 + dy / 4} h${dy / 2} v${dy / 2} h${-dy / 2} z\n\t\t\tM${-node.width / 2 + dy},${-node.height / 2 + dy / 4} h${node.width - dy - dy / 4} v${dy / 2} h${-node.width + dy + dy / 4} z\n\t\t`)\n\tshapeSvg.insert(\"rect\", \":first-child\")\n\t\t.attr(\"rx\", 3).attr(\"ry\", 3)\n\t\t.attr(\"x\", -node.width / 2)\n\t\t.attr(\"y\", -node.height / 2)\n\t\t.attr(\"width\", node.width)\n\t\t.attr(\"height\", node.height);\n\n\tnode.intersect = function (point) {\n\t\treturn intersectRect(node, point);\n\t};\n\n\treturn shapeSvg;\n}\n\nexport const shapes: { [key: string]: (parent: SVGElement, node: D3Node) => SVGElement } = {\n\tbox: (parent: SVGElement, node: D3Node) => rect(new D3Element(parent), node, node).node(),\n\troundedbox: (parent: SVGElement, node: D3Node) => rect(new D3Element(parent), node, node, true).node(),\n\tcomponent: (parent: SVGElement, node: D3Node) => component(new D3Element(parent), node, node).node(),\n\tcylinder: (parent: SVGElement, node: D3Node) => cylinder(new D3Element(parent), node, node).node(),\n\tperson: (parent: SVGElement, node: D3Node) => person(new D3Element(parent), node, node).node(),\n\tcircle: (parent: SVGElement, node: D3Node) => circle(new D3Element(parent), node, node).node(),\n\tellipse: (parent: SVGElement, node: D3Node) => ellipse(new D3Element(parent), node, node).node(),\n\thexagon: (parent: SVGElement, node: D3Node) => hexagon(new D3Element(parent), node, node).node(),\n\tfolder: (parent: SVGElement, node: D3Node) => folder(new D3Element(parent), node, node).node(),\n\tmobiledevicelandscape: (parent: SVGElement, node: D3Node) => mobiledevicelandscape(new D3Element(parent), node, node).node(),\n\tmobiledeviceportrait: (parent: SVGElement, node: D3Node) => mobiledeviceportrait(new D3Element(parent), node, node).node(),\n\tmobiledevice: (parent: SVGElement, node: D3Node) => mobiledeviceportrait(new D3Element(parent), node, node).node(),\n\tpipe: (parent: SVGElement, node: D3Node) => pipe(new D3Element(parent), node, node).node(),\n\trobot: (parent: SVGElement, node: D3Node) => robot(new D3Element(parent), node, node).node(),\n\twebbrowser: (parent: SVGElement, node: D3Node) => webbrowser(new D3Element(parent), node, node).node(),\n}\n","/**\n * Undo functionality\n * at every change in the document, Undo can save a new version\n * so the user can \"undo\" and \"redo\" changes by reverting to an\n * older version of the document\n */\n\nexport class Undo {\n\tprivate readonly versions: Doc[] = [];\n\tprivate pos: number = 0;\n\tprivate lastSavedPos: number = 0;\n\tprivate readonly exportDoc: () => Doc;\n\tprivate readonly importDoc: (d: Doc) => void;\n\tchange: () => void;\n\tprivate tmpPreviousState: Doc | null = null;\n\n\tconstructor(id: string, exportDoc: () => Doc, importDoc: (d: Doc) => void) {\n\t\tthis.exportDoc = exportDoc;\n\t\tthis.importDoc = importDoc;\n\t\tthis.change = debounce(() => this.saveNow(), 300);\n\t}\n\n\t// Store the state previous to the changes collected in the debounce period\n\tbeforeChange() {\n\t\tif (!this.tmpPreviousState) {\n\t\t\tthis.tmpPreviousState = this.deepClone(this.exportDoc());\n\t\t}\n\t}\n\n\tlength() {\n\t\treturn this.versions.length;\n\t}\n\n\tcurrentState() {\n\t\treturn this.deepClone(this.versions[this.pos - 1]);\n\t}\n\n\tprivate saveNow() {\n\t\tif (!this.tmpPreviousState) {\n\t\t\tthrow Error(\"undo.change() was called without previously calling undo.beforeChange()!\");\n\t\t}\n\t\t\n\t\tthis.versions[this.pos] = this.deepClone(this.exportDoc());\n\t\tthis.versions[this.pos - 1] = this.tmpPreviousState;\n\t\tthis.tmpPreviousState = null;\n\t\tthis.pos += 1;\n\t\t\n\t\t// Remove anything that might be on top of this version\n\t\tthis.versions.splice(this.pos);\n\t}\n\n\tprivate deepClone(doc: Doc): Doc {\n\t\t// Use modern structuredClone if available, fallback to JSON\n\t\tif (typeof structuredClone !== 'undefined') {\n\t\t\treturn structuredClone(doc);\n\t\t}\n\t\treturn JSON.parse(JSON.stringify(doc));\n\t}\n\n\tundo() {\n\t\tif (this.pos < 2) return;\n\t\tthis.pos -= 1;\n\t\tconst doc = this.versions[this.pos - 1];\n\t\tthis.importDoc(this.deepClone(doc));\n\t}\n\n\tredo() {\n\t\tif (this.pos > this.versions.length - 1) return;\n\t\tconst doc = this.versions[this.pos];\n\t\tthis.importDoc(this.deepClone(doc));\n\t\tthis.pos += 1;\n\t}\n\n\tchanged() {\n\t\treturn this.pos !== this.lastSavedPos;\n\t}\n\n\tsetSaved() {\n\t\tthis.lastSavedPos = this.pos;\n\t}\n}\n\nfunction debounce(func: () => void, wait: number) {\n\tlet timeout: ReturnType;\n\treturn function () {\n\t\tconst context = this;\n\t\tconst later = function () {\n\t\t\ttimeout = null;\n\t\t\tfunc.apply(context);\n\t\t};\n\t\tclearTimeout(timeout);\n\t\ttimeout = setTimeout(later, wait);\n\t};\n}","import {applyStyle, SVG_STYLES} from \"./constants\";\nimport {create} from \"./svg-create\";\nimport {svgTextWrap} from \"./svg-text\";\n\ninterface TextBlockLayout {\n\tlines: string[];\n\tfontSize: number;\n\tlineHeight: number;\n\tbold: boolean;\n\tfield?: string;\n\tgapAfter: number;\n}\n\nexport interface NodeContentLayout {\n\tblocks: TextBlockLayout[];\n\ttextHeight: number;\n\tminimumHeight: number;\n}\n\nconst HORIZONTAL_PADDING = 18;\nconst VERTICAL_PADDING = 18;\nconst TITLE_GAP = 6;\nconst METADATA_GAP = 10;\n\nfunction textBlock(\n\ttext: string,\n\twidth: number,\n\tfontSize: number,\n\tbold: boolean,\n\tgapAfter: number,\n\tfield?: string,\n): TextBlockLayout {\n\tconst attrs = {\n\t\t\"font-family\": String(SVG_STYLES.nodeText[\"font-family\"]),\n\t\t\"font-size\": `${fontSize}px`,\n\t\t\"font-weight\": bold ? \"bold\" : \"normal\",\n\t};\n\tconst wrapped = svgTextWrap(text, width, attrs);\n\tconst lines = wrapped.lines.length > 0 ? wrapped.lines : [\"\"];\n\n\treturn {\n\t\tlines,\n\t\tfontSize,\n\t\tlineHeight: fontSize + 2,\n\t\tbold,\n\t\tfield,\n\t\tgapAfter,\n\t};\n}\n\nexport function layoutNodeContent(\n\ttitle: string,\n\tsubtitle: string,\n\tdescription: string,\n\tnodeWidth: number,\n\tfontSize: number,\n): NodeContentLayout {\n\tconst textWidth = Math.max(nodeWidth - HORIZONTAL_PADDING * 2, 80);\n\tconst blocks = [\n\t\ttextBlock(title, textWidth, fontSize, true, TITLE_GAP, \"name\"),\n\t\ttextBlock(`[${subtitle}]`, textWidth, fontSize * 0.75, false, METADATA_GAP),\n\t\ttextBlock(description, textWidth, Math.min(fontSize * 0.8, 16), false, 0, \"description\"),\n\t];\n\tconst textHeight = blocks.reduce(\n\t\t(height, block) => height + block.lines.length * block.lineHeight + block.gapAfter,\n\t\t0,\n\t);\n\n\treturn {\n\t\tblocks,\n\t\ttextHeight,\n\t\tminimumHeight: textHeight + VERTICAL_PADDING * 2,\n\t};\n}\n\nexport function buildNodeContent(layout: NodeContentLayout, color?: string): SVGGElement {\n\tconst group = create.element(\"g\") as SVGGElement;\n\tlet top = -layout.textHeight / 2;\n\n\tlayout.blocks.forEach((block) => {\n\t\tconst text = create.text(\"\", {\"text-anchor\": \"middle\"});\n\t\tapplyStyle(text, SVG_STYLES.nodeText);\n\t\tif (color) {\n\t\t\ttext.setAttribute(\"fill\", color);\n\t\t}\n\t\tif (block.field) {\n\t\t\ttext.setAttribute(\"data-field\", block.field);\n\t\t}\n\n\t\tblock.lines.forEach((line, index) => {\n\t\t\tconst span = create.element(\"tspan\", {\n\t\t\t\tx: 0,\n\t\t\t\ty: top + block.fontSize + index * block.lineHeight,\n\t\t\t\t\"font-size\": `${block.fontSize}px`,\n\t\t\t\t\"font-weight\": block.bold ? \"bold\" : \"normal\",\n\t\t\t});\n\t\t\tspan.textContent = line;\n\t\t\ttext.append(span);\n\t\t});\n\n\t\tgroup.append(text);\n\t\ttop += block.lines.length * block.lineHeight + block.gapAfter;\n\t});\n\n\treturn group;\n}\n","import {defs} from \"./defs\";\nimport {create, setPosition} from \"./svg-create\";\nimport {cursorInteraction} from \"svg-editor-tools/lib/cursor-interaction\";\nimport {shapeLabelOffsetY, shapes} from \"./shapes\";\nimport {\n\tboxesOverlap,\n\tcabDistance,\n\tinsideBox,\n\tintersectPolylineBox,\n\tproject,\n\tscaleBox,\n\tSegment,\n\tuncenterBox\n} from \"./intersect\";\nimport {autoLayout, LayoutOptions, validateCurrentLayout} from \"./layout\";\nimport {Undo} from \"./undo\";\nimport {\n\tADD_LABEL_VERTEX,\n\tADD_VERTEX,\n\tDEL_VERTEX,\n\tDESELECT,\n\tfindShortcut,\n\tREDO,\n\tSELECT_ALL,\n\tUNDO,\n\tZOOM_100,\n\tZOOM_FIT,\n\tZOOM_IN,\n\tZOOM_OUT\n} from \"../shortcuts\";\nimport {\n\tPoint,\n\tBBox,\n\tNodeStyle,\n\tEdgeStyle,\n\tDEFAULT_EDGE_STYLE,\n\tDEFAULT_NODE_STYLE,\n\tSVG_STYLES,\n\tSVG_PADDING,\n\tDEFAULT_GRID_SIZE,\n\tapplyStyle\n} from \"./constants\";\nimport {\n\tcalculateEdgeVertices,\n\tcalculateLabelPlacement,\n\tcreateEdgePath,\n\tEdgeLabelPlacement\n} from \"./edge-utils\";\nimport {\n\tbuildNodeContent,\n\tlayoutNodeContent,\n\tNodeContentLayout\n} from \"./node-content\";\nimport {ValidatedLayout} from \"./geometry\";\n\n\n// Point and BBox interfaces are now imported from constants\n\nexport interface Group extends BBox {\n\tid: string;\n\tname: string;\n\tnodes: (Node | Group)[];\n\tref?: SVGGElement;\n\tstyle: NodeStyle;\n\tlayoutBounds?: BBox;\n\tlayoutTitleBounds?: BBox;\n}\n\nexport interface NodeLink {\n\thref: string;\n\texportHref: string;\n}\n\nexport type LayoutDirection = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT';\n\nexport interface Node extends BBox {\n\tid: string;\n\ttitle: string;\n\tsub: string;\n\tdescription: string;\n\n\tref?: SVGGElement;\n\tselected?: boolean;\n\n\tintersect: (p: Point) => Point\n\n\tstyle: NodeStyle\n\tlink?: NodeLink\n\tcontentLayout: NodeContentLayout\n}\n\n// NodeStyle interface is now imported from constants\n\n// EdgeStyle interface is now imported from constants\n\n// Default styles are now imported from constants\nconst defaultEdgeStyle = DEFAULT_EDGE_STYLE;\nconst defaultNodeStyle = DEFAULT_NODE_STYLE;\n\n// Edge and EdgeVertex interfaces are now defined in edge-utils.ts\n// Using local interfaces for compatibility with existing code\nexport interface Edge {\n\tid: string;\n\tlabel: string;\n\tfrom: Node;\n\tto: Node;\n\tvertices?: EdgeVertex[];\n\tref?: SVGGElement;\n\tstyle: EdgeStyle;\n\tinitVertex: (p: Point) => EdgeVertex;\n\tuserDeletedVertices?: boolean; // Track if user explicitly deleted vertices\n\tlabelVertex?: EdgeVertex; // ELK-calculated label position (separate from routing vertices)\n\tlabelBounds?: BBox;\n\tlayoutSections?: Point[][];\n\tlayoutLabelBounds?: BBox;\n}\n\ninterface EdgeVertex extends Point {\n\tid: string\n\tselected?: boolean\n\tedge: Edge\n\tref?: SVGElement\n\tlabel?: boolean\n\tauto?: boolean\n}\n\ninterface SavedEdgePoint extends Point {\n\tlabel?: boolean;\n\tauto?: boolean;\n}\n\ninterface Layout {\n\t[k: string]: Point | SavedEdgePoint[] | boolean\n}\n\nexport class GraphData {\n\tid: string;\n\tname: string;\n\tnodesMap: Map;\n\tedges: Edge[];\n\tedgeVertices: Map\n\tgroupsMap: Map;\n\tmetadata: any;\n\tlayoutDirection?: LayoutDirection;\n\tvalidatedLayout?: ValidatedLayout;\n\tcolorToVarMap: Map = new Map(); // For CSS custom properties theming\n\tprivate _undo: Undo;\n\tprivate _gridVisible: boolean = false;\n\tprivate _snapToGrid: boolean = true;\n\tprivate _gridSize: number = 25;\n\tprivate _skipAutoFit: boolean = false;\n\n\tconstructor(id?: string, name?: string) {\n\t\tthis.id = id;\n\t\tthis.name = name;\n\n\t\tthis.edges = [];\n\t\tthis.edgeVertices = new Map;\n\t\tthis.nodesMap = new Map;\n\t\tthis.groupsMap = new Map;\n\n\t\tthis._undo = new Undo(\n\t\t\tthis.id,\n\t\t\t() => this.exportLayout(true),\n\t\t\t(lo) => this.importLayout(lo, true)\n\t\t)\n\n\t\t// @ts-ignore\n\t\twindow.graph = this\n\t}\n\n\t// after the graph model is build using addNode, addEdge etc, initialize\n\tinit(layout?: Layout) {\n\t\tlayout && this.importLayout(layout)\n\t\tthis._undo = new Undo(\n\t\t\tthis.id,\n\t\t\t() => this.exportLayout(true),\n\t\t\t(lo) => this.importLayout(lo, true)\n\t\t)\n\t\tif (this._undo.length()) {\n\t\t\tthis.importLayout(this._undo.currentState())\n\t\t}\n\t\t\n\t\t// Save initial state so first action is undoable\n\t\tthis._undo.beforeChange()\n\t\tthis._undo.change()\n\t}\n\n\taddNode(id: string, label: string, sub: string, description: string, style: NodeStyle, link?: NodeLink) {\n\t\tif (this.nodesMap.has(id)) throw Error('duplicate node: ' + id)\n\t\tconst nodeStyle = {...defaultNodeStyle, ...style};\n\t\tconst shape = (nodeStyle.shape || 'Box').toLowerCase();\n\t\tconst minimumWidth = 280;\n\t\tconst minimumHeight = shape === 'person' ? 240 : 180;\n\t\tconst width = Math.max(minimumWidth, nodeStyle.width || 0);\n\t\tconst fontSize = nodeStyle.fontSize || 22;\n\t\tconst contentLayout = layoutNodeContent(label, sub, description, width, fontSize);\n\t\tlet height = Math.max(minimumHeight, nodeStyle.height || 0, contentLayout.minimumHeight);\n\n\t\t// Some shapes shift labels down to reserve visual space for an icon,\n\t\t// header, or curved edge. Grow until the shifted content also fits.\n\t\tfor (let i = 0; i < 20; i++) {\n\t\t\tconst requiredHeight = contentLayout.minimumHeight +\n\t\t\t\tMath.abs(shapeLabelOffsetY(shape, width, height));\n\t\t\tif (requiredHeight <= height + 0.1) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\theight = requiredHeight;\n\t\t}\n\n\t\tconst n: Node = {\n\t\t\tid, title: label, sub, description, style: nodeStyle,\n\t\t\tx: 0, y: 0, width, height, intersect: null, link, contentLayout\n\t\t}\n\t\tthis.nodesMap.set(n.id, n)\n\t}\n\n\tnodes() {\n\t\treturn Array.from(this.nodesMap.values())\n\t}\n\n\taddEdge(id: string, fromNode: string, toNode: string, label: string, vertices: Point[], style: EdgeStyle) {\n\t\tvertices && vertices.forEach((p, i) => {\n\t\t\tconst v = p as EdgeVertex\n\t\t\tv.id = `v-${id}-${i}`\n\t\t\tthis.edgeVertices.set(v.id, v)\n\t\t})\n\t\t// Deterministic vertex IDs.\n\t\t//\n\t\t// `mdl svg` renders diagrams headlessly and saves the resulting SVG. If we\n\t\t// generate random vertex IDs, the SVG changes on every run even when the\n\t\t// underlying model and layout are unchanged. Use a stable hash instead.\n\t\tconst fnv1a36 = (input: string) => {\n\t\t\tlet h = 0x811c9dc5\n\t\t\tfor (let i = 0; i < input.length; i++) {\n\t\t\t\th ^= input.charCodeAt(i)\n\t\t\t\th = Math.imul(h, 0x01000193)\n\t\t\t}\n\t\t\treturn (h >>> 0).toString(36)\n\t\t}\n\t\tconst stableVertexID = (edgeID: string, p: Point) => {\n\t\t\tconst x = (p as any).x\n\t\t\tconst y = (p as any).y\n\t\t\treturn `v-${edgeID}-a-${fnv1a36(`${edgeID}:${x}:${y}`)}`\n\t\t}\n\t\tconst initVertex = (p: Point) => {\n\t\t\tconst v = p as EdgeVertex\n\t\t\tif (!v.id) {\n\t\t\t\tv.id = stableVertexID(edge.id, p)\n\t\t\t\tthis.edgeVertices.set(v.id, v)\n\t\t\t}\n\t\t\tv.edge = edge\n\t\t\treturn p as EdgeVertex\n\t\t}\n\t\tconst edge = {\n\t\t\tid,\n\t\t\tfrom: this.nodesMap.get(fromNode),\n\t\t\tto: this.nodesMap.get(toNode),\n\t\t\tlabel,\n\t\t\tvertices: null as EdgeVertex[],\n\t\t\tstyle: {...defaultEdgeStyle, ...style},\n\t\t\tinitVertex,\n\t\t\tuserDeletedVertices: false\n\t\t}\n\t\tthis.edges.push(edge)\n\t\tif (vertices) {\n\t\t\tedge.vertices = vertices.map(p => edge.initVertex(p))\n\t\t}\n\t}\n\n\taddGroup(id: string, name: string, nodesOrGroups: string[], style: NodeStyle) {\n\t\tif (this.groupsMap.has(id)) {\n\t\t\tconsole.error(`Group exists: ${id} ${name}`)\n\t\t\treturn\n\t\t}\n\t\tconst group: Group = {\n\t\t\tid, name, x: null, y: null, width: null, height: null,\n\t\t\tnodes: nodesOrGroups.map(k => {\n\t\t\t\tconst n = this.nodesMap.get(k) || this.groupsMap.get(k)\n\t\t\t\tif (!n) console.error(`Node or group ${k} not found for group ${id} \"${name}\"`)\n\t\t\t\treturn n\n\t\t\t}).filter(Boolean),\n\t\t\tstyle\n\t\t}\n\t\tthis.groupsMap.set(id, group)\n\t}\n\n\t// private rebuildNode(node: Node) {\n\t// \tconst p = node.ref.parentElement;\n\t// \tp.removeChild(node.ref)\n\t// \tnode.ref = buildNode(node, this)\n\t// \tp.appendChild(node.ref)\n\t// \tthis.redrawEdges(node)\n\t// \tthis.redrawGroups(node)\n\t// }\n\n\tsetNodeSelected(node: Node, selected: boolean) {\n\t\tnode.selected = selected\n\t\tselected ?\n\t\t\tnode.ref.classList.add('selected') :\n\t\t\tnode.ref.classList.remove('selected')\n\t\tthis.updateEdgesSel()\n\t}\n\n\tprivate updateEdgesSel() {\n\t\tthis.edges.forEach(e => {\n\t\t\tif (e.to.selected || e.from.selected) {\n\t\t\t\te.ref.classList.add('selected')\n\t\t\t} else {\n\t\t\t\te.ref.classList.remove('selected')\n\t\t\t}\n\t\t})\n\t}\n\n\t/** clearResolvedLayout returns drawing to manual mode after any user edit. */\n\tprivate clearResolvedLayout() {\n\t\tthis.validatedLayout = undefined\n\t\tthis.edges.forEach(edge => {\n\t\t\tedge.layoutSections = undefined\n\t\t\tedge.layoutLabelBounds = undefined\n\t\t})\n\t\tthis.groupsMap.forEach(group => {\n\t\t\tgroup.layoutBounds = undefined\n\t\t\tgroup.layoutTitleBounds = undefined\n\t\t})\n\t}\n\n\tmoveNode(n: Node, x: number, y: number, disableSnap: boolean = false, skipUndo: boolean = false) {\n\t\tif (!n) return\n\t\t\n\t\t// Apply snap-to-grid if enabled and not explicitly disabled\n\t\tif (this._snapToGrid && !disableSnap) {\n\t\t\tconst snapped = this.snapToGrid(x, y);\n\t\t\tx = snapped.x;\n\t\t\ty = snapped.y;\n\t\t}\n\t\t\n\t\tif (n.x == x && n.y == y) return\n\t\t\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.beforeChange()\n\t\t}\n\t\tthis.clearResolvedLayout()\n\t\tn.x = x;\n\t\tn.y = y;\n\t\tsetPosition(n.ref, x, y)\n\t\tthis.redrawEdges(n);\n\t\tthis.redrawGroups(n)\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.change()\n\t\t}\n\t}\n\n\tmoveEdgeVertex(v: EdgeVertex, x: number, y: number, disableSnap: boolean = false, skipUndo: boolean = false) {\n\t\t\n\t\tif (this._snapToGrid && !disableSnap) {\n\t\t\tconst snapped = this.snapToGrid(x, y);\n\t\t\tx = snapped.x;\n\t\t\ty = snapped.y;\n\t\t}\n\t\t// Use exact coordinates (no rounding needed with modern grid system)\n\t\t\n\t\tif (v.x == x && v.y == y) return\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.beforeChange()\n\t\t}\n\t\tthis.clearResolvedLayout()\n\t\tv.x = x;\n\t\tv.y = y;\n\t\tthis.redrawEdge(v.edge)\n\t\tif (!skipUndo) {\n\t\t\tthis._undo.change()\n\t\t}\n\t}\n\n\tmoveSelected(dx: number, dy: number, disableSnap: boolean = false) {\n\t\tthis.nodes().forEach(n => n.selected && this.moveNode(n, n.x + dx, n.y + dy, disableSnap, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, v.x + dx, v.y + dy, disableSnap, false))\n\t}\n\n\tinsertEdgeVertex(edge: Edge, p: Point, pos: number, isLabel: boolean) {\n\t\tthis._undo.beforeChange()\n\t\tthis.clearResolvedLayout()\n\t\tconst v = edge.initVertex(p)\n\t\tv.selected = true\n\t\tif (isLabel) { // when shift down, make it label position\n\t\t\tedge.vertices.forEach(v => v.label = false)\n\t\t\tv.label = true\n\t\t}\n\t\tedge.vertices.splice(pos - 1, 0, v)\n\t\tthis.redrawEdge(edge)\n\t\tthis._undo.change()\n\t}\n\n\tdeleteEdgeVertex(v: EdgeVertex) {\n\t\tthis._undo.beforeChange()\n\t\tthis.clearResolvedLayout()\n\t\t\n\t\tconst index = v.edge.vertices.indexOf(v)\n\t\tif (index >= 0) {\n\t\t\tv.edge.vertices.splice(index, 1)\n\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\n\t\t\t// Mark that user explicitly deleted vertices from this edge\n\t\t\tv.edge.userDeletedVertices = true\n\t\t}\n\t\t\n\t\tthis.redrawEdge(v.edge)\n\t\tthis._undo.change()\n\t}\n\n\tchanged() {\n\t\treturn this._undo.changed()\n\t}\n\n\tundo() {\n\t\tthis._undo.undo()\n\t}\n\n\tredo() {\n\t\tthis._undo.redo()\n\t}\n\n\t// moves the entire graph to be aligned top-left of the drawing area\n\t// used to bring back to visible the nodes that end up at negative coordinates\n\talignTopLeft() {\n\t\tconst contentBounds = this.calculateContentBounds()\n\t\tconst padding = 100 // Reasonable padding for viewport\n\t\t\n\t\tconst offsetX = -contentBounds.x + padding\n\t\tconst offsetY = -contentBounds.y + padding\n\t\t\n\t\t// Set flag to prevent React useEffect from calling fitToView during this operation\n\t\tthis._skipAutoFit = true\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\tthis.nodesMap.forEach(node => {\n\t\t\tthis.moveNode(node, node.x + offsetX, node.y + offsetY, true, true) // Disable snap and undo during reset\n\t\t})\n\t\t\n\t\tthis.edgeVertices.forEach(vertex => {\n\t\t\tthis.moveEdgeVertex(vertex, vertex.x + offsetX, vertex.y + offsetY, true, true) // Disable snap and undo during reset\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t\t\n\t\t// DON'T clear view state here - let resetPanTransform handle it to avoid React useEffect recursion\n\t}\n\t\n\t// Reset pan transform to (0,0) while preserving zoom\n\tresetPanTransform() {\n\t\tconst currentZoom = getZoom()\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.setAttribute('transform', `scale(${currentZoom}) translate(0, 0)`)\n\t\t\tupdatePanningOptimized(this)\n\t\t}\n\t\t\n\t\t// Clear view state so this reset is not overridden\n\t\tclearViewState(this.id)\n\t\t\n\t\t// Reset the skip auto fit flag after reset is complete\n\t\tthis._skipAutoFit = false\n\t}\n\t\n\t// Check if auto-fit should be skipped (used by React useEffect)\n\tshouldSkipAutoFit(): boolean {\n\t\treturn this._skipAutoFit\n\t}\n\n\t// Reset view to default state: 100% zoom, centered at origin\n\tresetView() {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\t// Reset to 100% zoom, centered at origin\n\t\t\tzoomGroup.setAttribute('transform', 'scale(1) translate(0, 0)')\n\t\t\tupdatePanning()\n\t\t}\n\t\t\n\t\t// Clear any saved view state so this reset position is not overridden\n\t\tclearViewState(this.id)\n\t}\n\n\t//redraw connected edges\n\tprivate redrawEdges(n: Node) {\n\t\tthis.edges.forEach(e => (n == e.from || n == e.to) && this.redrawEdge(e))\n\t\tthis.updateEdgesSel()\n\t}\n\n\tredrawEdge(e: Edge) {\n\t\tconst p = e.ref.parentElement;\n\t\tp.removeChild(e.ref)\n\t\te.ref = buildEdge(e)\n\t\tp.append(e.ref)\n\t}\n\n\tprivate redrawGroups(node: Node) {\n\t\tthis.groupsMap.forEach(group => {\n\t\t\t//if (group.nodes.indexOf(node) == -1) return\n\t\t\tconst p = group.ref.parentElement\n\t\t\tp.removeChild(group.ref)\n\t\t\tbuildGroup(group)\n\t\t\tp.append(group.ref)\n\t\t})\n\t}\n\n\texportSVG() {\n\t\tif (!this.validatedLayout) {\n\t\t\tthis.validatedLayout = validateCurrentLayout(this)\n\t\t}\n\t\t// Get the original SVG\n\t\tconst originalSvg: SVGSVGElement = document.querySelector('svg#graph')\n\t\tconst elastic = originalSvg.querySelector('rect.elastic')\n\t\t\n\t\t// Clone the SVG for export (completely separate from the live one)\n\t\tconst exportSvg = originalSvg.cloneNode(true) as SVGSVGElement\n\n\t\t// Standalone SVGs navigate to sibling view files instead of editor routes.\n\t\texportSvg.querySelectorAll('a.nodeLink[data-export-href]').forEach(link => {\n\t\t\tlink.setAttribute('href', link.getAttribute('data-export-href') || '')\n\t\t\tlink.removeAttribute('data-export-href')\n\t\t})\n\t\t\n\t\t// Remove elastic element from export\n\t\tconst exportElastic = exportSvg.querySelector('rect.elastic')\n\t\tif (exportElastic) {\n\t\t\texportElastic.remove()\n\t\t}\n\t\t\n\t\t// Export the exact bounds that passed geometry validation. Recomputing\n\t\t// them here can omit routed relationship labels from the SVG canvas.\n\t\tconst contentBounds = this.validatedLayout.bounds\n\t\t\n\t\t// Add padding around content\n\t\tconst padding = 50\n\t\t\n\t\t// Calculate final export dimensions (always positive)\n\t\tconst exportWidth = contentBounds.width + (padding * 2)\n\t\tconst exportHeight = contentBounds.height + (padding * 2)\n\t\t\n\t\t// Calculate offset to move content to start at (padding, padding) within the export area\n\t\tconst offsetX = -contentBounds.x + padding\n\t\tconst offsetY = -contentBounds.y + padding\n\t\t\n\t\t// Apply export positioning to the cloned SVG elements\n\t\tconst exportZoomGroup = exportSvg.querySelector('g.zoom') as SVGGElement\n\t\tif (exportZoomGroup) {\n\t\t\t// Reset zoom to 1 and apply offset transform to position content properly\n\t\t\texportZoomGroup.setAttribute('transform', `scale(1) translate(${offsetX}, ${offsetY})`)\n\t\t}\n\t\t\n\t\t// Set proper viewBox and dimensions for export - viewBox always starts at (0,0)\n\t\texportSvg.setAttribute('viewBox', `0 0 ${exportWidth} ${exportHeight}`)\n\t\texportSvg.setAttribute('width', String(exportWidth))\n\t\texportSvg.setAttribute('height', String(exportHeight))\n\t\t\n\t\t// Add required SVG namespace for browser compatibility\n\t\texportSvg.setAttribute('xmlns', 'http://www.w3.org/2000/svg')\n\t\t\n\t\t// Convert inline styles to CSS custom properties for theming support\n\t\tthis.convertStylesToCustomProperties(exportSvg)\n\t\t\n\t\t// Inject metadata with current layout\n\t\tconst script = document.createElement('script')\n\t\tscript.setAttribute('type', 'application/json')\n\t\tthis.metadata.layout = this.exportLayout()\n\t\tscript.append('')\n\t\texportSvg.insertBefore(script, exportSvg.firstChild)\n\t\t\n\t\t// Get the export SVG as string\n\t\tconst src = exportSvg.outerHTML\n\t\t\n\t\t// No restoration needed since we never touched the original SVG!\n\t\treturn src\n\t}\n\n\t// Convert inline fill/stroke attributes to CSS custom properties with fallbacks.\n\t// This enables theming: container pages can override colors via CSS variables.\n\tprivate convertStylesToCustomProperties(svg: SVGSVGElement) {\n\t\tif (this.colorToVarMap.size === 0) return\n\n\t\t// Process all elements with fill attribute\n\t\tsvg.querySelectorAll('[fill]').forEach(el => {\n\t\t\tconst fill = el.getAttribute('fill')\n\t\t\tif (fill && this.colorToVarMap.has(fill)) {\n\t\t\t\tel.setAttribute('fill', `var(${this.colorToVarMap.get(fill)}, ${fill})`)\n\t\t\t}\n\t\t})\n\n\t\t// Process all elements with stroke attribute\n\t\tsvg.querySelectorAll('[stroke]').forEach(el => {\n\t\t\tconst stroke = el.getAttribute('stroke')\n\t\t\tif (stroke && this.colorToVarMap.has(stroke)) {\n\t\t\t\tel.setAttribute('stroke', `var(${this.colorToVarMap.get(stroke)}, ${stroke})`)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Calculate the actual bounds of all content including nodes, edges, and groups\n\tcalculateContentBounds(): BBox {\n\t\tlet minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n\t\t\n\t\t// Process nodes (including their actual dimensions)\n\t\tthis.nodes().forEach(node => {\n\t\t\tconst left = node.x - node.width / 2\n\t\t\tconst right = node.x + node.width / 2\n\t\t\tconst top = node.y - node.height / 2\n\t\t\tconst bottom = node.y + node.height / 2\n\t\t\t\n\t\t\tminX = Math.min(minX, left)\n\t\t\tmaxX = Math.max(maxX, right)\n\t\t\tminY = Math.min(minY, top)\n\t\t\tmaxY = Math.max(maxY, bottom)\n\t\t})\n\t\t\n\t\t// Process edge vertices (much faster than complex label calculations)\n\t\tthis.edgeVertices.forEach(vertex => {\n\t\t\tminX = Math.min(minX, vertex.x - 5)\n\t\t\tmaxX = Math.max(maxX, vertex.x + 5)\n\t\t\tminY = Math.min(minY, vertex.y - 5)\n\t\t\tmaxY = Math.max(maxY, vertex.y + 5)\n\t\t})\n\t\t\n\t\t// Process groups\n\t\tthis.groupsMap.forEach(group => {\n\t\t\tconst left = group.x - group.width / 2\n\t\t\tconst right = group.x + group.width / 2\n\t\t\tconst top = group.y - group.height / 2\n\t\t\tconst bottom = group.y + group.height / 2\n\t\t\t\n\t\t\tminX = Math.min(minX, left)\n\t\t\tmaxX = Math.max(maxX, right)\n\t\t\tminY = Math.min(minY, top)\n\t\t\tmaxY = Math.max(maxY, bottom)\n\t\t})\n\t\t\n\t\t// Process edges (simplified - just endpoints and vertices, skip complex label calculations)\n\t\tthis.edges.forEach(edge => {\n\t\t\t// Edge endpoints\n\t\t\tminX = Math.min(minX, edge.from.x - 10, edge.to.x - 10)\n\t\t\tmaxX = Math.max(maxX, edge.from.x + 10, edge.to.x + 10)\n\t\t\tminY = Math.min(minY, edge.from.y - 10, edge.to.y - 10)\n\t\t\tmaxY = Math.max(maxY, edge.from.y + 10, edge.to.y + 10)\n\t\t\t\n\t\t\t// Edge vertices (if any)\n\t\t\tif (edge.vertices) {\n\t\t\t\tedge.vertices.forEach(vertex => {\n\t\t\t\t\tminX = Math.min(minX, vertex.x - 10)\n\t\t\t\t\tmaxX = Math.max(maxX, vertex.x + 10)\n\t\t\t\t\tminY = Math.min(minY, vertex.y - 10)\n\t\t\t\t\tmaxY = Math.max(maxY, vertex.y + 10)\n\t\t\t\t})\n\t\t\t}\n\t\t\t\n\t\t\t// Simplified label bounds (avoid expensive path calculations)\n\t\t\tif (edge.label && edge.label.trim()) {\n\t\t\t\t// Just use approximate center between from and to nodes\n\t\t\t\tconst centerX = (edge.from.x + edge.to.x) / 2\n\t\t\t\tconst centerY = (edge.from.y + edge.to.y) / 2\n\t\t\t\tconst approxLabelSize = edge.label.length * 10 + 50 // Rough estimate\n\t\t\t\t\n\t\t\t\tminX = Math.min(minX, centerX - approxLabelSize)\n\t\t\t\tmaxX = Math.max(maxX, centerX + approxLabelSize)\n\t\t\t\tminY = Math.min(minY, centerY - 25)\n\t\t\t\tmaxY = Math.max(maxY, centerY + 25)\n\t\t\t}\n\t\t})\n\t\t\n\t\t// Handle empty graph\n\t\tif (minX === Infinity) {\n\t\t\treturn { x: 0, y: 0, width: 100, height: 100 }\n\t\t}\n\t\t\n\t\treturn {\n\t\t\tx: minX,\n\t\t\ty: minY,\n\t\t\twidth: maxX - minX,\n\t\t\theight: maxY - minY\n\t\t}\n\t}\n\n\n\n\t/**\n\t * @param full when true, the edges without vertices are saved too, used for undo buffer\n\t * for saving, full is false\n\t */\n\texportLayout(full = false) {\n\t\tconst ret: Layout = {}\n\t\tthis.nodes().forEach(n => ret[n.id] = {x: n.x, y: n.y})\n\t\tthis.edges.forEach(e => {\n\t\t\tif (!e.vertices) return\n\t\t\t// Save all vertices (both user and auto-generated), preserving their properties\n\t\t\tconst lst = e.vertices.map(v => ({\n\t\t\t\tx: v.x, \n\t\t\t\ty: v.y, \n\t\t\t\tlabel: v.label,\n\t\t\t\tauto: v.auto // Preserve auto flag so we know which are ELK-generated\n\t\t\t}));\n\t\t\tif (lst.length || full) {\n\t\t\t\tret[`e-${e.id}`] = lst\n\t\t\t}\n\t\t\t// Also save the userDeletedVertices flag as metadata\n\t\t\tif (e.userDeletedVertices) {\n\t\t\t\tret[`e-${e.id}-deleted`] = true\n\t\t\t}\n\t\t})\n\t\treturn ret\n\t}\n\n\tsetSaved() {\n\t\tthis._undo.setSaved()\n\t}\n\n\timportLayout(layout: Layout, rerender = false) {\n\t\tthis.clearResolvedLayout()\n\t\tfor (const [k, v] of Object.entries(layout)) {\n\t\t\tconst n = this.nodesMap.get(k)\n\t\t\tif (n) {\n\t\t\t\tconst point = requiredSavedPoint(k, v)\n\t\t\t\tn.x = point.x\n\t\t\t\tn.y = point.y\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (k.endsWith('-deleted')) {\n\t\t\t\tconst edgeId = k.slice(2, -8); // Remove 'e-' prefix and '-deleted' suffix\n\t\t\t\tconst edge = this.edges.find(candidate => candidate.id === edgeId)\n\t\t\t\tif (!edge || v !== true) {\n\t\t\t\t\tthrow new Error(`invalid deleted-edge layout entry ${k}`)\n\t\t\t\t}\n\t\t\t\tedge.userDeletedVertices = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (k.startsWith('e-')) {\n\t\t\t\tconst edge = this.edges.find(candidate => candidate.id === k.slice(2))\n\t\t\t\tif (!edge || !Array.isArray(v)) {\n\t\t\t\t\tthrow new Error(`invalid edge layout entry ${k}`)\n\t\t\t\t}\n\t\t\t\tfor (const vertex of edge.vertices ?? []) {\n\t\t\t\t\tthis.edgeVertices.delete(vertex.id)\n\t\t\t\t}\n\t\t\t\tedge.vertices = v.map((saved, index) => {\n\t\t\t\t\tconst point = requiredSavedPoint(`${k}[${index}]`, saved)\n\t\t\t\t\tconst vertex = edge.initVertex(point)\n\t\t\t\t\tvertex.label = saved.label\n\t\t\t\t\tvertex.auto = saved.auto\n\t\t\t\t\treturn vertex\n\t\t\t\t})\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthrow new Error(`layout contains unknown element ${k}`)\n\t\t}\n\t\tfor (const node of this.nodes()) {\n\t\t\tif (!(node.id in layout)) {\n\t\t\t\tthrow new Error(`layout is missing node ${node.id}`)\n\t\t\t}\n\t\t}\n\t\tif (rerender) {\n\t\t\tthis.nodes().forEach(n => setPosition(n.ref, n.x, n.y))\n\t\t\tthis.edges.forEach(e => this.redrawEdge(e))\n\t\t\tthis.updateEdgesSel()\n\t\t\tthis.redrawGroups(null)\n\t\t}\n\t}\n\n\tasync autoLayout(options?: LayoutOptions) {\n\t\tconst auto = await autoLayout(this, options)\n\t\tthis._undo.beforeChange()\n\n\t\tfor (const placedNode of auto.nodes) {\n\t\t\tconst node = this.nodesMap.get(placedNode.id)\n\t\t\tif (!node) {\n\t\t\t\tthrow new Error(`layout contains unknown node ${placedNode.id}`)\n\t\t\t}\n\t\t\tnode.x = placedNode.x\n\t\t\tnode.y = placedNode.y\n\t\t\tsetPosition(node.ref, node.x, node.y)\n\t\t}\n\n\t\tfor (const placedGroup of auto.groups) {\n\t\t\tconst group = this.groupsMap.get(placedGroup.id)\n\t\t\tif (!group) {\n\t\t\t\tthrow new Error(`layout contains unknown group ${placedGroup.id}`)\n\t\t\t}\n\t\t\tgroup.layoutBounds = placedGroup.bounds\n\t\t\tgroup.layoutTitleBounds = placedGroup.titleBounds\n\t\t\tgroup.x = placedGroup.bounds.x + placedGroup.bounds.width / 2\n\t\t\tgroup.y = placedGroup.bounds.y + placedGroup.bounds.height / 2\n\t\t\tgroup.width = placedGroup.bounds.width\n\t\t\tgroup.height = placedGroup.bounds.height\n\t\t}\n\n\t\tfor (const placedEdge of auto.edges) {\n\t\t\tconst edge = this.edges.find(candidate => candidate.id === placedEdge.id)\n\t\t\tif (!edge) {\n\t\t\t\tthrow new Error(`layout contains unknown edge ${placedEdge.id}`)\n\t\t\t}\n\t\t\tfor (const vertex of edge.vertices ?? []) {\n\t\t\t\tthis.edgeVertices.delete(vertex.id)\n\t\t\t}\n\t\t\tedge.layoutSections = placedEdge.sections\n\t\t\tedge.layoutLabelBounds = placedEdge.labelBounds\n\t\t\tedge.vertices = interiorRoutePoints(placedEdge.sections).map(point => {\n\t\t\t\tconst vertex = edge.initVertex(point)\n\t\t\t\tvertex.auto = true\n\t\t\t\treturn vertex\n\t\t\t})\n\t\t\tedge.userDeletedVertices = false\n\t\t}\n\n\t\tthis.validatedLayout = auto.validated\n\t\tthis.edges.forEach(edge => this.redrawEdge(edge))\n\t\tthis.redrawGroups(null)\n\t\tthis.fitToView()\n\t\tthis._undo.change()\n\t}\n\n\talignSelectionV() {\n\t\tconst lst: Point[] = this.nodes().filter(n => n.selected)\n\t\tlst.push(...Array.from(this.edgeVertices.values()).filter(v => v.selected))\n\t\tlet minY = Math.min(...lst.map(p => p.y))\n\t\tthis.nodesMap.forEach(n => n.selected && this.moveNode(n, n.x, minY, false, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, v.x, minY, false, false))\n\t}\n\n\talignSelectionH() {\n\t\tconst lst: Point[] = this.nodes().filter(n => n.selected)\n\t\tlst.push(...Array.from(this.edgeVertices.values()).filter(v => v.selected))\n\t\tlet minX = Math.min(...lst.map(p => p.x))\n\t\tthis.nodesMap.forEach(n => n.selected && this.moveNode(n, minX, n.y, false, false))\n\t\tthis.edgeVertices.forEach(v => v.selected && this.moveEdgeVertex(v, minX, v.y, false, false))\n\t}\n\n\tdistributeSelectionH() {\n\t\tconst selectedNodes = this.nodes().filter(n => n.selected)\n\t\tconst selectedVertices = Array.from(this.edgeVertices.values()).filter(v => v.selected)\n\t\t\n\t\tif (selectedNodes.length + selectedVertices.length < 3) return // Need at least 3 elements to distribute\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\t// Combine and sort by X coordinate\n\t\tconst allElements = [...selectedNodes, ...selectedVertices]\n\t\tallElements.sort((a, b) => a.x - b.x)\n\t\t\n\t\tconst minX = allElements[0].x\n\t\tconst maxX = allElements[allElements.length - 1].x\n\t\tconst spacing = (maxX - minX) / (allElements.length - 1)\n\t\t\n\t\t// Distribute elements evenly between leftmost and rightmost\n\t\tallElements.forEach((element, index) => {\n\t\t\tconst newX = minX + (index * spacing)\n\t\t\tif ('title' in element) {\n\t\t\t\t// It's a Node\n\t\t\t\tthis.moveNode(element as Node, newX, element.y, false, true)\n\t\t\t} else {\n\t\t\t\t// It's an EdgeVertex\n\t\t\t\tthis.moveEdgeVertex(element as EdgeVertex, newX, element.y, false, true)\n\t\t\t}\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t}\n\n\tdistributeSelectionV() {\n\t\tconst selectedNodes = this.nodes().filter(n => n.selected)\n\t\tconst selectedVertices = Array.from(this.edgeVertices.values()).filter(v => v.selected)\n\t\t\n\t\tif (selectedNodes.length + selectedVertices.length < 3) return // Need at least 3 elements to distribute\n\t\t\n\t\tthis._undo.beforeChange()\n\t\t\n\t\t// Combine and sort by Y coordinate\n\t\tconst allElements = [...selectedNodes, ...selectedVertices]\n\t\tallElements.sort((a, b) => a.y - b.y)\n\t\t\n\t\tconst minY = allElements[0].y\n\t\tconst maxY = allElements[allElements.length - 1].y\n\t\tconst spacing = (maxY - minY) / (allElements.length - 1)\n\t\t\n\t\t// Distribute elements evenly between topmost and bottommost\n\t\tallElements.forEach((element, index) => {\n\t\t\tconst newY = minY + (index * spacing)\n\t\t\tif ('title' in element) {\n\t\t\t\t// It's a Node\n\t\t\t\tthis.moveNode(element as Node, element.x, newY, false, true)\n\t\t\t} else {\n\t\t\t\t// It's an EdgeVertex\n\t\t\t\tthis.moveEdgeVertex(element as EdgeVertex, element.x, newY, false, true)\n\t\t\t}\n\t\t})\n\t\t\n\t\tthis._undo.change()\n\t}\n\n\t// Set edge selection state\n\tsetEdgeSelected(edge: Edge, selected: boolean) {\n\t\t// Mark the edge as selected by selecting its connected nodes\n\t\tif (selected) {\n\t\t\tthis.setNodeSelected(edge.from, true)\n\t\t\tthis.setNodeSelected(edge.to, true)\n\t\t}\n\t\t// Update visual selection\n\t\tthis.updateEdgesSel()\n\t}\n\n\t// Fit the entire graph to the current viewport\n\tfitToView() {\n\t\tconst contentBounds = this.calculateContentBounds()\n\t\t\n\t\t// Handle edge case where there's no content\n\t\tif (contentBounds.width === 0 || contentBounds.height === 0) {\n\t\t\treturn\n\t\t}\n\t\t\n\t\t// Get viewport dimensions\n\t\tconst viewportWidth = svg.parentElement?.clientWidth || 800\n\t\tconst viewportHeight = svg.parentElement?.clientHeight || 600\n\t\t\n\t\t// Add padding around content\n\t\tconst padding = 40\n\t\t\n\t\t// Calculate zoom to fit content with padding\n\t\tconst zoomX = (viewportWidth - padding * 2) / contentBounds.width\n\t\tconst zoomY = (viewportHeight - padding * 2) / contentBounds.height\n\t\tconst optimalZoom = Math.min(zoomX, zoomY)\n\t\t\n\t\t// Clamp zoom between reasonable bounds\n\t\tconst finalZoom = Math.max(Math.min(optimalZoom, 2), 0.1)\n\t\t\n\t\t// Calculate content center in drawing coordinates\n\t\tconst contentCenterX = contentBounds.x + contentBounds.width / 2\n\t\tconst contentCenterY = contentBounds.y + contentBounds.height / 2\n\t\t\n\t\t// Calculate viewport center in screen coordinates\n\t\tconst viewportCenterX = viewportWidth / 2\n\t\tconst viewportCenterY = viewportHeight / 2\n\t\t\n\t\t// Calculate translation needed to center content in viewport\n\t\t// With translate(x,y) scale(zoom), translation is in screen coordinates\n\t\tconst translateX = viewportCenterX - (contentCenterX * finalZoom)\n\t\tconst translateY = viewportCenterY - (contentCenterY * finalZoom)\n\t\t\n\t\t// Apply zoom and translation transform\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.setAttribute('transform', `translate(${translateX}, ${translateY}) scale(${finalZoom})`)\n\t\t}\n\t\t\n\t\t// Update panning\n\t\tupdatePanning()\n\t\t\n\t\t// Save view state so this fit position is preserved after reload\n\t\tsaveViewState(this.id)\n\t}\n\n\t// Save current layout state for restoration\n\tprivate saveLayoutState(): Layout {\n\t\treturn this.exportLayout(true) // Include all vertices for complete state\n\t}\n\n\t// Restore layout state\n\tprivate restoreLayoutState(state: Layout) {\n\t\tthis.importLayout(state, true) // Rerender after restoring\n\t}\n\n\t// Grid functionality\n\tisGridVisible(): boolean {\n\t\treturn this._gridVisible;\n\t}\n\n\tisSnapToGrid(): boolean {\n\t\treturn this._snapToGrid;\n\t}\n\n\tgetGridSize(): number {\n\t\treturn this._gridSize;\n\t}\n\n\ttoggleGrid() {\n\t\tthis._gridVisible = !this._gridVisible;\n\t\tthis.updateGridDisplay();\n\t\t// Force toolbar update by dispatching a custom event\n\t\twindow.dispatchEvent(new CustomEvent('gridStateChanged'));\n\t}\n\n\ttoggleSnapToGrid() {\n\t\tthis._snapToGrid = !this._snapToGrid;\n\t\t// Force toolbar update by dispatching a custom event\n\t\twindow.dispatchEvent(new CustomEvent('gridStateChanged'));\n\t}\n\n\tsnapAllToGrid() {\n\t\tif (!this._snapToGrid) return;\n\t\t\n\t\tthis._undo.beforeChange();\n\t\tthis.nodes().forEach(node => {\n\t\t\tconst snappedX = Math.round(node.x / this._gridSize) * this._gridSize;\n\t\t\tconst snappedY = Math.round(node.y / this._gridSize) * this._gridSize;\n\t\t\tthis.moveNode(node, snappedX, snappedY, false, true);\n\t\t});\n\t\tthis._undo.change();\n\t}\n\n\t// Helper method to snap a point to grid\n\tprivate snapToGrid(x: number, y: number): { x: number, y: number } {\n\t\treturn {\n\t\t\tx: Math.round(x / this._gridSize) * this._gridSize,\n\t\t\ty: Math.round(y / this._gridSize) * this._gridSize\n\t\t};\n\t}\n\n\tupdateGridDisplay() {\n\t\tif (!svg) return;\n\n\t\t// Remove existing grid pattern and background\n\t\tconst existingGrid = svg.querySelector('#grid-pattern');\n\t\tif (existingGrid) {\n\t\t\texistingGrid.remove();\n\t\t}\n\n\t\tconst existingGridRect = svg.querySelector('#grid-background');\n\t\tif (existingGridRect) {\n\t\t\texistingGridRect.remove();\n\t\t}\n\n\t\tif (!this._gridVisible) return;\n\n\t\t// Create grid pattern in defs\n\t\tlet defs = svg.querySelector('defs');\n\t\tif (!defs) {\n\t\t\tdefs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');\n\t\t\tsvg.insertBefore(defs, svg.firstChild);\n\t\t}\n\n\t\tconst pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');\n\t\tpattern.id = 'grid-pattern';\n\t\tpattern.setAttribute('width', this._gridSize.toString());\n\t\tpattern.setAttribute('height', this._gridSize.toString());\n\t\tpattern.setAttribute('patternUnits', 'userSpaceOnUse');\n\n\t\tconst path = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n\t\tpath.setAttribute('d', `M ${this._gridSize} 0 L 0 0 0 ${this._gridSize}`);\n\t\tpath.setAttribute('fill', 'none');\n\t\tpath.setAttribute('stroke', '#d0d0d0');\n\t\tpath.setAttribute('stroke-width', '1');\n\t\tpath.setAttribute('opacity', '0.8');\n\n\t\tpattern.appendChild(path);\n\t\tdefs.appendChild(pattern);\n\n\t\t// Create grid background rectangle\n\t\tconst rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');\n\t\trect.id = 'grid-background';\n\t\trect.setAttribute('x', '-10000');\n\t\trect.setAttribute('y', '-10000');\n\t\trect.setAttribute('width', '20000');\n\t\trect.setAttribute('height', '20000');\n\t\trect.setAttribute('fill', 'url(#grid-pattern)');\n\t\trect.setAttribute('pointer-events', 'none');\n\n\t\t// Insert grid as first child of zoom group so it transforms with content\n\t\tconst zoomGroup = svg.querySelector('g.zoom');\n\t\tif (zoomGroup) {\n\t\t\tzoomGroup.insertBefore(rect, zoomGroup.firstChild);\n\t\t}\n\t}\n}\n\n/** interiorRoutePoints returns only the editable points between edge endpoints. */\nfunction interiorRoutePoints(sections: Point[][]): Point[] {\n\tconst route: Point[] = []\n\tfor (const section of sections) {\n\t\tfor (const point of section) {\n\t\t\tconst previous = route[route.length - 1]\n\t\t\tif (!previous || previous.x !== point.x || previous.y !== point.y) {\n\t\t\t\troute.push(point)\n\t\t\t}\n\t\t}\n\t}\n\treturn route.slice(1, -1)\n}\n\n/** requiredSavedPoint checks coordinates when reading saved editor layout data. */\nfunction requiredSavedPoint(\n\tid: string,\n\tvalue: Point | SavedEdgePoint | SavedEdgePoint[] | boolean,\n): SavedEdgePoint {\n\tif (\n\t\ttypeof value !== 'object' ||\n\t\tArray.isArray(value) ||\n\t\t!Number.isFinite(value.x) ||\n\t\t!Number.isFinite(value.y)\n\t) {\n\t\tthrow new Error(`layout entry ${id} must contain valid x and y coordinates`)\n\t}\n\treturn value\n}\n\nfunction escapeCdata(code: string) {\n\treturn code.replace(/]]>/g, ']]]>]> clickListener(e))\n\t// addCursorInteraction(svg) // Call will be updated in buildGraph\n}\nsvg.setAttribute('width', '100%')\nsvg.setAttribute('height', '100%')\n\nlet clickListener: (e: MouseEvent) => void\nlet dragging = false;\nlet selectListener: (n: Node) => void\n\n\nexport const buildGraph = (data: GraphData, onNodeSelect: (n: Node) => void, dragMode: 'pan' | 'select') => {\n\t// empty svg\n\tsvg.innerHTML = defs\n\tdocument.body.append(svg) // make sure svg element is connected, we will measure texts sizes\n\t// @ts-ignore\n\tsvg.__data = data\n\n\tselectListener = onNodeSelect\n\n\t//use event delegation\n\tclickListener = e => {\n\t\tif (dragging) {\n\t\t\treturn;\n\t\t}\n\t\t// const el = (e.target as any).closest('.node > .expand');\n\t}\n\n\t_buildGraph(data)\n\tconst elasticEl = create.rect(300, 300, 50, 50, 0, 'elastic')\n\tsvg.append(elasticEl)\n\n\t// Initialize grid display now that the zoom group exists\n\tdata.updateGridDisplay()\n\n\t// Call addCursorInteraction with dragMode\n\taddCursorInteraction(svg, dragMode)\n\n\treturn {\n\t\tsvg,\n\t\tsetZoom,\n\t}\n}\n\nexport const buildGraphView = (data: GraphData) => {\n\tsvg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n\tsvg.setAttribute('id', 'graph')\n\t_buildGraph(data)\n\treturn svg\n}\n\nconst _buildGraph = (data: GraphData) => {\n\t//toplevel groups\n\tconst zoomG = create.element('g', {}, 'zoom') as SVGGElement\n\tconst nodesG = create.element('g', {}, 'nodes') as SVGGElement\n\tconst edgesG = create.element('g', {}, 'edges') as SVGGElement\n\tconst groupsG = create.element('g', {}, 'groups') as SVGGElement\n\tzoomG.append(groupsG, edgesG, nodesG)\n\n\n\tdata.nodesMap.forEach((n) => {\n\t\tbuildNode(n, data)\n\t\tnodesG.append(n.ref)\n\t})\n\n\tdata.edges.forEach(edge => {\n\t\tedge.labelBounds = undefined\n\t})\n\tdata.edges.forEach(e => {\n\t\tbuildEdge(e)\n\t\tedgesG.append(e.ref)\n\t})\n\n\tdata.groupsMap.forEach((group) => {\n\t\tbuildGroup(group)\n\t\tgroupsG.append(group.ref)\n\t})\n\n\tsvg.append(zoomG)\n}\n\nfunction buildEdge(edge: Edge) {\n\tconst n1 = edge.from;\n\n\tconst g = create.element('g', {}, 'edge') as SVGGElement\n\tg.setAttribute('id', edge.id)\n\tg.setAttribute('data-from', edge.from.id)\n\tg.setAttribute('data-to', edge.to.id)\n\n\tconst position = (edge.style.position || 50) / 100\n\n\tconst vertices = edge.layoutSections\n\t\t? completeRoutePoints(edge.layoutSections)\n\t\t: calculateEdgeVertices(edge)\n\tconst {bg, txt} = edge.layoutLabelBounds\n\t\t? buildPlacedEdgeLabel(edge.layoutLabelBounds, edge)\n\t\t: buildEdgeLabel(calculateLabelPlacement(vertices, position, n1), edge)\n\tg.append(bg, txt)\n\n\tconst path = createEdgePath(vertices)\n\n\tconst p = create.path(path, {'marker-end': 'url(#arrow)'}, 'edge')\n\tp.setAttribute('fill', 'none')\n\tp.setAttribute('stroke', edge.style.color)\n\tp.setAttribute('stroke-width', String(edge.style.thickness))\n\tp.setAttribute('stroke-linecap', 'round')\n\tedge.style.dashed && p.setAttribute('stroke-dasharray', '8')\n\tg.append(p)\n\t\n\t// Debug visualization removed - arrow issue fixed\n\n\t// drag handlers\n\tfor (const p of edge.vertices ?? []) {\n\t\tconst v = p as EdgeVertex\n\t\tv.ref = create.element('circle', {id: v.id, cx: p.x, cy: p.y, r: 7, fill: 'none'}, 'v-dot')\n\t\tv.selected && v.ref.classList.add('selected')\n\t\tv.auto && v.ref.classList.add('auto')\n\t\tg.append(v.ref)\n\t}\n\n\tedge.ref = g\n\treturn g\n}\n\n/** completeRoutePoints joins connected layout sections without duplicate points. */\nfunction completeRoutePoints(sections: Point[][]): Point[] {\n\tconst route: Point[] = []\n\tfor (const section of sections) {\n\t\tfor (const point of section) {\n\t\t\tconst previous = route[route.length - 1]\n\t\t\tif (!previous || previous.x !== point.x || previous.y !== point.y) {\n\t\t\t\troute.push(point)\n\t\t\t}\n\t\t}\n\t}\n\treturn route\n}\n\n/** buildPlacedEdgeLabel draws one label in the box chosen by layout. */\nfunction buildPlacedEdgeLabel(bounds: BBox, edge: Edge) {\n\tconst centerX = bounds.x + bounds.width / 2\n\tconst centerY = bounds.y + bounds.height / 2\n\tconst {txt} = create.textArea(\n\t\tedge.label,\n\t\t200,\n\t\tedge.style.fontSize,\n\t\tfalse,\n\t\tcenterX,\n\t\tbounds.y,\n\t\t'middle',\n\t)\n\tapplyStyle(txt, styles.edgeText)\n\ttxt.setAttribute('stroke', 'none')\n\ttxt.setAttribute('font-size', String(edge.style.fontSize))\n\ttxt.setAttribute('fill', edge.style.color)\n\ttxt.setAttribute('data-field', 'label')\n\tconst bg = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\tapplyStyle(bg, styles.edgeRect)\n\treturn {\n\t\tbg,\n\t\ttxt,\n\t}\n}\n\n/**\n * buildEdgeLabel adapts an older saved label anchor into a complete label box.\n * It uses one fixed side and lets validation reject any resulting collision.\n */\nfunction buildEdgeLabel(placement: EdgeLabelPlacement, edge: Edge) {\n\tconst labelGap = 12;\n\tconst fontSize = edge.style.fontSize\n\tlet {txt, dy, maxW} = create.textArea(edge.label, 200, fontSize, false, placement.x, placement.y, 'middle')\n\tdy -= fontSize / 2\n\tmaxW += fontSize\n\tconst centerX = placement.orientation === 'vertical'\n\t\t? placement.x + maxW / 2 + labelGap\n\t\t: placement.x\n\tconst centerY = placement.orientation === 'vertical'\n\t\t? placement.y\n\t\t: placement.y - dy / 2 - labelGap\n\tconst bounds = {\n\t\tx: centerX - maxW / 2,\n\t\ty: centerY - dy / 2,\n\t\twidth: maxW,\n\t\theight: dy,\n\t}\n\ttxt.querySelectorAll('tspan').forEach((span: SVGTSpanElement) => {\n\t\tspan.setAttribute('x', String(centerX))\n\t})\n\ttxt.setAttribute('y', String(centerY - dy / 2))\n\n\tapplyStyle(txt, styles.edgeText)\n\ttxt.setAttribute('stroke', 'none')\n\ttxt.setAttribute('font-size', String(edge.style.fontSize))\n\ttxt.setAttribute('fill', edge.style.color)\n\n\tconst bg = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\tapplyStyle(bg, styles.edgeRect)\n\ttxt.setAttribute('data-field', 'label')\n\tedge.labelBounds = bounds\n\treturn {bg, txt}\n}\n\n\nfunction buildNode(n: Node, data: GraphData) {\n\t// @ts-ignore\n\twindow.gdata = data\n\n\tconst g = create.element('g', {}, 'node') as SVGGElement\n\tg.setAttribute('id', n.id)\n\tn.selected && g.classList.add('selected')\n\tsetPosition(g, n.x, n.y)\n\tconst link = n.link\n\t\t? create.element('a', {\n\t\t\thref: n.link.href,\n\t\t\t'data-export-href': n.link.exportHref,\n\t\t\t'aria-label': `Open ${n.title}`,\n\t\t}, 'nodeLink') as SVGAElement\n\t\t: null\n\tconst content = link || g\n\tif (link) {\n\t\tg.classList.add('linked')\n\t\tg.append(link)\n\t}\n\n\t// Ensure we use the correct shape from style, defaulting to Box\n\tconst shapeType = n.style.shape || 'Box';\n\tconst shapeFn = shapes[shapeType.toLowerCase()] || shapes.box\n\tconst shape: SVGElement = shapeFn(content, n);\n\n\tshape.classList.add('nodeBorder')\n\n\t// Apply generic styles first\n\tapplyStyle(shape, styles.nodeBorder)\n\t// Then apply tag-specific styles to override generic ones\n\tshape.setAttribute('fill', n.style.background)\n\tshape.setAttribute('stroke', n.style.stroke)\n\t// Consistent border width for all elements\n\tshape.setAttribute('stroke-width', '3')\n\tshape.setAttribute('opacity', String(n.style.opacity))\n\tsetBorderStyle(shape, n.style.border)\n\n\tconst tg = buildNodeContent(n.contentLayout, n.style.color)\n\tconst labelOffsetY = Number(content.getAttribute('label-offset-y')) || 0\n\tsetPosition(tg, 0, labelOffsetY / 2)\n\tcontent.append(tg)\n\n\t// @ts-ignore\n\tg.__data = n;\n\tn.ref = g;\n\n\treturn g\n}\n\n\nfunction buildGroup(group: Group) {\n\tif (group.nodes.length == 0) {\n\t\treturn\n\t}\n\tconst g = create.element('g', {}, 'group') as SVGGElement\n\tif (group.layoutBounds && group.layoutTitleBounds) {\n\t\tconst bounds = group.layoutBounds\n\t\tconst title = group.layoutTitleBounds\n\t\tconst rect = create.rect(bounds.width, bounds.height, bounds.x, bounds.y)\n\t\tapplyStyle(rect, styles.groupRect)\n\t\tgroup.style.stroke && rect.setAttribute('stroke', group.style.stroke)\n\t\tgroup.style.background && rect.setAttribute('fill', group.style.background)\n\t\tconst text = create.text(group.name, {\n\t\t\tx: title.x,\n\t\t\ty: title.y,\n\t\t\t'dominant-baseline': 'hanging',\n\t\t})\n\t\tapplyStyle(text, styles.groupText)\n\t\tgroup.style.color && text.setAttribute('fill', group.style.color)\n\t\tg.append(rect, text)\n\t\tgroup.ref = g\n\t\treturn\n\t}\n\n\tlet p0: Point = {x: 1e100, y: 1e100}, p1: Point = {x: 0, y: 0}\n\tgroup.nodes.forEach(n => {\n\t\t// Calculate visual bounds accounting for shapes that extend beyond center\n\t\tconst shape = (n as Node).style?.shape?.toLowerCase() || 'box'\n\t\t\n\t\tlet topExtension = n.height / 2\n\t\tlet bottomExtension = n.height / 2\n\t\t\n\t\tif (shape === 'robot') {\n\t\t\t// Robot shape extends above with antenna\n\t\t\t// Account for antenna height (h * 0.08) plus some margin\n\t\t\tconst antennaH = n.height * 0.12\n\t\t\ttopExtension = n.height / 2 + antennaH\n\t\t\tbottomExtension = n.height / 2\n\t\t} else if (shape === 'hexagon') {\n\t\t\t// Hexagon extends to ±0.866 * (width/2) vertically\n\t\t\t// For width=280, that's ±121.24px from center\n\t\t\tconst hexHeight = n.width / 2 * 0.866\n\t\t\ttopExtension = hexHeight\n\t\t\tbottomExtension = hexHeight\n\t\t}\n\t\t\n\t\tconst b = {\n\t\t\tx: n.x - n.width / 2,\n\t\t\ty: n.y - topExtension,\n\t\t\twidth: n.width,\n\t\t\theight: topExtension + bottomExtension\n\t\t}\n\t\tp0.x = Math.min(p0.x, b.x)\n\t\tp0.y = Math.min(p0.y, b.y)\n\t\tp1.x = Math.max(p1.x, b.x + b.width)\n\t\tp1.y = Math.max(p1.y, b.y + b.height)\n\t})\n\tconst pad = 25 // Padding around content\n\tconst labelHeight = 30 // Space for the group label at bottom\n\tconst w = Math.max(p1.x - p0.x, 200)\n\tconst h = p1.y - p0.y\n\tconst bb = {\n\t\tx: p0.x - pad,\n\t\ty: p0.y - pad,\n\t\twidth: w + pad * 2,\n\t\theight: h + pad * 2 + labelHeight,\n\t}\n\tconst r = create.rect(bb.width, bb.height, bb.x, bb.y)\n\tgroup.x = bb.x + bb.width / 2\n\tgroup.y = bb.y + bb.height / 2\n\tgroup.width = bb.width\n\tgroup.height = bb.height\n\tapplyStyle(r, styles.groupRect)\n\tgroup.style.stroke && r.setAttribute('stroke', group.style.stroke)\n\tgroup.style.background && r.setAttribute('fill', group.style.background)\n\n\tconst txt = create.text(group.name, {x: p0.x, y: bb.y + bb.height - styles.groupText[\"font-size\"]})\n\tapplyStyle(txt, styles.groupText)\n\tgroup.style.color && txt.setAttribute('fill', group.style.color)\n\n\tg.append(r, txt)\n\tgroup.ref = g\n}\n\nfunction findClosestSegment(graph: GraphData, p: Point) {\n\t// find the closest point on a segment\n\tlet fnd = {dst: Number.POSITIVE_INFINITY, pos: -1, edge: null as Edge, prj: null as Point}\n\tgraph.edges.forEach(edge => {\n\t\tconst vertices = edge.vertices || []\n\t\tconst pts = [edge.from, ...vertices, edge.to]\n\t\tfor (let i = 1; i < pts.length; i++) {\n\t\t\tconst prj = project(p, pts[i - 1], pts[i])\n\t\t\tconst dst = cabDistance(p, prj)\n\t\t\tif (dst > 50) continue\n\t\t\tif (dst < fnd.dst) {\n\t\t\t\tfnd = {dst, pos: i, prj, edge}\n\t\t\t}\n\t\t}\n\t})\n\treturn fnd.edge ? fnd : null\n}\n\nfunction mouseToDrawing(e: MouseEvent): Point {\n\t// transform event coords to drawing coords\n\tconst b = svg.getBoundingClientRect()\n\tconst z = getZoom()\n\t\n\t// Get current pan transform\n\tconst currentTransform = getCurrentTransform()\n\t\n\t// Convert screen coordinates to drawing coordinates accounting for zoom and pan\n\t// The transform values are in screen coordinates, so we need to divide by zoom and subtract\n\treturn {\n\t\tx: (e.clientX - b.x - currentTransform.x) / z,\n\t\ty: (e.clientY - b.y - currentTransform.y) / z\n\t}\n}\n\ninterface Handle extends Point {\n\tid: string\n\tselected?: boolean\n\tref?: SVGElement\n}\n\n// Custom cursor interaction that prioritizes panning over selection\nfunction addCustomCursorInteraction(svg: SVGSVGElement, conn: {\n\tnodeFromEvent(e: MouseEvent): Handle | null;\n\tsetSelection(handles: Handle[]): void;\n\tsetDragging(dragging: boolean): void;\n\tisSelected(handle: Handle): boolean;\n\tgetSelection(): Handle[];\n\tgetZoom(): number;\n\tmoveNode(h: Handle, x: number, y: number): void;\n\tboxSelection(box: DOMRect, add: boolean): void;\n\tupdatePanning(): void;\n}, dragMode: 'pan' | 'select') {\n\tlet ini: { x: number; y: number; n: Handle }[] = []\n\tlet elastic: any = null\n\tlet isPanning = false\n\tlet panStartX = 0\n\tlet panStartY = 0\n\tlet initialTransform = { x: 0, y: 0 }\n\tlet pendingSelectionChange: { node: Handle; shiftKey: boolean } | null = null\n\tlet pendingNavigation: string | null = null\n\tlet hasDragged = false\n\tlet suppressLinkClick = false\n\t\n\t// Store event listeners for cleanup\n\tconst eventListeners: Array<{ element: Element | Window, event: string, handler: EventListener }> = []\n\n\t// Simple elastic selection box implementation - use mouseToDrawing for proper coordinate conversion\n\tfunction createElastic() {\n\t\tlet startDrawingX = 0, startDrawingY = 0, rect: SVGRectElement | null = null\n\t\t\n\t\treturn {\n\t\t\tini(e: MouseEvent) {\n\t\t\t\t// Use mouseToDrawing to get proper drawing coordinates (accounts for zoom and pan)\n\t\t\t\tconst pt = mouseToDrawing(e)\n\t\t\t\tstartDrawingX = pt.x\n\t\t\t\tstartDrawingY = pt.y\n\t\t\t\t\n\t\t\t\trect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\")\n\t\t\t\trect.setAttribute('fill', 'rgba(0, 100, 255, 0.1)')\n\t\t\t\trect.setAttribute('stroke', 'rgba(0, 100, 255, 0.5)')\n\t\t\t\trect.setAttribute('stroke-width', '1')\n\t\t\t\trect.setAttribute('stroke-dasharray', '3,3')\n\t\t\t\trect.setAttribute('x', String(startDrawingX))\n\t\t\t\trect.setAttribute('y', String(startDrawingY))\n\t\t\t\trect.setAttribute('width', '0')\n\t\t\t\trect.setAttribute('height', '0')\n\t\t\t\t\n\t\t\t\t// Add to the zoom group so it transforms with the content\n\t\t\t\tconst zoomGroup = svg.querySelector('g.zoom')\n\t\t\t\tif (zoomGroup) {\n\t\t\t\t\tzoomGroup.appendChild(rect)\n\t\t\t\t} else {\n\t\t\t\t\tsvg.appendChild(rect)\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdate(e: MouseEvent) {\n\t\t\t\tif (!rect) return\n\t\t\t\t\n\t\t\t\t// Convert current mouse position to drawing coordinates (accounts for zoom and pan)\n\t\t\t\tconst currentPt = mouseToDrawing(e)\n\t\t\t\tconst currentDrawingX = currentPt.x\n\t\t\t\tconst currentDrawingY = currentPt.y\n\t\t\t\t\n\t\t\t\t// Calculate rectangle bounds in drawing coordinates\n\t\t\t\tconst x = Math.min(startDrawingX, currentDrawingX)\n\t\t\t\tconst y = Math.min(startDrawingY, currentDrawingY)\n\t\t\t\tconst width = Math.abs(currentDrawingX - startDrawingX)\n\t\t\t\tconst height = Math.abs(currentDrawingY - startDrawingY)\n\t\t\t\t\n\t\t\t\trect.setAttribute('x', String(x))\n\t\t\t\trect.setAttribute('y', String(y))\n\t\t\t\trect.setAttribute('width', String(width))\n\t\t\t\trect.setAttribute('height', String(height))\n\t\t\t},\n\t\t\tend(): DOMRect | null {\n\t\t\t\tif (!rect) return null\n\t\t\t\t\n\t\t\t\t// Get final rectangle bounds in drawing coordinates\n\t\t\t\tconst x = parseFloat(rect.getAttribute('x') || '0')\n\t\t\t\tconst y = parseFloat(rect.getAttribute('y') || '0')\n\t\t\t\tconst width = parseFloat(rect.getAttribute('width') || '0')\n\t\t\t\tconst height = parseFloat(rect.getAttribute('height') || '0')\n\t\t\t\t\n\t\t\t\trect.remove()\n\t\t\t\trect = null\n\t\t\t\t\n\t\t\t\t// Return drawing coordinates directly for boxSelection since we're now working in the same coordinate system\n\t\t\t\tif (width > 5 && height > 5) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tx: x, y: y, width: width, height: height,\n\t\t\t\t\t\tleft: x, top: y, right: x + width, bottom: y + height\n\t\t\t\t\t} as DOMRect\n\t\t\t\t}\n\t\t\t\treturn null\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction getCurrentTransformLocal() {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (!zoomGroup) return { x: 0, y: 0 }\n\t\t\n\t\tconst transform = zoomGroup.getAttribute('transform') || ''\n\t\tconst translateMatch = transform.match(/translate\\(([^,]+),([^)]+)\\)/)\n\t\tif (translateMatch) {\n\t\t\treturn {\n\t\t\t\tx: parseFloat(translateMatch[1]) || 0,\n\t\t\t\ty: parseFloat(translateMatch[2]) || 0\n\t\t\t}\n\t\t}\n\t\treturn { x: 0, y: 0 }\n\t}\n\n\tfunction setTransform(x: number, y: number) {\n\t\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement\n\t\tif (!zoomGroup) return\n\t\t\n\t\tconst zoom = getZoom()\n\t\tzoomGroup.setAttribute('transform', `translate(${x}, ${y}) scale(${zoom})`)\n\t}\n\n\tfunction onMouseDown(e: MouseEvent) {\n\t\te.preventDefault();\n\t\thasDragged = false\n\t\tpendingSelectionChange = null\n\t\tconst target = e.target\n\t\tconst link = target instanceof Element ? target.closest('a.nodeLink') : null\n\t\tpendingNavigation = e.shiftKey ? null : link?.getAttribute('href') || null\n\n\t\tconst node = conn.nodeFromEvent(e)\n\t\t\n\t\t// Determine effective mode: invert if shift is held\n\t\tconst effectiveMode = e.shiftKey ? (dragMode === 'pan' ? 'select' : 'pan') : dragMode\n\t\t\n\t\tif (!node) { // Clicked on empty space\n\t\t\tif (effectiveMode === 'pan') {\n\t\t\t\t// Pan mode: pan and deselect\n\t\t\t\tisPanning = true;\n\t\t\t\telastic = null;\n\t\t\t\tpanStartX = e.clientX;\n\t\t\t\tpanStartY = e.clientY;\n\t\t\t\tinitialTransform = getCurrentTransformLocal();\n\t\t\t\tini = [];\n\t\t\t\t// Deselect all elements when clicking empty space in pan mode\n\t\t\t\tconn.setSelection([]);\n\t\t\t} else {\n\t\t\t\t// Select mode: ONLY select, no panning\n\t\t\t\tisPanning = false;\n\t\t\t\telastic = createElastic();\n\t\t\t\tif (elastic) elastic.ini(e);\n\t\t\t\tini = [];\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Clicked on a node/vertex - behavior depends on effective mode\n\t\tif (effectiveMode === 'pan') {\n\t\t\t// Pan mode: select the element, don't pan\n\t\t\tisPanning = false;\n\t\t\telastic = null;\n\t\t\t\n\t\t\tconst nodes = conn.getSelection()\n\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t// Clicking on a selected node - prepare to drag all selected elements\n\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t\t// No selection change needed since we're clicking on already selected element\n\t\t\t} else {\n\t\t\t\t// Clicking on an unselected node - select only this element\n\t\t\t\tconn.setSelection([node]);\n\t\t\t\tini = [{ x: node.x, y: node.y, n: node }];\n\t\t\t}\n\t\t} else {\n\t\t\t// Select mode: selection/drag logic\n\t\t\tisPanning = false; // Ensure no panning if a node is clicked\n\t\t\telastic = null; // Ensure no selection box if a node is clicked\n\t\t\tconst nodes = conn.getSelection()\n\t\t\t\n\t\t\tif (e.shiftKey && dragMode === 'select') {\n\t\t\t\t// Shift+click in select mode: immediately change selection (no dragging expected)\n\t\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t\tconst index = nodes.findIndex(n => n.id === node.id)\n\t\t\t\t\tif (index >= 0) nodes.splice(index, 1)\n\t\t\t\t} else {\n\t\t\t\t\tnodes.push(node)\n\t\t\t\t}\n\t\t\t\tconn.setSelection(nodes)\n\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t} else {\n\t\t\t\t// Regular click: defer selection change until we know if it's a drag or click\n\t\t\t\tif (conn.isSelected(node)) {\n\t\t\t\t\t// Clicking on a selected node - prepare to drag all selected elements\n\t\t\t\t\tini = nodes.map(n => ({ x: n.x, y: n.y, n }))\n\t\t\t\t\t// No pending selection change needed since we're clicking on already selected element\n\t\t\t\t\tpendingSelectionChange = null\n\t\t\t\t} else {\n\t\t\t\t\t// Clicking on an unselected node - defer selection change until we determine if it's a click or drag\n\t\t\t\t\tpendingSelectionChange = { node, shiftKey: e.shiftKey }\n\t\t\t\t\t// For now, prepare to drag just the clicked node (we'll update selection when drag starts)\n\t\t\t\t\tini = [{ x: node.x, y: node.y, n: node }]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction onMouseMove(e: MouseEvent, dx: number, dy: number) {\n\t\t// Check if we've moved enough to consider this a drag\n\t\tconst dragThreshold = 3 // pixels\n\t\tif (!hasDragged && (Math.abs(dx) > dragThreshold || Math.abs(dy) > dragThreshold)) {\n\t\t\thasDragged = true\n\t\t\tpendingNavigation = null\n\t\t\t\n\t\t\t// If we have a pending selection change and we're now dragging, apply it\n\t\t\tif (pendingSelectionChange) {\n\t\t\t\tconst nodes = conn.getSelection()\n\t\t\t\tnodes.length = 0\n\t\t\t\tnodes.push(pendingSelectionChange.node)\n\t\t\t\tconn.setSelection(nodes)\n\t\t\t\t// Update ini to drag only the newly selected node\n\t\t\t\tini = [{ x: pendingSelectionChange.node.x, y: pendingSelectionChange.node.y, n: pendingSelectionChange.node }]\n\t\t\t\tpendingSelectionChange = null\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (isPanning) {\n\t\t\t// Pan the view - apply mouse delta directly (no zoom division needed)\n\t\t\t// setTransform expects screen coordinates, dx/dy are already screen pixel deltas\n\t\t\tconst newX = initialTransform.x + dx\n\t\t\tconst newY = initialTransform.y + dy\n\t\t\tsetTransform(newX, newY)\n\t\t} else if (ini.length > 0 && hasDragged) {\n\t\t\t// Move selected nodes/vertices (only if we've actually started dragging)\n\t\t\t// dx, dy are screen pixel deltas, convert to drawing coordinate deltas\n\t\t\tconst zoom = conn.getZoom()\n\t\t\tconst drawingDx = dx / zoom\n\t\t\tconst drawingDy = dy / zoom\n\t\t\tini.forEach(item => {\n\t\t\t\t// item.x, item.y are initial drawing coordinates\n\t\t\t\t// Add the drawing coordinate delta to get new position\n\t\t\t\tconn.moveNode(item.n, item.x + drawingDx, item.y + drawingDy)\n\t\t\t})\n\t\t\tconn.setDragging(true)\n\t\t} else if (elastic) {\n\t\t\t// Update selection box\n\t\t\telastic.update(e)\n\t\t\tconn.setDragging(true)\n\t\t}\n\t}\n\n\tfunction onMouseUp(e: MouseEvent) {\n\t\tconn.setDragging(false)\n\t\tconst navigation = hasDragged ? null : pendingNavigation\n\t\tif (hasDragged) {\n\t\t\tsuppressLinkClick = true\n\t\t\twindow.setTimeout(() => {\n\t\t\t\tsuppressLinkClick = false\n\t\t\t}, 0)\n\t\t}\n\t\t\n\t\t// If we have a pending selection change and didn't drag, apply it now (it was just a click)\n\t\tif (pendingSelectionChange && !hasDragged) {\n\t\t\tconst nodes = conn.getSelection()\n\t\t\tnodes.length = 0\n\t\t\tnodes.push(pendingSelectionChange.node)\n\t\t\tconn.setSelection(nodes)\n\t\t}\n\t\t\n\t\tif (elastic) {\n\t\t\tconst box = elastic.end()\n\t\t\tif (box) {\n\t\t\t\tconn.boxSelection(box, e.shiftKey)\n\t\t\t} else if (!ini.length) {\n\t\t\t\t// Deselect if no box was drawn\n\t\t\t\tconn.setSelection([])\n\t\t\t}\n\t\t\telastic = null\n\t\t}\n\t\t\n\t\t// Save view state if user was panning (user-initiated view change)\n\t\tif (isPanning && hasDragged) {\n\t\t\tconst graphData = (svg as any).__data as GraphData\n\t\t\tif (graphData && graphData.id) {\n\t\t\t\tsaveViewState(graphData.id)\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Reset state\n\t\tpendingSelectionChange = null\n\t\tpendingNavigation = null\n\t\thasDragged = false\n\t\tisPanning = false\n\t\tconn.updatePanning()\n\t\tif (navigation) {\n\t\t\twindow.location.href = navigation\n\t\t}\n\t}\n\n\tfunction onClick(e: MouseEvent) {\n\t\tconst target = e.target\n\t\tif (!(target instanceof Element) || !target.closest('a.nodeLink')) return\n\t\te.preventDefault()\n\t\tif (!suppressLinkClick) return\n\t\te.stopPropagation()\n\t\tsuppressLinkClick = false\n\t}\n\n\t// Add drag and drop functionality\n\tfunction addDnd(element: SVGSVGElement) {\n\t\tlet md: { ex: number; ey: number } | null = null\n\n\t\tfunction convertEvent(e: MouseEvent | TouchEvent): MouseEvent {\n\t\t\tif ('changedTouches' in e && e.changedTouches) {\n\t\t\t\treturn e.changedTouches[0] as any\n\t\t\t}\n\t\t\treturn e as MouseEvent\n\t\t}\n\n\t\tfunction onMouseMoveHandler(e: MouseEvent | TouchEvent) {\n\t\t\tif (!md) return\n\t\t\te = convertEvent(e)\n\t\t\tonMouseMove(e, e.clientX - md.ex, e.clientY - md.ey)\n\t\t}\n\n\t\tfunction removeListeners() {\n\t\t\tdocument.removeEventListener('touchmove', onMouseMoveHandler as any)\n\t\t\tdocument.removeEventListener('mousemove', onMouseMoveHandler as any)\n\t\t\tdocument.removeEventListener('mouseup', onMouseUpHandler)\n\t\t\tdocument.removeEventListener('touchend', onMouseUpHandler)\n\t\t}\n\n\t\tfunction onMouseUpHandler(e: MouseEvent | TouchEvent) {\n\t\t\tremoveListeners()\n\t\t\tonMouseUp(convertEvent(e))\n\t\t\tmd = null\n\t\t}\n\n\t\tfunction onMouseDownHandler(e: MouseEvent | TouchEvent) {\n\t\t\te = convertEvent(e)\n\t\t\tmd = { ex: e.clientX, ey: e.clientY }\n\t\t\tonMouseDown(e)\n\t\t\tdocument.addEventListener('touchmove', onMouseMoveHandler as any)\n\t\t\tdocument.addEventListener('mousemove', onMouseMoveHandler as any)\n\t\t\tdocument.addEventListener('mouseup', onMouseUpHandler)\n\t\t\tdocument.addEventListener('touchend', onMouseUpHandler)\n\t\t}\n\n\t\telement.addEventListener('mousedown', onMouseDownHandler as any)\n\t\telement.addEventListener('touchstart', onMouseDownHandler as any)\n\t\t\n\t\t// Track these listeners for cleanup\n\t\teventListeners.push(\n\t\t\t{ element, event: 'mousedown', handler: onMouseDownHandler as any },\n\t\t\t{ element, event: 'touchstart', handler: onMouseDownHandler as any }\n\t\t)\n\t}\n\n\taddDnd(svg)\n\tsvg.addEventListener('click', onClick)\n\teventListeners.push({ element: svg, event: 'click', handler: onClick })\n\t\n\t// Return cleanup function\n\treturn () => {\n\t\teventListeners.forEach(({ element, event, handler }) => {\n\t\t\telement.removeEventListener(event, handler)\n\t\t})\n\t}\n}\n\nexport function addCursorInteraction(svg: SVGSVGElement, dragMode: 'pan' | 'select') {\n\t// Clean up any existing event listeners to prevent conflicts\n\tconst existingCleanup = (svg as any).__cursorInteractionCleanup\n\tif (existingCleanup) {\n\t\texistingCleanup()\n\t}\n\n\tfunction getData(el: SVGElement) {\n\t\t// @ts-ignore\n\t\treturn el.__data\n\t}\n\n\tconst gd = () => (getData(svg) as GraphData)\n\t\n\t// Store event listeners for cleanup\n\tconst eventListeners: Array<{ element: Element | Window, event: string, handler: EventListener }> = []\n\n\tconst beforeUnloadHandler = (e: BeforeUnloadEvent) => {\n\t\tif (!gd().changed()) return\n\t\te.preventDefault()\n\t\te.returnValue = ''\n\t}\n\twindow.addEventListener(\"beforeunload\", beforeUnloadHandler)\n\teventListeners.push({ element: window, event: 'beforeunload', handler: beforeUnloadHandler })\n\n\tfunction setDotSelected(d: Handle, selected: boolean) {\n\t\td.selected = selected\n\t\tconst dotEl = svg.querySelector('#' + d.id)\n\t\td.selected ? dotEl.classList.add('selected') : dotEl.classList.remove('selected')\n\t}\n\n\t// show moving dot along edge when ALT is pressed\n\tconst mouseMoveHandler = (e: MouseEvent) => {\n\t\tif (!e.altKey) return\n\t\tconst fnd = findClosestSegment(gd(), mouseToDrawing(e))\n\t\tif (fnd) {\n\t\t\tconst {prj} = fnd\n\t\t\tconst parent = svg.querySelector('g.edges')\n\t\t\tlet dot = parent.querySelector('#prj')\n\t\t\tif (!dot) {\n\t\t\t\tdot = create.element('circle', {id: 'prj', cx: prj.x, cy: prj.y, r: 7})\n\t\t\t\tparent.append(dot)\n\t\t\t}\n\t\t\tdot.setAttribute('cx', String(prj.x))\n\t\t\tdot.setAttribute('cy', String(prj.y))\n\t\t} else {\n\t\t\tremovePrjDot()\n\t\t}\n\t}\n\tsvg.addEventListener('mousemove', mouseMoveHandler)\n\teventListeners.push({ element: svg, event: 'mousemove', handler: mouseMoveHandler })\n\n\tconst keyUpHandler = (e: KeyboardEvent) => {\n\t\tconst key = findShortcut(e, true)\n\t\tif (key == ADD_VERTEX || key == ADD_LABEL_VERTEX) return\n\t\tremovePrjDot()\n\t}\n\twindow.addEventListener('keyup', keyUpHandler)\n\teventListeners.push({ element: window, event: 'keyup', handler: keyUpHandler })\n\n\tfunction removePrjDot() {\n\t\tconst el = svg.querySelector('g.edges #prj')\n\t\tel && el.parentElement.removeChild(el)\n\t}\n\n\tconst clickHandler = (e: MouseEvent) => {\n\t\tconst key = findShortcut(e, true)\n\t\tif (key != ADD_LABEL_VERTEX && key != ADD_VERTEX) return\n\t\tconst fnd = findClosestSegment(gd(), mouseToDrawing(e))\n\t\tif (fnd) {\n\t\t\tconst {edge, pos, prj} = fnd\n\t\t\t// depending on keyboard modifier, make it label position\n\t\t\tgd().insertEdgeVertex(edge, prj, pos, key == ADD_LABEL_VERTEX)\n\t\t\tremovePrjDot()\n\t\t}\n\t}\n\tsvg.addEventListener('click', clickHandler)\n\teventListeners.push({ element: svg, event: 'click', handler: clickHandler })\n\n\tconst wheelHandler = (e: WheelEvent) => {\n\t\t// Handle wheel zoom directly without relying on shortcuts\n\t\t// deltaY > 0 means scrolling down (zoom out), deltaY < 0 means scrolling up (zoom in)\n\t\tconst delta = Math.sign(e.deltaY) * 0.1 // Normalize to 0.1 zoom steps\n\t\tconst currentZoom = getZoom()\n\t\tconst newZoom = Math.max(0.1, Math.min(5, currentZoom - delta)) // Clamp zoom between 0.1 and 5\n\t\t\n\t\tif (newZoom !== currentZoom) {\n\t\t\t// Convert absolute screen coordinates to SVG-relative coordinates\n\t\t\tconst rect = svg.getBoundingClientRect()\n\t\t\tconst svgX = e.clientX - rect.left\n\t\t\tconst svgY = e.clientY - rect.top\n\t\t\tsetZoomCentered(newZoom, svgX, svgY)\n\t\t\te.preventDefault()\n\t\t\t\n\t\t\t// Save view state after user wheel zoom\n\t\t\tconst graphData = (svg as any).__data as GraphData\n\t\t\tif (graphData && graphData.id) {\n\t\t\t\tsaveViewState(graphData.id)\n\t\t\t}\n\t\t}\n\t}\n\tsvg.addEventListener('wheel', wheelHandler)\n\teventListeners.push({ element: svg, event: 'wheel', handler: wheelHandler })\n\n\tconst keyDownHandler = (e: KeyboardEvent) => {\n\t\tconst shortcut = findShortcut(e)\n\t\t\n\t\t\n\t\tif (shortcut) {\n\t\t\te.preventDefault() // Prevent browser default for all recognized shortcuts\n\t\t}\n\t\t\n\t\tswitch (shortcut) {\n\t\t\tcase DEL_VERTEX:\n\t\t\t\tconst selectedVertices = Array.from(gd().edgeVertices.values()).filter(v => v.selected);\n\t\t\t\tselectedVertices.forEach(v => {\n\t\t\t\t\tgd().deleteEdgeVertex(v)\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\tcase UNDO:\n\t\t\t\tgd().undo()\n\t\t\t\tbreak\n\t\t\tcase REDO:\n\t\t\t\tgd().redo()\n\t\t\t\tbreak\n\t\t\tcase ZOOM_IN:\n\t\t\t\tconst newZoomIn = Math.min(5, getZoom() * 1.2)\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(newZoomIn)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_OUT:\n\t\t\t\tconst newZoomOut = Math.max(0.1, getZoom() / 1.2)\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(newZoomOut)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_100:\n\t\t\t\t// Center zoom on viewport center like mouse wheel\n\t\t\t\tsetZoomCentered(1)\n\t\t\t\tsaveViewState(gd().id) // Save after user keyboard zoom\n\t\t\t\tbreak\n\t\t\tcase ZOOM_FIT:\n\t\t\t\tgd().fitToView()\n\t\t\t\t// Don't save view state here - fitToView should not be persisted\n\t\t\t\tbreak\n\t\t\tcase SELECT_ALL:\n\t\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, true))\n\t\t\t\tgd().edgeVertices.forEach(v => setDotSelected(v, true))\n\t\t\t\tbreak\n\t\t\tcase DESELECT:\n\t\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, false))\n\t\t\t\tgd().edgeVertices.forEach(v => setDotSelected(v, false))\n\t\t\t\tbreak\n\t\t}\n\t}\n\twindow.addEventListener('keydown', keyDownHandler)\n\teventListeners.push({ element: window, event: 'keydown', handler: keyDownHandler })\n\n\t// Custom cursor interaction with pan-first behavior\n\tconst customInteractionCleanup = addCustomCursorInteraction(svg, {\n\t\tnodeFromEvent(e: MouseEvent): Handle {\n\t\t\te.preventDefault()\n\t\t\t// node clicked\n\t\t\tlet el = (e.target as SVGElement).closest('g.nodes g.node') as SVGElement\n\t\t\tif (el) return getData(el)\n\t\t\t// vertex dot clicked\n\t\t\tel = (e.target as SVGElement).closest('g.edges g.edge .v-dot') as SVGElement\n\t\t\tif (el) {\n\t\t\t\treturn gd().edgeVertices.get(el.id)\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\t\tsetSelection(handles: Handle[]) {\n\t\t\t// nodes\n\t\t\tgd().nodes().forEach(n => gd().setNodeSelected(n, handles.some(h => h.id == n.id)))\n\t\t\t// dots\n\t\t\tgd().edgeVertices.forEach(d => setDotSelected(d, handles.some(h => h.id == d.id)))\n\t\t\tselectListener(gd().nodes().find(n => n.selected))\n\t\t},\n\t\tsetDragging(d: boolean) {\n\t\t\tdragging = d\n\t\t},\n\t\tisSelected(handle: Handle): boolean {\n\t\t\treturn handle.selected\n\t\t},\n\t\tgetSelection(): Handle[] {\n\t\t\tconst ret: Handle[] = gd().nodes().filter(n => n.selected)\n\t\t\tgd().edgeVertices.forEach(d => d.selected && ret.push(d))\n\t\t\treturn ret\n\t\t},\n\t\tgetZoom: getZoom,\n\t\tmoveNode(h: Handle, x: number, y: number) {\n\t\t\tif (gd().nodesMap.has(h.id))\n\t\t\t\tgd().moveNode(h as Node, x, y)\n\t\t\telse {\n\t\t\t\t(h as EdgeVertex).auto = false\n\t\t\t\tgd().moveEdgeVertex(h as EdgeVertex, x, y)\n\t\t\t}\n\t\t},\n\t\tboxSelection(box: DOMRect, add) {\n\t\t\t// Box is now already in drawing coordinates, no need to scale\n\t\t\t// nodes\n\t\t\tgd().nodesMap.forEach(n => {\n\t\t\t\tconst inBox = boxesOverlap(uncenterBox(n), box)\n\t\t\t\tif (inBox) {\n\t\t\t\t\t// Toggle selection for elements in the box\n\t\t\t\t\tgd().setNodeSelected(n, !n.selected)\n\t\t\t\t} else if (!add) {\n\t\t\t\t\t// If not holding shift and element is outside box, deselect it\n\t\t\t\t\tgd().setNodeSelected(n, false)\n\t\t\t\t}\n\t\t\t})\n\t\t\t// dots\n\t\t\tgd().edgeVertices.forEach(d => {\n\t\t\t\tconst inBox = insideBox(d, box, false)\n\t\t\t\tif (inBox) {\n\t\t\t\t\t// Toggle selection for elements in the box\n\t\t\t\t\tsetDotSelected(d, !d.selected)\n\t\t\t\t} else if (!add) {\n\t\t\t\t\t// If not holding shift and element is outside box, deselect it\n\t\t\t\t\tsetDotSelected(d, false)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tselectListener(gd().nodes().find(n => n.selected))\n\t\t},\n\t\tupdatePanning: updatePanning,\n\t}, dragMode)\n\t\n\t// Store cleanup function on the SVG element for later use\n\tconst cleanup = () => {\n\t\teventListeners.forEach(({ element, event, handler }) => {\n\t\t\telement.removeEventListener(event, handler)\n\t\t})\n\t\tif (customInteractionCleanup) {\n\t\t\tcustomInteractionCleanup()\n\t\t}\n\t}\n\t\n\t// Store cleanup function on SVG element\n\t;(svg as any).__cursorInteractionCleanup = cleanup\n}\n\nexport function getZoom() {\n\tif (!svg) return 1\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return 1\n\t\n\t// Parse zoom from transform attribute to match how we set it\n\tconst transform = el.getAttribute('transform') || ''\n\tconst scaleMatch = transform.match(/scale\\(([^)]+)\\)/)\n\tif (scaleMatch) {\n\t\treturn parseFloat(scaleMatch[1]) || 1\n\t}\n\treturn 1\n}\n\n// svgPadding is now imported as SVG_PADDING from constants.ts\n\nexport function setZoom(zoom: number) {\n\tif (!svg) return\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return\n\t\n\t// Preserve existing translation when setting zoom\n\tconst currentTransform = getCurrentTransform()\n\tel.setAttribute('transform', `translate(${currentTransform.x}, ${currentTransform.y}) scale(${zoom})`)\n\t\n\t// also set panning size\n\tupdatePanning()\n}\n\nexport function setZoomCentered(newZoom: number, centerX?: number, centerY?: number) {\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tconst oldZoom = getZoom()\n\t\n\t// If no center point provided, use viewport center\n\tif (centerX === undefined || centerY === undefined) {\n\t\t// Use the parent container's dimensions for the visible viewport\n\t\t// The SVG might be larger than the visible area due to overflow\n\t\tconst container = svg.parentElement\n\t\tif (container) {\n\t\t\tcenterX = container.clientWidth / 2\n\t\t\tcenterY = container.clientHeight / 2\n\t\t} else {\n\t\t\t// Fallback to SVG dimensions if no parent\n\t\t\tcenterX = svg.clientWidth / 2\n\t\t\tcenterY = svg.clientHeight / 2\n\t\t}\n\t}\n\t\n\t// Get current transform\n\tconst currentTransform = getCurrentTransform()\n\t\n\t// Convert screen coordinates to drawing coordinates\n\t// For transform order translate(tx, ty) scale(s):\n\t// screen_point = (drawing_point * scale) + translation\n\t// So: drawing_point = (screen_point - translation) / scale\n\tconst drawingX = (centerX - currentTransform.x) / oldZoom\n\tconst drawingY = (centerY - currentTransform.y) / oldZoom\n\t\n\t// Calculate new translation to keep the same drawing point at the same screen position\n\t// screen_point = (drawing_point * new_scale) + new_translation\n\t// So: new_translation = screen_point - (drawing_point * new_scale)\n\tconst newTranslateX = centerX - (drawingX * newZoom)\n\tconst newTranslateY = centerY - (drawingY * newZoom)\n\t\n\t// Apply the new transform\n\tel.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY}) scale(${newZoom})`)\n\t\n\t// Update panning\n\tupdatePanning()\n}\n\nfunction getCurrentTransform() {\n\tif (!svg) return { x: 0, y: 0 }\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return { x: 0, y: 0 }\n\t\n\tconst transform = el.getAttribute('transform') || ''\n\tconst translateMatch = transform.match(/translate\\(([^,]+),([^)]+)\\)/)\n\tif (translateMatch) {\n\t\treturn {\n\t\t\tx: parseFloat(translateMatch[1]) || 0,\n\t\t\ty: parseFloat(translateMatch[2]) || 0\n\t\t}\n\t}\n\treturn { x: 0, y: 0 }\n}\n\nfunction updatePanning() {\n\tif (!svg) return\n\tconst el = svg.querySelector('g.zoom') as SVGGElement\n\tif (!el) return\n\tconst bb = el.getBBox()\n\tconst zoom = getZoom()\n\tif (!svg.parentElement) return\n\tconst w = Math.max(svg.parentElement.clientWidth / zoom, bb.x + bb.width + SVG_PADDING)\n\tconst h = Math.max(svg.parentElement.clientHeight / zoom, bb.y + bb.height + SVG_PADDING)\n\tsvg.setAttribute('width', String(w * zoom))\n\tsvg.setAttribute('height', String(h * zoom))\n\t\n\t// Note: View state saving removed from here to prevent interference with reset/fit functions\n\t// View state is now only saved on user interactions and page unload\n}\n\n// Optimized version that uses pre-calculated content bounds instead of expensive getBBox()\nfunction updatePanningOptimized(graphData: GraphData) {\n\tconst bb = graphData.calculateContentBounds() // Use already calculated bounds\n\tconst zoom = getZoom()\n\tconst w = Math.max(svg.parentElement.clientWidth / zoom, bb.x + bb.width + SVG_PADDING)\n\tconst h = Math.max(svg.parentElement.clientHeight / zoom, bb.y + bb.height + SVG_PADDING)\n\tsvg.setAttribute('width', String(w * zoom))\n\tsvg.setAttribute('height', String(h * zoom))\n}\n\nexport const getZoomAuto = () => {\n\t// Get the graph data to calculate proper content bounds\n\tconst graphData = (svg as any).__data as GraphData\n\tif (!graphData) {\n\t\treturn 1 // Default zoom if no graph data\n\t}\n\t\n\t// Use proper content bounds calculation\n\tconst contentBounds = graphData.calculateContentBounds()\n\tconst viewportWidth = svg.parentElement?.clientWidth || 800\n\tconst viewportHeight = svg.parentElement?.clientHeight || 600\n\t\n\t// Add padding around content\n\tconst padding = 40\n\t\n\t// Calculate zoom to fit content with padding\n\tconst zoomX = (viewportWidth - padding * 2) / contentBounds.width\n\tconst zoomY = (viewportHeight - padding * 2) / contentBounds.height\n\tconst zoom = Math.min(zoomX, zoomY)\n\t\n\t// Clamp zoom between reasonable bounds\n\treturn Math.max(Math.min(zoom, 2), 0.1)\n}\n\nconst setBorderStyle = (el: SVGElement, style: string) => {\n\tif (style == 'Dashed') el.setAttribute('stroke-dasharray', '4')\n\telse if (style == 'Dotted') el.setAttribute('stroke-dasharray', '2')\n}\n\nconst styles = {\n\t//node styles\n\tnodeBorder: {\n\t\t// Don't set fill and stroke here - let tag-specific styles handle colors\n\t\tfilter: 'url(#shadow)',\n\t},\n\tnodeText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\n\t//edge styles\n\tedgeText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tstroke: \"none\"\n\t},\n\n\tedgeRect: {\n\t\tfill: \"none\",\n\t\tstroke: \"none\",\n\t},\n\n\t//group styles\n\tgroupRect: {\n\t\t//fill: \"none\",\n\t\tfill: \"rgba(0, 0, 0, 0.02)\",\n\t\tstroke: \"#666\",\n\t\t'stroke-width': 3,\n\t\t\"stroke-dasharray\": 4,\n\t},\n\tgroupText: {\n\t\t'font-family': 'Arial, sans-serif',\n\t\tfill: \"#666\",\n\t\t\"font-size\": 22,\n\t\t\"font-weight\": \"bold\",\n\t\tcursor: \"default\"\n\t}\n}\n\n// View state persistence - similar to undo cache but for zoom/pan\nconst viewStateCache = new Map();\n\n// Save current view state (zoom and pan)\nexport function saveViewState(graphId: string) {\n\tif (!svg) return;\n\t\n\tconst zoom = getZoom();\n\tconst transform = getCurrentTransform();\n\t\n\tconst state = {\n\t\tzoom,\n\t\ttransform: { x: transform.x, y: transform.y }\n\t};\n\t\n\tviewStateCache.set(graphId, state);\n}\n\n// Restore view state if it exists\nexport function restoreViewState(graphId: string): boolean {\n\tif (!svg || !viewStateCache.has(graphId)) {\n\t\treturn false;\n\t}\n\t\n\tconst state = viewStateCache.get(graphId);\n\tif (!state) {\n\t\treturn false;\n\t}\n\t\n\t// Restore zoom and transform\n\tconst zoomGroup = svg.querySelector('g.zoom') as SVGGElement;\n\tif (zoomGroup) {\n\t\tzoomGroup.setAttribute('transform', `scale(${state.zoom}) translate(${state.transform.x}, ${state.transform.y})`);\n\t\tupdatePanning();\n\t}\n\t\n\treturn true;\n}\n\n// Clear view state for a graph\nexport function clearViewState(graphId: string) {\n\tviewStateCache.delete(graphId);\n}","// This file is the only owner of automatic positions. It gives ELK the exact\n// measured sizes, reads ELK's complete result, and does not repair that result.\n\nimport type {\n\tElkExtendedEdge,\n\tElkLabel,\n\tElkNode,\n\tElkPoint,\n} from \"elkjs/lib/elk-api\";\n\nimport {\n\tMeasuredDiagram,\n\tMeasuredEdge,\n\tMeasuredGroup,\n\tMeasuredNode,\n\tPoint,\n\tResolvedEdge,\n\tResolvedGroup,\n\tResolvedLayout,\n\tResolvedNode,\n} from \"./geometry\";\n\n/** AutomaticLayoutOptions are the choices a caller may make before layout. */\nexport interface AutomaticLayoutOptions {\n\tdirection: \"UP\" | \"DOWN\" | \"LEFT\" | \"RIGHT\";\n\tnodeSpacing: number;\n\tlayerSpacing: number;\n}\n\nconst defaultOptions: AutomaticLayoutOptions = {\n\tdirection: \"DOWN\",\n\tnodeSpacing: 80,\n\tlayerSpacing: 80,\n};\n\n/**\n * resolveAutomatic runs one compound ELK layout with exact measured sizes.\n * ELK errors and incomplete results are returned to the caller unchanged.\n */\nexport async function resolveAutomatic(\n\tdiagram: MeasuredDiagram,\n\toptions: Partial = {},\n): Promise {\n\tconst settings = {...defaultOptions, ...options};\n\tconst graph = buildLayoutTree(diagram, settings);\n\tconst ELK = await import(\"elkjs/lib/elk.bundled.js\").then(module => module.default);\n\tconst elk = new ELK();\n\tconst result = await elk.layout(graph);\n\treturn readResolvedLayout(diagram, result);\n}\n\n/**\n * buildLayoutTree creates one ELK tree. Nodes belong to their declared group,\n * groups belong to their declared parent, and each edge belongs to the nearest\n * group shared by both connected nodes.\n */\nfunction buildLayoutTree(\n\tdiagram: MeasuredDiagram,\n\toptions: AutomaticLayoutOptions,\n): ElkNode {\n\tconst nodes = new Map(diagram.nodes.map(node => [node.id, node]));\n\tconst groups = new Map(diagram.groups.map(group => [group.id, group]));\n\tcheckMeasuredLinks(diagram, nodes, groups);\n\n\tconst childrenByParent = new Map();\n\tfor (const node of diagram.nodes) {\n\t\tappend(childrenByParent, node.parentId, node);\n\t}\n\tconst groupsByParent = new Map();\n\tfor (const group of diagram.groups) {\n\t\tappend(groupsByParent, group.parentId, group);\n\t}\n\n\tconst elkGroups = new Map();\n\tconst buildGroup = (group: MeasuredGroup): ElkNode => {\n\t\tconst child: ElkNode = {\n\t\t\tid: group.id,\n\t\t\tchildren: [\n\t\t\t\t...(groupsByParent.get(group.id) ?? []).map(buildGroup),\n\t\t\t\t...(childrenByParent.get(group.id) ?? []).map(buildNode),\n\t\t\t],\n\t\t\tedges: [],\n\t\t\tlabels: [{\n\t\t\t\ttext: group.id,\n\t\t\t\twidth: group.titleSize.width,\n\t\t\t\theight: group.titleSize.height,\n\t\t\t}],\n\t\t\tlayoutOptions: groupLayoutOptions(options, group.titleSize.height),\n\t\t};\n\t\telkGroups.set(group.id, child);\n\t\treturn child;\n\t};\n\n\tconst root: ElkNode = {\n\t\tid: diagram.id,\n\t\tchildren: [\n\t\t\t...(groupsByParent.get(undefined) ?? []).map(buildGroup),\n\t\t\t...(childrenByParent.get(undefined) ?? []).map(buildNode),\n\t\t],\n\t\tedges: [],\n\t\tlayoutOptions: rootLayoutOptions(options),\n\t};\n\n\tfor (const edge of diagram.edges) {\n\t\tconst owner = lowestCommonGroup(edge, nodes, groups);\n\t\tconst container = owner ? elkGroups.get(owner) : root;\n\t\tif (!container) {\n\t\t\tthrow new Error(`edge ${edge.id} has missing layout group ${owner}`);\n\t\t}\n\t\tcontainer.edges?.push(buildEdge(edge));\n\t}\n\treturn root;\n}\n\n/** buildNode passes the measured node size to ELK without padding or guesses. */\nfunction buildNode(node: MeasuredNode): ElkNode {\n\treturn {\n\t\tid: node.id,\n\t\twidth: node.size.width,\n\t\theight: node.size.height,\n\t\tlayoutOptions: {\n\t\t\t\"elk.nodeSize.constraints\": \"FIXED_SIZE\",\n\t\t\t\"elk.portConstraints\": \"FREE\",\n\t\t},\n\t};\n}\n\n/** buildEdge passes the exact label size and stable endpoint IDs to ELK. */\nfunction buildEdge(edge: MeasuredEdge): ElkExtendedEdge {\n\treturn {\n\t\tid: edge.id,\n\t\tsources: [edge.sourceId],\n\t\ttargets: [edge.targetId],\n\t\tlabels: edge.labelSize ? [{\n\t\t\ttext: edge.id,\n\t\t\twidth: edge.labelSize.width,\n\t\t\theight: edge.labelSize.height,\n\t\t\tlayoutOptions: {\n\t\t\t\t\"elk.edgeLabels.inline\": \"false\",\n\t\t\t\t\"elk.edgeLabels.placement\": \"CENTER\",\n\t\t\t},\n\t\t}] : [],\n\t};\n}\n\n/** rootLayoutOptions set the rules shared by the full compound layout. */\nfunction rootLayoutOptions(options: AutomaticLayoutOptions): Record {\n\treturn {\n\t\t\"elk.algorithm\": \"layered\",\n\t\t\"elk.direction\": options.direction,\n\t\t\"elk.edgeRouting\": \"ORTHOGONAL\",\n\t\t\"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n\t\t\"elk.padding\": \"[top=40,left=40,bottom=40,right=40]\",\n\t\t\"elk.spacing.nodeNode\": String(options.nodeSpacing),\n\t\t\"elk.spacing.componentComponent\": String(options.nodeSpacing),\n\t\t\"elk.layered.spacing.nodeNodeBetweenLayers\": String(options.layerSpacing),\n\t\t\"elk.layered.spacing.edgeNodeBetweenLayers\": \"30\",\n\t\t\"elk.layered.spacing.edgeEdgeBetweenLayers\": \"20\",\n\t\t\"elk.layered.nodePlacement.strategy\": \"NETWORK_SIMPLEX\",\n\t\t\"elk.layered.nodePlacement.favorStraightEdges\": \"true\",\n\t\t\"elk.layered.crossingMinimization.strategy\": \"LAYER_SWEEP\",\n\t\t\"elk.layered.unnecessaryBendpoints\": \"true\",\n\t\t\"elk.edgeLabels.inline\": \"false\",\n\t\t\"elk.edgeLabels.avoidOverlap\": \"true\",\n\t\t\"elk.layered.edgeLabels.sideSelection\": \"SMART_DOWN\",\n\t\t\"elk.spacing.edgeLabel\": \"12\",\n\t};\n}\n\n/** groupLayoutOptions reserve title space and keep each group's children together. */\nfunction groupLayoutOptions(\n\toptions: AutomaticLayoutOptions,\n\ttitleHeight: number,\n): Record {\n\treturn {\n\t\t...rootLayoutOptions(options),\n\t\t\"elk.padding\": `[top=${titleHeight + 30},left=30,bottom=30,right=30]`,\n\t\t\"elk.nodeLabels.placement\": \"[H_LEFT,V_TOP,INSIDE]\",\n\t\t\"elk.spacing.labelNode\": \"10\",\n\t};\n}\n\n/**\n * checkMeasuredLinks rejects broken ownership and endpoints before calling ELK,\n * so ELK never has to guess what the model meant.\n */\nfunction checkMeasuredLinks(\n\tdiagram: MeasuredDiagram,\n\tnodes: Map,\n\tgroups: Map,\n): void {\n\tfor (const node of diagram.nodes) {\n\t\tif (node.parentId && !groups.has(node.parentId)) {\n\t\t\tthrow new Error(`node ${node.id} has missing parent group ${node.parentId}`);\n\t\t}\n\t}\n\tfor (const group of diagram.groups) {\n\t\tif (group.parentId && !groups.has(group.parentId)) {\n\t\t\tthrow new Error(`group ${group.id} has missing parent group ${group.parentId}`);\n\t\t}\n\t}\n\tfor (const edge of diagram.edges) {\n\t\tif (!nodes.has(edge.sourceId) || !nodes.has(edge.targetId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`edge ${edge.id} connects missing node ${edge.sourceId} or ${edge.targetId}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/** lowestCommonGroup finds the nearest boundary that contains both endpoints. */\nfunction lowestCommonGroup(\n\tedge: MeasuredEdge,\n\tnodes: Map,\n\tgroups: Map,\n): string | undefined {\n\tconst sourceGroups = parentGroups(nodes.get(edge.sourceId)?.parentId, groups);\n\tconst targetGroups = new Set(parentGroups(nodes.get(edge.targetId)?.parentId, groups));\n\treturn sourceGroups.find(group => targetGroups.has(group));\n}\n\n/** parentGroups returns parent IDs from nearest to farthest. */\nfunction parentGroups(\n\tparentId: string | undefined,\n\tgroups: Map,\n): string[] {\n\tconst result: string[] = [];\n\tlet current = parentId;\n\twhile (current) {\n\t\tresult.push(current);\n\t\tcurrent = groups.get(current)?.parentId;\n\t}\n\treturn result;\n}\n\n/** readResolvedLayout changes ELK's parent-relative values into diagram positions. */\nfunction readResolvedLayout(\n\tdiagram: MeasuredDiagram,\n\troot: ElkNode,\n): ResolvedLayout {\n\tif (root.width === undefined || root.height === undefined) {\n\t\tthrow new Error(`ELK did not size diagram ${diagram.id}`);\n\t}\n\tconst nodes: ResolvedNode[] = [];\n\tconst groups: ResolvedGroup[] = [];\n\tconst edges: ResolvedEdge[] = [];\n\tconst measuredNodes = new Map(diagram.nodes.map(node => [node.id, node]));\n\tconst measuredGroups = new Map(diagram.groups.map(group => [group.id, group]));\n\n\treadContainer(\n\t\troot,\n\t\t0,\n\t\t0,\n\t\tmeasuredNodes,\n\t\tmeasuredGroups,\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t);\n\treturn {\n\t\tdiagramId: diagram.id,\n\t\tbounds: {x: 0, y: 0, width: root.width, height: root.height},\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t};\n}\n\n/**\n * readContainer walks one ELK group, adding its parent's position exactly once\n * to each child, edge point, and label box.\n */\nfunction readContainer(\n\tcontainer: ElkNode,\n\tparentX: number,\n\tparentY: number,\n\tmeasuredNodes: Map,\n\tmeasuredGroups: Map,\n\tnodes: ResolvedNode[],\n\tgroups: ResolvedGroup[],\n\tedges: ResolvedEdge[],\n): void {\n\tconst containerX = parentX + (container.x ?? 0);\n\tconst containerY = parentY + (container.y ?? 0);\n\tfor (const child of container.children ?? []) {\n\t\tconst childX = containerX + requiredNumber(child.x, child.id, \"x\");\n\t\tconst childY = containerY + requiredNumber(child.y, child.id, \"y\");\n\t\tconst width = requiredNumber(child.width, child.id, \"width\");\n\t\tconst height = requiredNumber(child.height, child.id, \"height\");\n\t\tconst measuredNode = measuredNodes.get(child.id);\n\t\tif (measuredNode) {\n\t\t\tnodes.push({\n\t\t\t\tid: child.id,\n\t\t\t\tparentId: measuredNode.parentId,\n\t\t\t\tbounds: {x: childX, y: childY, width, height},\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconst measuredGroup = measuredGroups.get(child.id);\n\t\tif (!measuredGroup) {\n\t\t\tthrow new Error(`ELK returned unknown node ${child.id}`);\n\t\t}\n\t\tconst label = requiredLabel(child.labels?.[0], child.id);\n\t\tgroups.push({\n\t\t\tid: child.id,\n\t\t\tparentId: measuredGroup.parentId,\n\t\t\tbounds: {x: childX, y: childY, width, height},\n\t\t\ttitleBounds: {\n\t\t\t\tx: childX + requiredNumber(label.x, child.id, \"title x\"),\n\t\t\t\ty: childY + requiredNumber(label.y, child.id, \"title y\"),\n\t\t\t\twidth: requiredNumber(label.width, child.id, \"title width\"),\n\t\t\t\theight: requiredNumber(label.height, child.id, \"title height\"),\n\t\t\t},\n\t\t});\n\t\treadContainer(\n\t\t\tchild,\n\t\t\tcontainerX,\n\t\t\tcontainerY,\n\t\t\tmeasuredNodes,\n\t\t\tmeasuredGroups,\n\t\t\tnodes,\n\t\t\tgroups,\n\t\t\tedges,\n\t\t);\n\t}\n\tfor (const edge of container.edges ?? []) {\n\t\tedges.push(readEdge(edge, containerX, containerY));\n\t}\n}\n\n/** readEdge copies every ELK section and label without choosing new positions. */\nfunction readEdge(edge: ElkExtendedEdge, offsetX: number, offsetY: number): ResolvedEdge {\n\tconst sections = (edge.sections ?? []).map(section => [\n\t\taddOffset(section.startPoint, offsetX, offsetY),\n\t\t...(section.bendPoints ?? []).map(point => addOffset(point, offsetX, offsetY)),\n\t\taddOffset(section.endPoint, offsetX, offsetY),\n\t]);\n\tconst label = edge.labels?.[0];\n\treturn {\n\t\tid: edge.id,\n\t\tsourceId: requiredEndpoint(edge.sources, edge.id, \"source\"),\n\t\ttargetId: requiredEndpoint(edge.targets, edge.id, \"target\"),\n\t\tsections,\n\t\tlabelBounds: label ? {\n\t\t\tx: offsetX + requiredNumber(label.x, edge.id, \"label x\"),\n\t\t\ty: offsetY + requiredNumber(label.y, edge.id, \"label y\"),\n\t\t\twidth: requiredNumber(label.width, edge.id, \"label width\"),\n\t\t\theight: requiredNumber(label.height, edge.id, \"label height\"),\n\t\t} : undefined,\n\t};\n}\n\n/** addOffset changes one parent-relative ELK point into a diagram point. */\nfunction addOffset(point: ElkPoint, offsetX: number, offsetY: number): Point {\n\treturn {x: offsetX + point.x, y: offsetY + point.y};\n}\n\n/** requiredNumber rejects incomplete ELK output instead of filling in zero. */\nfunction requiredNumber(\n\tvalue: number | undefined,\n\telementId: string,\n\tfield: string,\n): number {\n\tif (value === undefined) {\n\t\tthrow new Error(`ELK omitted ${field} for ${elementId}`);\n\t}\n\treturn value;\n}\n\n/** requiredEndpoint requires each MDL relationship to have exactly one endpoint. */\nfunction requiredEndpoint(\n\tendpoints: string[],\n\tedgeId: string,\n\tfield: string,\n): string {\n\tif (endpoints.length !== 1) {\n\t\tthrow new Error(`ELK returned ${endpoints.length} ${field}s for edge ${edgeId}`);\n\t}\n\treturn endpoints[0];\n}\n\n/** requiredLabel rejects a group whose measured title was not placed by ELK. */\nfunction requiredLabel(label: ElkLabel | undefined, groupId: string): ElkLabel {\n\tif (!label) {\n\t\tthrow new Error(`ELK omitted title for group ${groupId}`);\n\t}\n\treturn label;\n}\n\n/** append adds a child to one parent entry while keeping model order. */\nfunction append(\n\titems: Map,\n\tparentId: string | undefined,\n\titem: T,\n): void {\n\tconst siblings = items.get(parentId) ?? [];\n\tsiblings.push(item);\n\titems.set(parentId, siblings);\n}\n","// This file connects the editable graph to the layout pipeline. It measures\n// the current SVG once, asks ELK for one complete answer, validates that answer,\n// and returns positions without changing them.\n\nimport {resolveAutomatic} from \"./automatic-layout\";\nimport {\n\tMeasuredDiagram,\n\tMeasuredEdge,\n\tMeasuredGroup,\n\tMeasuredNode,\n\tRect,\n\tResolvedLayout,\n\tValidatedLayout,\n\tvalidateResolvedLayout,\n} from \"./geometry\";\nimport {Edge, GraphData, Group, LayoutDirection, Node} from \"./graph\";\nimport {calculateEdgeVertices} from \"./edge-utils\";\nimport {resolveManual} from \"./manual-layout\";\n\n/** LayoutOptions are the explicit choices available to editor and CLI callers. */\nexport interface LayoutOptions {\n\tdirection?: LayoutDirection;\n\tnodeSpacing?: number;\n\tlayerSpacing?: number;\n\tcompactLayout?: boolean;\n}\n\n/** GraphLayout carries checked positions back to the editable graph. */\nexport interface GraphLayout {\n\tvalidated: ValidatedLayout;\n\tnodes: Array<{id: string; x: number; y: number}>;\n\tgroups: Array<{id: string; bounds: Rect; titleBounds: Rect}>;\n\tedges: Array<{\n\t\tid: string;\n\t\tsections: Array>;\n\t\tlabelBounds?: Rect;\n\t}>;\n}\n\n/**\n * autoLayout measures, lays out, and validates a graph. It does not catch\n * errors because callers must not save a diagram after any failed step.\n */\nexport async function autoLayout(\n\tgraph: GraphData,\n\toptions: LayoutOptions = {},\n): Promise {\n\tawait document.fonts.ready;\n\tconst measured = measureGraph(graph);\n\tconst spacing = options.compactLayout ? 56 : 80;\n\tconst resolved = await resolveAutomatic(measured, {\n\t\tdirection: options.direction ?? graph.layoutDirection ?? \"DOWN\",\n\t\tnodeSpacing: options.nodeSpacing ?? spacing,\n\t\tlayerSpacing: options.layerSpacing ?? spacing,\n\t});\n\tconst validated = validateResolvedLayout(measured, resolved);\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\treturn {\n\t\tvalidated,\n\t\tnodes: validated.nodes.map(node => {\n\t\t\tconst measuredNode = measuredNodes.get(node.id);\n\t\t\tif (!measuredNode) {\n\t\t\t\tthrow new Error(`validated layout contains unknown node ${node.id}`);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tid: node.id,\n\t\t\t\tx: node.bounds.x + measuredNode.centerOffset.x,\n\t\t\t\ty: node.bounds.y + measuredNode.centerOffset.y,\n\t\t\t};\n\t\t}),\n\t\tgroups: validated.groups.map(group => ({\n\t\t\tid: group.id,\n\t\t\tbounds: group.bounds,\n\t\t\ttitleBounds: group.titleBounds,\n\t\t})),\n\t\tedges: validated.edges.map(edge => ({\n\t\t\tid: edge.id,\n\t\t\tsections: edge.sections,\n\t\t\tlabelBounds: edge.labelBounds,\n\t\t})),\n\t};\n}\n\n/**\n * validateCurrentLayout adapts the fully drawn editor state into the manual\n * layout contract. It never reads automatic positions to fill missing data.\n */\nexport function validateCurrentLayout(graph: GraphData): ValidatedLayout {\n\tconst measured = measureGraph(graph);\n\tconst measuredNodes = new Map(measured.nodes.map(node => [node.id, node]));\n\tconst nodes = graph.nodes().map(node => {\n\t\tconst measuredNode = measuredNodes.get(node.id);\n\t\tif (!measuredNode) {\n\t\t\tthrow new Error(`measured diagram omitted node ${node.id}`);\n\t\t}\n\t\treturn {\n\t\t\tid: node.id,\n\t\t\tparentId: measuredNode.parentId,\n\t\t\tbounds: {\n\t\t\t\tx: node.x - measuredNode.centerOffset.x,\n\t\t\t\ty: node.y - measuredNode.centerOffset.y,\n\t\t\t\t...measuredNode.size,\n\t\t\t},\n\t\t};\n\t});\n\tconst groups = Array.from(graph.groupsMap.values()).map(group => {\n\t\tif (!group.ref?.isConnected) {\n\t\t\tthrow new Error(`group ${group.id} must be drawn before saving`);\n\t\t}\n\t\tconst boundary = group.ref.querySelector(\"rect\");\n\t\tconst title = group.ref.querySelector(\"text\");\n\t\tif (!(boundary instanceof SVGGraphicsElement) || !(title instanceof SVGGraphicsElement)) {\n\t\t\tthrow new Error(`group ${group.id} is missing its boundary or title`);\n\t\t}\n\t\treturn {\n\t\t\tid: group.id,\n\t\t\tparentId: measured.groups.find(candidate => candidate.id === group.id)?.parentId,\n\t\t\tbounds: copyBox(boundary.getBBox()),\n\t\t\ttitleBounds: copyBox(title.getBBox()),\n\t\t};\n\t});\n\tconst edges = graph.edges.map(edge => {\n\t\tconst sections = edge.layoutSections ?? [calculateEdgeVertices(edge)];\n\t\tconst labelBounds = edge.layoutLabelBounds ?? edge.labelBounds;\n\t\tif (edge.label.trim().length > 0 && !labelBounds) {\n\t\t\tthrow new Error(`edge ${edge.id} is missing its label position`);\n\t\t}\n\t\treturn {\n\t\t\tid: edge.id,\n\t\t\tsourceId: edge.from.id,\n\t\t\ttargetId: edge.to.id,\n\t\t\tsections,\n\t\t\tlabelBounds: labelBounds ? {...labelBounds} : undefined,\n\t\t};\n\t});\n\tconst snapshot: ResolvedLayout = {\n\t\tdiagramId: graph.id,\n\t\tbounds: contentBounds(nodes.map(node => node.bounds), groups, edges),\n\t\tnodes,\n\t\tgroups,\n\t\tedges,\n\t};\n\treturn resolveManual(measured, snapshot);\n}\n\n/**\n * measureGraph reads the exact node and label boxes already drawn by the\n * browser. Parent IDs come only from the model's declared groups.\n */\nfunction measureGraph(graph: GraphData): MeasuredDiagram {\n\tconst {nodeParents, groupParents} = declaredParents(graph);\n\treturn {\n\t\tid: graph.id,\n\t\tnodes: graph.nodes().map(node => measureNode(node, nodeParents.get(node.id))),\n\t\tgroups: Array.from(graph.groupsMap.values()).map(group =>\n\t\t\tmeasureGroup(group, groupParents.get(group.id)),\n\t\t),\n\t\tedges: graph.edges.map(measureEdge),\n\t};\n}\n\n/** measureNode returns the complete box drawn for one node and its center. */\nfunction measureNode(node: Node, parentId: string | undefined): MeasuredNode {\n\tif (!node.ref?.isConnected) {\n\t\tthrow new Error(`node ${node.id} must be drawn before layout`);\n\t}\n\tconst bounds = node.ref.getBBox();\n\tcheckMeasuredBox(node.id, bounds);\n\treturn {\n\t\tid: node.id,\n\t\tparentId,\n\t\tsize: {width: bounds.width, height: bounds.height},\n\t\tcontentSize: {width: bounds.width, height: bounds.height},\n\t\tcenterOffset: {x: -bounds.x, y: -bounds.y},\n\t\tshape: node.style.shape ?? \"Box\",\n\t};\n}\n\n/** measureGroup reads the title box already drawn by the browser. */\nfunction measureGroup(group: Group, parentId: string | undefined): MeasuredGroup {\n\tif (!group.ref?.isConnected) {\n\t\tthrow new Error(`group ${group.id} must be drawn before layout`);\n\t}\n\tconst title = group.ref.querySelector(\"text\");\n\tif (!(title instanceof SVGGraphicsElement)) {\n\t\tthrow new Error(`group ${group.id} is missing its drawn title`);\n\t}\n\tconst bounds = title.getBBox();\n\tcheckMeasuredBox(group.id, bounds);\n\treturn {\n\t\tid: group.id,\n\t\tparentId,\n\t\ttitleSize: {width: bounds.width, height: bounds.height},\n\t};\n}\n\n/** measureEdge uses the label background that the current SVG already drew. */\nfunction measureEdge(edge: Edge): MeasuredEdge {\n\tconst hasLabel = edge.label.trim().length > 0;\n\tif (hasLabel && !edge.labelBounds) {\n\t\tthrow new Error(`edge ${edge.id} must be drawn before layout`);\n\t}\n\treturn {\n\t\tid: edge.id,\n\t\tsourceId: edge.from.id,\n\t\ttargetId: edge.to.id,\n\t\tlabelSize: hasLabel ? {\n\t\t\twidth: edge.labelBounds?.width ?? 0,\n\t\t\theight: edge.labelBounds?.height ?? 0,\n\t\t} : undefined,\n\t};\n}\n\n/**\n * declaredParents records model ownership and rejects elements that appear in\n * more than one group. Visual position never decides ownership.\n */\nfunction declaredParents(graph: GraphData): {\n\tnodeParents: Map;\n\tgroupParents: Map;\n} {\n\tconst nodeParents = new Map();\n\tconst groupParents = new Map();\n\tfor (const group of graph.groupsMap.values()) {\n\t\tfor (const member of group.nodes) {\n\t\t\tconst parents = isGroup(member) ? groupParents : nodeParents;\n\t\t\tconst existing = parents.get(member.id);\n\t\t\tif (existing && existing !== group.id) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`${isGroup(member) ? \"group\" : \"node\"} ${member.id} belongs to both ` +\n\t\t\t\t\t`${existing} and ${group.id}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tparents.set(member.id, group.id);\n\t\t}\n\t}\n\treturn {nodeParents, groupParents};\n}\n\n/** checkMeasuredBox rejects missing or invalid browser measurements. */\nfunction checkMeasuredBox(id: string, box: Rect): void {\n\tif (\n\t\t!Number.isFinite(box.x) ||\n\t\t!Number.isFinite(box.y) ||\n\t\t!Number.isFinite(box.width) ||\n\t\t!Number.isFinite(box.height) ||\n\t\tbox.width <= 0 ||\n\t\tbox.height <= 0\n\t) {\n\t\tthrow new Error(`browser returned invalid measured box for ${id}`);\n\t}\n}\n\n/** contentBounds returns the smallest box that contains all drawn geometry. */\nfunction contentBounds(\n\tnodes: Rect[],\n\tgroups: Array<{bounds: Rect; titleBounds: Rect}>,\n\tedges: Array<{sections: Array>; labelBounds?: Rect}>,\n): Rect {\n\tconst boxes = [\n\t\t...nodes,\n\t\t...groups.flatMap(group => [group.bounds, group.titleBounds]),\n\t\t...edges.flatMap(edge => edge.labelBounds ? [edge.labelBounds] : []),\n\t];\n\tconst points = edges.flatMap(edge => edge.sections.flat());\n\tif (boxes.length === 0 && points.length === 0) {\n\t\treturn {x: 0, y: 0, width: 0, height: 0};\n\t}\n\tconst left = Math.min(\n\t\t...boxes.map(box => box.x),\n\t\t...points.map(point => point.x),\n\t);\n\tconst top = Math.min(\n\t\t...boxes.map(box => box.y),\n\t\t...points.map(point => point.y),\n\t);\n\tconst right = Math.max(\n\t\t...boxes.map(box => box.x + box.width),\n\t\t...points.map(point => point.x),\n\t);\n\tconst bottom = Math.max(\n\t\t...boxes.map(box => box.y + box.height),\n\t\t...points.map(point => point.y),\n\t);\n\treturn {x: left, y: top, width: right - left, height: bottom - top};\n}\n\n/** copyBox copies the browser's read-only SVG box into the layout contract. */\nfunction copyBox(box: DOMRect): Rect {\n\treturn {x: box.x, y: box.y, width: box.width, height: box.height};\n}\n\n/** isGroup tells group boundaries from nodes without using display names. */\nfunction isGroup(member: Node | Group): member is Group {\n\treturn \"nodes\" in member;\n}\n","// Manual layout has its own entry point. It never fills missing positions from\n// automatic layout and never changes the positions a user authored.\n\nimport {\n\tMeasuredDiagram,\n\tResolvedLayout,\n\tValidatedLayout,\n\tvalidateResolvedLayout,\n} from \"./geometry\";\n\n/**\n * resolveManual checks one complete saved layout against the current measured\n * diagram. Missing or old IDs, incomplete routes, and collisions are errors.\n */\nexport function resolveManual(\n\tmeasured: MeasuredDiagram,\n\tcompleteSnapshot: ResolvedLayout,\n): ValidatedLayout {\n\treturn validateResolvedLayout(measured, completeSnapshot);\n}\n","import {GraphData, LayoutDirection, NodeLink} from \"./graph-view/graph\";\n\n\ninterface Model {\n\tname: string\n\tdescription: string\n\tversion: string\n\tmodel: {\n\t\tenterprise: {\n\t\t\tname: string\n\t\t}\n\t\tpeople: Element[]\n\t\tsoftwareSystems: Element[]\n\t\tdeploymentNodes: Element[]\n\t}\n\tviews: {\n\t\tsystemLandscapeViews: View[]\n\t\tcontainerViews: View[]\n\t\tcomponentViews: View[]\n\t\tdynamicViews: View[]\n\t\tdeploymentViews: View[]\n\t\tstyles: {\n\t\t\telements: {\n\t\t\t\t[key: string]: string\n\t\t\t}[];\n\t\t\trelationships: {\n\t\t\t\t[key: string]: string\n\t\t\t}[]\n\t\t}\n\t}\n}\n\ninterface Layouts {\n\t[key: string]: { // keyed by view key\n\t\t[key: string]: { x: number; y: number } // keyed by element id\n\t}\n}\n\ninterface Element {\n\tid: string;\n\tname: string;\n\ttechnology?: string;\n\tdescription?: string;\n\turl?: string;\n\tparent?: Element;\n\ttags?: string;\n\tlocation?: string;\n\tcontainers?: Element[];\n\tcomponents?: Element[];\n\trelationships?: Relation[];\n\tproperties?: { [key: string]: string }\n\tchildren?: Element[];\n\tinfrastructureNodes?: Element[];\n}\n\ninterface Relation {\n\tid: string;\n\tdescription: string;\n\ttags: string;\n\tsourceId: string;\n\tdestinationId: string;\n\ttechnology: string;\n\tinteractionStyle: string;\n}\n\ntype RankDirection = 'TopBottom' | 'BottomTop' | 'LeftRight' | 'RightLeft';\n\ninterface View {\n\tkey: string;\n\ttitle: string;\n\tdescription: string\n\tautomaticLayout?: {\n\t\trankDirection?: RankDirection;\n\t};\n\telements: {\n\t\tid: string\n\t}[];\n\trelationships: {\n\t\tid: string;\n\t\tvertices: { x: number; y: number }[];\n\t\trouting: string; // takes priority over style\n\t}[];\n\tsoftwareSystemId: string;\n}\n\nconst layoutDirections: Record = {\n\tTopBottom: 'DOWN',\n\tBottomTop: 'UP',\n\tLeftRight: 'RIGHT',\n\tRightLeft: 'LEFT',\n};\n\ninterface Metadata {\n\tname: string\n\tdescription: string\n\tversion: string\n\telements: {\n\t\tid: string\n\t\ttags?: string;\n\t\tlocation?: string;\n\t\tproperties?: { [key: string]: string };\n\t\telementViewKey?: string;\n\t\ttechnology?: string;\n\t\turl?: string;\n\t}[]\n}\n\nexport type ViewsList = {\n\tkey: string;\n\ttitle: string;\n\tsection: string;\n}[]\n\nexport const parseView = (model: Model, layouts: Layouts, viewKey: string) => {\n\n\tconst elements = new Map();\n\tconst relations = new Map();\n\n\tconst collectRels = (el: Element) => {\n\t\tif (Array.isArray(el.relationships)) {\n\t\t\tel.relationships.forEach(rel => {\n\t\t\t\trelations.set(rel.id, rel)\n\t\t\t})\n\t\t}\n\t}\n\n\t// People\n\tmodel.model.people && model.model.people.forEach((el: Element) => {\n\t\telements.set(el.id, el)\n\t\tif (Array.isArray(el.relationships)) {\n\t\t\tel.relationships.forEach(rel => {\n\t\t\t\trelations.set(rel.id, rel)\n\t\t\t})\n\t\t}\n\t})\n\t// Software Systems\n\tmodel.model.softwareSystems && model.model.softwareSystems.forEach((el: Element) => {\n\t\telements.set(el.id, el)\n\t\tcollectRels(el)\n\n\t\tif (Array.isArray(el.containers)) {\n\t\t\tel.containers.forEach((el1: Element) => {\n\t\t\t\tel1.parent = el;\n\t\t\t\telements.set(el1.id, el1)\n\t\t\t\tcollectRels(el1)\n\t\t\t\tif (Array.isArray(el1.components)) {\n\t\t\t\t\tel1.components.forEach((el2: Element) => {\n\t\t\t\t\t\tel2.parent = el1;\n\t\t\t\t\t\telements.set(el2.id, el2)\n\t\t\t\t\t\tcollectRels(el2)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\t// Deployment Nodes\n\tif (model.model.deploymentNodes) {\n\t\tconst containerInstances = (el: any) => {\n\t\t\tel.containerInstances && el.containerInstances.forEach((item: any) => {\n\t\t\t\tconst el1 = {...elements.get(item.containerId), id: item.id}\n\t\t\t\telements.set(el1.id, el1)\n\t\t\t\tel1.parent = el\n\t\t\t\tcollectRels(item)\n\t\t\t})\n\t\t}\n\n\t\tconst recAddNodes = (el: Element, parent: Element) => {\n\t\t\tel.parent = parent;\n\t\t\telements.set(el.id, el)\n\t\t\tcollectRels(el)\n\t\t\tcontainerInstances(el)\n\t\t\tel.children && el.children.forEach((el1: Element) => recAddNodes(el1, el))\n\t\t\tel.infrastructureNodes && el.infrastructureNodes.forEach((el1: Element) => recAddNodes(el1, el))\n\t\t}\n\n\t\tmodel.model.deploymentNodes.forEach((el: Element) => recAddNodes(el, null))\n\t}\n\n\t// Create graph from selected view\n\tconst {view, section} = getView(model, viewKey)\n\n\tif (!view) return null\n\n\tconst graph = new GraphData(view.key, view.title || view.key)\n\tconst rankDirection = view.automaticLayout?.rankDirection\n\tgraph.layoutDirection = rankDirection ? layoutDirections[rankDirection] : undefined\n\tconst metadata: Metadata = {name: graph.name, description: view.description, version: model.version, elements: []}\n\tgraph.metadata = metadata\n\n\tif (!view.elements) return graph\n\n\t//grouping rules - elements that are groups will not be nodes\n\tconst groupingIDs: { [key: string]: boolean } = {}\n\tif (section == 'deploymentViews' || section == 'containerViews') {\n\t\tview.elements.forEach(ref => {\n\t\t\tconst el = elements.get(ref.id)\n\t\t\tif (el?.parent) {\n\t\t\t\tgroupingIDs[el.parent.id] = true\n\t\t\t}\n\t\t})\n\t} else if (view.softwareSystemId) {\n\t\t//don't show grouping if the element is listed in the view\n\t\tif (!view.elements.find(ref => ref.id == view.softwareSystemId))\n\t\t\tgroupingIDs[view.softwareSystemId] = true\n\t} else if (section == 'systemLandscapeViews') {\n\t\t// create a virtual parent element from enterprise\n\t\tconst p: Element = {id: '__enterprise__', ...model.model.enterprise}\n\t\telements.set(p.id, p)\n\t\tif (model.model.people) model.model.people.filter(el => el.location != 'External').forEach(el => el.parent = p)\n\t\tif (model.model.softwareSystems) model.model.softwareSystems.filter(el => el.location != 'External').forEach(el => el.parent = p)\n\t\tgroupingIDs[p.id] = true\n\t}\n\n\tconst styles = model.views.styles\n\n\t// Build color-to-variable mapping for CSS custom properties theming\n\tconst cssClassName = (tag: string) => tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n\t\n\tif (styles?.elements) {\n\t\tstyles.elements.forEach(s => {\n\t\t\tif (s.tag) {\n\t\t\t\tconst varPrefix = `--mdl-${cssClassName(s.tag)}`\n\t\t\t\tif (s.background) graph.colorToVarMap.set(s.background as string, `${varPrefix}-bg`)\n\t\t\t\tif (s.color) graph.colorToVarMap.set(s.color as string, `${varPrefix}-color`)\n\t\t\t\tif (s.stroke) graph.colorToVarMap.set(s.stroke as string, `${varPrefix}-stroke`)\n\t\t\t}\n\t\t})\n\t}\n\n\tif (styles?.relationships) {\n\t\tstyles.relationships.forEach(s => {\n\t\t\tif (s.tag) {\n\t\t\t\tconst varPrefix = `--mdl-rel-${cssClassName(s.tag)}`\n\t\t\t\tif (s.color) graph.colorToVarMap.set(s.color as string, `${varPrefix}-color`)\n\t\t\t}\n\t\t})\n\t}\n\n\t//nodes\n\tview.elements.forEach((ref) => {\n\t\t// except grouping elements\n\t\tif (groupingIDs[ref.id]) return\n\n\t\tconst el = elements.get(ref.id)\n\t\tconst elementViewKey = el ? lookupContainerViewKey(model, el.id) : undefined\n\n\t\tlet sub = ''\n\t\tlet style = {}\n\t\tif (el) {\n\t\t\tconst tags = el.tags.split(',')\n\t\t\tsub = tags[tags.length - 1] // subtitle is []\n\t\t\tif (el.technology)\n\t\t\t\tsub += ': ' + el.technology // or [: ]\n\n\t\t\ttags.forEach(tag => {\n\t\t\t\tconst s = styles && styles.elements && styles.elements.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t}\n\n\t\tgraph.addNode(\n\t\t\tref.id,\n\t\t\tel ? (el.name || ref.id) : ref.id,\n\t\t\tsub,\n\t\t\t(el && el.description) ? el.description : '',\n\t\t\tstyle,\n\t\t\tnodeLink(el, elementViewKey)\n\t\t)\n\t\tel && metadata.elements.push({\n\t\t\tid: el.id,\n\t\t\ttags: el.tags,\n\t\t\tlocation: el.location,\n\t\t\tproperties: el.properties,\n\t\t\telementViewKey,\n\t\t\ttechnology: el.technology,\n\t\t\turl: el.url\n\t\t})\n\t})\n\t//edges\n\tif (Array.isArray(view.relationships)) {\n\t\tview.relationships.forEach(ref => {\n\t\t\tconst rel = relations.get(ref.id)\n\t\t\tif (!rel) return;\n\n\t\t\tif (!graph.nodesMap.has(rel.sourceId)) {\n\t\t\t\tif (elements.has(rel.sourceId)) {\n\t\t\t\t\tconst el = elements.get(rel.sourceId)\n\t\t\t\t\tconsole.warn('Element not found in this view: ', el.id, el.name)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('Element not found: ', rel.sourceId)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!graph.nodesMap.has(rel.destinationId)) {\n\t\t\t\tif (elements.has(rel.destinationId)) {\n\t\t\t\t\tconst el = elements.get(rel.destinationId)\n\t\t\t\t\tconsole.warn('Element not found in this view: ', el.id, el.name)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.warn('Element not found: ', rel.destinationId)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet style: any = {}\n\t\t\trel.tags.split(',').forEach(tag => {\n\t\t\t\tconst s = styles && styles.relationships && styles.relationships.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t\tif (ref.routing) style.routing = ref.routing\n\n\t\t\tgraph.addEdge(rel.id, rel.sourceId, rel.destinationId, rel.description, ref.vertices, style)\n\t\t})\n\t}\n\n\t//groups\n\t//sort by depth to solve dependency\n\tconst level = (el: Element) => {\n\t\tlet i = 0\n\t\tfor (let p = el.parent; p; p = p.parent) i++;\n\t\treturn i\n\t}\n\tconst gElements = Object.keys(groupingIDs)\n\t\t.map(id => elements.get(id))\n\t\t.sort((a, b) => level(a) > level(b) ? -1 : 1)\n\n\tgElements.forEach(parent => {\n\t\tlet style = {}\n\t\tif (section == 'deploymentViews') {\n\t\t\tconst el = elements.get(parent.id)\n\t\t\tconst tags = el.tags.split(',')\n\t\t\ttags.forEach(tag => {\n\t\t\t\tconst s = styles && styles.elements && styles.elements.find(s => s.tag == tag)\n\t\t\t\ts && (style = {...style, ...s})\n\t\t\t})\n\t\t}\n\t\t\n\t\t// Filter group members more carefully to respect boundaries\n\t\tconst groupMembers = view.elements\n\t\t\t.map(ref => elements.get(ref.id))\n\t\t\t.filter(el => {\n\t\t\t\tif (!el || el.parent !== parent) return false;\n\t\t\t\t\n\t\t\t\t// For system landscape views, respect the location-based grouping\n\t\t\t\tif (section === 'systemLandscapeViews' && parent.id === '__enterprise__') {\n\t\t\t\t\t// Only include elements that are explicitly non-external\n\t\t\t\t\treturn el.location !== 'External';\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// For other view types, include if parent matches\n\t\t\t\treturn true;\n\t\t\t})\n\t\t\t.map(el => el.id);\n\t\t\n\t\t// Only create group if it has members\n\t\tif (groupMembers.length > 0) {\n\t\t\tgraph.addGroup(\n\t\t\t\tparent.id,\n\t\t\t\tparent.name,\n\t\t\t\tgroupMembers,\n\t\t\t\tstyle\n\t\t\t)\n\t\t}\n\t})\n\n\t//layout if any and init graph\n\tgraph.init(layouts[graph.id])\n\treturn graph\n}\n\nfunction nodeLink(element: Element | undefined, elementViewKey: string | undefined): NodeLink | undefined {\n\tif (elementViewKey) {\n\t\tconst encodedViewKey = encodeURIComponent(elementViewKey)\n\t\treturn {\n\t\t\thref: `?id=${encodedViewKey}`,\n\t\t\texportHref: `${encodedViewKey}.svg`,\n\t\t}\n\t}\n\tif (element?.url) {\n\t\treturn {\n\t\t\thref: element.url,\n\t\t\texportHref: element.url,\n\t\t}\n\t}\n\treturn undefined\n}\n\n// lookup the view in all Views sections in the model. return the view and the section\nfunction getView(model: Model, viewKey: string) {\n\tlet view: View = null, section: string = ''\n\tObject.keys(model.views).filter(s => s.endsWith('Views')).some((s: string) => {\n\t\treturn ((model.views as any)[s]).some((v: View) => {\n\t\t\tif (v.key == viewKey) {\n\t\t\t\tview = v\n\t\t\t\tsection = s\n\t\t\t\treturn true\n\t\t\t}\n\t\t})\n\t})\n\treturn {view, section}\n}\n\n\nfunction lookupContainerViewKey(model: Model, softwareSystemId: string) {\n\tconst view = model.views.containerViews?.find(candidate => candidate.softwareSystemId == softwareSystemId)\n\treturn view?.key\n}\n\nexport const listViews = (model: any) => {\n\tconst viewsList: ViewsList = []\n\tconst sections = Object.keys(model.views).filter(section => section.endsWith('Views'))\n\tsections.forEach(s => {\n\t\tmodel.views[s].forEach((v: View) => {\n\t\t\tviewsList.push({key: v.key, title: v.title || v.key, section: s})\n\t\t})\n\t})\n\treturn viewsList;\n}\n","import React, {FC} from \"react\";\nimport { isMac, getModifierKeyName, getModifierKeyProperty } from './utils/platform';\n\ninterface Combination {\n\tctrl?: boolean;\n\tshift?: boolean;\n\talt?: boolean;\n\twheel?: boolean\n\tkey?: string;\n\tclick?: boolean;\n}\n\ninterface Shortcut {\n\tid: string,\n\thelp: string,\n\tcombinations: Combination[]\n}\n\nexport const SAVE = 'save'\n\nexport const UNDO = 'undo'\nexport const REDO = 'redo'\nexport const ADD_VERTEX = 'add-vertex'\nexport const ADD_LABEL_VERTEX = 'add-label-vertex'\nexport const DEL_VERTEX = 'del-vertex'\n\nexport const ZOOM_IN = 'zoom-in'\nexport const ZOOM_OUT = 'zoom-out'\nexport const ZOOM_FIT = 'zoom-fit'\nexport const ZOOM_100 = 'zoom-100'\n\nexport const SELECT_ALL = 'select-all'\nexport const DESELECT = 'deselect'\n\nexport const MOVE_LEFT = 'move-left'\nexport const MOVE_RIGHT = 'move-right'\nexport const MOVE_UP = 'move-up'\nexport const MOVE_DOWN = 'move-down'\nexport const MOVE_LEFT_FINE = 'move-left-fine'\nexport const MOVE_RIGHT_FINE = 'move-right-fine'\nexport const MOVE_UP_FINE = 'move-up-fine'\nexport const MOVE_DOWN_FINE = 'move-down-fine'\n\nexport const PAN_VIEW = 'pan-view'\nexport const SELECT_ELEMENT = 'select-element'\nexport const MULTI_SELECT = 'multi-select'\nexport const BOX_SELECT = 'box-select'\nexport const MOVE_ELEMENTS = 'move-elements'\n\nexport const HELP = 'help'\n\n// New shortcuts for toolbar buttons\nexport const TOGGLE_DRAG_MODE = 'toggle_drag_mode'\nexport const ALIGN_HORIZONTAL = 'align_horizontal'\nexport const ALIGN_VERTICAL = 'align_vertical'\nexport const DISTRIBUTE_HORIZONTAL = 'distribute_horizontal'\nexport const DISTRIBUTE_VERTICAL = 'distribute_vertical'\nexport const AUTO_LAYOUT = 'auto_layout'\nexport const RESET_POSITION = 'reset_position'\nexport const TOGGLE_GRID = 'toggle_grid'\nexport const TOGGLE_SNAP_TO_GRID = 'toggle_snap_to_grid'\nexport const SNAP_ALL_TO_GRID = 'snap_all_to_grid'\n\nconst shortcuts: { name: string; list: Shortcut[] }[] = [\n\t{\n\t\tname: 'Help',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: HELP,\n\t\t\t\thelp: 'Show/hide this help',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{key: '?', shift: true},\n\t\t\t\t\t{key: 'F1', shift: true}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'File',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: SAVE,\n\t\t\t\thelp: 'Save',\n\t\t\t\tcombinations: [{key: 's', ctrl: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'History',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: UNDO,\n\t\t\t\thelp: 'Undo',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: 'z'},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: REDO,\n\t\t\t\thelp: 'Redo',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, shift: true, key: 'z'},\n\t\t\t\t\t{ctrl: true, key: 'y'},\n\t\t\t\t]\n\t\t\t}\n\n\t\t],\n\t},\n\t{\n\t\tname: 'Relationship editing',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ADD_VERTEX,\n\t\t\t\thelp: 'Add relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{alt: true, click: true},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ADD_LABEL_VERTEX,\n\t\t\t\thelp: 'Add label anchor relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{alt: true, shift: true, click: true},\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DEL_VERTEX,\n\t\t\t\thelp: 'Remove relationship vertex',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{key: 'DELETE'},\n\t\t\t\t\t{key: 'BACKSPACE'}\n\t\t\t\t]\n\t\t\t},\n\t\t]\n\t},\n\t{\n\t\tname: 'Zoom',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ZOOM_IN,\n\t\t\t\thelp: 'Zoom in',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: '='}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_OUT,\n\t\t\t\thelp: 'Zoom out',\n\t\t\t\tcombinations: [\n\t\t\t\t\t{ctrl: true, key: '-'}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_FIT,\n\t\t\t\thelp: 'Zoom - fit',\n\t\t\t\tcombinations: [{ctrl: true, key: '9'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ZOOM_100,\n\t\t\t\thelp: 'Zoom 100%',\n\t\t\t\tcombinations: [{ctrl: true, key: '0'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'wheel_zoom',\n\t\t\t\thelp: 'Zoom in/out with mouse wheel',\n\t\t\t\tcombinations: [{wheel: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Mouse Interactions',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: PAN_VIEW,\n\t\t\t\thelp: 'Pan view (drag empty space)',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: SELECT_ELEMENT,\n\t\t\t\thelp: 'Select element',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MULTI_SELECT,\n\t\t\t\thelp: 'Add/remove from selection',\n\t\t\t\tcombinations: [{shift: true, click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: BOX_SELECT,\n\t\t\t\thelp: 'Box selection (drag empty space)',\n\t\t\t\tcombinations: [{shift: true, click: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_ELEMENTS,\n\t\t\t\thelp: 'Move selected elements',\n\t\t\t\tcombinations: [{click: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Select',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: SELECT_ALL,\n\t\t\t\thelp: 'Select All',\n\t\t\t\tcombinations: [{ctrl: true, key: 'a'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DESELECT,\n\t\t\t\thelp: 'Deselect',\n\t\t\t\tcombinations: [{key: 'ESC'}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Move',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: MOVE_UP,\n\t\t\t\thelp: 'Move up (grid increment)',\n\t\t\t\tcombinations: [{key: 'UP'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_UP_FINE,\n\t\t\t\thelp: 'Move up (1 pixel)',\n\t\t\t\tcombinations: [{key: 'UP', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_RIGHT,\n\t\t\t\thelp: 'Move right (grid increment)',\n\t\t\t\tcombinations: [{key: 'RIGHT'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_RIGHT_FINE,\n\t\t\t\thelp: 'Move right (1 pixel)',\n\t\t\t\tcombinations: [{key: 'RIGHT', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_DOWN,\n\t\t\t\thelp: 'Move down (grid increment)',\n\t\t\t\tcombinations: [{key: 'DOWN'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_DOWN_FINE,\n\t\t\t\thelp: 'Move down (1 pixel)',\n\t\t\t\tcombinations: [{key: 'DOWN', shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_LEFT,\n\t\t\t\thelp: 'Move left (grid increment)',\n\t\t\t\tcombinations: [{key: 'LEFT'}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: MOVE_LEFT_FINE,\n\t\t\t\thelp: 'Move left (1 pixel)',\n\t\t\t\tcombinations: [{key: 'LEFT', shift: true}]\n\t\t\t},\n\t\t]\n\t},\n\t\t\t{\n\t\t\tname: 'View',\n\t\t\tlist: [\n\t\t\t\t{\n\t\t\t\t\tid: TOGGLE_DRAG_MODE,\n\t\t\t\t\thelp: 'Toggle between pan and select mode',\n\t\t\t\t\tcombinations: [{key: 't'}]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: RESET_POSITION,\n\t\t\t\t\thelp: 'Reset position and view',\n\t\t\t\t\tcombinations: [{key: 'Home', ctrl: true}]\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t{\n\t\tname: 'Alignment',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: ALIGN_HORIZONTAL,\n\t\t\t\thelp: 'Align selected elements horizontally',\n\t\t\t\tcombinations: [{key: 'h', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: ALIGN_VERTICAL,\n\t\t\t\thelp: 'Align selected elements vertically',\n\t\t\t\tcombinations: [{key: 'a', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DISTRIBUTE_HORIZONTAL,\n\t\t\t\thelp: 'Distribute selected elements horizontally',\n\t\t\t\tcombinations: [{key: 'h', ctrl: true, alt: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: DISTRIBUTE_VERTICAL,\n\t\t\t\thelp: 'Distribute selected elements vertically',\n\t\t\t\tcombinations: [{key: 'v', ctrl: true, alt: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Layout',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: AUTO_LAYOUT,\n\t\t\t\thelp: 'Auto layout all elements',\n\t\t\t\tcombinations: [{key: 'l', ctrl: true}]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tname: 'Grid',\n\t\tlist: [\n\t\t\t{\n\t\t\t\tid: TOGGLE_GRID,\n\t\t\t\thelp: 'Toggle grid visibility',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: TOGGLE_SNAP_TO_GRID,\n\t\t\t\thelp: 'Toggle snap to grid',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true, shift: true}]\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: SNAP_ALL_TO_GRID,\n\t\t\t\thelp: 'Snap all elements to grid',\n\t\t\t\tcombinations: [{key: 'g', ctrl: true, alt: true}]\n\t\t\t}\n\t\t]\n\t}\n]\n\nconst shortcutMap = shortcuts\n\t.reduce((lst, s) => lst.concat(s.list), [] as Shortcut[])\n\t.reduce<{ [k: string]: Shortcut }>((map, s) => {\n\t\tmap[s.id] = s;\n\t\treturn map\n\t}, {})\n\nconst checkKey = (e: KeyboardEvent | MouseEvent, shortcut: Shortcut, click: boolean, wheel: boolean) => {\n\treturn shortcut.combinations.some(c => {\n\t\tif (Boolean(c.shift) != e.shiftKey) return false\n\t\t// Use platform-appropriate modifier key\n\t\tif (c.ctrl) {\n\t\t\tconst modifierKey = getModifierKeyProperty(e as KeyboardEvent);\n\t\t\tif (!modifierKey) return false;\n\t\t}\n\t\tif (Boolean(c.alt) != e.altKey) return false\n\t\tif (click) return c.click\n\t\tif (wheel) return c.wheel\n\t\tif (c.key) {\n\t\t\tconst ke = e as KeyboardEvent\n\t\t\tif (c.key == 'DELETE') return ke.key == 'Delete'\n\t\t\tif (c.key == 'BACKSPACE') return ke.key == 'Backspace'\n\t\t\tif (c.key == 'ESC') return ke.key == 'Escape'\n\t\t\tif (c.key == 'UP') return ke.key == 'ArrowUp'\n\t\t\tif (c.key == 'DOWN') return ke.key == 'ArrowDown'\n\t\t\tif (c.key == 'LEFT') return ke.key == 'ArrowLeft'\n\t\t\tif (c.key == 'RIGHT') return ke.key == 'ArrowRight'\n\t\t\treturn c.key && ke.key && c.key.toLowerCase() == ke.key.toLowerCase()\n\t\t}\n\t\treturn false\n\t})\n}\n\nexport const findShortcut = (e: KeyboardEvent | MouseEvent, click = false, wheel = false) => {\n\t// Find all matching shortcuts\n\tconst matches = Object.keys(shortcutMap).filter(k => checkKey(e, shortcutMap[k], click, wheel))\n\t\n\tif (matches.length === 0) return undefined\n\tif (matches.length === 1) return matches[0]\n\t\n\t// If multiple matches, prefer the one with more specific modifiers\n\t// Sort by number of modifiers (shift, ctrl, alt) in descending order\n\tconst sortedMatches = matches.sort((a, b) => {\n\t\tconst aShortcut = shortcutMap[a]\n\t\tconst bShortcut = shortcutMap[b]\n\t\t\n\t\tconst aModifiers = aShortcut.combinations[0]\n\t\tconst bModifiers = bShortcut.combinations[0]\n\t\t\n\t\tconst aCount = (aModifiers.shift ? 1 : 0) + (aModifiers.ctrl ? 1 : 0) + (aModifiers.alt ? 1 : 0)\n\t\tconst bCount = (bModifiers.shift ? 1 : 0) + (bModifiers.ctrl ? 1 : 0) + (bModifiers.alt ? 1 : 0)\n\t\t\n\t\treturn bCount - aCount // Descending order (more modifiers first)\n\t})\n\t\n\treturn sortedMatches[0]\n}\n\nconst comboText = (c: Combination) => {\n\treturn [\n\t\tc.ctrl && getModifierKeyName().toUpperCase(),\n\t\tc.shift && 'SHIFT',\n\t\tc.alt && 'ALT',\n\t\tc.key && (c.key.length > 1 ? c.key : `\"${c.key.toUpperCase()}\"`),\n\t\tc.click && 'CLICK',\n\t\tc.wheel && 'WHEEL'\n\t].filter(Boolean).join(' + ')\n}\n\nexport const Help: FC = () => {\n\treturn
\n\t\t

Shortcuts

\n\t\t\n\t\t\t\n\t\t\t{\n\t\t\t\tshortcuts.map(section => <>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{section.list.map(item => \n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t)}\n\t\t\t\t)\n\t\t\t}\n\t\t\t\n\t\t
{section.name}
{item.combinations.map(comboText).join(', ')}{item.help}
\n\t
\n}\n","/**\n * Robust platform detection utilities\n */\n\n/**\n * Detects if the user is on a Mac platform using multiple detection methods\n * for maximum compatibility across browsers and future-proofing.\n */\nexport const isMac = (): boolean => {\n if (typeof navigator === 'undefined') return false;\n \n // Method 1: Check userAgentData (modern browsers, most reliable)\n if ('userAgentData' in navigator && (navigator as any).userAgentData) {\n const platform = (navigator as any).userAgentData.platform;\n if (platform && platform.toLowerCase().includes('mac')) {\n return true;\n }\n }\n \n // Method 2: Check userAgent string (widely supported)\n const userAgent = navigator.userAgent.toLowerCase();\n if (userAgent.includes('mac os') || userAgent.includes('macintosh')) {\n return true;\n }\n \n // Method 3: Check platform (fallback, deprecated but still widely supported)\n if (navigator.platform) {\n const platform = navigator.platform.toLowerCase();\n if (platform.includes('mac') || platform.includes('darwin')) {\n return true;\n }\n }\n \n // Method 4: Check for Mac-specific features as additional validation\n try {\n const testEvent = new KeyboardEvent('keydown', { metaKey: true });\n if (testEvent.metaKey !== undefined) {\n // Additional heuristic: Mac typically has different key layouts\n return /mac|darwin|os x/i.test(navigator.userAgent);\n }\n } catch (e) {\n // Ignore errors in older browsers\n }\n \n return false;\n};\n\n/**\n * Gets the appropriate modifier key name for the current platform\n */\nexport const getModifierKeyName = (): string => {\n return isMac() ? 'Cmd' : 'Ctrl';\n};\n\n/**\n * Gets the appropriate modifier key property for keyboard events\n */\nexport const getModifierKeyProperty = (event: KeyboardEvent): boolean => {\n return isMac() ? event.metaKey : event.ctrlKey;\n};\n\n/**\n * Gets the appropriate Alt key name for the current platform\n */\nexport const getAltKeyName = (): string => {\n return isMac() ? 'Option' : 'Alt';\n}; ","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/api.js\";\nimport ___CSS_LOADER_GET_URL_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/getUrl.js\";\nvar ___CSS_LOADER_URL_IMPORT_0___ = new URL(\"@fontsource/inter/files/inter-latin-400-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_1___ = new URL(\"@fontsource/inter/files/inter-latin-500-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_2___ = new URL(\"@fontsource/inter/files/inter-latin-600-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_URL_IMPORT_3___ = new URL(\"@fontsource/inter/files/inter-latin-700-normal.woff2\", import.meta.url);\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\nvar ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);\nvar ___CSS_LOADER_URL_REPLACEMENT_1___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_1___);\nvar ___CSS_LOADER_URL_REPLACEMENT_2___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_2___);\nvar ___CSS_LOADER_URL_REPLACEMENT_3___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_3___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `/* Local Inter files keep browser text measurement identical without internet access. */\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 400;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_0___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 500;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_1___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 600;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_2___}) format(\"woff2\");\n}\n\n@font-face {\n\tfont-family: \"Inter\";\n\tfont-style: normal;\n\tfont-display: swap;\n\tfont-weight: 700;\n\tsrc: url(${___CSS_LOADER_URL_REPLACEMENT_3___}) format(\"woff2\");\n}\n`, \"\",{\"version\":3,\"sources\":[\"webpack://./src/fonts.css\"],\"names\":[],\"mappings\":\"AAAA,uFAAuF;AACvF;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF;;AAEA;CACC,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,gBAAgB;CAChB,4DAAgF;AACjF\",\"sourcesContent\":[\"/* Local Inter files keep browser text measurement identical without internet access. */\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 400;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-400-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 500;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-500-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 600;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-600-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\\n@font-face {\\n\\tfont-family: \\\"Inter\\\";\\n\\tfont-style: normal;\\n\\tfont-display: swap;\\n\\tfont-weight: 700;\\n\\tsrc: url(\\\"@fontsource/inter/files/inter-latin-700-normal.woff2\\\") format(\\\"woff2\\\");\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `html, body, #root {\n height: 100%;\n}\n\nbody {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n color: #666;\n margin: 0;\n}\n\n#root {\n display: flex;\n flex-direction: column;\n}\n#root > div.graph {\n flex: 1;\n overflow: auto;\n position: relative;\n}\n\n.toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 4px 10px;\n background-color: #f0f0f0;\n border-bottom: 1px solid #cccccc;\n}\n\n.toolbar > div {\n display: flex;\n align-items: center;\n}\n\n.toolbar button {\n padding: 5px 8px;\n margin: 0 2px;\n cursor: pointer;\n}\n\n.toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Drag mode toggle button styles */\n.toolbar button.mode-toggle {\n position: relative;\n border: 1px solid #8f9fc9;\n width: 40px;\n min-height: 28px;\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\n border-color: #8f9fc9;\n color: white;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n}\n\n.toolbar button.mode-toggle:hover {\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\n border-color: #abb8db;\n}\n\n.toolbar button.mode-toggle.select-mode:active {\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\n}\n\n/* Pan mode and active toggle - darker blue */\n.toolbar button.mode-toggle.pan-mode,\n.toolbar button.active-toggle {\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n.toolbar button.mode-toggle.pan-mode:hover,\n.toolbar button.active-toggle:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\n.toolbar button.mode-toggle.pan-mode:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\n}\n\n/* Toggle buttons when inactive - gray styling */\n.toolbar button.inactive-toggle {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n.toolbar button.inactive-toggle:hover {\n background: #b1b1b1;\n}\n\n.toolbar button.inactive-toggle:active {\n background: #a1a1a1;\n}\n\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\n background: #c1c1c1;\n color: #999;\n border-color: #c1c1c1;\n box-shadow: none;\n}\n\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\n.toolbar button .fas {\n font-size: 1em;\n vertical-align: middle;\n}\n\n/* Zoom percentage display */\n.toolbar button.zoom-display {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\n padding: 6px 8px;\n text-align: center;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n margin-right: 25px; /* Large space between groups */\n}\n\n.toolbar-group:last-child {\n margin-right: 0; /* Remove right margin from the last group (help button) */\n}\n\nbutton {\n border: none;\n background: linear-gradient(to bottom, #4a90e2, #357abd);\n border-color: #2968a3;\n border-radius: 3px;\n padding: 6px 10px;\n color: #fff;\n outline: none;\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\n margin-right: 5px;\n min-width: 32px;\n min-height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n line-height: 1;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n position: relative;\n}\n\nbutton:hover {\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\n}\n\nbutton:active {\n background: linear-gradient(to bottom, #357abd, #2968a3);\n}\n\nbutton:last-child {\n margin-right: 0;\n}\n\n/* Save button when no changes - gray */\nbutton.action {\n background: #c1c1c1;\n color: #999;\n}\n\nbutton.action:active {\n background: #b1b1b1;\n}\n\n/* Save button when there are changes - orange */\nbutton.grp {\n background: linear-gradient(to bottom, #ee9564, #de7d48);\n margin-right: 0;\n}\n\nbutton.grp:active {\n background: #d67540;\n}\n\n/* Auto-arrange button with AI purple to blue gradient */\nbutton.auto-arrange {\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\n border-color: #4a4c93;\n}\n\nbutton.auto-arrange:hover {\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\n border-color: #5a5ca3;\n}\n\nbutton.auto-arrange:active {\n background: linear-gradient(135deg, #593B83, #357abd);\n border-color: #3a3c83;\n}\n\nselect {\n border: 1px solid #ccc;\n background: white;\n border-radius: 3px;\n padding: 3px 7px;\n color: #666;\n outline: none;\n margin-right: 5px;\n font-size: 12px;\n}\n\nselect:disabled {\n background: #f5f5f5;\n color: #999;\n}\n\nbutton:disabled {\n background: #c1c1c1;\n color: #999;\n}\n\n#root > div > svg {\n position: absolute;\n user-select: none;\n}\n\n\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\n stroke: #29c229;\n}\n.edge .v-dot {\n fill: transparent;\n stroke: transparent;\n stroke-width: 3px;\n cursor: pointer;\n transition: stroke 0.15s ease;\n}\n.edge .v-dot:hover {\n stroke: #999;\n fill: rgba(153, 153, 153, 0.1);\n}\n.edge .v-dot.selected {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.1);\n}\n.edge .v-dot.selected:hover {\n stroke: #29c229;\n fill: rgba(41, 194, 41, 0.2);\n}\n.edge .v-dot.auto.selected {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.1);\n}\n.edge .v-dot.auto.selected:hover {\n stroke: #777;\n fill: rgba(119, 119, 119, 0.2);\n}\ncircle#prj {\n fill: none;\n stroke: #777;\n}\n\n.nodeShadow {\n fill: none;\n stroke-width: 4px;\n stroke: rgba(0, 0, 0, 0.13);\n}\n\ng.node {\n user-select: none;\n cursor: default;\n}\n\ng.node.linked {\n cursor: pointer;\n}\n\ng.node text {\n pointer-events: none;\n}\n\n.icon {\n fill: #aaa;\n stroke: #fff;\n}\n#icon-cube {\n fill: #aaa;\n}\n\n/* Ensure all button icons are uncolored */\nbutton .icon,\nbutton svg,\nbutton path {\n fill: currentColor !important;\n stroke: none !important;\n}\n\n/* Font Awesome icon styling in buttons */\nbutton i {\n font-size: 12px;\n color: inherit;\n}\n\nrect.elastic {\n pointer-events: none;\n stroke: none;\n fill: #3bd8281f;\n display: none;\n}\nrect.elastic.on {\n display: block;\n}\n\n.popover {\n position: absolute;\n top: 50px;\n bottom: 10px;\n overflow: auto;\n right: 10px;\n background: ghostwhite;\n padding: 30px;\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\n border: solid 1px #eee;\n}\n\n.popover th {\n text-align: left;\n padding: 20px 0px 10px;\n}\n.popover td {\n padding-right: 20px;\n font-size: 14px;\n}\n\n/* Simple tooltip system with smart positioning */\n[data-tooltip] {\n position: relative;\n}\n\n[data-tooltip]:hover::after {\n content: attr(data-tooltip);\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.9);\n color: white;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px !important;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\n font-weight: normal !important;\n white-space: nowrap;\n z-index: 1000;\n pointer-events: none;\n margin-top: 5px;\n animation: tooltip-appear 0.1s ease-out;\n min-width: 120px;\n max-width: calc(100vw - 20px);\n box-sizing: border-box;\n}\n\n[data-tooltip]:hover::before {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 4px solid transparent;\n border-bottom-color: rgba(0, 0, 0, 0.9);\n z-index: 1000;\n pointer-events: none;\n margin-top: 1px;\n animation: tooltip-appear 0.1s ease-out;\n}\n\n/* Special handling for rightmost elements that might overflow */\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 0;\n transform: none;\n}\n\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\n /* Apply to last 2 toolbar groups (save and help buttons) */\n left: auto;\n right: 16px;\n transform: none;\n}\n\n@keyframes tooltip-appear {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-5px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n}\n\nselect {\n position: relative;\n}\n\n/* Robot shape internal elements - inherit stroke from parent node */\n.node .robot-eye-socket {\n fill: none;\n stroke: inherit;\n stroke-width: 2;\n}\n\n.node .robot-eye {\n fill: currentColor;\n stroke: none;\n}\n\n.node .robot-mouth {\n stroke: inherit;\n}\n\n.node .robot-antenna {\n stroke: inherit;\n}\n\n.node .robot-antenna-ball {\n fill: currentColor;\n stroke: inherit;\n stroke-width: 1.5;\n}\n\n.node .robot-panel {\n stroke: inherit;\n}\n\n.node .robot-indicator {\n fill: currentColor;\n stroke: none;\n}`, \"\",{\"version\":3,\"sources\":[\"webpack://./src/style.css\"],\"names\":[],\"mappings\":\"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\",\"sourcesContent\":[\"html, body, #root {\\n height: 100%;\\n}\\n\\nbody {\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\\n color: #666;\\n margin: 0;\\n}\\n\\n#root {\\n display: flex;\\n flex-direction: column;\\n}\\n#root > div.graph {\\n flex: 1;\\n overflow: auto;\\n position: relative;\\n}\\n\\n.toolbar {\\n display: flex;\\n justify-content: space-between;\\n align-items: center;\\n padding: 4px 10px;\\n background-color: #f0f0f0;\\n border-bottom: 1px solid #cccccc;\\n}\\n\\n.toolbar > div {\\n display: flex;\\n align-items: center;\\n}\\n\\n.toolbar button {\\n padding: 5px 8px;\\n margin: 0 2px;\\n cursor: pointer;\\n}\\n\\n.toolbar button:disabled {\\n opacity: 0.5;\\n cursor: not-allowed;\\n}\\n\\n/* Drag mode toggle button styles */\\n.toolbar button.mode-toggle {\\n position: relative;\\n border: 1px solid #8f9fc9;\\n width: 40px;\\n min-height: 28px;\\n background: linear-gradient(to bottom, #abb8db, #8f9fc9);\\n border-color: #8f9fc9;\\n color: white;\\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\\n}\\n\\n.toolbar button.mode-toggle:hover {\\n background: linear-gradient(to bottom, #bcc7e0, #abb8db);\\n border-color: #abb8db;\\n}\\n\\n.toolbar button.mode-toggle.select-mode:active {\\n background: linear-gradient(to bottom, #8f9fc9, #7a8bb5);\\n}\\n\\n/* Pan mode and active toggle - darker blue */\\n.toolbar button.mode-toggle.pan-mode,\\n.toolbar button.active-toggle {\\n background: linear-gradient(to bottom, #4a90e2, #357abd);\\n border-color: #2968a3;\\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\\n}\\n\\n.toolbar button.mode-toggle.pan-mode:hover,\\n.toolbar button.active-toggle:hover {\\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\\n}\\n\\n.toolbar button.mode-toggle.pan-mode:active {\\n background: linear-gradient(to bottom, #357abd, #2968a3);\\n box-shadow: inset 0 1px 2px rgba(0,0,0,0.2);\\n}\\n\\n/* Toggle buttons when inactive - gray styling */\\n.toolbar button.inactive-toggle {\\n background: #c1c1c1;\\n color: #999;\\n border-color: #c1c1c1;\\n box-shadow: none;\\n}\\n\\n.toolbar button.inactive-toggle:hover {\\n background: #b1b1b1;\\n}\\n\\n.toolbar button.inactive-toggle:active {\\n background: #a1a1a1;\\n}\\n\\n/* Toggle buttons when disabled and not active - gray like other disabled buttons */\\n.toolbar button.mode-toggle:disabled:not(.pan-mode):not(.active-toggle),\\n.toolbar button.active-toggle:disabled:not(.active-toggle) {\\n background: #c1c1c1;\\n color: #999;\\n border-color: #c1c1c1;\\n box-shadow: none;\\n}\\n\\n/* Ensure Font Awesome icons are sized appropriately if not already handled */\\n.toolbar button .fas {\\n font-size: 1em;\\n vertical-align: middle;\\n}\\n\\n/* Zoom percentage display */\\n.toolbar button.zoom-display {\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\\n font-size: 11px;\\n font-weight: 600;\\n font-variant-numeric: tabular-nums;\\n min-width: 50px; /* Wider to prevent size change between 99% and 100% */\\n padding: 6px 8px;\\n text-align: center;\\n}\\n\\n.toolbar-group {\\n display: flex;\\n align-items: center;\\n margin-right: 25px; /* Large space between groups */\\n}\\n\\n.toolbar-group:last-child {\\n margin-right: 0; /* Remove right margin from the last group (help button) */\\n}\\n\\nbutton {\\n border: none;\\n background: linear-gradient(to bottom, #4a90e2, #357abd);\\n border-color: #2968a3;\\n border-radius: 3px;\\n padding: 6px 10px;\\n color: #fff;\\n outline: none;\\n box-shadow: 2px 2px 2px rgba(0, 0, 0, .2);\\n margin-right: 5px;\\n min-width: 32px;\\n min-height: 28px;\\n display: inline-flex;\\n align-items: center;\\n justify-content: center;\\n font-size: 14px;\\n line-height: 1;\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\\n position: relative;\\n}\\n\\nbutton:hover {\\n background: linear-gradient(to bottom, #5ba0f2, #4585c7);\\n}\\n\\nbutton:active {\\n background: linear-gradient(to bottom, #357abd, #2968a3);\\n}\\n\\nbutton:last-child {\\n margin-right: 0;\\n}\\n\\n/* Save button when no changes - gray */\\nbutton.action {\\n background: #c1c1c1;\\n color: #999;\\n}\\n\\nbutton.action:active {\\n background: #b1b1b1;\\n}\\n\\n/* Save button when there are changes - orange */\\nbutton.grp {\\n background: linear-gradient(to bottom, #ee9564, #de7d48);\\n margin-right: 0;\\n}\\n\\nbutton.grp:active {\\n background: #d67540;\\n}\\n\\n/* Auto-arrange button with AI purple to blue gradient */\\nbutton.auto-arrange {\\n background: linear-gradient(135deg, #6A4C93, #4a90e2);\\n border-color: #4a4c93;\\n}\\n\\nbutton.auto-arrange:hover {\\n background: linear-gradient(135deg, #7B5DAD, #5ba0f2);\\n border-color: #5a5ca3;\\n}\\n\\nbutton.auto-arrange:active {\\n background: linear-gradient(135deg, #593B83, #357abd);\\n border-color: #3a3c83;\\n}\\n\\nselect {\\n border: 1px solid #ccc;\\n background: white;\\n border-radius: 3px;\\n padding: 3px 7px;\\n color: #666;\\n outline: none;\\n margin-right: 5px;\\n font-size: 12px;\\n}\\n\\nselect:disabled {\\n background: #f5f5f5;\\n color: #999;\\n}\\n\\nbutton:disabled {\\n background: #c1c1c1;\\n color: #999;\\n}\\n\\n#root > div > svg {\\n position: absolute;\\n user-select: none;\\n}\\n\\n\\n.node.selected .nodeBorder, .edge.selected path, .edge.selected rect {\\n stroke: #29c229;\\n}\\n.edge .v-dot {\\n fill: transparent;\\n stroke: transparent;\\n stroke-width: 3px;\\n cursor: pointer;\\n transition: stroke 0.15s ease;\\n}\\n.edge .v-dot:hover {\\n stroke: #999;\\n fill: rgba(153, 153, 153, 0.1);\\n}\\n.edge .v-dot.selected {\\n stroke: #29c229;\\n fill: rgba(41, 194, 41, 0.1);\\n}\\n.edge .v-dot.selected:hover {\\n stroke: #29c229;\\n fill: rgba(41, 194, 41, 0.2);\\n}\\n.edge .v-dot.auto.selected {\\n stroke: #777;\\n fill: rgba(119, 119, 119, 0.1);\\n}\\n.edge .v-dot.auto.selected:hover {\\n stroke: #777;\\n fill: rgba(119, 119, 119, 0.2);\\n}\\ncircle#prj {\\n fill: none;\\n stroke: #777;\\n}\\n\\n.nodeShadow {\\n fill: none;\\n stroke-width: 4px;\\n stroke: rgba(0, 0, 0, 0.13);\\n}\\n\\ng.node {\\n user-select: none;\\n cursor: default;\\n}\\n\\ng.node.linked {\\n cursor: pointer;\\n}\\n\\ng.node text {\\n pointer-events: none;\\n}\\n\\n.icon {\\n fill: #aaa;\\n stroke: #fff;\\n}\\n#icon-cube {\\n fill: #aaa;\\n}\\n\\n/* Ensure all button icons are uncolored */\\nbutton .icon,\\nbutton svg,\\nbutton path {\\n fill: currentColor !important;\\n stroke: none !important;\\n}\\n\\n/* Font Awesome icon styling in buttons */\\nbutton i {\\n font-size: 12px;\\n color: inherit;\\n}\\n\\nrect.elastic {\\n pointer-events: none;\\n stroke: none;\\n fill: #3bd8281f;\\n display: none;\\n}\\nrect.elastic.on {\\n display: block;\\n}\\n\\n.popover {\\n position: absolute;\\n top: 50px;\\n bottom: 10px;\\n overflow: auto;\\n right: 10px;\\n background: ghostwhite;\\n padding: 30px;\\n box-shadow: 3px 3px 5px rgba(0,0,0, .2);\\n border: solid 1px #eee;\\n}\\n\\n.popover th {\\n text-align: left;\\n padding: 20px 0px 10px;\\n}\\n.popover td {\\n padding-right: 20px;\\n font-size: 14px;\\n}\\n\\n/* Simple tooltip system with smart positioning */\\n[data-tooltip] {\\n position: relative;\\n}\\n\\n[data-tooltip]:hover::after {\\n content: attr(data-tooltip);\\n position: absolute;\\n top: 100%;\\n left: 50%;\\n transform: translateX(-50%);\\n background: rgba(0, 0, 0, 0.9);\\n color: white;\\n padding: 6px 12px;\\n border-radius: 4px;\\n font-size: 12px !important;\\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;\\n font-weight: normal !important;\\n white-space: nowrap;\\n z-index: 1000;\\n pointer-events: none;\\n margin-top: 5px;\\n animation: tooltip-appear 0.1s ease-out;\\n min-width: 120px;\\n max-width: calc(100vw - 20px);\\n box-sizing: border-box;\\n}\\n\\n[data-tooltip]:hover::before {\\n content: '';\\n position: absolute;\\n top: 100%;\\n left: 50%;\\n transform: translateX(-50%);\\n border: 4px solid transparent;\\n border-bottom-color: rgba(0, 0, 0, 0.9);\\n z-index: 1000;\\n pointer-events: none;\\n margin-top: 1px;\\n animation: tooltip-appear 0.1s ease-out;\\n}\\n\\n/* Special handling for rightmost elements that might overflow */\\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::after {\\n /* Apply to last 2 toolbar groups (save and help buttons) */\\n left: auto;\\n right: 0;\\n transform: none;\\n}\\n\\n.toolbar-group:nth-last-child(-n+2) button[data-tooltip]:hover::before {\\n /* Apply to last 2 toolbar groups (save and help buttons) */\\n left: auto;\\n right: 16px;\\n transform: none;\\n}\\n\\n@keyframes tooltip-appear {\\n from {\\n opacity: 0;\\n transform: translateX(-50%) translateY(-5px);\\n }\\n to {\\n opacity: 1;\\n transform: translateX(-50%) translateY(0);\\n }\\n}\\n\\nselect {\\n position: relative;\\n}\\n\\n/* Robot shape internal elements - inherit stroke from parent node */\\n.node .robot-eye-socket {\\n fill: none;\\n stroke: inherit;\\n stroke-width: 2;\\n}\\n\\n.node .robot-eye {\\n fill: currentColor;\\n stroke: none;\\n}\\n\\n.node .robot-mouth {\\n stroke: inherit;\\n}\\n\\n.node .robot-antenna {\\n stroke: inherit;\\n}\\n\\n.node .robot-antenna-ball {\\n fill: currentColor;\\n stroke: inherit;\\n stroke-width: 1.5;\\n}\\n\\n.node .robot-panel {\\n stroke: inherit;\\n}\\n\\n.node .robot-indicator {\\n fill: currentColor;\\n stroke: none;\\n}\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","\n import API from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./fonts.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./fonts.css\";\n export default content && content.locals ? content.locals : undefined;\n","\n import API from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../node_modules/.pnpm/style-loader@4.0.0_webpack@5.109.2/node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./style.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../node_modules/.pnpm/css-loader@7.1.4_webpack@5.109.2/node_modules/css-loader/dist/cjs.js!./style.css\";\n export default content && content.locals ? content.locals : undefined;\n"],"names":["el","style","Object","keys","forEach","key","value","setProperty","toString","nodeBorder","fill","stroke","filter","nodeText","edgeText","edgeRect","groupRect","groupText","cursor","thickness","color","opacity","fontSize","dashed","p1","p2","Math","sqrt","x","y","width","height","background","shape","calculateEdgeVertices","edge","n1","from","n2","to","vertices","concat","unshift","push","interior","slice","firstRoutingVertex","find","vertex","label","lastRoutingVertex","index","length","intersect","Error","id","calculateLabelPlacement","position","defaultPoint","segment","point","labelIndex","findIndex","movable","labelVertex","auto","adjacentSegments","p","q","reduce","longest","candidate","_constants__WEBPACK_IMPORTED_MODULE_0__","gd","undefined","targetLength","sum","traversed","segmentPosition","horizontalDistance","abs","verticalDistance","orientation","createEdgePath","path","Symbol","GeometryValidationError","issues","constructor","super","map","issue","message","join","this","name","validateResolvedLayout","measured","resolved","diagramId","checkUniqueIDs","nodes","node","groups","group","edges","checkCoverage","measuredNodes","Map","measuredGroups","resolvedNodes","resolvedGroups","checkFiniteRect","bounds","expected","get","nearlyEqual","size","contentSize","checkRectInBounds","titleBounds","contains","titleSize","forEachPair","first","second","overlaps","checkNodeOverlaps","checkGroupOverlaps","child","parentId","parent","checkOwnership","measuredEdges","labels","sourceId","targetId","sections","some","section","checkSectionConnections","isFinitePoint","pointInRect","values","routeIntersectsRect","inset","sourceInside","belongsToGroup","routeViolatesGroup","labelBounds","labelSize","checkEdges","ids","seen","Set","has","add","actual","expectedIDs","actualIDs","rect","Number","isFinite","isFiniteRect","samePoint","source","target","start","end","pointOnBorder","nodeId","groupId","shouldBeInside","dx","dy","minimum","maximum","origin","delta","lower","upper","EPSILON","max","min","segmentIntersectsRect","outer","inner","inclusive","amount","withinX","withinY","onVertical","onHorizontal","items","visit","code","elementIds","measuredText","measurementSVG","measureSVGText","text","attrs","attributes","entries","sort","localeCompare","JSON","stringify","measurementKey","cached","document","createElementNS","setAttribute","textContent","isConnected","visibility","pointerEvents","body","appendChild","getBBox","remove","set","svgTextWrap","maxW","lines","trim","split","flatMap","paragraph","paragraphLines","currentLine","word","parts","breakLongWord","part","nextLine","line","maxWidth","currentPart","character","nextPart","create","element","type","className","k","v","String","classList","use","setAttributeNS","d","t","textArea","bold","anchor","txt","i","span","append","r","rx","ry","icon","expand","expanded","g","transform","setPosition","insideBox","b","centeredBox","intersectRect","box","w","h","s","q1","q2","denominator","a","numerator1","numerator2","result","onLine1","onLine2","segmentIntersection","ret","intersectRectFull","intersectEllipse","ellCenter","nodeCenter","si","c","radicand_sqrt","pos","project","atob","len","dot","cabDistance","cylinderRadiusY","shapeLabelOffsetY","toLowerCase","D3Element","_el","attr","insert","el2","insertBefore","querySelector","bbox","rounded","shapeSvg","_ellipse","mobiledeviceportrait","shapes","roundedbox","component","cylinder","cy","person","circle","ellipse","hexagon","sz","n","folder","mobiledevicelandscape","mobiledevice","pipe","robot","headSize","headR","antennaH","antennaR","eyeR","eyeSpacing","earW","earH","bodyW","bodyTop","bodyH","headTop","eyeY","mouthY","mouthW","webbrowser","Undo","versions","lastSavedPos","exportDoc","importDoc","change","tmpPreviousState","func","timeout","context","clearTimeout","setTimeout","apply","debounce","saveNow","beforeChange","deepClone","currentState","splice","doc","structuredClone","parse","undo","redo","changed","setSaved","textBlock","gapAfter","field","wrapped","constants","M5","lineHeight","defaultEdgeStyle","Y$","defaultNodeStyle","nD","GraphData","nodesMap","edgeVertices","groupsMap","metadata","layoutDirection","validatedLayout","colorToVarMap","_undo","_gridVisible","_snapToGrid","_gridSize","_skipAutoFit","exportLayout","lo","importLayout","window","graph","init","layout","addNode","sub","description","link","nodeStyle","minimumHeight","contentLayout","title","subtitle","nodeWidth","textWidth","HORIZONTAL_PADDING","blocks","textHeight","block","VERTICAL_PADDING","layoutNodeContent","requiredHeight","Array","addEdge","fromNode","toNode","initVertex","edgeID","input","charCodeAt","imul","fnv1a36","stableVertexID","userDeletedVertices","addGroup","nodesOrGroups","console","error","Boolean","setNodeSelected","selected","ref","updateEdgesSel","e","clearResolvedLayout","layoutSections","layoutLabelBounds","layoutBounds","layoutTitleBounds","moveNode","disableSnap","skipUndo","snapped","snapToGrid","redrawEdges","redrawGroups","moveEdgeVertex","redrawEdge","moveSelected","insertEdgeVertex","isLabel","deleteEdgeVertex","indexOf","delete","alignTopLeft","contentBounds","calculateContentBounds","offsetX","offsetY","resetPanTransform","currentZoom","getZoom","zoomGroup","svg","graphData","bb","zoom","parentElement","clientWidth","clientHeight","updatePanningOptimized","clearViewState","shouldSkipAutoFit","resetView","updatePanning","removeChild","buildEdge","buildGroup","exportSVG","U","originalSvg","exportSvg","cloneNode","querySelectorAll","getAttribute","removeAttribute","exportElastic","exportWidth","padding","exportHeight","exportZoomGroup","convertStylesToCustomProperties","script","createElement","replace","firstChild","outerHTML","minX","Infinity","minY","maxX","maxY","left","right","top","bottom","centerX","centerY","approxLabelSize","full","lst","rerender","requiredSavedPoint","endsWith","edgeId","startsWith","isArray","saved","autoLayout","options","placedNode","placedGroup","placedEdge","interiorRoutePoints","validated","fitToView","alignSelectionV","alignSelectionH","distributeSelectionH","selectedNodes","selectedVertices","allElements","spacing","newX","distributeSelectionV","newY","setEdgeSelected","viewportWidth","viewportHeight","zoomX","zoomY","optimalZoom","finalZoom","translateX","translateY","saveViewState","saveLayoutState","restoreLayoutState","state","isGridVisible","isSnapToGrid","getGridSize","toggleGrid","updateGridDisplay","dispatchEvent","CustomEvent","toggleSnapToGrid","snapAllToGrid","snappedX","round","snappedY","existingGrid","existingGridRect","defs","pattern","route","previous","clickListener","addEventListener","selectListener","dragging","buildGraph","data","onNodeSelect","dragMode","innerHTML","__data","_buildGraph","elasticEl","addCursorInteraction","setZoom","buildGraphView","zoomG","nodesG","edgesG","groupsG","gdata","href","exportHref","content","shapeType","AV","styles","setBorderStyle","border","tg","buildNodeContent","buildNode","completeRoutePoints","edge_utils","l","bg","buildPlacedEdgeLabel","placement","buildEdgeLabel","IW","Zr","cx","p0","topExtension","bottomExtension","hexHeight","pad","findClosestSegment","fnd","dst","POSITIVE_INFINITY","prj","pts","mouseToDrawing","getBoundingClientRect","z","currentTransform","getCurrentTransform","clientX","clientY","addCustomCursorInteraction","conn","ini","elastic","isPanning","panStartX","panStartY","initialTransform","pendingSelectionChange","pendingNavigation","hasDragged","suppressLinkClick","eventListeners","onClick","Element","closest","preventDefault","stopPropagation","md","convertEvent","changedTouches","onMouseMoveHandler","getSelection","setSelection","setTransform","drawingDx","drawingDy","item","setDragging","update","onMouseMove","ex","ey","onMouseUpHandler","removeEventListener","navigation","boxSelection","shiftKey","location","onMouseUp","onMouseDownHandler","nodeFromEvent","effectiveMode","isSelected","translateMatch","match","parseFloat","getCurrentTransformLocal","startDrawingX","startDrawingY","pt","currentPt","currentDrawingX","currentDrawingY","createElastic","onMouseDown","event","handler","addDnd","existingCleanup","__cursorInteractionCleanup","getData","beforeUnloadHandler","returnValue","setDotSelected","dotEl","mouseMoveHandler","altKey","removePrjDot","keyUpHandler","shortcuts","Yp","Zj","_s","clickHandler","wheelHandler","sign","deltaY","newZoom","setZoomCentered","keyDownHandler","shortcut","bl","Ur","hU","i1","mD","F","Gn","customInteractionCleanup","handles","handle","b1","b2","scaleMatch","oldZoom","container","newTranslateX","newTranslateY","viewStateCache","graphId","restoreViewState","defaultOptions","direction","nodeSpacing","layerSpacing","async","resolveAutomatic","diagram","checkMeasuredLinks","childrenByParent","groupsByParent","elkGroups","children","layoutOptions","groupLayoutOptions","root","rootLayoutOptions","owner","lowestCommonGroup","buildLayoutTree","elk","__webpack_require__","then","bind","module","default","readContainer","readResolvedLayout","sources","targets","titleHeight","sourceGroups","parentGroups","targetGroups","current","parentX","parentY","containerX","containerY","childX","requiredNumber","childY","measuredNode","measuredGroup","requiredLabel","readEdge","addOffset","startPoint","bendPoints","endPoint","requiredEndpoint","elementId","endpoints","siblings","fonts","ready","measureGraph","compactLayout","geometry","centerOffset","validateCurrentLayout","boundary","SVGGraphicsElement","copyBox","snapshot","completeSnapshot","resolveManual","nodeParents","groupParents","member","parents","isGroup","existing","declaredParents","checkMeasuredBox","measureNode","measureGroup","measureEdge","hasLabel","boxes","points","flat","layoutDirections","TopBottom","BottomTop","LeftRight","RightLeft","model","viewsList","views","layouts","viewKey","elements","relations","collectRels","relationships","rel","people","softwareSystems","containers","el1","components","deploymentNodes","containerInstances","containerId","recAddNodes","infrastructureNodes","view","getView","_graph_view_graph__WEBPACK_IMPORTED_MODULE_0__","jg","rankDirection","automaticLayout","version","groupingIDs","softwareSystemId","enterprise","cssClassName","tag","varPrefix","elementViewKey","containerViews","lookupContainerViewKey","tags","technology","encodedViewKey","encodeURIComponent","url","nodeLink","properties","warn","destinationId","routing","level","groupMembers","ADD_VERTEX","ADD_LABEL_VERTEX","DEL_VERTEX","ZOOM_IN","ZOOM_OUT","ZOOM_FIT","ZOOM_100","SELECT_ALL","DESELECT","MOVE_LEFT","MOVE_RIGHT","MOVE_UP","MOVE_DOWN","MOVE_LEFT_FINE","MOVE_RIGHT_FINE","MOVE_UP_FINE","MOVE_DOWN_FINE","TOGGLE_DRAG_MODE","ALIGN_HORIZONTAL","ALIGN_VERTICAL","DISTRIBUTE_HORIZONTAL","DISTRIBUTE_VERTICAL","AUTO_LAYOUT","RESET_POSITION","TOGGLE_GRID","TOGGLE_SNAP_TO_GRID","SNAP_ALL_TO_GRID","list","help","combinations","shift","ctrl","alt","click","wheel","shortcutMap","comboText","_utils_platform__WEBPACK_IMPORTED_MODULE_1__","sy","toUpperCase","react_jsx_runtime__WEBPACK_IMPORTED_MODULE_2__","jsxs","jsx","Fragment","colSpan","matches","SA","ke","checkKey","aShortcut","bShortcut","aModifiers","bModifiers","aCount","isMac","navigator","userAgentData","platform","includes","userAgent","KeyboardEvent","metaKey","test","ctrlKey","___CSS_LOADER_URL_IMPORT_0___","URL","___CSS_LOADER_URL_IMPORT_1___","___CSS_LOADER_URL_IMPORT_2___","___CSS_LOADER_URL_IMPORT_3___","___CSS_LOADER_EXPORT___","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default","___CSS_LOADER_URL_REPLACEMENT_0___","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default","___CSS_LOADER_URL_REPLACEMENT_1___","___CSS_LOADER_URL_REPLACEMENT_2___","___CSS_LOADER_URL_REPLACEMENT_3___","names","mappings","sourcesContent","sourceRoot","__WEBPACK_DEFAULT_EXPORT__","styleTagTransform","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default","setAttributes","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default","domAPI","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default","insertStyleElement","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default","_node_modules_pnpm_style_loader_4_0_0_webpack_5_109_2_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_cjs_js_fonts_css__WEBPACK_IMPORTED_MODULE_6__","A","locals","_node_modules_pnpm_css_loader_7_1_4_webpack_5_109_2_node_modules_css_loader_dist_cjs_js_style_css__WEBPACK_IMPORTED_MODULE_6__"],"sourceRoot":""} \ No newline at end of file diff --git a/cmd/mdl/webapp/src/graph-view/graph.ts b/cmd/mdl/webapp/src/graph-view/graph.ts index 4f519962..171e2f45 100644 --- a/cmd/mdl/webapp/src/graph-view/graph.ts +++ b/cmd/mdl/webapp/src/graph-view/graph.ts @@ -528,8 +528,9 @@ export class GraphData { exportElastic.remove() } - // Calculate actual content bounds including all elements - const contentBounds = this.calculateContentBounds() + // Export the exact bounds that passed geometry validation. Recomputing + // them here can omit routed relationship labels from the SVG canvas. + const contentBounds = this.validatedLayout.bounds // Add padding around content const padding = 50