From f9ec77702487ebc6dc6824b38bda4e8682f72ac8 Mon Sep 17 00:00:00 2001 From: "Raphael (manual office deploy after cloud-state fix)" Date: Thu, 20 Aug 2026 12:12:21 -0700 Subject: [PATCH] Isolate headless SVG renders from editor reloads Prevent unrelated interactive MDL sessions from interrupting automated rendering through the shared LiveReload port. --- cmd/mdl/svg_e2e_test.go | 83 +++++++++++++++++++++++++++++++++ cmd/mdl/webapp/dist/main.js | 2 +- cmd/mdl/webapp/dist/main.js.map | 2 +- cmd/mdl/webapp/src/index.tsx | 14 ++++-- 4 files changed, 94 insertions(+), 7 deletions(-) diff --git a/cmd/mdl/svg_e2e_test.go b/cmd/mdl/svg_e2e_test.go index 902f1207..7d28a909 100644 --- a/cmd/mdl/svg_e2e_test.go +++ b/cmd/mdl/svg_e2e_test.go @@ -3,15 +3,19 @@ package main import ( "context" "encoding/xml" + "fmt" "io" + "net" "net/url" "os" "os/exec" "path/filepath" + "sync/atomic" "testing" "time" "github.com/chromedp/chromedp" + "github.com/jaschaephraim/lrserver" ) type nodeOverflow struct { @@ -104,6 +108,69 @@ func TestSVGEndToEnd(t *testing.T) { } } +// TestSVGHeadlessDoesNotConnectToLiveReload verifies automated rendering is +// isolated from interactive editor reloads sent on the process-wide port. +func TestSVGHeadlessDoesNotConnectToLiveReload(t *testing.T) { + if !hasChrome() { + t.Skip("skipping: Chrome/Chromium not available in PATH") + } + + reloadServer := lrserver.New(lrserver.DefaultName, lrserver.DefaultPort) + serverDone := make(chan error, 1) + go func() { + serverDone <- reloadServer.ListenAndServe() + }() + if err := waitForTCP("127.0.0.1:35729", 5*time.Second); err != nil { + select { + case serverErr := <-serverDone: + t.Skipf("live-reload port unavailable: %v", serverErr) + default: + t.Fatalf("start live-reload server: %v", err) + } + } + t.Cleanup(func() { + if err := reloadServer.Close(); err != nil { + t.Errorf("close live-reload server: %v", err) + } + }) + + var connected atomic.Bool + stopObserving := make(chan struct{}) + observerDone := make(chan struct{}) + go func() { + defer close(observerDone) + ticker := time.NewTicker(time.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if reloadServer.IsConnected() { + connected.Store(true) + } + case <-stopObserving: + return + } + } + }() + + outDir := t.TempDir() + cfg := config{ + dir: outDir, + port: 0, + timeout: 30 * time.Second, + views: SliceFlag{"SystemContext"}, + } + err := runSVG("goa.design/model/examples/basic/model", cfg) + close(stopObserving) + <-observerDone + if err != nil { + t.Fatalf("runSVG failed: %v", err) + } + if connected.Load() { + t.Fatal("headless renderer connected to the interactive LiveReload server") + } +} + func TestSVGNodeTextFits(t *testing.T) { if !hasChrome() { t.Skip("skipping: Chrome/Chromium not available in PATH") @@ -353,6 +420,22 @@ func inspectVerticalEdgeLabelOverlaps(t *testing.T, path string) []verticalEdgeL return overlaps } +// waitForTCP waits until a local test server accepts connections. +func waitForTCP(address string, timeout time.Duration) error { + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + connection, err := net.DialTimeout("tcp", address, 100*time.Millisecond) + if err == nil { + if closeErr := connection.Close(); closeErr != nil { + return closeErr + } + return nil + } + time.Sleep(10 * time.Millisecond) + } + return fmt.Errorf("timeout waiting for %s", address) +} + func newChromeContext(t *testing.T) (context.Context, func()) { t.Helper() diff --git a/cmd/mdl/webapp/dist/main.js b/cmd/mdl/webapp/dist/main.js index d1053471..26d35d70 100644 --- a/cmd/mdl/webapp/dist/main.js +++ b/cmd/mdl/webapp/dist/main.js @@ -1,2 +1,2 @@ -"use strict";(self.webpackChunkapp=self.webpackChunkapp||[]).push([[792],{486(e,t,n){n.d(t,{Root:()=>M,S:()=>$});var o=n(763),i=n(408),r=(n(538),n(32));const a={TopBottom:"DOWN",BottomTop:"UP",LeftRight:"RIGHT",RightLeft:"LEFT"},s=e=>{const t=[];return Object.keys(e.views).filter(e=>e.endsWith("Views")).forEach(n=>{e.views[n].forEach(e=>{t.push({key:e.key,title:e.title||e.key,section:n})})}),t};var l=n(264);const d={};function c(e){const t=e.replace(/([A-Z])/g," $1");return t.charAt(0).toUpperCase()+t.slice(1)}var h=n(686),u=n(987);const g=({model:e,currentID:t,onViewChange:n,graph:o,onAutoLayout:i,onSave:r,onToggleHelp:a,saving:l,layouting:d,dragMode:c,setDragMode:h})=>{const g=s(e);return(0,u.jsxs)("div",{className:"toolbar",children:[(0,u.jsx)(A,{views:g,currentID:t,onViewChange:n}),(0,u.jsx)(p,{graph:o,onAutoLayout:i,onSave:r,onToggleHelp:a,saving:l,layouting:d,dragMode:c,setDragMode:h})]})},A=({views:e,currentID:t,onViewChange:n})=>(0,u.jsxs)("div",{children:["View:",e.length>1?(0,u.jsxs)("select",{onChange:e=>n(e.target.value),value:t,children:[(0,u.jsx)("option",{disabled:!0,value:"",hidden:!0,children:"..."}),e.map(e=>(0,u.jsx)("option",{value:e.key,children:c(e.section)+": "+e.title},e.key))]}):(0,u.jsx)("span",{style:{marginLeft:"8px",fontWeight:"bold"},children:e[0]?c(e[0].section)+": "+e[0].title:"No views available"})]}),p=({graph:e,onAutoLayout:t,onSave:n,onToggleHelp:o,saving:i,layouting:r,dragMode:a,setDragMode:s})=>(0,u.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(m,{dragMode:a,setDragMode:s})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(f,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(y,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(b,{onAutoLayout:t,layouting:r})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(x,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(v,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(C,{onSave:n,saving:i,graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(k,{onToggleHelp:o})})]}),m=({dragMode:e,setDragMode:t})=>(0,u.jsx)("button",{className:"mode-toggle "+("select"===e?"select-mode":"pan-mode"),onClick:()=>t("pan"===e?"select":"pan"),"data-tooltip":"pan"===e?"Pan Mode: Drag to pan the view (T)":"Select Mode: Drag to select elements, Shift+click to add/remove selection (T)",children:"pan"===e?(0,u.jsx)("i",{className:"fas fa-hand-paper"}):(0,u.jsx)("i",{className:"fas fa-mouse-pointer"})}),f=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>e.undo(),"data-tooltip":`Undo the last change made to the diagram (${t}+Z)`,children:(0,u.jsx)("i",{className:"fas fa-undo"})}),(0,u.jsx)("button",{onClick:()=>e.redo(),"data-tooltip":`Redo the last undone action (${t}+Shift+Z / ${t}+Y)`,children:(0,u.jsx)("i",{className:"fas fa-redo"})})]})},y=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>e.alignSelectionH(),"data-tooltip":`Align all selected elements horizontally (left edges) (${t}+Shift+H)`,children:(0,u.jsx)("i",{className:"fas fa-align-left"})}),(0,u.jsx)("button",{onClick:()=>e.alignSelectionV(),"data-tooltip":`Align all selected elements vertically (top edges) (${t}+Shift+A)`,children:(0,u.jsx)("i",{className:"fas fa-align-left",style:{transform:"rotate(90deg)"}})}),(0,u.jsx)("button",{onClick:()=>e.distributeSelectionH(),"data-tooltip":`Distribute selected elements evenly horizontally (equal spacing) (${t}+Alt+H)`,children:(0,u.jsx)("i",{className:"fas fa-ellipsis-h"})}),(0,u.jsx)("button",{onClick:()=>e.distributeSelectionV(),"data-tooltip":`Distribute selected elements evenly vertically (equal spacing) (${t}+Alt+V)`,children:(0,u.jsx)("i",{className:"fas fa-ellipsis-v"})})]})},b=({onAutoLayout:e,layouting:t})=>{const n=(0,h.sy)();return(0,u.jsx)("button",{className:"auto-arrange",onClick:e,disabled:t,"data-tooltip":`Automatically arrange all elements using the Layered algorithm (${n}+L)`,children:t?(0,u.jsx)("i",{className:"fas fa-spinner fa-spin"}):(0,u.jsx)("i",{className:"fas fa-magic"})})},x=({graph:e})=>{const[t,n]=(0,o.useState)(e.isGridVisible()),[i,r]=(0,o.useState)(e.isSnapToGrid()),a=(0,h.sy)();return o.useEffect(()=>{const t=()=>{n(e.isGridVisible()),r(e.isSnapToGrid())};return t(),window.addEventListener("gridStateChanged",t),()=>{window.removeEventListener("gridStateChanged",t)}},[e]),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{className:t?"active-toggle":"inactive-toggle",onClick:()=>{e.toggleGrid(),n(e.isGridVisible())},"data-tooltip":`Toggle grid visibility (${a}+G)`,children:(0,u.jsx)("i",{className:"fas fa-th"})}),(0,u.jsx)("button",{className:i?"active-toggle":"inactive-toggle",onClick:()=>{e.toggleSnapToGrid(),r(e.isSnapToGrid())},"data-tooltip":`Toggle snap to grid (${a}+Shift+G)`,children:(0,u.jsx)("i",{className:"fas fa-magnet"})}),(0,u.jsx)("button",{onClick:()=>{e.snapAllToGrid()},disabled:!i,"data-tooltip":`Snap all elements to grid (${a}+Alt+G)`,children:(0,u.jsx)("i",{className:"fas fa-border-all"})})]})},w=()=>{const[e,t]=(0,o.useState)(100);return(0,o.useEffect)(()=>{const e=()=>{const e=Math.round(100*(0,i.IX)());t(e)};e();const n=setInterval(e,100);return()=>clearInterval(n)},[]),(0,u.jsxs)("button",{onClick:()=>(0,i.a_)(1),className:"zoom-display","data-tooltip":"Click to reset zoom to 100%",children:[e,"%"]})},v=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>{(0,i.a_)(Math.max(.1,(0,i.IX)()/1.2))},"data-tooltip":`Zoom out to see more of the diagram (${t}+-)`,children:(0,u.jsx)("i",{className:"fas fa-search-minus"})}),(0,u.jsx)(w,{}),(0,u.jsx)("button",{onClick:()=>{(0,i.a_)(Math.min(5,1.2*(0,i.IX)()))},"data-tooltip":`Zoom in to see details more clearly (${t}+=)`,children:(0,u.jsx)("i",{className:"fas fa-search-plus"})}),(0,u.jsx)("button",{onClick:()=>{e.fitToView()},"data-tooltip":`Fit diagram to view (${t}+9)`,children:(0,u.jsx)("i",{className:"fas fa-expand"})})]})},C=({onSave:e,saving:t,graph:n})=>{const[i,r]=(0,o.useState)(!1),a=(0,h.sy)();return(0,o.useEffect)(()=>{const e=()=>{r(n.changed())};e();const t=setInterval(e,100);return()=>clearInterval(t)},[n]),(0,u.jsx)("button",{className:i?"grp":"action",disabled:t,onClick:e,"data-tooltip":`Save the current diagram layout (${a}+S)`,children:t?(0,u.jsx)("i",{className:"fas fa-spinner fa-spin"}):(0,u.jsx)("i",{className:"fas fa-save"})})},k=({onToggleHelp:e})=>(0,u.jsx)("button",{onClick:e,"data-tooltip":"Show keyboard shortcuts and help information (Shift+? / Shift+F1)",children:(0,u.jsx)("i",{className:"fas fa-question-circle"})}),I=(0,o.lazy)(()=>Promise.resolve().then(n.bind(n,264)).then(e=>({default:e.Help}))),B=(0,o.lazy)(()=>n.e(948).then(n.bind(n,948)).then(e=>({default:e.Graph}))),E=(e,t)=>{const n=document.documentElement;if(!e)return delete n.dataset.mdlAutomationStatus,void delete n.dataset.mdlAutomationError;n.dataset.mdlAutomationStatus=e,t?n.dataset.mdlAutomationError=t:delete n.dataset.mdlAutomationError},S=(e,t)=>{console.error(`${e} failed:`,t),alert(`${e} failed. See console for details.`)},M=({model:e,layout:t})=>(0,u.jsx)(r.Kd,{children:(0,u.jsx)(r.BV,{children:(0,u.jsx)(r.qh,{path:"/",element:(0,u.jsx)(D,{model:e,layouts:t})})})}),$=()=>{var e;(e=new URLSearchParams(document.location.search).get("id")||"")?delete d[e]:Object.keys(d).forEach(e=>delete d[e])},D=({model:e,layouts:t})=>{const[n,s]=(0,r.ok)(),c=decodeURI(n.get("id")||""),[h,A]=(0,o.useState)(!1),[p,m]=(0,o.useState)("pan"),[f,y]=(0,o.useState)(null),b=(0,o.useRef)(null),x=(0,o.useRef)(0),w=((e,t,n)=>{if(d[n])return d[n];const o=((e,t,n)=>{const o=new Map,r=new Map,s=e=>{Array.isArray(e.relationships)&&e.relationships.forEach(e=>{r.set(e.id,e)})};if(e.model.people&&e.model.people.forEach(e=>{o.set(e.id,e),Array.isArray(e.relationships)&&e.relationships.forEach(e=>{r.set(e.id,e)})}),e.model.softwareSystems&&e.model.softwareSystems.forEach(e=>{o.set(e.id,e),s(e),Array.isArray(e.containers)&&e.containers.forEach(t=>{t.parent=e,o.set(t.id,t),s(t),Array.isArray(t.components)&&t.components.forEach(e=>{e.parent=t,o.set(e.id,e),s(e)})})}),e.model.deploymentNodes){const t=e=>{e.containerInstances&&e.containerInstances.forEach(t=>{const n={...o.get(t.containerId),id:t.id};o.set(n.id,n),n.parent=e,s(t)})},n=(e,i)=>{e.parent=i,o.set(e.id,e),s(e),t(e),e.children&&e.children.forEach(t=>n(t,e)),e.infrastructureNodes&&e.infrastructureNodes.forEach(t=>n(t,e))};e.model.deploymentNodes.forEach(e=>n(e,null))}const{view:l,section:d}=function(e,t){let n=null,o="";return Object.keys(e.views).filter(e=>e.endsWith("Views")).some(i=>e.views[i].some(e=>{if(e.key==t)return n=e,o=i,!0})),{view:n,section:o}}(e,n);if(!l)return null;const c=new i.jg(l.key,l.title||l.key),h=l.automaticLayout?.rankDirection;c.layoutDirection=h?a[h]:void 0;const u={name:c.name,description:l.description,version:e.version,elements:[]};if(c.metadata=u,!l.elements)return c;const g={};if("deploymentViews"==d||"containerViews"==d)l.elements.forEach(e=>{const t=o.get(e.id);t?.parent&&(g[t.parent.id]=!0)});else if(l.softwareSystemId)l.elements.find(e=>e.id==l.softwareSystemId)||(g[l.softwareSystemId]=!0);else if("systemLandscapeViews"==d){const t={id:"__enterprise__",...e.model.enterprise};o.set(t.id,t),e.model.people&&e.model.people.filter(e=>"External"!=e.location).forEach(e=>e.parent=t),e.model.softwareSystems&&e.model.softwareSystems.filter(e=>"External"!=e.location).forEach(e=>e.parent=t),g[t.id]=!0}const A=e.views.styles,p=e=>e.toLowerCase().replace(/[^a-z0-9-]/g,"-");A?.elements&&A.elements.forEach(e=>{if(e.tag){const t=`--mdl-${p(e.tag)}`;e.background&&c.colorToVarMap.set(e.background,`${t}-bg`),e.color&&c.colorToVarMap.set(e.color,`${t}-color`),e.stroke&&c.colorToVarMap.set(e.stroke,`${t}-stroke`)}}),A?.relationships&&A.relationships.forEach(e=>{if(e.tag){const t=`--mdl-rel-${p(e.tag)}`;e.color&&c.colorToVarMap.set(e.color,`${t}-color`)}}),l.elements.forEach(t=>{if(g[t.id])return;const n=o.get(t.id),i=n?function(e,t){const n=e.views.containerViews?.find(e=>e.softwareSystemId==t);return n?.key}(e,n.id):void 0;let r="",a={};if(n){const e=n.tags.split(",");r=e[e.length-1],n.technology&&(r+=": "+n.technology),e.forEach(e=>{const t=A&&A.elements&&A.elements.find(t=>t.tag==e);t&&(a={...a,...t})})}c.addNode(t.id,n&&n.name||t.id,r,n&&n.description?n.description:"",a,function(e,t){if(t){const e=encodeURIComponent(t);return{href:`?id=${e}`,exportHref:`${e}.svg`}}if(e?.url)return{href:e.url,exportHref:e.url}}(n,i)),n&&u.elements.push({id:n.id,tags:n.tags,location:n.location,properties:n.properties,elementViewKey:i,technology:n.technology,url:n.url})}),Array.isArray(l.relationships)&&l.relationships.forEach(e=>{const t=r.get(e.id);if(!t)return;if(!c.nodesMap.has(t.sourceId)){if(o.has(t.sourceId)){const e=o.get(t.sourceId);console.warn("Element not found in this view: ",e.id,e.name)}else console.warn("Element not found: ",t.sourceId);return}if(!c.nodesMap.has(t.destinationId)){if(o.has(t.destinationId)){const e=o.get(t.destinationId);console.warn("Element not found in this view: ",e.id,e.name)}else console.warn("Element not found: ",t.destinationId);return}let n={};t.tags.split(",").forEach(e=>{const t=A&&A.relationships&&A.relationships.find(t=>t.tag==e);t&&(n={...n,...t})}),e.routing&&(n.routing=e.routing),c.addEdge(t.id,t.sourceId,t.destinationId,t.description,e.vertices,n)});const m=e=>{let t=0;for(let n=e.parent;n;n=n.parent)t++;return t};return Object.keys(g).map(e=>o.get(e)).sort((e,t)=>m(e)>m(t)?-1:1).forEach(e=>{let t={};"deploymentViews"==d&&o.get(e.id).tags.split(",").forEach(e=>{const n=A&&A.elements&&A.elements.find(t=>t.tag==e);n&&(t={...t,...n})});const n=l.elements.map(e=>o.get(e.id)).filter(t=>!(!t||t.parent!==e||"systemLandscapeViews"===d&&"__enterprise__"===e.id&&"External"===t.location)).map(e=>e.id);n.length>0&&c.addGroup(e.id,e.name,n,t)}),c.init(t[c.id]),c})(e,t,n);return o&&(d[n]=o),o})(e,t,c),{layouting:v,handleAutoLayout:C}=(e=>{const[t,n]=(0,o.useState)(!1);return{layouting:t,handleAutoLayout:(0,o.useCallback)(async t=>{n(!0);try{const n={direction:e.layoutDirection||"DOWN",...t||{}};await e.autoLayout(n)}finally{n(!1)}},[e])}})(w||{}),{saving:k,handleSave:M}=((e,t)=>{const[n,i]=(0,o.useState)(!1);return{saving:n,handleSave:(0,o.useCallback)(async()=>{i(!0);try{const n=await fetch("data/save?id="+encodeURIComponent(t),{method:"post",body:e.exportSVG()});if(202!==n.status){const e=(await n.text()).trim();throw new Error(e||`save failed with HTTP ${n.status}`)}e.setSaved()}finally{i(!1)}},[e,t])}})(w||{},c);if(!w)return(0,u.jsx)(j,{model:e});const $=(0,o.useCallback)(()=>{A(!h)},[h]),D=(0,o.useCallback)(()=>{y(c)},[c]),L=(0,o.useCallback)(()=>{C().catch(e=>S("Layout",e))},[C]),T=(0,o.useCallback)(()=>{M().catch(e=>S("Save",e))},[M]);(0,o.useEffect)(()=>{w&&w.name&&(document.title=`${w.name} - Model`)},[w]),(0,o.useEffect)(()=>{const e=Object.fromEntries(n.entries()),t="1"===e.auto||"true"===e.auto,o="1"===e.save||"true"===e.save;if(!t&&!o)return b.current=null,x.current++,void E(null);if(f!==c)return;const i=`${c}:${n.toString()}`;if(b.current===i)return;b.current=i;const r=++x.current;E("running");const a=(e.direction||"").toUpperCase(),s="1"===e.compact||"true"===e.compact,l={};["UP","DOWN","LEFT","RIGHT"].includes(a)&&(l.direction=a),s&&(l.compactLayout=!0),(async()=>{try{t&&await C(l),o&&await M(),x.current===r&&E("complete")}catch(e){const t=(e=>e instanceof Error?e.message:String(e))(e);console.error("Automation failed:",e),x.current===r&&E("error",t)}})()},[c,w,C,M,f,n]),((e,t,n,i,r,a)=>{(0,o.useEffect)(()=>{const o=o=>{const s=(0,l.Yp)(o);s&&o.preventDefault(),"help"===s?e():"save"===s?t():s===l.aX&&r&&i?r("pan"===i?"select":"pan"):n&&(s===l.t9?n.alignSelectionH():s===l.Jk?n.alignSelectionV():s===l.DE?n.distributeSelectionH():s===l.Vy?n.distributeSelectionV():s===l.Hd&&a?a():s===l._t?n.resetView():s===l.Op?n.toggleGrid():s===l.hZ?n.toggleSnapToGrid():s===l.OE?n.snapAllToGrid():s===l.Gg?n.moveSelected(-n.getGridSize(),0):s===l.J8?n.moveSelected(-1,0,!0):s===l.b3?n.moveSelected(n.getGridSize(),0):s===l.iD?n.moveSelected(1,0,!0):s===l.uK?n.moveSelected(0,-n.getGridSize()):s===l.l8?n.moveSelected(0,-1,!0):s===l.rB?n.moveSelected(0,n.getGridSize()):s===l.mt&&n.moveSelected(0,1,!0))};return window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[e,t,n,i,r,a])})($,T,w,p,m,L);const _=(0,o.useCallback)(e=>{s({id:encodeURIComponent(e)})},[s]),V=(0,o.useCallback)(e=>{if(e){const n=w.metadata.elements.find(t=>t.id===e);console.log((t=n,JSON.parse(JSON.stringify(t))))}var t},[w]);return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(g,{model:e,currentID:c,onViewChange:_,graph:w,onAutoLayout:L,onSave:T,onToggleHelp:$,saving:k,layouting:v,dragMode:p,setDragMode:m}),(0,u.jsx)(o.Suspense,{fallback:(0,u.jsx)("div",{children:"Loading graph..."}),children:(0,u.jsx)(B,{data:w,onSelect:V,onReady:D,dragMode:p},c)}),h&&(0,u.jsx)(o.Suspense,{fallback:(0,u.jsx)("div",{children:"Loading help..."}),children:(0,u.jsx)(I,{})})]})},j=({model:e})=>{const t=s(e);return o.useEffect(()=>{document.title="Model - Architecture Diagrams as Code",t.length>0&&(document.location.href="?id="+t[0].key)},[t]),t.length>0?(0,u.jsxs)(u.Fragment,{children:["Redirecting to ",t[0].title]}):(0,u.jsx)(u.Fragment,{children:"No views available"})}},408(e,t,n){n.d(t,{jg:()=>M,Qy:()=>W,ZG:()=>T,IX:()=>G,F_:()=>Q,Kp:()=>J,a_:()=>P});const o=(e,t,n)=>{const o=(()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","svg");return document.body.appendChild(e),{measure:(t,n)=>{const o=document.createElementNS("http://www.w3.org/2000/svg","text");o.setAttribute("x","0"),o.setAttribute("y","0");for(let e in n)o.setAttribute(e,n[e]);o.appendChild(document.createTextNode(t)),e.appendChild(o);const{width:i,height:r}=o.getBBox();return e.removeChild(o),{width:i,height:r}},clean:()=>{document.body.removeChild(e)}}})();let i=0;const r=e.trim().split("\n").map(e=>{const r=e.trim().split(/\s+/);let a=[],s=[];return r.forEach(e=>{if(o.measure(e,n).width>t){s.length>0&&(a.push(s.join(" ")),s=[]);const r=((e,t,n,o)=>{const i=[];let r="";for(let a=0;at&&r.length>0?(i.push(r),r=e[a]):r=s}return r.length>0&&i.push(r),i})(e,t,n,o);for(let e=0;e0&&(s=[r[r.length-1]])}else{const r=[...s,e],l=o.measure(r.join(" "),n);l.width>t&&s.length>0?(a.push(s.join(" ")),s=[e]):(i=Math.max(i,l.width),s=r)}}),s.length>0&&a.push(s.join(" ")),a}).reduce((e,t)=>e.concat(t),[]);return o.clean(),{lines:r,maxW:i}},i={element(e,t={},n){const o=document.createElementNS("http://www.w3.org/2000/svg",e);return Object.entries(t).forEach(([e,t])=>o.setAttribute(e,String(t))),n&&o.classList.add(n),o},use(e,t={}){const n=this.element("use",t);return n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+e),n},path(e,t={},n){return this.element("path",{...t,d:e},n)},text(e,t={}){const n=this.element("text",t);return e&&(n.textContent=e),n},textArea(e,t,n,i,r=0,a=0,s=""){const l={"font-size":`${n}px`,"font-weight":i?"bold":"normal"},{lines:d,maxW:c}=o(e,t,l),h=this.text("",{x:0,y:a,"text-anchor":s||void 0});return d.forEach((e,t)=>{const o=this.element("tspan",{x:r,dy:`${n+2}px`,...l});o.textContent=e,h.append(o)}),{txt:h,dy:(d.length+1)*(n+2),maxW:c}},rect(e,t,n=0,o=0,i=0,r){return this.element("rect",{x:n,y:o,rx:i,ry:i,width:e,height:t},r)},icon(e,t=0,n=0){return this.use(e,{x:t,y:n})},expand(e,t,n){const o=this.element("g",{transform:`translate(${e},${t})`},"expand");return o.append(this.rect(19,19,0,0,1),this.text(n?"-":"+",{x:10,y:14,"text-anchor":"middle"})),o}};function r(e,t,n){e.setAttribute("transform",`translate(${t},${n})`)}function a(e,t,n=!0){return n?e.x>t.x-t.width/2&&e.xt.y-t.height/2&&e.yt.x&&e.xt.y&&e.yfunction(e,t,n,o){let i,r,a,s,l,d={x:null,y:null,onLine1:!1,onLine2:!1};return i=(o.y-n.y)*(t.x-e.x)-(o.x-n.x)*(t.y-e.y),0==i||(r=e.y-n.y,a=e.x-n.x,s=(o.x-n.x)*r-(o.y-n.y)*a,l=(t.x-e.x)*r-(t.y-e.y)*a,r=s/i,a=l/i,d.x=e.x+r*(t.x-e.x),d.y=e.y+r*(t.y-e.y),r>0&&r<1&&(d.onLine1=!0),a>=0&&a<=1&&(d.onLine2=!0)),d}(e,t,n.p,n.q)).filter(e=>e.onLine1&&e.onLine2)}function d(e,t){return a(t,e)?{x:e.x,y:e.y}:l(e,t,e)[0]||{x:e.x,y:e.y}}function c(e,t,n,o,i){const r={x:i.x-e.x,y:i.y-e.y},a=o.x-e.x,s=o.y-e.y;a==r.x&&(r.x+=1e-7);const l=(s-r.y)/(a-r.x),d=s-l*a,c=n*n+t*t*l*l,h=2*t*t*d*l,u=t*t*d*d-t*t*n*n,g=Math.sqrt(h*h-4*c*u),A=r.x>a?(-h+g)/(2*c):(-h-g)/(2*c),p={x:A,y:l*A+d};return p.x+=e.x,p.y+=e.y,p}function h(e,t,n){let o=n.x-t.x,i=n.y-t.y,r=o*o+i*i,a=(e.x-t.x)*o+(e.y-t.y)*i,s=Math.min(1,Math.max(0,a/r));return{x:t.x+o*s,y:t.y+i*s}}function u(e,t){return Math.abs(t.x-e.x)+Math.abs(t.y-e.y)}function g(e){return e/2/(5.5+e/70)}function A(e,t,n){switch(e.toLowerCase()){case"cylinder":return 2*g(t);case"person":return.4*n;case"folder":return t/10;case"robot":return.35*n;case"webbrowser":return n/8;default:return 0}}class p{_el;constructor(e){this._el=e}node(){return this._el}attr(e,t){return this._el.setAttribute(e,String(t)),this}insert(e,t){const n=document.createElementNS("http://www.w3.org/2000/svg",e),o=this._el.insertBefore(n,this._el.querySelector(t));return new p(o)}}function m(e,t,n,o=!1){const i=e.insert("rect",":first-child").attr("rx",o?n.width/8:3).attr("ry",o?n.width/8:3).attr("x",-t.width/2).attr("y",-t.height/2).attr("width",t.width).attr("height",t.height);return n.intersect=function(e){return d(n,e)},i}function f(e,t,n,o,i){const r=e.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(e){return c(n,o,i,n,e)},r}function y(e,t,n){const o=n.width/8,i=n.width/14,r=e.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",-t.width/2).attr("y",-t.height/2-o).attr("width",t.width).attr("height",t.height+2*o),n.intersect=function(e){return d({x:n.x,y:n.y,width:n.width,height:n.height+2*o},e)},r}const b={box:(e,t)=>m(new p(e),t,t).node(),roundedbox:(e,t)=>m(new p(e),t,t,!0).node(),component:(e,t)=>function(e,t,n){const o=n.width/10,i=e.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(e){return d({x:n.x-o/2,y:n.y,width:n.width+o,height:n.height},e)},i}(new p(e),0,t).node(),cylinder:(e,t)=>function(e,t,n){const o=t.width,i=o/2,r=g(o),a=t.height,s=`M 0,${r} a${i},${r} 0,0,0 ${o} 0 a ${i},${r} 0,0,0 ${-o} 0 l 0,${a-2*r} a ${i},${r} 0,0,0 ${o} 0 l 0,${2*r-a}`,l=e.attr("label-offset-y",A("cylinder",o,a)).insert("path",":first-child").attr("d",s).attr("transform","translate("+-o/2+","+-a/2+")");return n.intersect=function(e){const t=d(n,e);let o=n.y+n.height/2-r;return t.y>o?c({x:n.x,y:o},i,r,n,e):(o=n.y-n.height/2+r,t.yfunction(e,t,n){const o=t.width,i=t.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}`,a=e.attr("label-offset-y",A("person",o,i)).insert("path",":first-child").attr("d",r).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(e){return d(n,e)},a}(new p(e),t,t).node(),circle:(e,t)=>function(e,t,n){return f(e,0,n,n.width/2,n.width/2)}(new p(e),0,t).node(),ellipse:(e,t)=>function(e,t,n){return f(e,0,n,.55*n.width,.45*n.width)}(new p(e),0,t).node(),hexagon:(e,t)=>function(e,t,n){const o=n.width/2,i=e.insert("polygon",":first-child").attr("points",[.5,.866,1,0,.5,-.866,-.5,-.866,-1,-0,-.5,.866,.5,.866].map(e=>e*o).join(",")).attr("width",n.width).attr("height",n.height);return n.intersect=function(e){return c(n,n.width/2,n.width/2,n,e)},i}(new p(e),0,t).node(),folder:(e,t)=>function(e,t,n){const o=n.width/20,i=e.attr("label-offset-y",A("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(e){return d({x:n.x,y:n.y+o/2,width:n.width,height:n.height+o},e)},i}(new p(e),0,t).node(),mobiledevicelandscape:(e,t)=>function(e,t,n){const o=n.width/8,i=n.width/14,r=e.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",-t.width/2-o).attr("y",-t.height/2).attr("width",t.width+2*o).attr("height",t.height),n.intersect=function(e){return d({x:n.x,y:n.y,width:n.width+2*o,height:n.height},e)},r}(new p(e),t,t).node(),mobiledeviceportrait:(e,t)=>y(new p(e),t,t).node(),mobiledevice:(e,t)=>y(new p(e),t,t).node(),pipe:(e,t)=>function(e,t,n){const o=n.width,i=n.height,r=i/2,a=r/(2.5+o/70),s=`M${-a},0\n\t\ta${a},${r} 0,0,1 0,${i}\n\t\ta${a},${r} 0,0,1 0,${-i}\n\t\tl${o},0\n\t\ta${a},${r} 0,0,1 0,${i}\n\t\tl${-o},0`,l=e.insert("path",":first-child").attr("d",s).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(e){return d({x:n.x-a,y:n.y,width:n.width+2*a,height:n.height},e)},l}(new p(e),0,t).node(),robot:(e,t)=>function(e,t,n){const o=n.width,i=n.height,r=Math.min(.28*o,.25*i),a=.2*r,s=.25*r,l=.08*r,c=.12*r,h=.22*r,u=.12*r,g=.3*r,p=o,m=-i/2+s+r,f=i-s-r,y=e.attr("label-offset-y",A("robot",o,i)).insert("g",":first-child");y.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-p/2).attr("y",m).attr("width",p).attr("height",f);const b=-i/2+s;y.insert("rect",":first-child").attr("rx",a).attr("ry",a).attr("x",-r/2).attr("y",b).attr("width",r).attr("height",r),y.insert("line",":first-child").attr("class","robot-antenna").attr("x1",0).attr("y1",b).attr("x2",0).attr("y2",-i/2+2*l).attr("stroke-width",.6*l).attr("stroke-linecap","round"),y.insert("circle",":first-child").attr("class","robot-antenna-ball").attr("cx",0).attr("cy",-i/2+2*l).attr("r",1.2*l);const x=b+.4*r;y.insert("circle",":first-child").attr("class","robot-eye").attr("cx",-h).attr("cy",x).attr("r",c),y.insert("circle",":first-child").attr("class","robot-eye").attr("cx",h).attr("cy",x).attr("r",c);const w=b+.7*r,v=.28*r;return y.insert("path",":first-child").attr("class","robot-mouth").attr("d",`M${-v/2},${w} Q0,${w+.3*v} ${v/2},${w}`).attr("fill","none").attr("stroke-width",.5*l).attr("stroke-linecap","round"),y.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",-r/2-u-1).attr("y",x-g/2).attr("width",u).attr("height",g),y.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",r/2+1).attr("y",x-g/2).attr("width",u).attr("height",g),n.intersect=function(e){return d(n,e)},y}(new p(e),0,t).node(),webbrowser:(e,t)=>function(e,t,n){const o=n.height/8,i=e.attr("label-offset-y",A("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(e){return d(n,e)},i}(new p(e),0,t).node()};var x=n(538);class w{versions=[];pos=0;lastSavedPos=0;exportDoc;importDoc;change;tmpPreviousState=null;constructor(e,t,n){this.exportDoc=t,this.importDoc=n,this.change=function(e){let t;return function(){const n=this;clearTimeout(t),t=setTimeout(function(){t=null,e.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(e){return"undefined"!=typeof structuredClone?structuredClone(e):JSON.parse(JSON.stringify(e))}undo(){if(this.pos<2)return;this.pos-=1;const e=this.versions[this.pos-1];this.importDoc(this.deepClone(e))}redo(){if(this.pos>this.versions.length-1)return;const e=this.versions[this.pos];this.importDoc(this.deepClone(e)),this.pos+=1}changed(){return this.pos!==this.lastSavedPos}setSaved(){this.lastSavedPos=this.pos}}var v=n(264);const C={"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},k=(e,t)=>{Object.keys(t).forEach(n=>{const o=t[n];"number"==typeof o?e.style.setProperty(n,o.toString()):e.style.setProperty(n,o)})},I=(e,t)=>Math.sqrt((t.x-e.x)*(t.x-e.x)+(t.y-e.y)*(t.y-e.y));function B(e,t,n,i,r,a){const s={"font-family":String(C["font-family"]),"font-size":`${n}px`,"font-weight":i?"bold":"normal"},l=o(e,t,s);return{lines:l.lines.length>0?l.lines:[""],fontSize:n,lineHeight:n+2,bold:i,field:a,gapAfter:r}}const E={thickness:3,color:"#999",opacity:1,fontSize:22,dashed:!0},S={width:280,height:180,background:"rgba(255, 255, 255, .9)",color:"#666",opacity:.9,stroke:"#999",fontSize:22,shape:"Box"};class M{id;name;nodesMap;edges;edgeVertices;groupsMap;metadata;layoutDirection;colorToVarMap=new Map;_undo;_gridVisible=!1;_snapToGrid=!0;_gridSize=25;_skipAutoFit=!1;constructor(e,t){this.id=e,this.name=t,this.edges=[],this.edgeVertices=new Map,this.nodesMap=new Map,this.groupsMap=new Map,this._undo=new w(this.id,()=>this.exportLayout(!0),e=>this.importLayout(e,!0)),window.graph=this}init(e){e&&this.importLayout(e),this._undo=new w(this.id,()=>this.exportLayout(!0),e=>this.importLayout(e,!0)),this._undo.length()&&this.importLayout(this._undo.currentState()),this._undo.beforeChange(),this._undo.change()}addNode(e,t,n,o,i,r){if(this.nodesMap.has(e))throw Error("duplicate node: "+e);const a={...S,...i},s=(a.shape||"Box").toLowerCase(),l="person"===s?240:180,d=Math.max(280,a.width||0),c=function(e,t,n,o,i){const r=Math.max(o-36,80),a=[B(e,r,i,!0,6,"name"),B(`[${t}]`,r,.75*i,!1,10),B(n,r,Math.min(.8*i,16),!1,0,"description")],s=a.reduce((e,t)=>e+t.lines.length*t.lineHeight+t.gapAfter,0);return{blocks:a,textHeight:s,minimumHeight:s+36}}(t,n,o,d,a.fontSize||22);let h=Math.max(l,a.height||0,c.minimumHeight);for(let e=0;e<20;e++){const e=c.minimumHeight+Math.abs(A(s,d,h));if(e<=h+.1)break;h=e}const u={id:e,title:t,sub:n,description:o,style:a,x:0,y:0,width:d,height:h,intersect:null,link:r,contentLayout:c};this.nodesMap.set(u.id,u)}nodes(){return Array.from(this.nodesMap.values())}addEdge(e,t,n,o,i,r){i&&i.forEach((t,n)=>{const o=t;o.id=`v-${e}-${n}`,this.edgeVertices.set(o.id,o)});const a={id:e,from:this.nodesMap.get(t),to:this.nodesMap.get(n),label:o,vertices:null,style:{...E,...r},initVertex:e=>{const t=e;return t.id||(t.id=((e,t)=>`v-${e}-a-${(e=>{let t=2166136261;for(let n=0;n>>0).toString(36)})(`${e}:${t.x}:${t.y}`)}`)(a.id,e),this.edgeVertices.set(t.id,t)),t.edge=a,e},userDeletedVertices:!1};this.edges.push(a),i&&(a.vertices=i.map(e=>a.initVertex(e)))}addGroup(e,t,n,o){if(this.groupsMap.has(e))return void console.error(`Group exists: ${e} ${t}`);const i={id:e,name:t,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 ${e} "${t}"`),o}).filter(Boolean),style:o};this.groupsMap.set(e,i)}setNodeSelected(e,t){e.selected=t,t?e.ref.classList.add("selected"):e.ref.classList.remove("selected"),this.updateEdgesSel()}updateEdgesSel(){this.edges.forEach(e=>{e.to.selected||e.from.selected?e.ref.classList.add("selected"):e.ref.classList.remove("selected")})}moveNode(e,t,n,o=!1,i=!1){if(e){if(this._snapToGrid&&!o){const e=this.snapToGrid(t,n);t=e.x,n=e.y}e.x==t&&e.y==n||(i||this._undo.beforeChange(),e.x=t,e.y=n,r(e.ref,t,n),this.redrawEdges(e),this.redrawGroups(e),i||this._undo.change())}}moveEdgeVertex(e,t,n,o=!1,i=!1){if(this._snapToGrid&&!o){const e=this.snapToGrid(t,n);t=e.x,n=e.y}e.x==t&&e.y==n||(i||this._undo.beforeChange(),e.x=t,e.y=n,this.redrawEdge(e.edge),i||this._undo.change())}moveSelected(e,t,n=!1){this.nodes().forEach(o=>o.selected&&this.moveNode(o,o.x+e,o.y+t,n,!1)),this.edgeVertices.forEach(o=>o.selected&&this.moveEdgeVertex(o,o.x+e,o.y+t,n,!1))}insertEdgeVertex(e,t,n,o){this._undo.beforeChange();const i=e.initVertex(t);i.selected=!0,o&&(e.vertices.forEach(e=>e.label=!1),i.label=!0),e.vertices.splice(n-1,0,i),this.redrawEdge(e),this._undo.change()}deleteEdgeVertex(e){this._undo.beforeChange();const t=e.edge.vertices.indexOf(e);t>=0&&(e.edge.vertices.splice(t,1),this.edgeVertices.delete(e.id),e.edge.userDeletedVertices=!0),this.redrawEdge(e.edge),this._undo.change()}changed(){return this._undo.changed()}undo(){this._undo.undo()}redo(){this._undo.redo()}alignTopLeft(){const e=this.calculateContentBounds(),t=100-e.x,n=100-e.y;this._skipAutoFit=!0,this._undo.beforeChange(),this.nodesMap.forEach(e=>{this.moveNode(e,e.x+t,e.y+n,!0,!0)}),this.edgeVertices.forEach(e=>{this.moveEdgeVertex(e,e.x+t,e.y+n,!0,!0)}),this._undo.change()}resetPanTransform(){const e=G(),t=D.querySelector("g.zoom");t&&(t.setAttribute("transform",`scale(${e}) translate(0, 0)`),function(e){const t=e.calculateContentBounds(),n=G(),o=Math.max(D.parentElement.clientWidth/n,t.x+t.width+20),i=Math.max(D.parentElement.clientHeight/n,t.y+t.height+20);D.setAttribute("width",String(o*n)),D.setAttribute("height",String(i*n))}(this)),ee(this.id),this._skipAutoFit=!1}shouldSkipAutoFit(){return this._skipAutoFit}resetView(){const e=D.querySelector("g.zoom");e&&(e.setAttribute("transform","scale(1) translate(0, 0)"),Y()),ee(this.id)}redrawEdges(e){this.edges.forEach(t=>(e==t.from||e==t.to)&&this.redrawEdge(t)),this.updateEdgesSel()}redrawEdge(e){const t=e.ref.parentElement;t.removeChild(e.ref),e.ref=V(this,e),t.append(e.ref)}redrawGroups(e){this.groupsMap.forEach(e=>{const t=e.ref.parentElement;t.removeChild(e.ref),q(e),t.append(e.ref)})}exportSVG(){const e=document.querySelector("svg#graph"),t=(e.querySelector("rect.elastic"),e.cloneNode(!0));t.querySelectorAll("a.nodeLink[data-export-href]").forEach(e=>{e.setAttribute("href",e.getAttribute("data-export-href")||""),e.removeAttribute("data-export-href")});const n=t.querySelector("rect.elastic");n&&n.remove();const o=this.calculateContentBounds(),i=o.width+100,r=o.height+100,a=50-o.x,s=50-o.y,l=t.querySelector("g.zoom");l&&l.setAttribute("transform",`scale(1) translate(${a}, ${s})`),t.setAttribute("viewBox",`0 0 ${i} ${r}`),t.setAttribute("width",String(i)),t.setAttribute("height",String(r)),t.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.convertStylesToCustomProperties(t);const d=document.createElement("script");return d.setAttribute("type","application/json"),this.metadata.layout=this.exportLayout(),d.append("/g,"]]]>]>"),t.insertBefore(d,t.firstChild),t.outerHTML}convertStylesToCustomProperties(e){0!==this.colorToVarMap.size&&(e.querySelectorAll("[fill]").forEach(e=>{const t=e.getAttribute("fill");t&&this.colorToVarMap.has(t)&&e.setAttribute("fill",`var(${this.colorToVarMap.get(t)}, ${t})`)}),e.querySelectorAll("[stroke]").forEach(e=>{const t=e.getAttribute("stroke");t&&this.colorToVarMap.has(t)&&e.setAttribute("stroke",`var(${this.colorToVarMap.get(t)}, ${t})`)}))}calculateContentBounds(){let e=1/0,t=1/0,n=-1/0,o=-1/0;return this.nodes().forEach(i=>{const r=i.x-i.width/2,a=i.x+i.width/2,s=i.y-i.height/2,l=i.y+i.height/2;e=Math.min(e,r),n=Math.max(n,a),t=Math.min(t,s),o=Math.max(o,l)}),this.edgeVertices.forEach(i=>{e=Math.min(e,i.x-5),n=Math.max(n,i.x+5),t=Math.min(t,i.y-5),o=Math.max(o,i.y+5)}),this.groupsMap.forEach(i=>{const r=i.x-i.width/2,a=i.x+i.width/2,s=i.y-i.height/2,l=i.y+i.height/2;e=Math.min(e,r),n=Math.max(n,a),t=Math.min(t,s),o=Math.max(o,l)}),this.edges.forEach(i=>{if(e=Math.min(e,i.from.x-10,i.to.x-10),n=Math.max(n,i.from.x+10,i.to.x+10),t=Math.min(t,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=>{e=Math.min(e,i.x-10),n=Math.max(n,i.x+10),t=Math.min(t,i.y-10),o=Math.max(o,i.y+10)}),i.label&&i.label.trim()){const r=(i.from.x+i.to.x)/2,a=(i.from.y+i.to.y)/2,s=10*i.label.length+50;e=Math.min(e,r-s),n=Math.max(n,r+s),t=Math.min(t,a-25),o=Math.max(o,a+25)}}),e===1/0?{x:0,y:0,width:100,height:100}:{x:e,y:t,width:n-e,height:o-t}}exportLayout(e=!1){const t={};return this.nodes().forEach(e=>t[e.id]={x:e.x,y:e.y}),this.edges.forEach(n=>{if(!n.vertices)return;const o=n.vertices.map(e=>({x:e.x,y:e.y,label:e.label,auto:e.auto}));(o.length||e)&&(t[`e-${n.id}`]=o),n.userDeletedVertices&&(t[`e-${n.id}-deleted`]=!0)}),t}setSaved(){this._undo.setSaved()}importLayout(e,t=!1){const n=[];Object.entries(e).forEach(([e,t])=>{e.startsWith("e-")||void 0===t.x||void 0===t.y?e.startsWith("e-")&&Array.isArray(t)&&t.forEach(e=>{void 0!==e.x&&void 0!==e.y&&n.push({x:e.x,y:e.y})}):n.push({x:t.x,y:t.y})});let o=0,i=0;if(n.length>0){const e=Math.min(...n.map(e=>e.x)),t=Math.min(...n.map(e=>e.y));if(e<-100||t<-100||Math.max(...n.map(e=>e.x))>3e3||Math.max(...n.map(e=>e.y))>2e3){const n=50;o=-e+n,i=-t+n}}Object.entries(e).forEach(([e,t])=>{const n=this.nodesMap.get(e);if(n)n.x=t.x+o,n.y=t.y+i;else if(e.startsWith("e-")&&!e.endsWith("-deleted")){const n=this.edges.find(t=>t.id==e.slice(2));if(!n)return;return n.vertices&&n.vertices.forEach(e=>this.edgeVertices.delete(e.id)),void(n.vertices=t.map(e=>{const t={x:e.x+o,y:e.y+i};Object.assign(t,e,{x:e.x+o,y:e.y+i});const r=n.initVertex(t);return e.auto&&(r.auto=!0),r}))}if(e.endsWith("-deleted")){const n=e.slice(2,-8),o=this.edges.find(e=>e.id==n);return void(o&&!0===t&&(o.userDeletedVertices=!0))}}),t&&(this.nodes().forEach(e=>r(e.ref,e.x,e.y)),this.edges.forEach(e=>this.redrawEdge(e)),this.updateEdgesSel(),this.redrawGroups(null))}async autoLayout(e){try{const t=await(0,x.k)(this,e);this._undo.beforeChange(),t.nodes.forEach(e=>{const t=this.nodesMap.get(e.id);t&&this.moveNode(t,e.x,e.y,!1,!0)}),t.edges.forEach(e=>{const t=this.edges.find(t=>t.id==e.id);if(t){if(t.vertices&&t.vertices.forEach(e=>{e.id&&this.edgeVertices.delete(e.id)}),t.vertices=[],t.userDeletedVertices=!1,e.vertices&&e.vertices.length>0&&(t.vertices=e.vertices.map(e=>{const n=t.initVertex(e);return n.auto=!0,n})),e.label){t.vertices&&(t.vertices.forEach(e=>{e.label&&this.edgeVertices.delete(e.id)}),t.vertices=t.vertices.filter(e=>!e.label));const n=t.initVertex(e.label);n.label=!0,n.auto=!0,t.vertices=t.vertices||[];const o=function(e,t,n,o){if(0===e.length)return 0;const i=[n,...e,o];let r=1/0,a=0;for(let e=0;ee.selected);e.push(...Array.from(this.edgeVertices.values()).filter(e=>e.selected));let t=Math.min(...e.map(e=>e.y));this.nodesMap.forEach(e=>e.selected&&this.moveNode(e,e.x,t,!1,!1)),this.edgeVertices.forEach(e=>e.selected&&this.moveEdgeVertex(e,e.x,t,!1,!1))}alignSelectionH(){const e=this.nodes().filter(e=>e.selected);e.push(...Array.from(this.edgeVertices.values()).filter(e=>e.selected));let t=Math.min(...e.map(e=>e.x));this.nodesMap.forEach(e=>e.selected&&this.moveNode(e,t,e.y,!1,!1)),this.edgeVertices.forEach(e=>e.selected&&this.moveEdgeVertex(e,t,e.y,!1,!1))}distributeSelectionH(){const e=this.nodes().filter(e=>e.selected),t=Array.from(this.edgeVertices.values()).filter(e=>e.selected);if(e.length+t.length<3)return;this._undo.beforeChange();const n=[...e,...t];n.sort((e,t)=>e.x-t.x);const o=n[0].x,i=(n[n.length-1].x-o)/(n.length-1);n.forEach((e,t)=>{const n=o+t*i;"title"in e?this.moveNode(e,n,e.y,!1,!0):this.moveEdgeVertex(e,n,e.y,!1,!0)}),this._undo.change()}distributeSelectionV(){const e=this.nodes().filter(e=>e.selected),t=Array.from(this.edgeVertices.values()).filter(e=>e.selected);if(e.length+t.length<3)return;this._undo.beforeChange();const n=[...e,...t];n.sort((e,t)=>e.y-t.y);const o=n[0].y,i=(n[n.length-1].y-o)/(n.length-1);n.forEach((e,t)=>{const n=o+t*i;"title"in e?this.moveNode(e,e.x,n,!1,!0):this.moveEdgeVertex(e,e.x,n,!1,!0)}),this._undo.change()}setEdgeSelected(e,t){t&&(this.setNodeSelected(e.from,!0),this.setNodeSelected(e.to,!0)),this.updateEdgesSel()}fitToView(){const e=this.calculateContentBounds();if(0===e.width||0===e.height)return;const t=D.parentElement?.clientWidth||800,n=D.parentElement?.clientHeight||600,o=(t-80)/e.width,i=(n-80)/e.height,r=Math.min(o,i),a=Math.max(Math.min(r,2),.1),s=t/2-(e.x+e.width/2)*a,l=n/2-(e.y+e.height/2)*a,d=D.querySelector("g.zoom");d&&d.setAttribute("transform",`translate(${s}, ${l}) scale(${a})`),Y(),J(this.id)}saveLayoutState(){return this.exportLayout(!0)}restoreLayoutState(e){this.importLayout(e,!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(e=>{const t=Math.round(e.x/this._gridSize)*this._gridSize,n=Math.round(e.y/this._gridSize)*this._gridSize;this.moveNode(e,t,n,!1,!0)}),this._undo.change())}snapToGrid(e,t){return{x:Math.round(e/this._gridSize)*this._gridSize,y:Math.round(t/this._gridSize)*this._gridSize}}updateGridDisplay(){if(!D)return;const e=D.querySelector("#grid-pattern");e&&e.remove();const t=D.querySelector("#grid-background");if(t&&t.remove(),!this._gridVisible)return;let n=D.querySelector("defs");n||(n=document.createElementNS("http://www.w3.org/2000/svg","defs"),D.insertBefore(n,D.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 a=D.querySelector("g.zoom");a&&a.insertBefore(r,a.firstChild)}}let $,D=document.querySelector("svg#graph");D||(D=document.createElementNS("http://www.w3.org/2000/svg","svg"),D.setAttribute("id","graph"),D.addEventListener("click",e=>$(e))),D.setAttribute("width","100%"),D.setAttribute("height","100%");let j,L=!1;const T=(e,t,n)=>{D.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(D),D.__data=e,j=t,$=e=>{},_(e);const o=i.rect(300,300,50,50,0,"elastic");return D.append(o),e.updateGridDisplay(),W(D,n),{svg:D,setZoom:H}},_=e=>{const t=i.element("g",{},"zoom"),n=i.element("g",{},"nodes"),o=i.element("g",{},"edges"),a=i.element("g",{},"groups");t.append(a,o,n),e.nodesMap.forEach(t=>{!function(e,t){window.gdata=t;const n=i.element("g",{},"node");n.setAttribute("id",e.id),e.selected&&n.classList.add("selected"),r(n,e.x,e.y);const o=e.link?i.element("a",{href:e.link.href,"data-export-href":e.link.exportHref,"aria-label":`Open ${e.title}`},"nodeLink"):null,a=o||n;o&&(n.classList.add("linked"),n.append(o));const s=e.style.shape||"Box",l=(b[s.toLowerCase()]||b.box)(a,e);l.classList.add("nodeBorder"),k(l,Z.nodeBorder),l.setAttribute("fill",e.style.background),l.setAttribute("stroke",e.style.stroke),l.setAttribute("stroke-width","3"),l.setAttribute("opacity",String(e.style.opacity)),X(l,e.style.border);const d=function(e,t){const n=i.element("g");let o=-e.textHeight/2;return e.blocks.forEach(e=>{const r=i.text("",{"text-anchor":"middle"});k(r,C),t&&r.setAttribute("fill",t),e.field&&r.setAttribute("data-field",e.field),e.lines.forEach((t,n)=>{const a=i.element("tspan",{x:0,y:o+e.fontSize+n*e.lineHeight,"font-size":`${e.fontSize}px`,"font-weight":e.bold?"bold":"normal"});a.textContent=t,r.append(a)}),n.append(r),o+=e.lines.length*e.lineHeight+e.gapAfter}),n}(e.contentLayout,e.style.color);r(d,0,(Number(a.getAttribute("label-offset-y"))||0)/2),a.append(d),n.__data=e,e.ref=n}(t,e),n.append(t.ref)}),e.edges.forEach(e=>{e.labelBounds=void 0}),e.edges.forEach(t=>{V(e,t),o.append(t.ref)}),e.groupsMap.forEach(e=>{q(e),a.append(e.ref)}),D.append(t)};function V(e,t){const n=t.from,o=t.to,r=i.element("g",{},"edge");r.setAttribute("id",t.id),r.setAttribute("data-from",t.from.id),r.setAttribute("data-to",t.to.id);const d=(t.style.position||50)/100,c=function(e,t){const n=e.from,o=e.to;let i=e.vertices?e.vertices.concat():[];if(0==i.length&&!e.userDeletedVertices){const r=t.edges.filter(t=>t.from==e.from&&t.to==e.to);let a=0;if(r.length>1){a=r.indexOf(e)-(r.length-1)/2;let t=0,s=0;Math.abs(n.x-o.x)>Math.abs(n.y-o.y)?s=70*a:t=200*a;const l=e.initVertex({x:(n.x+o.x)/2+t,y:(n.y+o.y)/2+s});l.label=!0,l.auto=!0,i.push(l)}}i.unshift(n),i.push(o);let r=i[i.length-1];for(let e=1;e0;e--)if(!i[e].label){a=i[e];break}const s=(e,t)=>{const n=e.style?.shape?.toLowerCase()||"box",o=t.x-e.x,i=t.y-e.y;if(e.x,e.y,Math.abs(o)<.01&&Math.abs(i)<.01)return{x:e.x+e.width/2,y:e.y};if("cylinder"===n){const t=e.width,n=t/2,r=n/(5.5+t/70),a=e.height/2,s=Math.atan2(i,o),l=Math.cos(s),d=Math.sin(s);let c=1/0;Math.abs(l)>.01&&(c=Math.min(c,Math.abs(n/l))),Math.abs(d)>.01&&(c=Math.min(c,Math.abs(a/d)));const h=e.x+l*c,u=e.y+d*c,g=e.y-a+r,A=e.y+a-r;if(uA){const t=u=0){const e=Math.sqrt(l),n=(-s+e)/(2*i),r=(-s-e)/(2*i);return{x:o>0?Math.max(n,r):Math.min(n,r),y:t}}}return{x:h,y:u}}if("circle"===n){const t=e.width/2,n=Math.atan2(i,o);return{x:e.x+Math.cos(n)*t,y:e.y+Math.sin(n)*t}}if("ellipse"===n){const t=.55*e.width,n=.45*e.width,r=Math.atan2(i,o),a=Math.cos(r),s=Math.sin(r),l=Math.sqrt(t*t*s*s+n*n*a*a);return{x:e.x+t*a*n/l,y:e.y+n*s*t/l}}{const t=e.width/2,n=e.height/2,r=Math.atan2(i,o),a=Math.cos(r),s=Math.sin(r);let l=1/0;return Math.abs(a)>.01&&(l=Math.min(l,Math.abs(t/a))),Math.abs(s)>.01&&(l=Math.min(l,Math.abs(n/s))),{x:e.x+a*l,y:e.y+s*l}}};let l=s(n,r),d=s(o,a);return i[0]=l,i[i.length-1]=d,i}(t,e),h=function(e,t,n){let o,i={x:n.x,y:n.y};const r=e.findIndex(e=>e.label);let a=!0;if(r>=0){const t=e[r];i=t,a=!0===t.auto;const n=[];r>0&&n.push({p:e[r-1],q:i}),re?I(t.p,t.q)>I(e.p,e.q)?t:e:t,void 0)}else{const n=e.slice(1).reduce((t,n,o)=>t+I(e[o],n),0)*t;let r=0;for(let t=1;t0&&r+s>=n){const e=(n-r)/s;i={x:a.p.x+(a.q.x-a.p.x)*e,y:a.p.y+(a.q.y-a.p.y)*e},o=a;break}r+=s}}const s=o?Math.abs(o.q.x-o.p.x):0,l=o?Math.abs(o.q.y-o.p.y):0;return{...i,orientation:l>s?"vertical":"horizontal",segment:o,movable:a}}(c,d,n),{bg:u,txt:g,bbox:A}=function(e,t,n){const o=t.style.fontSize;let{txt:r,dy:a,maxW:l}=i.textArea(t.label,200,o,!1,e.x,e.y,"middle");a-=o/2,l+=o;const d=[{x:e.x,y:e.y}];if(e.movable&&e.segment)for(const t of[.5,.35,.65,.2,.8]){const n={x:e.segment.p.x+(e.segment.q.x-e.segment.p.x)*t,y:e.segment.p.y+(e.segment.q.y-e.segment.p.y)*t};d.some(e=>Math.abs(e.x-n.x)<.1&&Math.abs(e.y-n.y)<.1)||d.push(n)}const c=[...n.nodes().map(e=>z(s(e),8)),...n.edges.filter(e=>e!==t&&e.labelBounds).map(e=>z(e.labelBounds,8))],h=d.flatMap(t=>"vertical"===e.orientation?[1,-1].map(n=>N(t.x+n*(l/2+12),t.y,l,a,t,e,c)):[-1,1].map(n=>N(t.x,t.y+n*(a/2+12),l,a,t,e,c))).reduce((e,t)=>t.score{e.setAttribute("x",String(u))}),r.setAttribute("y",String(g-a/2)),k(r,Z.edgeText),r.setAttribute("stroke","none"),r.setAttribute("font-size",String(t.style.fontSize)),r.setAttribute("fill",t.style.color);const p={...A},m=i.rect(p.width,p.height,p.x,p.y);return k(m,Z.edgeRect),r.setAttribute("data-field","label"),t.labelBounds=A,p.x+=p.width/2,p.y+=p.height/2,{bg:m,txt:r,bbox:p}}(h,t,e);r.append(u,g);const{segments:p,path:m}=function(e,t,n,o){const i=[];for(let t=1;t0&&i[i.length-1],function(e,t){for(let n=0;nMath.abs(i[1].x-o.p.x)+Math.abs(i[1].y-o.p.y)&&i.reverse();const t={p:i[1],q:o.q};o.q=i[0],e.splice(n+1,0,t),n+=1}}}}(i,t),i.length>0){r=`M${i[0].p.x},${i[0].p.y}`;for(let e=0;e{if("id"in e&&"edge"in e){const n=e;return n.edge=t,n}return t.initVertex(e)}),t.vertices.forEach((e,t)=>{const n=e;n.ref=i.element("circle",{id:n.id,cx:e.x,cy:e.y,r:7,fill:"none"},"v-dot"),n.selected&&n.ref.classList.add("selected"),n.auto&&n.ref.classList.add("auto"),r.append(n.ref)}),t.ref=r,r}function N(e,t,n,o,i,r,a){const s={x:e-n/2,y:t-o/2,width:n,height:o},l=a.reduce((e,t)=>{return e+(n=s,o=t,Math.max(0,Math.min(n.x+n.width,o.x+o.width)-Math.max(n.x,o.x))*Math.max(0,Math.min(n.y+n.height,o.y+o.height)-Math.max(n.y,o.y)));var n,o},0);return{centerX:e,centerY:t,bounds:s,score:1e3*l+I(i,r)}}function z(e,t){return{x:e.x-t,y:e.y-t,width:e.width+2*t,height:e.height+2*t}}function q(e){if(0==e.nodes.length)return;const t=i.element("g",{},"group");let n={x:1e100,y:1e100},o={x:0,y:0};e.nodes.forEach(e=>{const t=e.style?.shape?.toLowerCase()||"box";let i=e.height/2,r=e.height/2;if("robot"===t){const t=.12*e.height;i=e.height/2+t,r=e.height/2}else if("hexagon"===t){const t=e.width/2*.866;i=t,r=t}const a={x:e.x-e.width/2,y:e.y-i,width:e.width,height:i+r};n.x=Math.min(n.x,a.x),n.y=Math.min(n.y,a.y),o.x=Math.max(o.x,a.x+a.width),o.y=Math.max(o.y,a.y+a.height)});const r=Math.max(o.x-n.x,200),a=o.y-n.y,s={x:n.x-25,y:n.y-25,width:r+50,height:a+50+30},l=i.rect(s.width,s.height,s.x,s.y);e.x=s.x+s.width/2,e.y=s.y+s.height/2,e.width=s.width,e.height=s.height,k(l,Z.groupRect),e.style.stroke&&l.setAttribute("stroke",e.style.stroke),e.style.background&&l.setAttribute("fill",e.style.background);const d=i.text(e.name,{x:n.x,y:s.y+s.height-Z.groupText["font-size"]});k(d,Z.groupText),e.style.color&&d.setAttribute("fill",e.style.color),t.append(l,d),e.ref=t}function R(e,t){let n={dst:Number.POSITIVE_INFINITY,pos:-1,edge:null,prj:null};return e.edges.forEach(e=>{const o=e.vertices||[],i=[e.from,...o,e.to];for(let o=1;o50||a3||Math.abs(s)>3)&&(h=!0,c=null,d)){const e=t.getSelection();e.length=0,e.push(d.node),t.setSelection(e),o=[{x:d.node.x,y:d.node.y,n:d.node}],d=null}if(r)!function(t,n){const o=e.querySelector("g.zoom");if(!o)return;const i=G();o.setAttribute("transform",`translate(${t}, ${n}) scale(${i})`)}(l.x+a,l.y+s);else if(o.length>0&&h){const e=t.getZoom(),n=a/e,i=s/e;o.forEach(e=>{t.moveNode(e.n,e.x+n,e.y+i)}),t.setDragging(!0)}else i&&(i.update(n),t.setDragging(!0))}(n=m(n),n.clientX-p.ex,n.clientY-p.ey)}function y(n){document.removeEventListener("touchmove",f),document.removeEventListener("mousemove",f),document.removeEventListener("mouseup",y),document.removeEventListener("touchend",y),function(n){t.setDragging(!1);const a=h?null:c;if(h&&(u=!0,window.setTimeout(()=>{u=!1},0)),d&&!h){const e=t.getSelection();e.length=0,e.push(d.node),t.setSelection(e)}if(i){const e=i.end();e?t.boxSelection(e,n.shiftKey):o.length||t.setSelection([]),i=null}if(r&&h){const t=e.__data;t&&t.id&&J(t.id)}d=null,c=null,h=!1,r=!1,t.updatePanning(),a&&(window.location.href=a)}(m(n)),p=null}function b(u){u=m(u),p={ex:u.clientX,ey:u.clientY},function(u){u.preventDefault(),h=!1,d=null;const g=u.target,A=g instanceof Element?g.closest("a.nodeLink"):null;c=u.shiftKey?null:A?.getAttribute("href")||null;const p=t.nodeFromEvent(u),m=u.shiftKey?"pan"===n?"select":"pan":n;if(p)if("pan"===m){r=!1,i=null;const e=t.getSelection();t.isSelected(p)?o=e.map(e=>({x:e.x,y:e.y,n:e})):(t.setSelection([p]),o=[{x:p.x,y:p.y,n:p}])}else{r=!1,i=null;const e=t.getSelection();if(u.shiftKey&&"select"===n){if(t.isSelected(p)){const t=e.findIndex(e=>e.id===p.id);t>=0&&e.splice(t,1)}else e.push(p);t.setSelection(e),o=e.map(e=>({x:e.x,y:e.y,n:e}))}else t.isSelected(p)?(o=e.map(e=>({x:e.x,y:e.y,n:e})),d=null):(d={node:p,shiftKey:u.shiftKey},o=[{x:p.x,y:p.y,n:p}])}else"pan"===m?(r=!0,i=null,a=u.clientX,s=u.clientY,l=function(){const t=e.querySelector("g.zoom");if(!t)return{x:0,y:0};const n=(t.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return n?{x:parseFloat(n[1])||0,y:parseFloat(n[2])||0}:{x:0,y:0}}(),o=[],t.setSelection([])):(r=!1,i=function(){let t=0,n=0,o=null;return{ini(i){const r=F(i);t=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(t)),o.setAttribute("y",String(n)),o.setAttribute("width","0"),o.setAttribute("height","0");const a=e.querySelector("g.zoom");a?a.appendChild(o):e.appendChild(o)},update(e){if(!o)return;const i=F(e),r=i.x,a=i.y,s=Math.min(t,r),l=Math.min(n,a),d=Math.abs(r-t),c=Math.abs(a-n);o.setAttribute("x",String(s)),o.setAttribute("y",String(l)),o.setAttribute("width",String(d)),o.setAttribute("height",String(c))},end(){if(!o)return null;const e=parseFloat(o.getAttribute("x")||"0"),t=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:e,y:t,width:n,height:i,left:e,top:t,right:e+n,bottom:t+i}:null}}}(),i&&i.ini(u),o=[])}(u),document.addEventListener("touchmove",f),document.addEventListener("mousemove",f),document.addEventListener("mouseup",y),document.addEventListener("touchend",y)}A.addEventListener("mousedown",b),A.addEventListener("touchstart",b),g.push({element:A,event:"mousedown",handler:b},{element:A,event:"touchstart",handler:b})}(e),e.addEventListener("click",A),g.push({element:e,event:"click",handler:A}),()=>{g.forEach(({element:e,event:t,handler:n})=>{e.removeEventListener(t,n)})}}function W(e,t){const n=e.__cursorInteractionCleanup;function o(e){return e.__data}n&&n();const r=()=>o(e),l=[],d=e=>{r().changed()&&(e.preventDefault(),e.returnValue="")};function c(t,n){t.selected=n;const o=e.querySelector("#"+t.id);t.selected?o.classList.add("selected"):o.classList.remove("selected")}window.addEventListener("beforeunload",d),l.push({element:window,event:"beforeunload",handler:d});const h=t=>{if(!t.altKey)return;const n=R(r(),F(t));if(n){const{prj:t}=n,o=e.querySelector("g.edges");let r=o.querySelector("#prj");r||(r=i.element("circle",{id:"prj",cx:t.x,cy:t.y,r:7}),o.append(r)),r.setAttribute("cx",String(t.x)),r.setAttribute("cy",String(t.y))}else g()};e.addEventListener("mousemove",h),l.push({element:e,event:"mousemove",handler:h});const u=e=>{const t=(0,v.Yp)(e,!0);t!=v.Zj&&t!=v._s&&g()};function g(){const t=e.querySelector("g.edges #prj");t&&t.parentElement.removeChild(t)}window.addEventListener("keyup",u),l.push({element:window,event:"keyup",handler:u});const A=e=>{const t=(0,v.Yp)(e,!0);if(t!=v._s&&t!=v.Zj)return;const n=R(r(),F(e));if(n){const{edge:e,pos:o,prj:i}=n;r().insertEdgeVertex(e,i,o,t==v._s),g()}};e.addEventListener("click",A),l.push({element:e,event:"click",handler:A});const p=t=>{const n=.1*Math.sign(t.deltaY),o=G(),i=Math.max(.1,Math.min(5,o-n));if(i!==o){const n=e.getBoundingClientRect();P(i,t.clientX-n.left,t.clientY-n.top),t.preventDefault();const o=e.__data;o&&o.id&&J(o.id)}};e.addEventListener("wheel",p),l.push({element:e,event:"wheel",handler:p});const m=e=>{const t=(0,v.Yp)(e);switch(t&&e.preventDefault(),t){case v.bl:Array.from(r().edgeVertices.values()).filter(e=>e.selected).forEach(e=>{r().deleteEdgeVertex(e)});break;case"undo":r().undo();break;case"redo":r().redo();break;case v.Ur:P(Math.min(5,1.2*G())),J(r().id);break;case v.hU:P(Math.max(.1,G()/1.2)),J(r().id);break;case v.i1:P(1),J(r().id);break;case v.mD:r().fitToView();break;case v.F:r().nodes().forEach(e=>r().setNodeSelected(e,!0)),r().edgeVertices.forEach(e=>c(e,!0));break;case v.Gn:r().nodes().forEach(e=>r().setNodeSelected(e,!1)),r().edgeVertices.forEach(e=>c(e,!1))}};window.addEventListener("keydown",m),l.push({element:window,event:"keydown",handler:m});const f=O(e,{nodeFromEvent(e){e.preventDefault();let t=e.target.closest("g.nodes g.node");return t?o(t):(t=e.target.closest("g.edges g.edge .v-dot"),t?r().edgeVertices.get(t.id):null)},setSelection(e){r().nodes().forEach(t=>r().setNodeSelected(t,e.some(e=>e.id==t.id))),r().edgeVertices.forEach(t=>c(t,e.some(e=>e.id==t.id))),j(r().nodes().find(e=>e.selected))},setDragging(e){L=e},isSelected:e=>e.selected,getSelection(){const e=r().nodes().filter(e=>e.selected);return r().edgeVertices.forEach(t=>t.selected&&e.push(t)),e},getZoom:G,moveNode(e,t,n){r().nodesMap.has(e.id)?r().moveNode(e,t,n):(e.auto=!1,r().moveEdgeVertex(e,t,n))},boxSelection(e,t){r().nodesMap.forEach(n=>{var o,i;o=s(n),i=e,o.xi.x&&o.y+o.height>i.y?r().setNodeSelected(n,!n.selected):t||r().setNodeSelected(n,!1)}),r().edgeVertices.forEach(n=>{a(n,e,!1)?c(n,!n.selected):t||c(n,!1)}),j(r().nodes().find(e=>e.selected))},updatePanning:Y},t);e.__cursorInteractionCleanup=()=>{l.forEach(({element:e,event:t,handler:n})=>{e.removeEventListener(t,n)}),f&&f()}}function G(){if(!D)return 1;const e=D.querySelector("g.zoom");if(!e)return 1;const t=(e.getAttribute("transform")||"").match(/scale\(([^)]+)\)/);return t&&parseFloat(t[1])||1}function H(e){if(!D)return;const t=D.querySelector("g.zoom");if(!t)return;const n=U();t.setAttribute("transform",`translate(${n.x}, ${n.y}) scale(${e})`),Y()}function P(e,t,n){const o=D.querySelector("g.zoom"),i=G();if(void 0===t||void 0===n){const e=D.parentElement;e?(t=e.clientWidth/2,n=e.clientHeight/2):(t=D.clientWidth/2,n=D.clientHeight/2)}const r=U(),a=t-(t-r.x)/i*e,s=n-(n-r.y)/i*e;o.setAttribute("transform",`translate(${a}, ${s}) scale(${e})`),Y()}function U(){if(!D)return{x:0,y:0};const e=D.querySelector("g.zoom");if(!e)return{x:0,y:0};const t=(e.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return t?{x:parseFloat(t[1])||0,y:parseFloat(t[2])||0}:{x:0,y:0}}function Y(){if(!D)return;const e=D.querySelector("g.zoom");if(!e)return;const t=e.getBBox(),n=G();if(!D.parentElement)return;const o=Math.max(D.parentElement.clientWidth/n,t.x+t.width+20),i=Math.max(D.parentElement.clientHeight/n,t.y+t.height+20);D.setAttribute("width",String(o*n)),D.setAttribute("height",String(i*n))}const X=(e,t)=>{"Dashed"==t?e.setAttribute("stroke-dasharray","4"):"Dotted"==t&&e.setAttribute("stroke-dasharray","2")},Z={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"}},K=new Map;function J(e){if(!D)return;const t=G(),n=U(),o={zoom:t,transform:{x:n.x,y:n.y}};K.set(e,o)}function Q(e){if(!D||!K.has(e))return!1;const t=K.get(e);if(!t)return!1;const n=D.querySelector("g.zoom");return n&&(n.setAttribute("transform",`scale(${t.zoom}) translate(${t.transform.x}, ${t.transform.y})`),Y()),!0}function ee(e){K.delete(e)}function te(e,t,n){const o=e.x-t.x,i=e.y-t.y,r=n.x-t.x,a=n.y-t.y,s=o*r+i*a,l=r*r+a*a;if(0===l)return Math.sqrt(o*o+i*i);let d,c,h=s/l;h<0?(d=t.x,c=t.y):h>1?(d=n.x,c=n.y):(d=t.x+h*r,c=t.y+h*a);const u=e.x-d,g=e.y-c;return Math.sqrt(u*u+g*g)}},538(e,t,n){n.d(t,{k:()=>r}),n(408);function o(e={},t=!1){const n={nodeSpacing:e.nodeSpacing??80,layerSpacing:e.layerSpacing??60,componentSpacing:80,padding:40,groupMultiplier:.65};return t&&(n.nodeSpacing=Math.max(n.nodeSpacing*n.groupMultiplier,30),n.layerSpacing=Math.max(n.layerSpacing*n.groupMultiplier,35),n.componentSpacing=Math.max(n.componentSpacing*n.groupMultiplier,25),n.padding=Math.max(n.padding*n.groupMultiplier,15)),n}function i(e,t){const{direction:n="DOWN",compactLayout:o=!1}=t,i={"elk.algorithm":"layered","elk.direction":n,"elk.spacing.nodeNode":e.nodeSpacing.toString(),"elk.spacing.componentComponent":e.componentSpacing.toString(),"elk.padding":`[top=${e.padding},left=${e.padding},bottom=${e.padding},right=${e.padding}]`,"elk.layered.spacing.nodeNodeBetweenLayers":e.layerSpacing.toString(),"elk.layered.spacing.edgeNodeBetweenLayers":"10","elk.layered.spacing.edgeEdgeBetweenLayers":"10","elk.edgeRouting":"POLYLINE","elk.layered.unnecessaryBendpoints":"false","elk.layered.edgeRouting.orthogonal.mode":"DIRECTION_BASED","elk.layered.edgeRouting.orthogonal.spacing":"5","elk.layered.edgeRouting.orthogonal.nodeOverlapRatio":"0.1","elk.layered.compaction.connectedComponents":"true","elk.layered.compaction.postCompaction.strategy":"LEFT_RIGHT","elk.separateConnectedComponents":"true","elk.layered.nodePlacement.strategy":"NETWORK_SIMPLEX","elk.layered.nodePlacement.favorStraightEdges":"true","elk.layered.crossingMinimization.strategy":"LAYER_SWEEP","elk.layered.crossingMinimization.semiInteractive":"true","elk.hierarchyHandling":"SEPARATE_CHILDREN","elk.layered.considerModelOrder.strategy":"NONE","elk.edgeLabels.placement":"CENTER","elk.edgeLabels.inline":"true","elk.spacing.edgeLabel":"5","elk.edgeLabels.avoidOverlap":"false","elk.edgeLabels.considerModelOrder":"false","elk.layered.edgeLabels.sideSelection":"ALWAYS_UP"};return o&&(i["elk.spacing.nodeNode"]=Math.max(.7*e.nodeSpacing,30).toString(),i["elk.layered.spacing.nodeNodeBetweenLayers"]=Math.max(.7*e.layerSpacing,30).toString()),i}async function r(e,t={}){const r=new(await n.e(726).then(n.t.bind(n,862,23)).then(e=>e.default)),s={id:"root",layoutOptions:i(o(t,!1),t),children:[],edges:[]},l=new Map,d=new Map;e.nodesMap.forEach(e=>{if(!e.id)return;l.set(e.id,e);const t=Math.max(e.width||200,150),n=Math.max(e.height||100,250);d.set(e.id,{id:e.id,x:e.x,y:e.y,width:t+50,height:n+50,layoutOptions:{"elk.position":"","elk.nodeSize.constraints":"[FIXED_SIZE]"}})});const c=new Map,h=new Map,u=new Set;e.groupsMap.forEach(e=>{e.nodes.forEach(t=>{if(a(t))return h.has(t.id)||h.set(t.id,e.id),void u.add(t.id);c.has(t.id)||c.set(t.id,e.id)})});const g=new Map,A=e=>{const n=g.get(e.id);if(n)return n;const r=e.nodes.flatMap(t=>{if(a(t))return h.get(t.id)===e.id?[A(t)]:[];const n=d.get(t.id);return n&&c.get(t.id)===e.id?[n]:[]}),s={id:e.id,children:r,edges:[],layoutOptions:i(o(t,!0),t)};return g.set(e.id,s),s};e.groupsMap.forEach(e=>{if(!u.has(e.id)){const t=A(e);t.children.length>0&&s.children.push(t)}}),d.forEach((e,t)=>{c.has(t)||s.children.push(e)});const p=e=>{const t=[];let n=e;for(;n;)t.push(n),n=h.get(n);return t};if(e.edges.forEach(e=>{if(!e.id||!e.from?.id||!e.to?.id)return;if(!l.has(e.from.id)||!l.has(e.to.id))return void console.warn(`Skipping edge ${e.id}: source ${e.from.id} or target ${e.to.id} not found in nodes`);const t=e.label&&e.label.trim()?Math.min(7*e.label.length,200):0,n={id:e.id,sources:[e.from.id],targets:[e.to.id],labels:e.label&&e.label.trim()?[{id:`${e.id}-label`,text:e.label,width:t,height:20,layoutOptions:{"elk.edgeLabels.placement":"CENTER","elk.edgeLabels.inline":"true"}}]:[]},o=((e,t)=>{const n=p(c.get(e)),o=new Set(p(c.get(t)));return n.find(e=>o.has(e))})(e.from.id,e.to.id);(o?g.get(o):s).edges.push(n)}),!s.id||!s.children)throw new Error("Invalid ELK graph structure");try{const t=await r.layout(s),n=[],o=[],i=(e,t=0,o=0)=>{e.children?.forEach(e=>{e.children?i(e,t+(e.x||0),o+(e.y||0)):n.push({id:e.id,x:t+(e.x||0)+(e.width||0)/2,y:o+(e.y||0)+(e.height||0)/2})})},a=(t,n=0,i=0)=>{t.edges?.forEach(t=>{const r=[];let a;t.sections&&t.sections.length>0&&t.sections.forEach(e=>{e.startPoint&&r.push({x:n+e.startPoint.x,y:i+e.startPoint.y}),e.bendPoints&&e.bendPoints.length>0&&e.bendPoints.forEach(e=>{r.push({x:n+e.x,y:i+e.y})}),e.endPoint&&r.push({x:n+e.endPoint.x,y:i+e.endPoint.y})});const s=e.edges.find(e=>e.id===t.id);if(s?.label&&s.label.trim())if(t.labels&&t.labels.length>0){const e=t.labels[0];void 0!==e.x&&void 0!==e.y&&(a={x:n+e.x+(e.width||0)/2,y:i+e.y+(e.height||0)/2})}else if(r.length>=2){const e=Math.floor(r.length/2);if(r.length%2==0){const t=r[e-1],n=r[e];a={x:(t.x+n.x)/2,y:(t.y+n.y)/2}}else a=r[e]}o.push({id:t.id,vertices:r,label:a})}),t.children?.forEach(e=>{e.edges&&e.edges.length>0&&a(e,n+(e.x||0),i+(e.y||0))})};if(i(t),a(t),n.length>0){const e=Math.min(...n.map(e=>e.x)),t=Math.min(...n.map(e=>e.y)),i=50,r=-e+i,a=-t+i;n.forEach(e=>{e.x+=r,e.y+=a}),o.forEach(e=>{e.vertices.forEach(e=>{e.x+=r,e.y+=a}),e.label&&(e.label.x+=r,e.label.y+=a)})}return{nodes:n,edges:o}}catch(t){return console.warn("ELK layout failed, using fallback layout. Error:",t),function(e){const t=[],n=[];let o=0,i=0;const r=Math.ceil(Math.sqrt(e.nodesMap.size));let a=0;return e.nodesMap.forEach(e=>{t.push({id:e.id,x:o,y:i}),a++,a>=r?(a=0,o=0,i+=300):o+=300}),e.edges.forEach(e=>{n.push({id:e.id,vertices:[]})}),{nodes:t,edges:n}}(e)}}function a(e){return"nodes"in e}},245(e,t,n){var o=n(122),i=n(763),r=n(486),a=n(582),s=n.n(a),l=n(991),d=n.n(l),c=n(725),h=n.n(c),u=n(798),g=n.n(u),A=n(754),p=n.n(A),m=n(567),f=n.n(m),y=n(870),b={};b.styleTagTransform=f(),b.setAttributes=g(),b.insert=h().bind(null,"head"),b.domAPI=d(),b.insertStyleElement=p(),s()(y.A,b),y.A&&y.A.locals&&y.A.locals,n(769);class x{callback;handler;running=!1;timeoutId=null;constructor(e){this.callback=e,this.handler=()=>{this.running=!1,this.timeoutId=null,this.callback()}}start(e){this.running&&this.stop(),this.timeoutId=setTimeout(this.handler,e),this.running=!0}stop(){this.running&&null!==this.timeoutId&&(clearTimeout(this.timeoutId),this.running=!1,this.timeoutId=null)}isRunning(){return this.running}}class w{static DEFAULT_OPTIONS={minDelay:1e3,maxDelay:6e4,handshakeTimeout:5e3};static LIVERELOAD_PROTOCOLS=["http://livereload.com/protocols/official-9","http://livereload.com/protocols/2.x-remote-control"];uri;options;fileChangeHandler;socket=null;nextDelay;connectionDesired=!1;disconnectionReason="";handshakeTimeout;reconnectTimer;constructor(e,t){this.fileChangeHandler=e,this.options={...w.DEFAULT_OPTIONS,...t},this.uri="ws://localhost:35729/livereload",this.nextDelay=this.options.minDelay,this.handshakeTimeout=new x(()=>this.handleHandshakeTimeout()),this.reconnectTimer=new x(()=>this.attemptReconnection())}connect(){this.connectionDesired=!0,this.isSocketConnected()||(this.prepareForConnection(),this.createWebSocket())}disconnect(){this.connectionDesired=!1,this.reconnectTimer.stop(),this.isSocketConnected()&&(this.disconnectionReason="manual",this.socket.close())}isSocketConnected(){return null!==this.socket&&this.socket.readyState===WebSocket.OPEN}prepareForConnection(){this.reconnectTimer.stop(),this.disconnectionReason="cannot-connect"}createWebSocket(){this.socket=new WebSocket(this.uri),this.socket.onopen=()=>this.handleOpen(),this.socket.onclose=()=>this.handleClose(),this.socket.onmessage=e=>this.handleMessage(e),this.socket.onerror=()=>this.handleError()}handleOpen(){this.disconnectionReason="handshake-failed",this.startHandshake()}handleClose(){console.log(`WebSocket disconnected: ${this.disconnectionReason}. Retry in ${this.nextDelay}ms`),this.scheduleReconnection()}handleMessage(e){try{const t=JSON.parse(e.data);this.processMessage(t)}catch(e){console.error("Failed to parse WebSocket message:",e)}}handleError(){}processMessage(e){switch(e.command){case"hello":this.handleHelloMessage();break;case"reload":this.handleReloadMessage(e);break;default:console.log("Unknown WebSocket message received:",e)}}handleHelloMessage(){this.handshakeTimeout.stop(),this.nextDelay=this.options.minDelay}handleReloadMessage(e){this.reconnectTimer.stop(),this.connect(),e.path&&this.fileChangeHandler(e.path)}startHandshake(){const e={command:"hello",protocols:w.LIVERELOAD_PROTOCOLS,ver:"3.3.1"};this.sendCommand(e),this.handshakeTimeout.start(this.options.handshakeTimeout)}handleHandshakeTimeout(){this.isSocketConnected()&&(this.disconnectionReason="handshake-timeout",this.socket.close())}attemptReconnection(){this.connectionDesired&&this.connect()}scheduleReconnection(){this.connectionDesired&&(this.reconnectTimer.isRunning()||(this.reconnectTimer.start(this.nextDelay),this.nextDelay=Math.min(this.options.maxDelay,2*this.nextDelay)))}sendCommand(e){this.isSocketConnected()&&this.socket.send(JSON.stringify(e))}}var v=n(987);const C=(0,i.lazy)(()=>Promise.resolve().then(n.bind(n,486)).then(e=>({default:e.Root}))),k=()=>{const[e,t]=(0,i.useState)({data:null,error:null,loading:!0}),n=async()=>{t(e=>({...e,loading:!0,error:null}));try{const[e,n]=await Promise.all([fetch("data/model.json"),fetch("data/layout.json")]);if(!e.ok)throw new Error(`Failed to fetch model: ${e.statusText}`);if(!n.ok)throw new Error(`Failed to fetch layout: ${n.statusText}`);const[o,i]=await Promise.all([e.json(),n.json()]);t({data:{model:o,layout:i},error:null,loading:!1})}catch(e){console.error("Failed to load data:",e),t({data:null,error:e instanceof Error?e.message:"Unknown error occurred",loading:!1})}},o=e=>{e.endsWith(".svg")||(console.log("File changed:",e),(0,r.S)(),n())};return(0,i.useEffect)(()=>(new w(o).connect(),n(),()=>{}),[]),e.loading?(0,v.jsx)(I,{}):e.error?(0,v.jsx)(B,{error:e.error,onRetry:n}):e.data?(0,v.jsx)(i.Suspense,{fallback:(0,v.jsx)(I,{}),children:(0,v.jsx)(C,{model:e.data.model,layout:e.data.layout})}):(0,v.jsx)(B,{error:"No data available",onRetry:n})},I=()=>(0,v.jsx)("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh",fontFamily:"Arial, sans-serif"},children:(0,v.jsx)("div",{children:"Loading..."})}),B=({error:e,onRetry:t})=>(0,v.jsxs)("div",{style:{padding:"20px",color:"red",fontFamily:"monospace",whiteSpace:"pre-wrap",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100vh"},children:[(0,v.jsx)("h2",{children:"Error loading application"}),(0,v.jsx)("p",{children:e}),(0,v.jsx)("button",{onClick:t,style:{padding:"10px 20px",fontSize:"16px",cursor:"pointer",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px"},children:"Retry"})]}),E=document.getElementById("root");if(!E)throw new Error("Root container not found");(0,o.createRoot)(E).render((0,v.jsx)(k,{}))},264(e,t,n){n(763);var o=n(686),i=n(987);const r="add-vertex",a="add-label-vertex",s="del-vertex",l="zoom-in",d="zoom-out",c="zoom-fit",h="zoom-100",u="select-all",g="deselect",A="move-left",p="move-right",m="move-up",f="move-down",y="move-left-fine",b="move-right-fine",x="move-up-fine",w="move-down-fine",v="toggle_drag_mode",C="align_horizontal",k="align_vertical",I="distribute_horizontal",B="distribute_vertical",E="auto_layout",S="reset_position",M="toggle_grid",$="toggle_snap_to_grid",D="snap_all_to_grid",j=[{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:a,help:"Add label anchor relationship vertex",combinations:[{alt:!0,shift:!0,click:!0}]},{id:s,help:"Remove relationship vertex",combinations:[{key:"DELETE"},{key:"BACKSPACE"}]}]},{name:"Zoom",list:[{id:l,help:"Zoom in",combinations:[{ctrl:!0,key:"="}]},{id:d,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:g,help:"Deselect",combinations:[{key:"ESC"}]}]},{name:"Move",list:[{id:m,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:b,help:"Move right (1 pixel)",combinations:[{key:"RIGHT",shift:!0}]},{id:f,help:"Move down (grid increment)",combinations:[{key:"DOWN"}]},{id:w,help:"Move down (1 pixel)",combinations:[{key:"DOWN",shift:!0}]},{id:A,help:"Move left (grid increment)",combinations:[{key:"LEFT"}]},{id:y,help:"Move left (1 pixel)",combinations:[{key:"LEFT",shift:!0}]}]},{name:"View",list:[{id:v,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:C,help:"Align selected elements horizontally",combinations:[{key:"h",ctrl:!0,shift:!0}]},{id:k,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:B,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:M,help:"Toggle grid visibility",combinations:[{key:"g",ctrl:!0}]},{id:$,help:"Toggle snap to grid",combinations:[{key:"g",ctrl:!0,shift:!0}]},{id:D,help:"Snap all elements to grid",combinations:[{key:"g",ctrl:!0,alt:!0}]}]}],L=j.reduce((e,t)=>e.concat(t.list),[]).reduce((e,t)=>(e[t.id]=t,e),{}),T=e=>[e.ctrl&&(0,o.sy)().toUpperCase(),e.shift&&"SHIFT",e.alt&&"ALT",e.key&&(e.key.length>1?e.key:`"${e.key.toUpperCase()}"`),e.click&&"CLICK",e.wheel&&"WHEEL"].filter(Boolean).join(" + ");n.d(t,["DE",0,I,"F",0,u,"Gg",0,A,"Gn",0,g,"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:j.map(e=>(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("tr",{children:(0,i.jsx)("th",{colSpan:2,children:e.name})}),e.list.map(e=>(0,i.jsxs)("tr",{children:[(0,i.jsx)("td",{children:e.combinations.map(T).join(", ")}),(0,i.jsx)("td",{children:e.help})]}))]}))})})]}),"J8",0,y,"Jk",0,k,"OE",0,D,"Op",0,M,"Ur",0,l,"Vy",0,B,"Yp",0,(e,t=!1,n=!1)=>{const i=Object.keys(L).filter(i=>((e,t,n,i)=>t.combinations.some(t=>{if(Boolean(t.shift)!=e.shiftKey)return!1;if(t.ctrl&&!(0,o.SA)(e))return!1;if(Boolean(t.alt)!=e.altKey)return!1;if(n)return t.click;if(i)return t.wheel;if(t.key){const n=e;return"DELETE"==t.key?"Delete"==n.key:"BACKSPACE"==t.key?"Backspace"==n.key:"ESC"==t.key?"Escape"==n.key:"UP"==t.key?"ArrowUp"==n.key:"DOWN"==t.key?"ArrowDown"==n.key:"LEFT"==t.key?"ArrowLeft"==n.key:"RIGHT"==t.key?"ArrowRight"==n.key:t.key&&n.key&&t.key.toLowerCase()==n.key.toLowerCase()}return!1}))(e,L[i],t,n));if(0!==i.length)return 1===i.length?i[0]:i.sort((e,t)=>{const n=L[e],o=L[t],i=n.combinations[0],r=o.combinations[0],a=(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)-a})[0]},"Zj",0,r,"_s",0,a,"_t",0,S,"aX",0,v,"b3",0,p,"bl",0,s,"hU",0,d,"hZ",0,$,"i1",0,h,"iD",0,b,"l8",0,x,"mD",0,c,"mt",0,w,"rB",0,f,"t9",0,C,"uK",0,m])},686(e,t,n){const o=()=>{if("undefined"==typeof navigator)return!1;if("userAgentData"in navigator&&navigator.userAgentData){const e=navigator.userAgentData.platform;if(e&&e.toLowerCase().includes("mac"))return!0}const e=navigator.userAgent.toLowerCase();if(e.includes("mac os")||e.includes("macintosh"))return!0;if(navigator.platform){const e=navigator.platform.toLowerCase();if(e.includes("mac")||e.includes("darwin"))return!0}try{if(void 0!==new KeyboardEvent("keydown",{metaKey:!0}).metaKey)return/mac|darwin|os x/i.test(navigator.userAgent)}catch(e){}return!1};n.d(t,["SA",0,e=>o()?e.metaKey:e.ctrlKey,"sy",0,()=>o()?"Cmd":"Ctrl"])},870(e,t,n){var o=n(220),i=n.n(o),r=n(716),a=n.n(r)()(i());a.push([e.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 s=a;n.d(t,["A",0,s])}},e=>{e.O(0,[453,96],()=>e(e.s=245)),e.O()}]); +"use strict";(self.webpackChunkapp=self.webpackChunkapp||[]).push([[792],{486(e,t,n){n.d(t,{Root:()=>M,S:()=>$});var o=n(763),i=n(408),r=(n(538),n(32));const a={TopBottom:"DOWN",BottomTop:"UP",LeftRight:"RIGHT",RightLeft:"LEFT"},s=e=>{const t=[];return Object.keys(e.views).filter(e=>e.endsWith("Views")).forEach(n=>{e.views[n].forEach(e=>{t.push({key:e.key,title:e.title||e.key,section:n})})}),t};var l=n(264);const d={};function c(e){const t=e.replace(/([A-Z])/g," $1");return t.charAt(0).toUpperCase()+t.slice(1)}var h=n(686),u=n(987);const g=({model:e,currentID:t,onViewChange:n,graph:o,onAutoLayout:i,onSave:r,onToggleHelp:a,saving:l,layouting:d,dragMode:c,setDragMode:h})=>{const g=s(e);return(0,u.jsxs)("div",{className:"toolbar",children:[(0,u.jsx)(A,{views:g,currentID:t,onViewChange:n}),(0,u.jsx)(p,{graph:o,onAutoLayout:i,onSave:r,onToggleHelp:a,saving:l,layouting:d,dragMode:c,setDragMode:h})]})},A=({views:e,currentID:t,onViewChange:n})=>(0,u.jsxs)("div",{children:["View:",e.length>1?(0,u.jsxs)("select",{onChange:e=>n(e.target.value),value:t,children:[(0,u.jsx)("option",{disabled:!0,value:"",hidden:!0,children:"..."}),e.map(e=>(0,u.jsx)("option",{value:e.key,children:c(e.section)+": "+e.title},e.key))]}):(0,u.jsx)("span",{style:{marginLeft:"8px",fontWeight:"bold"},children:e[0]?c(e[0].section)+": "+e[0].title:"No views available"})]}),p=({graph:e,onAutoLayout:t,onSave:n,onToggleHelp:o,saving:i,layouting:r,dragMode:a,setDragMode:s})=>(0,u.jsxs)("div",{style:{display:"flex",alignItems:"center"},children:[(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(m,{dragMode:a,setDragMode:s})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(f,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(y,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(b,{onAutoLayout:t,layouting:r})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(x,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(v,{graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(C,{onSave:n,saving:i,graph:e})}),(0,u.jsx)("div",{className:"toolbar-group",children:(0,u.jsx)(k,{onToggleHelp:o})})]}),m=({dragMode:e,setDragMode:t})=>(0,u.jsx)("button",{className:"mode-toggle "+("select"===e?"select-mode":"pan-mode"),onClick:()=>t("pan"===e?"select":"pan"),"data-tooltip":"pan"===e?"Pan Mode: Drag to pan the view (T)":"Select Mode: Drag to select elements, Shift+click to add/remove selection (T)",children:"pan"===e?(0,u.jsx)("i",{className:"fas fa-hand-paper"}):(0,u.jsx)("i",{className:"fas fa-mouse-pointer"})}),f=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>e.undo(),"data-tooltip":`Undo the last change made to the diagram (${t}+Z)`,children:(0,u.jsx)("i",{className:"fas fa-undo"})}),(0,u.jsx)("button",{onClick:()=>e.redo(),"data-tooltip":`Redo the last undone action (${t}+Shift+Z / ${t}+Y)`,children:(0,u.jsx)("i",{className:"fas fa-redo"})})]})},y=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>e.alignSelectionH(),"data-tooltip":`Align all selected elements horizontally (left edges) (${t}+Shift+H)`,children:(0,u.jsx)("i",{className:"fas fa-align-left"})}),(0,u.jsx)("button",{onClick:()=>e.alignSelectionV(),"data-tooltip":`Align all selected elements vertically (top edges) (${t}+Shift+A)`,children:(0,u.jsx)("i",{className:"fas fa-align-left",style:{transform:"rotate(90deg)"}})}),(0,u.jsx)("button",{onClick:()=>e.distributeSelectionH(),"data-tooltip":`Distribute selected elements evenly horizontally (equal spacing) (${t}+Alt+H)`,children:(0,u.jsx)("i",{className:"fas fa-ellipsis-h"})}),(0,u.jsx)("button",{onClick:()=>e.distributeSelectionV(),"data-tooltip":`Distribute selected elements evenly vertically (equal spacing) (${t}+Alt+V)`,children:(0,u.jsx)("i",{className:"fas fa-ellipsis-v"})})]})},b=({onAutoLayout:e,layouting:t})=>{const n=(0,h.sy)();return(0,u.jsx)("button",{className:"auto-arrange",onClick:e,disabled:t,"data-tooltip":`Automatically arrange all elements using the Layered algorithm (${n}+L)`,children:t?(0,u.jsx)("i",{className:"fas fa-spinner fa-spin"}):(0,u.jsx)("i",{className:"fas fa-magic"})})},x=({graph:e})=>{const[t,n]=(0,o.useState)(e.isGridVisible()),[i,r]=(0,o.useState)(e.isSnapToGrid()),a=(0,h.sy)();return o.useEffect(()=>{const t=()=>{n(e.isGridVisible()),r(e.isSnapToGrid())};return t(),window.addEventListener("gridStateChanged",t),()=>{window.removeEventListener("gridStateChanged",t)}},[e]),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{className:t?"active-toggle":"inactive-toggle",onClick:()=>{e.toggleGrid(),n(e.isGridVisible())},"data-tooltip":`Toggle grid visibility (${a}+G)`,children:(0,u.jsx)("i",{className:"fas fa-th"})}),(0,u.jsx)("button",{className:i?"active-toggle":"inactive-toggle",onClick:()=>{e.toggleSnapToGrid(),r(e.isSnapToGrid())},"data-tooltip":`Toggle snap to grid (${a}+Shift+G)`,children:(0,u.jsx)("i",{className:"fas fa-magnet"})}),(0,u.jsx)("button",{onClick:()=>{e.snapAllToGrid()},disabled:!i,"data-tooltip":`Snap all elements to grid (${a}+Alt+G)`,children:(0,u.jsx)("i",{className:"fas fa-border-all"})})]})},w=()=>{const[e,t]=(0,o.useState)(100);return(0,o.useEffect)(()=>{const e=()=>{const e=Math.round(100*(0,i.IX)());t(e)};e();const n=setInterval(e,100);return()=>clearInterval(n)},[]),(0,u.jsxs)("button",{onClick:()=>(0,i.a_)(1),className:"zoom-display","data-tooltip":"Click to reset zoom to 100%",children:[e,"%"]})},v=({graph:e})=>{const t=(0,h.sy)();return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)("button",{onClick:()=>{(0,i.a_)(Math.max(.1,(0,i.IX)()/1.2))},"data-tooltip":`Zoom out to see more of the diagram (${t}+-)`,children:(0,u.jsx)("i",{className:"fas fa-search-minus"})}),(0,u.jsx)(w,{}),(0,u.jsx)("button",{onClick:()=>{(0,i.a_)(Math.min(5,1.2*(0,i.IX)()))},"data-tooltip":`Zoom in to see details more clearly (${t}+=)`,children:(0,u.jsx)("i",{className:"fas fa-search-plus"})}),(0,u.jsx)("button",{onClick:()=>{e.fitToView()},"data-tooltip":`Fit diagram to view (${t}+9)`,children:(0,u.jsx)("i",{className:"fas fa-expand"})})]})},C=({onSave:e,saving:t,graph:n})=>{const[i,r]=(0,o.useState)(!1),a=(0,h.sy)();return(0,o.useEffect)(()=>{const e=()=>{r(n.changed())};e();const t=setInterval(e,100);return()=>clearInterval(t)},[n]),(0,u.jsx)("button",{className:i?"grp":"action",disabled:t,onClick:e,"data-tooltip":`Save the current diagram layout (${a}+S)`,children:t?(0,u.jsx)("i",{className:"fas fa-spinner fa-spin"}):(0,u.jsx)("i",{className:"fas fa-save"})})},k=({onToggleHelp:e})=>(0,u.jsx)("button",{onClick:e,"data-tooltip":"Show keyboard shortcuts and help information (Shift+? / Shift+F1)",children:(0,u.jsx)("i",{className:"fas fa-question-circle"})}),I=(0,o.lazy)(()=>Promise.resolve().then(n.bind(n,264)).then(e=>({default:e.Help}))),B=(0,o.lazy)(()=>n.e(948).then(n.bind(n,948)).then(e=>({default:e.Graph}))),E=(e,t)=>{const n=document.documentElement;if(!e)return delete n.dataset.mdlAutomationStatus,void delete n.dataset.mdlAutomationError;n.dataset.mdlAutomationStatus=e,t?n.dataset.mdlAutomationError=t:delete n.dataset.mdlAutomationError},S=(e,t)=>{console.error(`${e} failed:`,t),alert(`${e} failed. See console for details.`)},M=({model:e,layout:t})=>(0,u.jsx)(r.Kd,{children:(0,u.jsx)(r.BV,{children:(0,u.jsx)(r.qh,{path:"/",element:(0,u.jsx)(D,{model:e,layouts:t})})})}),$=()=>{var e;(e=new URLSearchParams(document.location.search).get("id")||"")?delete d[e]:Object.keys(d).forEach(e=>delete d[e])},D=({model:e,layouts:t})=>{const[n,s]=(0,r.ok)(),c=decodeURI(n.get("id")||""),[h,A]=(0,o.useState)(!1),[p,m]=(0,o.useState)("pan"),[f,y]=(0,o.useState)(null),b=(0,o.useRef)(null),x=(0,o.useRef)(0),w=((e,t,n)=>{if(d[n])return d[n];const o=((e,t,n)=>{const o=new Map,r=new Map,s=e=>{Array.isArray(e.relationships)&&e.relationships.forEach(e=>{r.set(e.id,e)})};if(e.model.people&&e.model.people.forEach(e=>{o.set(e.id,e),Array.isArray(e.relationships)&&e.relationships.forEach(e=>{r.set(e.id,e)})}),e.model.softwareSystems&&e.model.softwareSystems.forEach(e=>{o.set(e.id,e),s(e),Array.isArray(e.containers)&&e.containers.forEach(t=>{t.parent=e,o.set(t.id,t),s(t),Array.isArray(t.components)&&t.components.forEach(e=>{e.parent=t,o.set(e.id,e),s(e)})})}),e.model.deploymentNodes){const t=e=>{e.containerInstances&&e.containerInstances.forEach(t=>{const n={...o.get(t.containerId),id:t.id};o.set(n.id,n),n.parent=e,s(t)})},n=(e,i)=>{e.parent=i,o.set(e.id,e),s(e),t(e),e.children&&e.children.forEach(t=>n(t,e)),e.infrastructureNodes&&e.infrastructureNodes.forEach(t=>n(t,e))};e.model.deploymentNodes.forEach(e=>n(e,null))}const{view:l,section:d}=function(e,t){let n=null,o="";return Object.keys(e.views).filter(e=>e.endsWith("Views")).some(i=>e.views[i].some(e=>{if(e.key==t)return n=e,o=i,!0})),{view:n,section:o}}(e,n);if(!l)return null;const c=new i.jg(l.key,l.title||l.key),h=l.automaticLayout?.rankDirection;c.layoutDirection=h?a[h]:void 0;const u={name:c.name,description:l.description,version:e.version,elements:[]};if(c.metadata=u,!l.elements)return c;const g={};if("deploymentViews"==d||"containerViews"==d)l.elements.forEach(e=>{const t=o.get(e.id);t?.parent&&(g[t.parent.id]=!0)});else if(l.softwareSystemId)l.elements.find(e=>e.id==l.softwareSystemId)||(g[l.softwareSystemId]=!0);else if("systemLandscapeViews"==d){const t={id:"__enterprise__",...e.model.enterprise};o.set(t.id,t),e.model.people&&e.model.people.filter(e=>"External"!=e.location).forEach(e=>e.parent=t),e.model.softwareSystems&&e.model.softwareSystems.filter(e=>"External"!=e.location).forEach(e=>e.parent=t),g[t.id]=!0}const A=e.views.styles,p=e=>e.toLowerCase().replace(/[^a-z0-9-]/g,"-");A?.elements&&A.elements.forEach(e=>{if(e.tag){const t=`--mdl-${p(e.tag)}`;e.background&&c.colorToVarMap.set(e.background,`${t}-bg`),e.color&&c.colorToVarMap.set(e.color,`${t}-color`),e.stroke&&c.colorToVarMap.set(e.stroke,`${t}-stroke`)}}),A?.relationships&&A.relationships.forEach(e=>{if(e.tag){const t=`--mdl-rel-${p(e.tag)}`;e.color&&c.colorToVarMap.set(e.color,`${t}-color`)}}),l.elements.forEach(t=>{if(g[t.id])return;const n=o.get(t.id),i=n?function(e,t){const n=e.views.containerViews?.find(e=>e.softwareSystemId==t);return n?.key}(e,n.id):void 0;let r="",a={};if(n){const e=n.tags.split(",");r=e[e.length-1],n.technology&&(r+=": "+n.technology),e.forEach(e=>{const t=A&&A.elements&&A.elements.find(t=>t.tag==e);t&&(a={...a,...t})})}c.addNode(t.id,n&&n.name||t.id,r,n&&n.description?n.description:"",a,function(e,t){if(t){const e=encodeURIComponent(t);return{href:`?id=${e}`,exportHref:`${e}.svg`}}if(e?.url)return{href:e.url,exportHref:e.url}}(n,i)),n&&u.elements.push({id:n.id,tags:n.tags,location:n.location,properties:n.properties,elementViewKey:i,technology:n.technology,url:n.url})}),Array.isArray(l.relationships)&&l.relationships.forEach(e=>{const t=r.get(e.id);if(!t)return;if(!c.nodesMap.has(t.sourceId)){if(o.has(t.sourceId)){const e=o.get(t.sourceId);console.warn("Element not found in this view: ",e.id,e.name)}else console.warn("Element not found: ",t.sourceId);return}if(!c.nodesMap.has(t.destinationId)){if(o.has(t.destinationId)){const e=o.get(t.destinationId);console.warn("Element not found in this view: ",e.id,e.name)}else console.warn("Element not found: ",t.destinationId);return}let n={};t.tags.split(",").forEach(e=>{const t=A&&A.relationships&&A.relationships.find(t=>t.tag==e);t&&(n={...n,...t})}),e.routing&&(n.routing=e.routing),c.addEdge(t.id,t.sourceId,t.destinationId,t.description,e.vertices,n)});const m=e=>{let t=0;for(let n=e.parent;n;n=n.parent)t++;return t};return Object.keys(g).map(e=>o.get(e)).sort((e,t)=>m(e)>m(t)?-1:1).forEach(e=>{let t={};"deploymentViews"==d&&o.get(e.id).tags.split(",").forEach(e=>{const n=A&&A.elements&&A.elements.find(t=>t.tag==e);n&&(t={...t,...n})});const n=l.elements.map(e=>o.get(e.id)).filter(t=>!(!t||t.parent!==e||"systemLandscapeViews"===d&&"__enterprise__"===e.id&&"External"===t.location)).map(e=>e.id);n.length>0&&c.addGroup(e.id,e.name,n,t)}),c.init(t[c.id]),c})(e,t,n);return o&&(d[n]=o),o})(e,t,c),{layouting:v,handleAutoLayout:C}=(e=>{const[t,n]=(0,o.useState)(!1);return{layouting:t,handleAutoLayout:(0,o.useCallback)(async t=>{n(!0);try{const n={direction:e.layoutDirection||"DOWN",...t||{}};await e.autoLayout(n)}finally{n(!1)}},[e])}})(w||{}),{saving:k,handleSave:M}=((e,t)=>{const[n,i]=(0,o.useState)(!1);return{saving:n,handleSave:(0,o.useCallback)(async()=>{i(!0);try{const n=await fetch("data/save?id="+encodeURIComponent(t),{method:"post",body:e.exportSVG()});if(202!==n.status){const e=(await n.text()).trim();throw new Error(e||`save failed with HTTP ${n.status}`)}e.setSaved()}finally{i(!1)}},[e,t])}})(w||{},c);if(!w)return(0,u.jsx)(j,{model:e});const $=(0,o.useCallback)(()=>{A(!h)},[h]),D=(0,o.useCallback)(()=>{y(c)},[c]),L=(0,o.useCallback)(()=>{C().catch(e=>S("Layout",e))},[C]),T=(0,o.useCallback)(()=>{M().catch(e=>S("Save",e))},[M]);(0,o.useEffect)(()=>{w&&w.name&&(document.title=`${w.name} - Model`)},[w]),(0,o.useEffect)(()=>{const e=Object.fromEntries(n.entries()),t="1"===e.auto||"true"===e.auto,o="1"===e.save||"true"===e.save;if(!t&&!o)return b.current=null,x.current++,void E(null);if(f!==c)return;const i=`${c}:${n.toString()}`;if(b.current===i)return;b.current=i;const r=++x.current;E("running");const a=(e.direction||"").toUpperCase(),s="1"===e.compact||"true"===e.compact,l={};["UP","DOWN","LEFT","RIGHT"].includes(a)&&(l.direction=a),s&&(l.compactLayout=!0),(async()=>{try{t&&await C(l),o&&await M(),x.current===r&&E("complete")}catch(e){const t=(e=>e instanceof Error?e.message:String(e))(e);console.error("Automation failed:",e),x.current===r&&E("error",t)}})()},[c,w,C,M,f,n]),((e,t,n,i,r,a)=>{(0,o.useEffect)(()=>{const o=o=>{const s=(0,l.Yp)(o);s&&o.preventDefault(),"help"===s?e():"save"===s?t():s===l.aX&&r&&i?r("pan"===i?"select":"pan"):n&&(s===l.t9?n.alignSelectionH():s===l.Jk?n.alignSelectionV():s===l.DE?n.distributeSelectionH():s===l.Vy?n.distributeSelectionV():s===l.Hd&&a?a():s===l._t?n.resetView():s===l.Op?n.toggleGrid():s===l.hZ?n.toggleSnapToGrid():s===l.OE?n.snapAllToGrid():s===l.Gg?n.moveSelected(-n.getGridSize(),0):s===l.J8?n.moveSelected(-1,0,!0):s===l.b3?n.moveSelected(n.getGridSize(),0):s===l.iD?n.moveSelected(1,0,!0):s===l.uK?n.moveSelected(0,-n.getGridSize()):s===l.l8?n.moveSelected(0,-1,!0):s===l.rB?n.moveSelected(0,n.getGridSize()):s===l.mt&&n.moveSelected(0,1,!0))};return window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[e,t,n,i,r,a])})($,T,w,p,m,L);const _=(0,o.useCallback)(e=>{s({id:encodeURIComponent(e)})},[s]),V=(0,o.useCallback)(e=>{if(e){const n=w.metadata.elements.find(t=>t.id===e);console.log((t=n,JSON.parse(JSON.stringify(t))))}var t},[w]);return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(g,{model:e,currentID:c,onViewChange:_,graph:w,onAutoLayout:L,onSave:T,onToggleHelp:$,saving:k,layouting:v,dragMode:p,setDragMode:m}),(0,u.jsx)(o.Suspense,{fallback:(0,u.jsx)("div",{children:"Loading graph..."}),children:(0,u.jsx)(B,{data:w,onSelect:V,onReady:D,dragMode:p},c)}),h&&(0,u.jsx)(o.Suspense,{fallback:(0,u.jsx)("div",{children:"Loading help..."}),children:(0,u.jsx)(I,{})})]})},j=({model:e})=>{const t=s(e);return o.useEffect(()=>{document.title="Model - Architecture Diagrams as Code",t.length>0&&(document.location.href="?id="+t[0].key)},[t]),t.length>0?(0,u.jsxs)(u.Fragment,{children:["Redirecting to ",t[0].title]}):(0,u.jsx)(u.Fragment,{children:"No views available"})}},408(e,t,n){n.d(t,{jg:()=>M,Qy:()=>W,ZG:()=>T,IX:()=>G,F_:()=>Q,Kp:()=>J,a_:()=>P});const o=(e,t,n)=>{const o=(()=>{const e=document.createElementNS("http://www.w3.org/2000/svg","svg");return document.body.appendChild(e),{measure:(t,n)=>{const o=document.createElementNS("http://www.w3.org/2000/svg","text");o.setAttribute("x","0"),o.setAttribute("y","0");for(let e in n)o.setAttribute(e,n[e]);o.appendChild(document.createTextNode(t)),e.appendChild(o);const{width:i,height:r}=o.getBBox();return e.removeChild(o),{width:i,height:r}},clean:()=>{document.body.removeChild(e)}}})();let i=0;const r=e.trim().split("\n").map(e=>{const r=e.trim().split(/\s+/);let a=[],s=[];return r.forEach(e=>{if(o.measure(e,n).width>t){s.length>0&&(a.push(s.join(" ")),s=[]);const r=((e,t,n,o)=>{const i=[];let r="";for(let a=0;at&&r.length>0?(i.push(r),r=e[a]):r=s}return r.length>0&&i.push(r),i})(e,t,n,o);for(let e=0;e0&&(s=[r[r.length-1]])}else{const r=[...s,e],l=o.measure(r.join(" "),n);l.width>t&&s.length>0?(a.push(s.join(" ")),s=[e]):(i=Math.max(i,l.width),s=r)}}),s.length>0&&a.push(s.join(" ")),a}).reduce((e,t)=>e.concat(t),[]);return o.clean(),{lines:r,maxW:i}},i={element(e,t={},n){const o=document.createElementNS("http://www.w3.org/2000/svg",e);return Object.entries(t).forEach(([e,t])=>o.setAttribute(e,String(t))),n&&o.classList.add(n),o},use(e,t={}){const n=this.element("use",t);return n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+e),n},path(e,t={},n){return this.element("path",{...t,d:e},n)},text(e,t={}){const n=this.element("text",t);return e&&(n.textContent=e),n},textArea(e,t,n,i,r=0,a=0,s=""){const l={"font-size":`${n}px`,"font-weight":i?"bold":"normal"},{lines:d,maxW:c}=o(e,t,l),h=this.text("",{x:0,y:a,"text-anchor":s||void 0});return d.forEach((e,t)=>{const o=this.element("tspan",{x:r,dy:`${n+2}px`,...l});o.textContent=e,h.append(o)}),{txt:h,dy:(d.length+1)*(n+2),maxW:c}},rect(e,t,n=0,o=0,i=0,r){return this.element("rect",{x:n,y:o,rx:i,ry:i,width:e,height:t},r)},icon(e,t=0,n=0){return this.use(e,{x:t,y:n})},expand(e,t,n){const o=this.element("g",{transform:`translate(${e},${t})`},"expand");return o.append(this.rect(19,19,0,0,1),this.text(n?"-":"+",{x:10,y:14,"text-anchor":"middle"})),o}};function r(e,t,n){e.setAttribute("transform",`translate(${t},${n})`)}function a(e,t,n=!0){return n?e.x>t.x-t.width/2&&e.xt.y-t.height/2&&e.yt.x&&e.xt.y&&e.yfunction(e,t,n,o){let i,r,a,s,l,d={x:null,y:null,onLine1:!1,onLine2:!1};return i=(o.y-n.y)*(t.x-e.x)-(o.x-n.x)*(t.y-e.y),0==i||(r=e.y-n.y,a=e.x-n.x,s=(o.x-n.x)*r-(o.y-n.y)*a,l=(t.x-e.x)*r-(t.y-e.y)*a,r=s/i,a=l/i,d.x=e.x+r*(t.x-e.x),d.y=e.y+r*(t.y-e.y),r>0&&r<1&&(d.onLine1=!0),a>=0&&a<=1&&(d.onLine2=!0)),d}(e,t,n.p,n.q)).filter(e=>e.onLine1&&e.onLine2)}function d(e,t){return a(t,e)?{x:e.x,y:e.y}:l(e,t,e)[0]||{x:e.x,y:e.y}}function c(e,t,n,o,i){const r={x:i.x-e.x,y:i.y-e.y},a=o.x-e.x,s=o.y-e.y;a==r.x&&(r.x+=1e-7);const l=(s-r.y)/(a-r.x),d=s-l*a,c=n*n+t*t*l*l,h=2*t*t*d*l,u=t*t*d*d-t*t*n*n,g=Math.sqrt(h*h-4*c*u),A=r.x>a?(-h+g)/(2*c):(-h-g)/(2*c),p={x:A,y:l*A+d};return p.x+=e.x,p.y+=e.y,p}function h(e,t,n){let o=n.x-t.x,i=n.y-t.y,r=o*o+i*i,a=(e.x-t.x)*o+(e.y-t.y)*i,s=Math.min(1,Math.max(0,a/r));return{x:t.x+o*s,y:t.y+i*s}}function u(e,t){return Math.abs(t.x-e.x)+Math.abs(t.y-e.y)}function g(e){return e/2/(5.5+e/70)}function A(e,t,n){switch(e.toLowerCase()){case"cylinder":return 2*g(t);case"person":return.4*n;case"folder":return t/10;case"robot":return.35*n;case"webbrowser":return n/8;default:return 0}}class p{_el;constructor(e){this._el=e}node(){return this._el}attr(e,t){return this._el.setAttribute(e,String(t)),this}insert(e,t){const n=document.createElementNS("http://www.w3.org/2000/svg",e),o=this._el.insertBefore(n,this._el.querySelector(t));return new p(o)}}function m(e,t,n,o=!1){const i=e.insert("rect",":first-child").attr("rx",o?n.width/8:3).attr("ry",o?n.width/8:3).attr("x",-t.width/2).attr("y",-t.height/2).attr("width",t.width).attr("height",t.height);return n.intersect=function(e){return d(n,e)},i}function f(e,t,n,o,i){const r=e.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(e){return c(n,o,i,n,e)},r}function y(e,t,n){const o=n.width/8,i=n.width/14,r=e.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",-t.width/2).attr("y",-t.height/2-o).attr("width",t.width).attr("height",t.height+2*o),n.intersect=function(e){return d({x:n.x,y:n.y,width:n.width,height:n.height+2*o},e)},r}const b={box:(e,t)=>m(new p(e),t,t).node(),roundedbox:(e,t)=>m(new p(e),t,t,!0).node(),component:(e,t)=>function(e,t,n){const o=n.width/10,i=e.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(e){return d({x:n.x-o/2,y:n.y,width:n.width+o,height:n.height},e)},i}(new p(e),0,t).node(),cylinder:(e,t)=>function(e,t,n){const o=t.width,i=o/2,r=g(o),a=t.height,s=`M 0,${r} a${i},${r} 0,0,0 ${o} 0 a ${i},${r} 0,0,0 ${-o} 0 l 0,${a-2*r} a ${i},${r} 0,0,0 ${o} 0 l 0,${2*r-a}`,l=e.attr("label-offset-y",A("cylinder",o,a)).insert("path",":first-child").attr("d",s).attr("transform","translate("+-o/2+","+-a/2+")");return n.intersect=function(e){const t=d(n,e);let o=n.y+n.height/2-r;return t.y>o?c({x:n.x,y:o},i,r,n,e):(o=n.y-n.height/2+r,t.yfunction(e,t,n){const o=t.width,i=t.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}`,a=e.attr("label-offset-y",A("person",o,i)).insert("path",":first-child").attr("d",r).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(e){return d(n,e)},a}(new p(e),t,t).node(),circle:(e,t)=>function(e,t,n){return f(e,0,n,n.width/2,n.width/2)}(new p(e),0,t).node(),ellipse:(e,t)=>function(e,t,n){return f(e,0,n,.55*n.width,.45*n.width)}(new p(e),0,t).node(),hexagon:(e,t)=>function(e,t,n){const o=n.width/2,i=e.insert("polygon",":first-child").attr("points",[.5,.866,1,0,.5,-.866,-.5,-.866,-1,-0,-.5,.866,.5,.866].map(e=>e*o).join(",")).attr("width",n.width).attr("height",n.height);return n.intersect=function(e){return c(n,n.width/2,n.width/2,n,e)},i}(new p(e),0,t).node(),folder:(e,t)=>function(e,t,n){const o=n.width/20,i=e.attr("label-offset-y",A("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(e){return d({x:n.x,y:n.y+o/2,width:n.width,height:n.height+o},e)},i}(new p(e),0,t).node(),mobiledevicelandscape:(e,t)=>function(e,t,n){const o=n.width/8,i=n.width/14,r=e.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",-t.width/2-o).attr("y",-t.height/2).attr("width",t.width+2*o).attr("height",t.height),n.intersect=function(e){return d({x:n.x,y:n.y,width:n.width+2*o,height:n.height},e)},r}(new p(e),t,t).node(),mobiledeviceportrait:(e,t)=>y(new p(e),t,t).node(),mobiledevice:(e,t)=>y(new p(e),t,t).node(),pipe:(e,t)=>function(e,t,n){const o=n.width,i=n.height,r=i/2,a=r/(2.5+o/70),s=`M${-a},0\n\t\ta${a},${r} 0,0,1 0,${i}\n\t\ta${a},${r} 0,0,1 0,${-i}\n\t\tl${o},0\n\t\ta${a},${r} 0,0,1 0,${i}\n\t\tl${-o},0`,l=e.insert("path",":first-child").attr("d",s).attr("transform","translate("+-o/2+","+-i/2+")");return n.intersect=function(e){return d({x:n.x-a,y:n.y,width:n.width+2*a,height:n.height},e)},l}(new p(e),0,t).node(),robot:(e,t)=>function(e,t,n){const o=n.width,i=n.height,r=Math.min(.28*o,.25*i),a=.2*r,s=.25*r,l=.08*r,c=.12*r,h=.22*r,u=.12*r,g=.3*r,p=o,m=-i/2+s+r,f=i-s-r,y=e.attr("label-offset-y",A("robot",o,i)).insert("g",":first-child");y.insert("rect",":first-child").attr("rx",3).attr("ry",3).attr("x",-p/2).attr("y",m).attr("width",p).attr("height",f);const b=-i/2+s;y.insert("rect",":first-child").attr("rx",a).attr("ry",a).attr("x",-r/2).attr("y",b).attr("width",r).attr("height",r),y.insert("line",":first-child").attr("class","robot-antenna").attr("x1",0).attr("y1",b).attr("x2",0).attr("y2",-i/2+2*l).attr("stroke-width",.6*l).attr("stroke-linecap","round"),y.insert("circle",":first-child").attr("class","robot-antenna-ball").attr("cx",0).attr("cy",-i/2+2*l).attr("r",1.2*l);const x=b+.4*r;y.insert("circle",":first-child").attr("class","robot-eye").attr("cx",-h).attr("cy",x).attr("r",c),y.insert("circle",":first-child").attr("class","robot-eye").attr("cx",h).attr("cy",x).attr("r",c);const w=b+.7*r,v=.28*r;return y.insert("path",":first-child").attr("class","robot-mouth").attr("d",`M${-v/2},${w} Q0,${w+.3*v} ${v/2},${w}`).attr("fill","none").attr("stroke-width",.5*l).attr("stroke-linecap","round"),y.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",-r/2-u-1).attr("y",x-g/2).attr("width",u).attr("height",g),y.insert("rect",":first-child").attr("rx",.25*u).attr("ry",.25*u).attr("x",r/2+1).attr("y",x-g/2).attr("width",u).attr("height",g),n.intersect=function(e){return d(n,e)},y}(new p(e),0,t).node(),webbrowser:(e,t)=>function(e,t,n){const o=n.height/8,i=e.attr("label-offset-y",A("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(e){return d(n,e)},i}(new p(e),0,t).node()};var x=n(538);class w{versions=[];pos=0;lastSavedPos=0;exportDoc;importDoc;change;tmpPreviousState=null;constructor(e,t,n){this.exportDoc=t,this.importDoc=n,this.change=function(e){let t;return function(){const n=this;clearTimeout(t),t=setTimeout(function(){t=null,e.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(e){return"undefined"!=typeof structuredClone?structuredClone(e):JSON.parse(JSON.stringify(e))}undo(){if(this.pos<2)return;this.pos-=1;const e=this.versions[this.pos-1];this.importDoc(this.deepClone(e))}redo(){if(this.pos>this.versions.length-1)return;const e=this.versions[this.pos];this.importDoc(this.deepClone(e)),this.pos+=1}changed(){return this.pos!==this.lastSavedPos}setSaved(){this.lastSavedPos=this.pos}}var v=n(264);const C={"font-family":"Inter, -apple-system, BlinkMacSystemFont, sans-serif",stroke:"none"},k=(e,t)=>{Object.keys(t).forEach(n=>{const o=t[n];"number"==typeof o?e.style.setProperty(n,o.toString()):e.style.setProperty(n,o)})},I=(e,t)=>Math.sqrt((t.x-e.x)*(t.x-e.x)+(t.y-e.y)*(t.y-e.y));function B(e,t,n,i,r,a){const s={"font-family":String(C["font-family"]),"font-size":`${n}px`,"font-weight":i?"bold":"normal"},l=o(e,t,s);return{lines:l.lines.length>0?l.lines:[""],fontSize:n,lineHeight:n+2,bold:i,field:a,gapAfter:r}}const E={thickness:3,color:"#999",opacity:1,fontSize:22,dashed:!0},S={width:280,height:180,background:"rgba(255, 255, 255, .9)",color:"#666",opacity:.9,stroke:"#999",fontSize:22,shape:"Box"};class M{id;name;nodesMap;edges;edgeVertices;groupsMap;metadata;layoutDirection;colorToVarMap=new Map;_undo;_gridVisible=!1;_snapToGrid=!0;_gridSize=25;_skipAutoFit=!1;constructor(e,t){this.id=e,this.name=t,this.edges=[],this.edgeVertices=new Map,this.nodesMap=new Map,this.groupsMap=new Map,this._undo=new w(this.id,()=>this.exportLayout(!0),e=>this.importLayout(e,!0)),window.graph=this}init(e){e&&this.importLayout(e),this._undo=new w(this.id,()=>this.exportLayout(!0),e=>this.importLayout(e,!0)),this._undo.length()&&this.importLayout(this._undo.currentState()),this._undo.beforeChange(),this._undo.change()}addNode(e,t,n,o,i,r){if(this.nodesMap.has(e))throw Error("duplicate node: "+e);const a={...S,...i},s=(a.shape||"Box").toLowerCase(),l="person"===s?240:180,d=Math.max(280,a.width||0),c=function(e,t,n,o,i){const r=Math.max(o-36,80),a=[B(e,r,i,!0,6,"name"),B(`[${t}]`,r,.75*i,!1,10),B(n,r,Math.min(.8*i,16),!1,0,"description")],s=a.reduce((e,t)=>e+t.lines.length*t.lineHeight+t.gapAfter,0);return{blocks:a,textHeight:s,minimumHeight:s+36}}(t,n,o,d,a.fontSize||22);let h=Math.max(l,a.height||0,c.minimumHeight);for(let e=0;e<20;e++){const e=c.minimumHeight+Math.abs(A(s,d,h));if(e<=h+.1)break;h=e}const u={id:e,title:t,sub:n,description:o,style:a,x:0,y:0,width:d,height:h,intersect:null,link:r,contentLayout:c};this.nodesMap.set(u.id,u)}nodes(){return Array.from(this.nodesMap.values())}addEdge(e,t,n,o,i,r){i&&i.forEach((t,n)=>{const o=t;o.id=`v-${e}-${n}`,this.edgeVertices.set(o.id,o)});const a={id:e,from:this.nodesMap.get(t),to:this.nodesMap.get(n),label:o,vertices:null,style:{...E,...r},initVertex:e=>{const t=e;return t.id||(t.id=((e,t)=>`v-${e}-a-${(e=>{let t=2166136261;for(let n=0;n>>0).toString(36)})(`${e}:${t.x}:${t.y}`)}`)(a.id,e),this.edgeVertices.set(t.id,t)),t.edge=a,e},userDeletedVertices:!1};this.edges.push(a),i&&(a.vertices=i.map(e=>a.initVertex(e)))}addGroup(e,t,n,o){if(this.groupsMap.has(e))return void console.error(`Group exists: ${e} ${t}`);const i={id:e,name:t,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 ${e} "${t}"`),o}).filter(Boolean),style:o};this.groupsMap.set(e,i)}setNodeSelected(e,t){e.selected=t,t?e.ref.classList.add("selected"):e.ref.classList.remove("selected"),this.updateEdgesSel()}updateEdgesSel(){this.edges.forEach(e=>{e.to.selected||e.from.selected?e.ref.classList.add("selected"):e.ref.classList.remove("selected")})}moveNode(e,t,n,o=!1,i=!1){if(e){if(this._snapToGrid&&!o){const e=this.snapToGrid(t,n);t=e.x,n=e.y}e.x==t&&e.y==n||(i||this._undo.beforeChange(),e.x=t,e.y=n,r(e.ref,t,n),this.redrawEdges(e),this.redrawGroups(e),i||this._undo.change())}}moveEdgeVertex(e,t,n,o=!1,i=!1){if(this._snapToGrid&&!o){const e=this.snapToGrid(t,n);t=e.x,n=e.y}e.x==t&&e.y==n||(i||this._undo.beforeChange(),e.x=t,e.y=n,this.redrawEdge(e.edge),i||this._undo.change())}moveSelected(e,t,n=!1){this.nodes().forEach(o=>o.selected&&this.moveNode(o,o.x+e,o.y+t,n,!1)),this.edgeVertices.forEach(o=>o.selected&&this.moveEdgeVertex(o,o.x+e,o.y+t,n,!1))}insertEdgeVertex(e,t,n,o){this._undo.beforeChange();const i=e.initVertex(t);i.selected=!0,o&&(e.vertices.forEach(e=>e.label=!1),i.label=!0),e.vertices.splice(n-1,0,i),this.redrawEdge(e),this._undo.change()}deleteEdgeVertex(e){this._undo.beforeChange();const t=e.edge.vertices.indexOf(e);t>=0&&(e.edge.vertices.splice(t,1),this.edgeVertices.delete(e.id),e.edge.userDeletedVertices=!0),this.redrawEdge(e.edge),this._undo.change()}changed(){return this._undo.changed()}undo(){this._undo.undo()}redo(){this._undo.redo()}alignTopLeft(){const e=this.calculateContentBounds(),t=100-e.x,n=100-e.y;this._skipAutoFit=!0,this._undo.beforeChange(),this.nodesMap.forEach(e=>{this.moveNode(e,e.x+t,e.y+n,!0,!0)}),this.edgeVertices.forEach(e=>{this.moveEdgeVertex(e,e.x+t,e.y+n,!0,!0)}),this._undo.change()}resetPanTransform(){const e=G(),t=D.querySelector("g.zoom");t&&(t.setAttribute("transform",`scale(${e}) translate(0, 0)`),function(e){const t=e.calculateContentBounds(),n=G(),o=Math.max(D.parentElement.clientWidth/n,t.x+t.width+20),i=Math.max(D.parentElement.clientHeight/n,t.y+t.height+20);D.setAttribute("width",String(o*n)),D.setAttribute("height",String(i*n))}(this)),ee(this.id),this._skipAutoFit=!1}shouldSkipAutoFit(){return this._skipAutoFit}resetView(){const e=D.querySelector("g.zoom");e&&(e.setAttribute("transform","scale(1) translate(0, 0)"),Y()),ee(this.id)}redrawEdges(e){this.edges.forEach(t=>(e==t.from||e==t.to)&&this.redrawEdge(t)),this.updateEdgesSel()}redrawEdge(e){const t=e.ref.parentElement;t.removeChild(e.ref),e.ref=V(this,e),t.append(e.ref)}redrawGroups(e){this.groupsMap.forEach(e=>{const t=e.ref.parentElement;t.removeChild(e.ref),q(e),t.append(e.ref)})}exportSVG(){const e=document.querySelector("svg#graph"),t=(e.querySelector("rect.elastic"),e.cloneNode(!0));t.querySelectorAll("a.nodeLink[data-export-href]").forEach(e=>{e.setAttribute("href",e.getAttribute("data-export-href")||""),e.removeAttribute("data-export-href")});const n=t.querySelector("rect.elastic");n&&n.remove();const o=this.calculateContentBounds(),i=o.width+100,r=o.height+100,a=50-o.x,s=50-o.y,l=t.querySelector("g.zoom");l&&l.setAttribute("transform",`scale(1) translate(${a}, ${s})`),t.setAttribute("viewBox",`0 0 ${i} ${r}`),t.setAttribute("width",String(i)),t.setAttribute("height",String(r)),t.setAttribute("xmlns","http://www.w3.org/2000/svg"),this.convertStylesToCustomProperties(t);const d=document.createElement("script");return d.setAttribute("type","application/json"),this.metadata.layout=this.exportLayout(),d.append("/g,"]]]>]>"),t.insertBefore(d,t.firstChild),t.outerHTML}convertStylesToCustomProperties(e){0!==this.colorToVarMap.size&&(e.querySelectorAll("[fill]").forEach(e=>{const t=e.getAttribute("fill");t&&this.colorToVarMap.has(t)&&e.setAttribute("fill",`var(${this.colorToVarMap.get(t)}, ${t})`)}),e.querySelectorAll("[stroke]").forEach(e=>{const t=e.getAttribute("stroke");t&&this.colorToVarMap.has(t)&&e.setAttribute("stroke",`var(${this.colorToVarMap.get(t)}, ${t})`)}))}calculateContentBounds(){let e=1/0,t=1/0,n=-1/0,o=-1/0;return this.nodes().forEach(i=>{const r=i.x-i.width/2,a=i.x+i.width/2,s=i.y-i.height/2,l=i.y+i.height/2;e=Math.min(e,r),n=Math.max(n,a),t=Math.min(t,s),o=Math.max(o,l)}),this.edgeVertices.forEach(i=>{e=Math.min(e,i.x-5),n=Math.max(n,i.x+5),t=Math.min(t,i.y-5),o=Math.max(o,i.y+5)}),this.groupsMap.forEach(i=>{const r=i.x-i.width/2,a=i.x+i.width/2,s=i.y-i.height/2,l=i.y+i.height/2;e=Math.min(e,r),n=Math.max(n,a),t=Math.min(t,s),o=Math.max(o,l)}),this.edges.forEach(i=>{if(e=Math.min(e,i.from.x-10,i.to.x-10),n=Math.max(n,i.from.x+10,i.to.x+10),t=Math.min(t,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=>{e=Math.min(e,i.x-10),n=Math.max(n,i.x+10),t=Math.min(t,i.y-10),o=Math.max(o,i.y+10)}),i.label&&i.label.trim()){const r=(i.from.x+i.to.x)/2,a=(i.from.y+i.to.y)/2,s=10*i.label.length+50;e=Math.min(e,r-s),n=Math.max(n,r+s),t=Math.min(t,a-25),o=Math.max(o,a+25)}}),e===1/0?{x:0,y:0,width:100,height:100}:{x:e,y:t,width:n-e,height:o-t}}exportLayout(e=!1){const t={};return this.nodes().forEach(e=>t[e.id]={x:e.x,y:e.y}),this.edges.forEach(n=>{if(!n.vertices)return;const o=n.vertices.map(e=>({x:e.x,y:e.y,label:e.label,auto:e.auto}));(o.length||e)&&(t[`e-${n.id}`]=o),n.userDeletedVertices&&(t[`e-${n.id}-deleted`]=!0)}),t}setSaved(){this._undo.setSaved()}importLayout(e,t=!1){const n=[];Object.entries(e).forEach(([e,t])=>{e.startsWith("e-")||void 0===t.x||void 0===t.y?e.startsWith("e-")&&Array.isArray(t)&&t.forEach(e=>{void 0!==e.x&&void 0!==e.y&&n.push({x:e.x,y:e.y})}):n.push({x:t.x,y:t.y})});let o=0,i=0;if(n.length>0){const e=Math.min(...n.map(e=>e.x)),t=Math.min(...n.map(e=>e.y));if(e<-100||t<-100||Math.max(...n.map(e=>e.x))>3e3||Math.max(...n.map(e=>e.y))>2e3){const n=50;o=-e+n,i=-t+n}}Object.entries(e).forEach(([e,t])=>{const n=this.nodesMap.get(e);if(n)n.x=t.x+o,n.y=t.y+i;else if(e.startsWith("e-")&&!e.endsWith("-deleted")){const n=this.edges.find(t=>t.id==e.slice(2));if(!n)return;return n.vertices&&n.vertices.forEach(e=>this.edgeVertices.delete(e.id)),void(n.vertices=t.map(e=>{const t={x:e.x+o,y:e.y+i};Object.assign(t,e,{x:e.x+o,y:e.y+i});const r=n.initVertex(t);return e.auto&&(r.auto=!0),r}))}if(e.endsWith("-deleted")){const n=e.slice(2,-8),o=this.edges.find(e=>e.id==n);return void(o&&!0===t&&(o.userDeletedVertices=!0))}}),t&&(this.nodes().forEach(e=>r(e.ref,e.x,e.y)),this.edges.forEach(e=>this.redrawEdge(e)),this.updateEdgesSel(),this.redrawGroups(null))}async autoLayout(e){try{const t=await(0,x.k)(this,e);this._undo.beforeChange(),t.nodes.forEach(e=>{const t=this.nodesMap.get(e.id);t&&this.moveNode(t,e.x,e.y,!1,!0)}),t.edges.forEach(e=>{const t=this.edges.find(t=>t.id==e.id);if(t){if(t.vertices&&t.vertices.forEach(e=>{e.id&&this.edgeVertices.delete(e.id)}),t.vertices=[],t.userDeletedVertices=!1,e.vertices&&e.vertices.length>0&&(t.vertices=e.vertices.map(e=>{const n=t.initVertex(e);return n.auto=!0,n})),e.label){t.vertices&&(t.vertices.forEach(e=>{e.label&&this.edgeVertices.delete(e.id)}),t.vertices=t.vertices.filter(e=>!e.label));const n=t.initVertex(e.label);n.label=!0,n.auto=!0,t.vertices=t.vertices||[];const o=function(e,t,n,o){if(0===e.length)return 0;const i=[n,...e,o];let r=1/0,a=0;for(let e=0;ee.selected);e.push(...Array.from(this.edgeVertices.values()).filter(e=>e.selected));let t=Math.min(...e.map(e=>e.y));this.nodesMap.forEach(e=>e.selected&&this.moveNode(e,e.x,t,!1,!1)),this.edgeVertices.forEach(e=>e.selected&&this.moveEdgeVertex(e,e.x,t,!1,!1))}alignSelectionH(){const e=this.nodes().filter(e=>e.selected);e.push(...Array.from(this.edgeVertices.values()).filter(e=>e.selected));let t=Math.min(...e.map(e=>e.x));this.nodesMap.forEach(e=>e.selected&&this.moveNode(e,t,e.y,!1,!1)),this.edgeVertices.forEach(e=>e.selected&&this.moveEdgeVertex(e,t,e.y,!1,!1))}distributeSelectionH(){const e=this.nodes().filter(e=>e.selected),t=Array.from(this.edgeVertices.values()).filter(e=>e.selected);if(e.length+t.length<3)return;this._undo.beforeChange();const n=[...e,...t];n.sort((e,t)=>e.x-t.x);const o=n[0].x,i=(n[n.length-1].x-o)/(n.length-1);n.forEach((e,t)=>{const n=o+t*i;"title"in e?this.moveNode(e,n,e.y,!1,!0):this.moveEdgeVertex(e,n,e.y,!1,!0)}),this._undo.change()}distributeSelectionV(){const e=this.nodes().filter(e=>e.selected),t=Array.from(this.edgeVertices.values()).filter(e=>e.selected);if(e.length+t.length<3)return;this._undo.beforeChange();const n=[...e,...t];n.sort((e,t)=>e.y-t.y);const o=n[0].y,i=(n[n.length-1].y-o)/(n.length-1);n.forEach((e,t)=>{const n=o+t*i;"title"in e?this.moveNode(e,e.x,n,!1,!0):this.moveEdgeVertex(e,e.x,n,!1,!0)}),this._undo.change()}setEdgeSelected(e,t){t&&(this.setNodeSelected(e.from,!0),this.setNodeSelected(e.to,!0)),this.updateEdgesSel()}fitToView(){const e=this.calculateContentBounds();if(0===e.width||0===e.height)return;const t=D.parentElement?.clientWidth||800,n=D.parentElement?.clientHeight||600,o=(t-80)/e.width,i=(n-80)/e.height,r=Math.min(o,i),a=Math.max(Math.min(r,2),.1),s=t/2-(e.x+e.width/2)*a,l=n/2-(e.y+e.height/2)*a,d=D.querySelector("g.zoom");d&&d.setAttribute("transform",`translate(${s}, ${l}) scale(${a})`),Y(),J(this.id)}saveLayoutState(){return this.exportLayout(!0)}restoreLayoutState(e){this.importLayout(e,!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(e=>{const t=Math.round(e.x/this._gridSize)*this._gridSize,n=Math.round(e.y/this._gridSize)*this._gridSize;this.moveNode(e,t,n,!1,!0)}),this._undo.change())}snapToGrid(e,t){return{x:Math.round(e/this._gridSize)*this._gridSize,y:Math.round(t/this._gridSize)*this._gridSize}}updateGridDisplay(){if(!D)return;const e=D.querySelector("#grid-pattern");e&&e.remove();const t=D.querySelector("#grid-background");if(t&&t.remove(),!this._gridVisible)return;let n=D.querySelector("defs");n||(n=document.createElementNS("http://www.w3.org/2000/svg","defs"),D.insertBefore(n,D.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 a=D.querySelector("g.zoom");a&&a.insertBefore(r,a.firstChild)}}let $,D=document.querySelector("svg#graph");D||(D=document.createElementNS("http://www.w3.org/2000/svg","svg"),D.setAttribute("id","graph"),D.addEventListener("click",e=>$(e))),D.setAttribute("width","100%"),D.setAttribute("height","100%");let j,L=!1;const T=(e,t,n)=>{D.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(D),D.__data=e,j=t,$=e=>{},_(e);const o=i.rect(300,300,50,50,0,"elastic");return D.append(o),e.updateGridDisplay(),W(D,n),{svg:D,setZoom:H}},_=e=>{const t=i.element("g",{},"zoom"),n=i.element("g",{},"nodes"),o=i.element("g",{},"edges"),a=i.element("g",{},"groups");t.append(a,o,n),e.nodesMap.forEach(t=>{!function(e,t){window.gdata=t;const n=i.element("g",{},"node");n.setAttribute("id",e.id),e.selected&&n.classList.add("selected"),r(n,e.x,e.y);const o=e.link?i.element("a",{href:e.link.href,"data-export-href":e.link.exportHref,"aria-label":`Open ${e.title}`},"nodeLink"):null,a=o||n;o&&(n.classList.add("linked"),n.append(o));const s=e.style.shape||"Box",l=(b[s.toLowerCase()]||b.box)(a,e);l.classList.add("nodeBorder"),k(l,Z.nodeBorder),l.setAttribute("fill",e.style.background),l.setAttribute("stroke",e.style.stroke),l.setAttribute("stroke-width","3"),l.setAttribute("opacity",String(e.style.opacity)),X(l,e.style.border);const d=function(e,t){const n=i.element("g");let o=-e.textHeight/2;return e.blocks.forEach(e=>{const r=i.text("",{"text-anchor":"middle"});k(r,C),t&&r.setAttribute("fill",t),e.field&&r.setAttribute("data-field",e.field),e.lines.forEach((t,n)=>{const a=i.element("tspan",{x:0,y:o+e.fontSize+n*e.lineHeight,"font-size":`${e.fontSize}px`,"font-weight":e.bold?"bold":"normal"});a.textContent=t,r.append(a)}),n.append(r),o+=e.lines.length*e.lineHeight+e.gapAfter}),n}(e.contentLayout,e.style.color);r(d,0,(Number(a.getAttribute("label-offset-y"))||0)/2),a.append(d),n.__data=e,e.ref=n}(t,e),n.append(t.ref)}),e.edges.forEach(e=>{e.labelBounds=void 0}),e.edges.forEach(t=>{V(e,t),o.append(t.ref)}),e.groupsMap.forEach(e=>{q(e),a.append(e.ref)}),D.append(t)};function V(e,t){const n=t.from,o=t.to,r=i.element("g",{},"edge");r.setAttribute("id",t.id),r.setAttribute("data-from",t.from.id),r.setAttribute("data-to",t.to.id);const d=(t.style.position||50)/100,c=function(e,t){const n=e.from,o=e.to;let i=e.vertices?e.vertices.concat():[];if(0==i.length&&!e.userDeletedVertices){const r=t.edges.filter(t=>t.from==e.from&&t.to==e.to);let a=0;if(r.length>1){a=r.indexOf(e)-(r.length-1)/2;let t=0,s=0;Math.abs(n.x-o.x)>Math.abs(n.y-o.y)?s=70*a:t=200*a;const l=e.initVertex({x:(n.x+o.x)/2+t,y:(n.y+o.y)/2+s});l.label=!0,l.auto=!0,i.push(l)}}i.unshift(n),i.push(o);let r=i[i.length-1];for(let e=1;e0;e--)if(!i[e].label){a=i[e];break}const s=(e,t)=>{const n=e.style?.shape?.toLowerCase()||"box",o=t.x-e.x,i=t.y-e.y;if(e.x,e.y,Math.abs(o)<.01&&Math.abs(i)<.01)return{x:e.x+e.width/2,y:e.y};if("cylinder"===n){const t=e.width,n=t/2,r=n/(5.5+t/70),a=e.height/2,s=Math.atan2(i,o),l=Math.cos(s),d=Math.sin(s);let c=1/0;Math.abs(l)>.01&&(c=Math.min(c,Math.abs(n/l))),Math.abs(d)>.01&&(c=Math.min(c,Math.abs(a/d)));const h=e.x+l*c,u=e.y+d*c,g=e.y-a+r,A=e.y+a-r;if(uA){const t=u=0){const e=Math.sqrt(l),n=(-s+e)/(2*i),r=(-s-e)/(2*i);return{x:o>0?Math.max(n,r):Math.min(n,r),y:t}}}return{x:h,y:u}}if("circle"===n){const t=e.width/2,n=Math.atan2(i,o);return{x:e.x+Math.cos(n)*t,y:e.y+Math.sin(n)*t}}if("ellipse"===n){const t=.55*e.width,n=.45*e.width,r=Math.atan2(i,o),a=Math.cos(r),s=Math.sin(r),l=Math.sqrt(t*t*s*s+n*n*a*a);return{x:e.x+t*a*n/l,y:e.y+n*s*t/l}}{const t=e.width/2,n=e.height/2,r=Math.atan2(i,o),a=Math.cos(r),s=Math.sin(r);let l=1/0;return Math.abs(a)>.01&&(l=Math.min(l,Math.abs(t/a))),Math.abs(s)>.01&&(l=Math.min(l,Math.abs(n/s))),{x:e.x+a*l,y:e.y+s*l}}};let l=s(n,r),d=s(o,a);return i[0]=l,i[i.length-1]=d,i}(t,e),h=function(e,t,n){let o,i={x:n.x,y:n.y};const r=e.findIndex(e=>e.label);let a=!0;if(r>=0){const t=e[r];i=t,a=!0===t.auto;const n=[];r>0&&n.push({p:e[r-1],q:i}),re?I(t.p,t.q)>I(e.p,e.q)?t:e:t,void 0)}else{const n=e.slice(1).reduce((t,n,o)=>t+I(e[o],n),0)*t;let r=0;for(let t=1;t0&&r+s>=n){const e=(n-r)/s;i={x:a.p.x+(a.q.x-a.p.x)*e,y:a.p.y+(a.q.y-a.p.y)*e},o=a;break}r+=s}}const s=o?Math.abs(o.q.x-o.p.x):0,l=o?Math.abs(o.q.y-o.p.y):0;return{...i,orientation:l>s?"vertical":"horizontal",segment:o,movable:a}}(c,d,n),{bg:u,txt:g,bbox:A}=function(e,t,n){const o=t.style.fontSize;let{txt:r,dy:a,maxW:l}=i.textArea(t.label,200,o,!1,e.x,e.y,"middle");a-=o/2,l+=o;const d=[{x:e.x,y:e.y}];if(e.movable&&e.segment)for(const t of[.5,.35,.65,.2,.8]){const n={x:e.segment.p.x+(e.segment.q.x-e.segment.p.x)*t,y:e.segment.p.y+(e.segment.q.y-e.segment.p.y)*t};d.some(e=>Math.abs(e.x-n.x)<.1&&Math.abs(e.y-n.y)<.1)||d.push(n)}const c=[...n.nodes().map(e=>z(s(e),8)),...n.edges.filter(e=>e!==t&&e.labelBounds).map(e=>z(e.labelBounds,8))],h=d.flatMap(t=>"vertical"===e.orientation?[1,-1].map(n=>N(t.x+n*(l/2+12),t.y,l,a,t,e,c)):[-1,1].map(n=>N(t.x,t.y+n*(a/2+12),l,a,t,e,c))).reduce((e,t)=>t.score{e.setAttribute("x",String(u))}),r.setAttribute("y",String(g-a/2)),k(r,Z.edgeText),r.setAttribute("stroke","none"),r.setAttribute("font-size",String(t.style.fontSize)),r.setAttribute("fill",t.style.color);const p={...A},m=i.rect(p.width,p.height,p.x,p.y);return k(m,Z.edgeRect),r.setAttribute("data-field","label"),t.labelBounds=A,p.x+=p.width/2,p.y+=p.height/2,{bg:m,txt:r,bbox:p}}(h,t,e);r.append(u,g);const{segments:p,path:m}=function(e,t,n,o){const i=[];for(let t=1;t0&&i[i.length-1],function(e,t){for(let n=0;nMath.abs(i[1].x-o.p.x)+Math.abs(i[1].y-o.p.y)&&i.reverse();const t={p:i[1],q:o.q};o.q=i[0],e.splice(n+1,0,t),n+=1}}}}(i,t),i.length>0){r=`M${i[0].p.x},${i[0].p.y}`;for(let e=0;e{if("id"in e&&"edge"in e){const n=e;return n.edge=t,n}return t.initVertex(e)}),t.vertices.forEach((e,t)=>{const n=e;n.ref=i.element("circle",{id:n.id,cx:e.x,cy:e.y,r:7,fill:"none"},"v-dot"),n.selected&&n.ref.classList.add("selected"),n.auto&&n.ref.classList.add("auto"),r.append(n.ref)}),t.ref=r,r}function N(e,t,n,o,i,r,a){const s={x:e-n/2,y:t-o/2,width:n,height:o},l=a.reduce((e,t)=>{return e+(n=s,o=t,Math.max(0,Math.min(n.x+n.width,o.x+o.width)-Math.max(n.x,o.x))*Math.max(0,Math.min(n.y+n.height,o.y+o.height)-Math.max(n.y,o.y)));var n,o},0);return{centerX:e,centerY:t,bounds:s,score:1e3*l+I(i,r)}}function z(e,t){return{x:e.x-t,y:e.y-t,width:e.width+2*t,height:e.height+2*t}}function q(e){if(0==e.nodes.length)return;const t=i.element("g",{},"group");let n={x:1e100,y:1e100},o={x:0,y:0};e.nodes.forEach(e=>{const t=e.style?.shape?.toLowerCase()||"box";let i=e.height/2,r=e.height/2;if("robot"===t){const t=.12*e.height;i=e.height/2+t,r=e.height/2}else if("hexagon"===t){const t=e.width/2*.866;i=t,r=t}const a={x:e.x-e.width/2,y:e.y-i,width:e.width,height:i+r};n.x=Math.min(n.x,a.x),n.y=Math.min(n.y,a.y),o.x=Math.max(o.x,a.x+a.width),o.y=Math.max(o.y,a.y+a.height)});const r=Math.max(o.x-n.x,200),a=o.y-n.y,s={x:n.x-25,y:n.y-25,width:r+50,height:a+50+30},l=i.rect(s.width,s.height,s.x,s.y);e.x=s.x+s.width/2,e.y=s.y+s.height/2,e.width=s.width,e.height=s.height,k(l,Z.groupRect),e.style.stroke&&l.setAttribute("stroke",e.style.stroke),e.style.background&&l.setAttribute("fill",e.style.background);const d=i.text(e.name,{x:n.x,y:s.y+s.height-Z.groupText["font-size"]});k(d,Z.groupText),e.style.color&&d.setAttribute("fill",e.style.color),t.append(l,d),e.ref=t}function R(e,t){let n={dst:Number.POSITIVE_INFINITY,pos:-1,edge:null,prj:null};return e.edges.forEach(e=>{const o=e.vertices||[],i=[e.from,...o,e.to];for(let o=1;o50||a3||Math.abs(s)>3)&&(h=!0,c=null,d)){const e=t.getSelection();e.length=0,e.push(d.node),t.setSelection(e),o=[{x:d.node.x,y:d.node.y,n:d.node}],d=null}if(r)!function(t,n){const o=e.querySelector("g.zoom");if(!o)return;const i=G();o.setAttribute("transform",`translate(${t}, ${n}) scale(${i})`)}(l.x+a,l.y+s);else if(o.length>0&&h){const e=t.getZoom(),n=a/e,i=s/e;o.forEach(e=>{t.moveNode(e.n,e.x+n,e.y+i)}),t.setDragging(!0)}else i&&(i.update(n),t.setDragging(!0))}(n=m(n),n.clientX-p.ex,n.clientY-p.ey)}function y(n){document.removeEventListener("touchmove",f),document.removeEventListener("mousemove",f),document.removeEventListener("mouseup",y),document.removeEventListener("touchend",y),function(n){t.setDragging(!1);const a=h?null:c;if(h&&(u=!0,window.setTimeout(()=>{u=!1},0)),d&&!h){const e=t.getSelection();e.length=0,e.push(d.node),t.setSelection(e)}if(i){const e=i.end();e?t.boxSelection(e,n.shiftKey):o.length||t.setSelection([]),i=null}if(r&&h){const t=e.__data;t&&t.id&&J(t.id)}d=null,c=null,h=!1,r=!1,t.updatePanning(),a&&(window.location.href=a)}(m(n)),p=null}function b(u){u=m(u),p={ex:u.clientX,ey:u.clientY},function(u){u.preventDefault(),h=!1,d=null;const g=u.target,A=g instanceof Element?g.closest("a.nodeLink"):null;c=u.shiftKey?null:A?.getAttribute("href")||null;const p=t.nodeFromEvent(u),m=u.shiftKey?"pan"===n?"select":"pan":n;if(p)if("pan"===m){r=!1,i=null;const e=t.getSelection();t.isSelected(p)?o=e.map(e=>({x:e.x,y:e.y,n:e})):(t.setSelection([p]),o=[{x:p.x,y:p.y,n:p}])}else{r=!1,i=null;const e=t.getSelection();if(u.shiftKey&&"select"===n){if(t.isSelected(p)){const t=e.findIndex(e=>e.id===p.id);t>=0&&e.splice(t,1)}else e.push(p);t.setSelection(e),o=e.map(e=>({x:e.x,y:e.y,n:e}))}else t.isSelected(p)?(o=e.map(e=>({x:e.x,y:e.y,n:e})),d=null):(d={node:p,shiftKey:u.shiftKey},o=[{x:p.x,y:p.y,n:p}])}else"pan"===m?(r=!0,i=null,a=u.clientX,s=u.clientY,l=function(){const t=e.querySelector("g.zoom");if(!t)return{x:0,y:0};const n=(t.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return n?{x:parseFloat(n[1])||0,y:parseFloat(n[2])||0}:{x:0,y:0}}(),o=[],t.setSelection([])):(r=!1,i=function(){let t=0,n=0,o=null;return{ini(i){const r=F(i);t=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(t)),o.setAttribute("y",String(n)),o.setAttribute("width","0"),o.setAttribute("height","0");const a=e.querySelector("g.zoom");a?a.appendChild(o):e.appendChild(o)},update(e){if(!o)return;const i=F(e),r=i.x,a=i.y,s=Math.min(t,r),l=Math.min(n,a),d=Math.abs(r-t),c=Math.abs(a-n);o.setAttribute("x",String(s)),o.setAttribute("y",String(l)),o.setAttribute("width",String(d)),o.setAttribute("height",String(c))},end(){if(!o)return null;const e=parseFloat(o.getAttribute("x")||"0"),t=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:e,y:t,width:n,height:i,left:e,top:t,right:e+n,bottom:t+i}:null}}}(),i&&i.ini(u),o=[])}(u),document.addEventListener("touchmove",f),document.addEventListener("mousemove",f),document.addEventListener("mouseup",y),document.addEventListener("touchend",y)}A.addEventListener("mousedown",b),A.addEventListener("touchstart",b),g.push({element:A,event:"mousedown",handler:b},{element:A,event:"touchstart",handler:b})}(e),e.addEventListener("click",A),g.push({element:e,event:"click",handler:A}),()=>{g.forEach(({element:e,event:t,handler:n})=>{e.removeEventListener(t,n)})}}function W(e,t){const n=e.__cursorInteractionCleanup;function o(e){return e.__data}n&&n();const r=()=>o(e),l=[],d=e=>{r().changed()&&(e.preventDefault(),e.returnValue="")};function c(t,n){t.selected=n;const o=e.querySelector("#"+t.id);t.selected?o.classList.add("selected"):o.classList.remove("selected")}window.addEventListener("beforeunload",d),l.push({element:window,event:"beforeunload",handler:d});const h=t=>{if(!t.altKey)return;const n=R(r(),F(t));if(n){const{prj:t}=n,o=e.querySelector("g.edges");let r=o.querySelector("#prj");r||(r=i.element("circle",{id:"prj",cx:t.x,cy:t.y,r:7}),o.append(r)),r.setAttribute("cx",String(t.x)),r.setAttribute("cy",String(t.y))}else g()};e.addEventListener("mousemove",h),l.push({element:e,event:"mousemove",handler:h});const u=e=>{const t=(0,v.Yp)(e,!0);t!=v.Zj&&t!=v._s&&g()};function g(){const t=e.querySelector("g.edges #prj");t&&t.parentElement.removeChild(t)}window.addEventListener("keyup",u),l.push({element:window,event:"keyup",handler:u});const A=e=>{const t=(0,v.Yp)(e,!0);if(t!=v._s&&t!=v.Zj)return;const n=R(r(),F(e));if(n){const{edge:e,pos:o,prj:i}=n;r().insertEdgeVertex(e,i,o,t==v._s),g()}};e.addEventListener("click",A),l.push({element:e,event:"click",handler:A});const p=t=>{const n=.1*Math.sign(t.deltaY),o=G(),i=Math.max(.1,Math.min(5,o-n));if(i!==o){const n=e.getBoundingClientRect();P(i,t.clientX-n.left,t.clientY-n.top),t.preventDefault();const o=e.__data;o&&o.id&&J(o.id)}};e.addEventListener("wheel",p),l.push({element:e,event:"wheel",handler:p});const m=e=>{const t=(0,v.Yp)(e);switch(t&&e.preventDefault(),t){case v.bl:Array.from(r().edgeVertices.values()).filter(e=>e.selected).forEach(e=>{r().deleteEdgeVertex(e)});break;case"undo":r().undo();break;case"redo":r().redo();break;case v.Ur:P(Math.min(5,1.2*G())),J(r().id);break;case v.hU:P(Math.max(.1,G()/1.2)),J(r().id);break;case v.i1:P(1),J(r().id);break;case v.mD:r().fitToView();break;case v.F:r().nodes().forEach(e=>r().setNodeSelected(e,!0)),r().edgeVertices.forEach(e=>c(e,!0));break;case v.Gn:r().nodes().forEach(e=>r().setNodeSelected(e,!1)),r().edgeVertices.forEach(e=>c(e,!1))}};window.addEventListener("keydown",m),l.push({element:window,event:"keydown",handler:m});const f=O(e,{nodeFromEvent(e){e.preventDefault();let t=e.target.closest("g.nodes g.node");return t?o(t):(t=e.target.closest("g.edges g.edge .v-dot"),t?r().edgeVertices.get(t.id):null)},setSelection(e){r().nodes().forEach(t=>r().setNodeSelected(t,e.some(e=>e.id==t.id))),r().edgeVertices.forEach(t=>c(t,e.some(e=>e.id==t.id))),j(r().nodes().find(e=>e.selected))},setDragging(e){L=e},isSelected:e=>e.selected,getSelection(){const e=r().nodes().filter(e=>e.selected);return r().edgeVertices.forEach(t=>t.selected&&e.push(t)),e},getZoom:G,moveNode(e,t,n){r().nodesMap.has(e.id)?r().moveNode(e,t,n):(e.auto=!1,r().moveEdgeVertex(e,t,n))},boxSelection(e,t){r().nodesMap.forEach(n=>{var o,i;o=s(n),i=e,o.xi.x&&o.y+o.height>i.y?r().setNodeSelected(n,!n.selected):t||r().setNodeSelected(n,!1)}),r().edgeVertices.forEach(n=>{a(n,e,!1)?c(n,!n.selected):t||c(n,!1)}),j(r().nodes().find(e=>e.selected))},updatePanning:Y},t);e.__cursorInteractionCleanup=()=>{l.forEach(({element:e,event:t,handler:n})=>{e.removeEventListener(t,n)}),f&&f()}}function G(){if(!D)return 1;const e=D.querySelector("g.zoom");if(!e)return 1;const t=(e.getAttribute("transform")||"").match(/scale\(([^)]+)\)/);return t&&parseFloat(t[1])||1}function H(e){if(!D)return;const t=D.querySelector("g.zoom");if(!t)return;const n=U();t.setAttribute("transform",`translate(${n.x}, ${n.y}) scale(${e})`),Y()}function P(e,t,n){const o=D.querySelector("g.zoom"),i=G();if(void 0===t||void 0===n){const e=D.parentElement;e?(t=e.clientWidth/2,n=e.clientHeight/2):(t=D.clientWidth/2,n=D.clientHeight/2)}const r=U(),a=t-(t-r.x)/i*e,s=n-(n-r.y)/i*e;o.setAttribute("transform",`translate(${a}, ${s}) scale(${e})`),Y()}function U(){if(!D)return{x:0,y:0};const e=D.querySelector("g.zoom");if(!e)return{x:0,y:0};const t=(e.getAttribute("transform")||"").match(/translate\(([^,]+),([^)]+)\)/);return t?{x:parseFloat(t[1])||0,y:parseFloat(t[2])||0}:{x:0,y:0}}function Y(){if(!D)return;const e=D.querySelector("g.zoom");if(!e)return;const t=e.getBBox(),n=G();if(!D.parentElement)return;const o=Math.max(D.parentElement.clientWidth/n,t.x+t.width+20),i=Math.max(D.parentElement.clientHeight/n,t.y+t.height+20);D.setAttribute("width",String(o*n)),D.setAttribute("height",String(i*n))}const X=(e,t)=>{"Dashed"==t?e.setAttribute("stroke-dasharray","4"):"Dotted"==t&&e.setAttribute("stroke-dasharray","2")},Z={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"}},K=new Map;function J(e){if(!D)return;const t=G(),n=U(),o={zoom:t,transform:{x:n.x,y:n.y}};K.set(e,o)}function Q(e){if(!D||!K.has(e))return!1;const t=K.get(e);if(!t)return!1;const n=D.querySelector("g.zoom");return n&&(n.setAttribute("transform",`scale(${t.zoom}) translate(${t.transform.x}, ${t.transform.y})`),Y()),!0}function ee(e){K.delete(e)}function te(e,t,n){const o=e.x-t.x,i=e.y-t.y,r=n.x-t.x,a=n.y-t.y,s=o*r+i*a,l=r*r+a*a;if(0===l)return Math.sqrt(o*o+i*i);let d,c,h=s/l;h<0?(d=t.x,c=t.y):h>1?(d=n.x,c=n.y):(d=t.x+h*r,c=t.y+h*a);const u=e.x-d,g=e.y-c;return Math.sqrt(u*u+g*g)}},538(e,t,n){n.d(t,{k:()=>r}),n(408);function o(e={},t=!1){const n={nodeSpacing:e.nodeSpacing??80,layerSpacing:e.layerSpacing??60,componentSpacing:80,padding:40,groupMultiplier:.65};return t&&(n.nodeSpacing=Math.max(n.nodeSpacing*n.groupMultiplier,30),n.layerSpacing=Math.max(n.layerSpacing*n.groupMultiplier,35),n.componentSpacing=Math.max(n.componentSpacing*n.groupMultiplier,25),n.padding=Math.max(n.padding*n.groupMultiplier,15)),n}function i(e,t){const{direction:n="DOWN",compactLayout:o=!1}=t,i={"elk.algorithm":"layered","elk.direction":n,"elk.spacing.nodeNode":e.nodeSpacing.toString(),"elk.spacing.componentComponent":e.componentSpacing.toString(),"elk.padding":`[top=${e.padding},left=${e.padding},bottom=${e.padding},right=${e.padding}]`,"elk.layered.spacing.nodeNodeBetweenLayers":e.layerSpacing.toString(),"elk.layered.spacing.edgeNodeBetweenLayers":"10","elk.layered.spacing.edgeEdgeBetweenLayers":"10","elk.edgeRouting":"POLYLINE","elk.layered.unnecessaryBendpoints":"false","elk.layered.edgeRouting.orthogonal.mode":"DIRECTION_BASED","elk.layered.edgeRouting.orthogonal.spacing":"5","elk.layered.edgeRouting.orthogonal.nodeOverlapRatio":"0.1","elk.layered.compaction.connectedComponents":"true","elk.layered.compaction.postCompaction.strategy":"LEFT_RIGHT","elk.separateConnectedComponents":"true","elk.layered.nodePlacement.strategy":"NETWORK_SIMPLEX","elk.layered.nodePlacement.favorStraightEdges":"true","elk.layered.crossingMinimization.strategy":"LAYER_SWEEP","elk.layered.crossingMinimization.semiInteractive":"true","elk.hierarchyHandling":"SEPARATE_CHILDREN","elk.layered.considerModelOrder.strategy":"NONE","elk.edgeLabels.placement":"CENTER","elk.edgeLabels.inline":"true","elk.spacing.edgeLabel":"5","elk.edgeLabels.avoidOverlap":"false","elk.edgeLabels.considerModelOrder":"false","elk.layered.edgeLabels.sideSelection":"ALWAYS_UP"};return o&&(i["elk.spacing.nodeNode"]=Math.max(.7*e.nodeSpacing,30).toString(),i["elk.layered.spacing.nodeNodeBetweenLayers"]=Math.max(.7*e.layerSpacing,30).toString()),i}async function r(e,t={}){const r=new(await n.e(726).then(n.t.bind(n,862,23)).then(e=>e.default)),s={id:"root",layoutOptions:i(o(t,!1),t),children:[],edges:[]},l=new Map,d=new Map;e.nodesMap.forEach(e=>{if(!e.id)return;l.set(e.id,e);const t=Math.max(e.width||200,150),n=Math.max(e.height||100,250);d.set(e.id,{id:e.id,x:e.x,y:e.y,width:t+50,height:n+50,layoutOptions:{"elk.position":"","elk.nodeSize.constraints":"[FIXED_SIZE]"}})});const c=new Map,h=new Map,u=new Set;e.groupsMap.forEach(e=>{e.nodes.forEach(t=>{if(a(t))return h.has(t.id)||h.set(t.id,e.id),void u.add(t.id);c.has(t.id)||c.set(t.id,e.id)})});const g=new Map,A=e=>{const n=g.get(e.id);if(n)return n;const r=e.nodes.flatMap(t=>{if(a(t))return h.get(t.id)===e.id?[A(t)]:[];const n=d.get(t.id);return n&&c.get(t.id)===e.id?[n]:[]}),s={id:e.id,children:r,edges:[],layoutOptions:i(o(t,!0),t)};return g.set(e.id,s),s};e.groupsMap.forEach(e=>{if(!u.has(e.id)){const t=A(e);t.children.length>0&&s.children.push(t)}}),d.forEach((e,t)=>{c.has(t)||s.children.push(e)});const p=e=>{const t=[];let n=e;for(;n;)t.push(n),n=h.get(n);return t};if(e.edges.forEach(e=>{if(!e.id||!e.from?.id||!e.to?.id)return;if(!l.has(e.from.id)||!l.has(e.to.id))return void console.warn(`Skipping edge ${e.id}: source ${e.from.id} or target ${e.to.id} not found in nodes`);const t=e.label&&e.label.trim()?Math.min(7*e.label.length,200):0,n={id:e.id,sources:[e.from.id],targets:[e.to.id],labels:e.label&&e.label.trim()?[{id:`${e.id}-label`,text:e.label,width:t,height:20,layoutOptions:{"elk.edgeLabels.placement":"CENTER","elk.edgeLabels.inline":"true"}}]:[]},o=((e,t)=>{const n=p(c.get(e)),o=new Set(p(c.get(t)));return n.find(e=>o.has(e))})(e.from.id,e.to.id);(o?g.get(o):s).edges.push(n)}),!s.id||!s.children)throw new Error("Invalid ELK graph structure");try{const t=await r.layout(s),n=[],o=[],i=(e,t=0,o=0)=>{e.children?.forEach(e=>{e.children?i(e,t+(e.x||0),o+(e.y||0)):n.push({id:e.id,x:t+(e.x||0)+(e.width||0)/2,y:o+(e.y||0)+(e.height||0)/2})})},a=(t,n=0,i=0)=>{t.edges?.forEach(t=>{const r=[];let a;t.sections&&t.sections.length>0&&t.sections.forEach(e=>{e.startPoint&&r.push({x:n+e.startPoint.x,y:i+e.startPoint.y}),e.bendPoints&&e.bendPoints.length>0&&e.bendPoints.forEach(e=>{r.push({x:n+e.x,y:i+e.y})}),e.endPoint&&r.push({x:n+e.endPoint.x,y:i+e.endPoint.y})});const s=e.edges.find(e=>e.id===t.id);if(s?.label&&s.label.trim())if(t.labels&&t.labels.length>0){const e=t.labels[0];void 0!==e.x&&void 0!==e.y&&(a={x:n+e.x+(e.width||0)/2,y:i+e.y+(e.height||0)/2})}else if(r.length>=2){const e=Math.floor(r.length/2);if(r.length%2==0){const t=r[e-1],n=r[e];a={x:(t.x+n.x)/2,y:(t.y+n.y)/2}}else a=r[e]}o.push({id:t.id,vertices:r,label:a})}),t.children?.forEach(e=>{e.edges&&e.edges.length>0&&a(e,n+(e.x||0),i+(e.y||0))})};if(i(t),a(t),n.length>0){const e=Math.min(...n.map(e=>e.x)),t=Math.min(...n.map(e=>e.y)),i=50,r=-e+i,a=-t+i;n.forEach(e=>{e.x+=r,e.y+=a}),o.forEach(e=>{e.vertices.forEach(e=>{e.x+=r,e.y+=a}),e.label&&(e.label.x+=r,e.label.y+=a)})}return{nodes:n,edges:o}}catch(t){return console.warn("ELK layout failed, using fallback layout. Error:",t),function(e){const t=[],n=[];let o=0,i=0;const r=Math.ceil(Math.sqrt(e.nodesMap.size));let a=0;return e.nodesMap.forEach(e=>{t.push({id:e.id,x:o,y:i}),a++,a>=r?(a=0,o=0,i+=300):o+=300}),e.edges.forEach(e=>{n.push({id:e.id,vertices:[]})}),{nodes:t,edges:n}}(e)}}function a(e){return"nodes"in e}},245(e,t,n){var o=n(122),i=n(763),r=n(486),a=n(582),s=n.n(a),l=n(991),d=n.n(l),c=n(725),h=n.n(c),u=n(798),g=n.n(u),A=n(754),p=n.n(A),m=n(567),f=n.n(m),y=n(870),b={};b.styleTagTransform=f(),b.setAttributes=g(),b.insert=h().bind(null,"head"),b.domAPI=d(),b.insertStyleElement=p(),s()(y.A,b),y.A&&y.A.locals&&y.A.locals,n(769);class x{callback;handler;running=!1;timeoutId=null;constructor(e){this.callback=e,this.handler=()=>{this.running=!1,this.timeoutId=null,this.callback()}}start(e){this.running&&this.stop(),this.timeoutId=setTimeout(this.handler,e),this.running=!0}stop(){this.running&&null!==this.timeoutId&&(clearTimeout(this.timeoutId),this.running=!1,this.timeoutId=null)}isRunning(){return this.running}}class w{static DEFAULT_OPTIONS={minDelay:1e3,maxDelay:6e4,handshakeTimeout:5e3};static LIVERELOAD_PROTOCOLS=["http://livereload.com/protocols/official-9","http://livereload.com/protocols/2.x-remote-control"];uri;options;fileChangeHandler;socket=null;nextDelay;connectionDesired=!1;disconnectionReason="";handshakeTimeout;reconnectTimer;constructor(e,t){this.fileChangeHandler=e,this.options={...w.DEFAULT_OPTIONS,...t},this.uri="ws://localhost:35729/livereload",this.nextDelay=this.options.minDelay,this.handshakeTimeout=new x(()=>this.handleHandshakeTimeout()),this.reconnectTimer=new x(()=>this.attemptReconnection())}connect(){this.connectionDesired=!0,this.isSocketConnected()||(this.prepareForConnection(),this.createWebSocket())}disconnect(){this.connectionDesired=!1,this.reconnectTimer.stop(),this.isSocketConnected()&&(this.disconnectionReason="manual",this.socket.close())}isSocketConnected(){return null!==this.socket&&this.socket.readyState===WebSocket.OPEN}prepareForConnection(){this.reconnectTimer.stop(),this.disconnectionReason="cannot-connect"}createWebSocket(){this.socket=new WebSocket(this.uri),this.socket.onopen=()=>this.handleOpen(),this.socket.onclose=()=>this.handleClose(),this.socket.onmessage=e=>this.handleMessage(e),this.socket.onerror=()=>this.handleError()}handleOpen(){this.disconnectionReason="handshake-failed",this.startHandshake()}handleClose(){console.log(`WebSocket disconnected: ${this.disconnectionReason}. Retry in ${this.nextDelay}ms`),this.scheduleReconnection()}handleMessage(e){try{const t=JSON.parse(e.data);this.processMessage(t)}catch(e){console.error("Failed to parse WebSocket message:",e)}}handleError(){}processMessage(e){switch(e.command){case"hello":this.handleHelloMessage();break;case"reload":this.handleReloadMessage(e);break;default:console.log("Unknown WebSocket message received:",e)}}handleHelloMessage(){this.handshakeTimeout.stop(),this.nextDelay=this.options.minDelay}handleReloadMessage(e){this.reconnectTimer.stop(),this.connect(),e.path&&this.fileChangeHandler(e.path)}startHandshake(){const e={command:"hello",protocols:w.LIVERELOAD_PROTOCOLS,ver:"3.3.1"};this.sendCommand(e),this.handshakeTimeout.start(this.options.handshakeTimeout)}handleHandshakeTimeout(){this.isSocketConnected()&&(this.disconnectionReason="handshake-timeout",this.socket.close())}attemptReconnection(){this.connectionDesired&&this.connect()}scheduleReconnection(){this.connectionDesired&&(this.reconnectTimer.isRunning()||(this.reconnectTimer.start(this.nextDelay),this.nextDelay=Math.min(this.options.maxDelay,2*this.nextDelay)))}sendCommand(e){this.isSocketConnected()&&this.socket.send(JSON.stringify(e))}}var v=n(987);const C=(0,i.lazy)(()=>Promise.resolve().then(n.bind(n,486)).then(e=>({default:e.Root}))),k=()=>{const[e,t]=(0,i.useState)({data:null,error:null,loading:!0}),n=async()=>{t(e=>({...e,loading:!0,error:null}));try{const[e,n]=await Promise.all([fetch("data/model.json"),fetch("data/layout.json")]);if(!e.ok)throw new Error(`Failed to fetch model: ${e.statusText}`);if(!n.ok)throw new Error(`Failed to fetch layout: ${n.statusText}`);const[o,i]=await Promise.all([e.json(),n.json()]);t({data:{model:o,layout:i},error:null,loading:!1})}catch(e){console.error("Failed to load data:",e),t({data:null,error:e instanceof Error?e.message:"Unknown error occurred",loading:!1})}},o=e=>{e.endsWith(".svg")||(console.log("File changed:",e),(0,r.S)(),n())};return(0,i.useEffect)(()=>{const e=new URLSearchParams(document.location.search),t=e.get("auto"),i=e.get("save"),r="1"===t||"true"===t||"1"===i||"true"===i?null:new w(o);return r?.connect(),n(),()=>{r?.disconnect()}},[]),e.loading?(0,v.jsx)(I,{}):e.error?(0,v.jsx)(B,{error:e.error,onRetry:n}):e.data?(0,v.jsx)(i.Suspense,{fallback:(0,v.jsx)(I,{}),children:(0,v.jsx)(C,{model:e.data.model,layout:e.data.layout})}):(0,v.jsx)(B,{error:"No data available",onRetry:n})},I=()=>(0,v.jsx)("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh",fontFamily:"Arial, sans-serif"},children:(0,v.jsx)("div",{children:"Loading..."})}),B=({error:e,onRetry:t})=>(0,v.jsxs)("div",{style:{padding:"20px",color:"red",fontFamily:"monospace",whiteSpace:"pre-wrap",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100vh"},children:[(0,v.jsx)("h2",{children:"Error loading application"}),(0,v.jsx)("p",{children:e}),(0,v.jsx)("button",{onClick:t,style:{padding:"10px 20px",fontSize:"16px",cursor:"pointer",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px"},children:"Retry"})]}),E=document.getElementById("root");if(!E)throw new Error("Root container not found");(0,o.createRoot)(E).render((0,v.jsx)(k,{}))},264(e,t,n){n(763);var o=n(686),i=n(987);const r="add-vertex",a="add-label-vertex",s="del-vertex",l="zoom-in",d="zoom-out",c="zoom-fit",h="zoom-100",u="select-all",g="deselect",A="move-left",p="move-right",m="move-up",f="move-down",y="move-left-fine",b="move-right-fine",x="move-up-fine",w="move-down-fine",v="toggle_drag_mode",C="align_horizontal",k="align_vertical",I="distribute_horizontal",B="distribute_vertical",E="auto_layout",S="reset_position",M="toggle_grid",$="toggle_snap_to_grid",D="snap_all_to_grid",j=[{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:a,help:"Add label anchor relationship vertex",combinations:[{alt:!0,shift:!0,click:!0}]},{id:s,help:"Remove relationship vertex",combinations:[{key:"DELETE"},{key:"BACKSPACE"}]}]},{name:"Zoom",list:[{id:l,help:"Zoom in",combinations:[{ctrl:!0,key:"="}]},{id:d,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:g,help:"Deselect",combinations:[{key:"ESC"}]}]},{name:"Move",list:[{id:m,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:b,help:"Move right (1 pixel)",combinations:[{key:"RIGHT",shift:!0}]},{id:f,help:"Move down (grid increment)",combinations:[{key:"DOWN"}]},{id:w,help:"Move down (1 pixel)",combinations:[{key:"DOWN",shift:!0}]},{id:A,help:"Move left (grid increment)",combinations:[{key:"LEFT"}]},{id:y,help:"Move left (1 pixel)",combinations:[{key:"LEFT",shift:!0}]}]},{name:"View",list:[{id:v,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:C,help:"Align selected elements horizontally",combinations:[{key:"h",ctrl:!0,shift:!0}]},{id:k,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:B,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:M,help:"Toggle grid visibility",combinations:[{key:"g",ctrl:!0}]},{id:$,help:"Toggle snap to grid",combinations:[{key:"g",ctrl:!0,shift:!0}]},{id:D,help:"Snap all elements to grid",combinations:[{key:"g",ctrl:!0,alt:!0}]}]}],L=j.reduce((e,t)=>e.concat(t.list),[]).reduce((e,t)=>(e[t.id]=t,e),{}),T=e=>[e.ctrl&&(0,o.sy)().toUpperCase(),e.shift&&"SHIFT",e.alt&&"ALT",e.key&&(e.key.length>1?e.key:`"${e.key.toUpperCase()}"`),e.click&&"CLICK",e.wheel&&"WHEEL"].filter(Boolean).join(" + ");n.d(t,["DE",0,I,"F",0,u,"Gg",0,A,"Gn",0,g,"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:j.map(e=>(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("tr",{children:(0,i.jsx)("th",{colSpan:2,children:e.name})}),e.list.map(e=>(0,i.jsxs)("tr",{children:[(0,i.jsx)("td",{children:e.combinations.map(T).join(", ")}),(0,i.jsx)("td",{children:e.help})]}))]}))})})]}),"J8",0,y,"Jk",0,k,"OE",0,D,"Op",0,M,"Ur",0,l,"Vy",0,B,"Yp",0,(e,t=!1,n=!1)=>{const i=Object.keys(L).filter(i=>((e,t,n,i)=>t.combinations.some(t=>{if(Boolean(t.shift)!=e.shiftKey)return!1;if(t.ctrl&&!(0,o.SA)(e))return!1;if(Boolean(t.alt)!=e.altKey)return!1;if(n)return t.click;if(i)return t.wheel;if(t.key){const n=e;return"DELETE"==t.key?"Delete"==n.key:"BACKSPACE"==t.key?"Backspace"==n.key:"ESC"==t.key?"Escape"==n.key:"UP"==t.key?"ArrowUp"==n.key:"DOWN"==t.key?"ArrowDown"==n.key:"LEFT"==t.key?"ArrowLeft"==n.key:"RIGHT"==t.key?"ArrowRight"==n.key:t.key&&n.key&&t.key.toLowerCase()==n.key.toLowerCase()}return!1}))(e,L[i],t,n));if(0!==i.length)return 1===i.length?i[0]:i.sort((e,t)=>{const n=L[e],o=L[t],i=n.combinations[0],r=o.combinations[0],a=(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)-a})[0]},"Zj",0,r,"_s",0,a,"_t",0,S,"aX",0,v,"b3",0,p,"bl",0,s,"hU",0,d,"hZ",0,$,"i1",0,h,"iD",0,b,"l8",0,x,"mD",0,c,"mt",0,w,"rB",0,f,"t9",0,C,"uK",0,m])},686(e,t,n){const o=()=>{if("undefined"==typeof navigator)return!1;if("userAgentData"in navigator&&navigator.userAgentData){const e=navigator.userAgentData.platform;if(e&&e.toLowerCase().includes("mac"))return!0}const e=navigator.userAgent.toLowerCase();if(e.includes("mac os")||e.includes("macintosh"))return!0;if(navigator.platform){const e=navigator.platform.toLowerCase();if(e.includes("mac")||e.includes("darwin"))return!0}try{if(void 0!==new KeyboardEvent("keydown",{metaKey:!0}).metaKey)return/mac|darwin|os x/i.test(navigator.userAgent)}catch(e){}return!1};n.d(t,["SA",0,e=>o()?e.metaKey:e.ctrlKey,"sy",0,()=>o()?"Cmd":"Ctrl"])},870(e,t,n){var o=n(220),i=n.n(o),r=n(716),a=n.n(r)()(i());a.push([e.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 s=a;n.d(t,["A",0,s])}},e=>{e.O(0,[453,96],()=>e(e.s=245)),e.O()}]); //# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/cmd/mdl/webapp/dist/main.js.map b/cmd/mdl/webapp/dist/main.js.map index 383b7222..fb438865 100644 --- a/cmd/mdl/webapp/dist/main.js.map +++ b/cmd/mdl/webapp/dist/main.js.map @@ -1 +1 @@ -{"version":3,"file":"main.js","mappings":"wJAqFA,MAAMA,EAA2D,CAChEC,UAAW,OACXC,UAAW,KACXC,UAAW,QACXC,UAAW,QA8TCC,EAAaC,IACzB,MAAMC,EAAuB,GAO7B,OANiBC,OAAOC,KAAKH,EAAMI,OAAOC,OAAOC,GAAWA,EAAQC,SAAS,UACpEC,QAAQC,IAChBT,EAAMI,MAAMK,GAAGD,QAASE,IACvBT,EAAUU,KAAK,CAACC,IAAKF,EAAEE,IAAKC,MAAOH,EAAEG,OAASH,EAAEE,IAAKN,QAASG,QAGzDR,gBClYR,MAAMa,EAAuC,CAAC,ECvBvC,SAASC,EAAaC,GAC3B,MAAMC,EAAQD,EAAME,QAAQ,WAAY,OACxC,OAAOD,EAAME,OAAO,GAAGC,cAAgBH,EAAMI,MAAM,EACrD,uBCYO,MAAMC,EAA4B,EACvCtB,QAAOuB,YAAWC,eAAcC,QAChCC,eAAcC,SAAQC,eAAcC,SAAQC,YAC5CC,WAAUC,kBAEV,MAAM5B,EAAQL,EAAUC,GAExB,OACE,EAAAiC,EAAAC,MAAA,OAAKC,UAAU,UAASC,SAAA,EACtB,EAAAH,EAAAI,KAACC,EAAY,CACXlC,MAAOA,EACPmB,UAAWA,EACXC,aAAcA,KAEhB,EAAAS,EAAAI,KAACE,EAAc,CACbd,MAAOA,EACPC,aAAcA,EACdC,OAAQA,EACRC,aAAcA,EACdC,OAAQA,EACRC,UAAWA,EACXC,SAAUA,EACVC,YAAaA,QAMfM,EAID,EAAGlC,QAAOmB,YAAWC,mBACxB,EAAAS,EAAAC,MAAA,OAAAE,SAAA,CAAK,QAEFhC,EAAMoC,OAAS,GACd,EAAAP,EAAAC,MAAA,UAAQO,SAAUC,GAAKlB,EAAakB,EAAEC,OAAOC,OAAQA,MAAOrB,EAAUa,SAAA,EACpE,EAAAH,EAAAI,KAAA,UAAQQ,UAAQ,EAACD,MAAM,GAAGE,QAAM,EAAAV,SAAC,QAChChC,EAAM2C,IAAIC,IACT,EAAAf,EAAAI,KAAA,UAAuBO,MAAOI,EAAKpC,IAAIwB,SACpCrB,EAAaiC,EAAK1C,SAAW,KAAO0C,EAAKnC,OAD/BmC,EAAKpC,UAMtB,EAAAqB,EAAAI,KAAA,QAAMY,MAAO,CAAEC,WAAY,MAAOC,WAAY,QAASf,SACpDhC,EAAM,GAAKW,EAAaX,EAAM,GAAGE,SAAW,KAAOF,EAAM,GAAGS,MAAQ,0BAMvE0B,EASD,EACHd,QAAOC,eAAcC,SAAQC,eAAcC,SAAQC,YACnDC,WAAUC,kBAEV,EAAAC,EAAAC,MAAA,OAAKe,MAAO,CAAEG,QAAS,OAAQC,WAAY,UAAWjB,SAAA,EACpD,EAAAH,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACiB,EAAc,CAACvB,SAAUA,EAAUC,YAAaA,OAEnD,EAAAC,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACkB,EAAe,CAAC9B,MAAOA,OAE1B,EAAAQ,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACmB,EAAgB,CAAC/B,MAAOA,OAE3B,EAAAQ,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACoB,EAAc,CAAC/B,aAAcA,EAAcI,UAAWA,OAEzD,EAAAG,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACqB,EAAY,CAACjC,MAAOA,OAEvB,EAAAQ,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACsB,EAAY,CAAClC,MAAOA,OAEvB,EAAAQ,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACuB,EAAU,CAACjC,OAAQA,EAAQE,OAAQA,EAAQJ,MAAOA,OAErD,EAAAQ,EAAAI,KAAA,OAAKF,UAAU,gBAAeC,UAC5B,EAAAH,EAAAI,KAACwB,EAAU,CAACjC,aAAcA,SAK1B0B,EAGD,EAAGvB,WAAUC,kBAChB,EAAAC,EAAAI,KAAA,UACEF,UAAW,gBAA4B,WAAbJ,EAAwB,cAAgB,YAClE+B,QAAS,IAAM9B,EAAyB,QAAbD,EAAqB,SAAW,OAC3D,eAA2B,QAAbA,EAAqB,qCAAuC,gFAAgFK,SAE5I,QAAbL,GAAqB,EAAAE,EAAAI,KAAA,KAAGF,UAAU,uBAA2B,EAAAF,EAAAI,KAAA,KAAGF,UAAU,2BAIzEoB,EAA4C,EAAG9B,YACnD,MAAMsC,GAAS,EAAAC,EAAAC,MACf,OACE,EAAAhC,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,EACE,EAAAH,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAM0C,OAAQ,eAAc,6CAA6CJ,OAAY3B,UAC1G,EAAAH,EAAAI,KAAA,KAAGF,UAAU,mBAEf,EAAAF,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAM2C,OAAQ,eAAc,gCAAgCL,eAAoBA,OAAY3B,UACjH,EAAAH,EAAAI,KAAA,KAAGF,UAAU,sBAMfqB,EAA6C,EAAG/B,YACpD,MAAMsC,GAAS,EAAAC,EAAAC,MACf,OACE,EAAAhC,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,EACE,EAAAH,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAM4C,kBAAmB,eAAc,0DAA0DN,aAAkB3B,UACxI,EAAAH,EAAAI,KAAA,KAAGF,UAAU,yBAEf,EAAAF,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAM6C,kBAAmB,eAAc,uDAAuDP,aAAkB3B,UACrI,EAAAH,EAAAI,KAAA,KAAGF,UAAU,oBAAoBc,MAAO,CAACsB,UAAW,sBAEtD,EAAAtC,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAM+C,uBAAwB,eAAc,qEAAqET,WAAgB3B,UACtJ,EAAAH,EAAAI,KAAA,KAAGF,UAAU,yBAEf,EAAAF,EAAAI,KAAA,UAAQyB,QAAS,IAAMrC,EAAMgD,uBAAwB,eAAc,mEAAmEV,WAAgB3B,UACpJ,EAAAH,EAAAI,KAAA,KAAGF,UAAU,4BAMfsB,EAGD,EAAG/B,eAAcI,gBACpB,MAAMiC,GAAS,EAAAC,EAAAC,MACf,OACE,EAAAhC,EAAAI,KAAA,UACEF,UAAU,eACV2B,QAASpC,EACTmB,SAAUf,EACV,eAAc,mEAAmEiC,OAAY3B,SAE5FN,GAAY,EAAAG,EAAAI,KAAA,KAAGF,UAAU,4BAAgC,EAAAF,EAAAI,KAAA,KAAGF,UAAU,oBAKvEuB,EAAyC,EAAGjC,YAChD,MAAOiD,EAAaC,IAAkB,EAAAC,EAAAC,UAASpD,EAAMqD,kBAC9CC,EAAYC,IAAiB,EAAAJ,EAAAC,UAASpD,EAAMwD,gBAC7ClB,GAAS,EAAAC,EAAAC,MAkCf,OA/BAW,EAAAM,UAAgB,KACd,MAAMC,EAAkB,KACtBR,EAAelD,EAAMqD,iBACrBE,EAAcvD,EAAMwD,iBAStB,OALAE,IAGAC,OAAOC,iBAAiB,mBAAoBF,GAErC,KACLC,OAAOE,oBAAoB,mBAAoBH,KAEhD,CAAC1D,KAiBF,EAAAQ,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,EACE,EAAAH,EAAAI,KAAA,UACEF,UAAWuC,EAAc,gBAAkB,kBAC3CZ,QAlBmB,KACvBrC,EAAM8D,aACNZ,EAAelD,EAAMqD,kBAiBjB,eAAc,2BAA2Bf,OAAY3B,UAErD,EAAAH,EAAAI,KAAA,KAAGF,UAAU,iBAEf,EAAAF,EAAAI,KAAA,UACEF,UAAW4C,EAAa,gBAAkB,kBAC1CjB,QApBmB,KACvBrC,EAAM+D,mBACNR,EAAcvD,EAAMwD,iBAmBhB,eAAc,wBAAwBlB,aAAkB3B,UAExD,EAAAH,EAAAI,KAAA,KAAGF,UAAU,qBAEf,EAAAF,EAAAI,KAAA,UACEyB,QArBgB,KACpBrC,EAAMgE,iBAqBF5C,UAAWkC,EACX,eAAc,8BAA8BhB,WAAgB3B,UAE5D,EAAAH,EAAAI,KAAA,KAAGF,UAAU,4BAMfuD,EAAkB,KACtB,MAAOC,EAAMC,IAAgB,EAAAhB,EAAAC,UAAS,KAiBtC,OAfA,EAAAD,EAAAM,WAAU,KACR,MAAMW,EAAa,KACjB,MAAMC,EAAcC,KAAKC,MAAkB,KAAZ,EAAAC,EAAAC,OAC/BN,EAAaE,IAIfD,IAGA,MAAMM,EAAWC,YAAYP,EAAY,KAEzC,MAAO,IAAMQ,cAAcF,IAC1B,KAGD,EAAAlE,EAAAC,MAAA,UACE4B,QAAS,KAAM,EAAAmC,EAAAK,IAAgB,GAC/BnE,UAAU,eACV,eAAa,8BAA6BC,SAAA,CAEzCuD,EAAK,QAKNhC,EAAyC,EAAGlC,YAChD,MAAMsC,GAAS,EAAAC,EAAAC,MACf,OACE,EAAAhC,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,EACE,EAAAH,EAAAI,KAAA,UAAQyB,QAAS,MACf,EAAAmC,EAAAK,IAAgBP,KAAKQ,IAAI,IAAK,EAAAN,EAAAC,MAAY,OACzC,eAAc,wCAAwCnC,OAAY3B,UACnE,EAAAH,EAAAI,KAAA,KAAGF,UAAU,2BAEf,EAAAF,EAAAI,KAACqD,EAAW,KACZ,EAAAzD,EAAAI,KAAA,UAAQyB,QAAS,MACf,EAAAmC,EAAAK,IAAgBP,KAAKS,IAAI,EAAe,KAAZ,EAAAP,EAAAC,SAC3B,eAAc,wCAAwCnC,OAAY3B,UACnE,EAAAH,EAAAI,KAAA,KAAGF,UAAU,0BAEf,EAAAF,EAAAI,KAAA,UAAQyB,QAAS,KAAQrC,EAAMgF,aAAgB,eAAc,wBAAwB1C,OAAY3B,UAC/F,EAAAH,EAAAI,KAAA,KAAGF,UAAU,wBAMfyB,EAID,EAAGjC,SAAQE,SAAQJ,YACtB,MAAOiF,EAAYC,IAAiB,EAAA/B,EAAAC,WAAS,GACvCd,GAAS,EAAAC,EAAAC,MAiBf,OAdA,EAAAW,EAAAM,WAAU,KACR,MAAM0B,EAAe,KACnBD,EAAclF,EAAMoF,YAItBD,IAGA,MAAMT,EAAWC,YAAYQ,EAAc,KAE3C,MAAO,IAAMP,cAAcF,IAC1B,CAAC1E,KAGF,EAAAQ,EAAAI,KAAA,UACEF,UAAWuE,EAAa,MAAQ,SAChC7D,SAAUhB,EACViC,QAASnC,EACT,eAAc,oCAAoCoC,OAAY3B,SAE7DP,GAAS,EAAAI,EAAAI,KAAA,KAAGF,UAAU,4BAAgC,EAAAF,EAAAI,KAAA,KAAGF,UAAU,mBAKpE0B,EAED,EAAGjC,mBAEJ,EAAAK,EAAAI,KAAA,UAAQyB,QAASlC,EAAc,eAAa,oEAAmEQ,UAC7G,EAAAH,EAAAI,KAAA,KAAGF,UAAU,6BCpUb2E,GAAO,EAAAlC,EAAAmC,MAAK,IAAMC,QAAAC,UAAAC,KAAAC,EAAAC,KAAAD,EAAA,MAAsBD,KAAKG,IAAM,CAAOC,QAASD,EAAOP,SAC1ES,GAAQ,EAAA3C,EAAAmC,MAAK,IAAMI,EAAAzE,EAAA,KAAAwE,KAAAC,EAAAC,KAAAD,EAAA,MAAmCD,KAAKG,IAAM,CAAOC,QAASD,EAAOE,UAUxFC,EAAsB,CAACC,EAAiCC,KAC5D,MAAMC,EAAOC,SAASC,gBACtB,IAAKJ,EAGH,cAFOE,EAAKG,QAAQC,gCACbJ,EAAKG,QAAQE,mBAGtBL,EAAKG,QAAQC,oBAAsBN,EAC/BC,EACFC,EAAKG,QAAQE,mBAAqBN,SAE3BC,EAAKG,QAAQE,oBAOlBC,EAAyB,CAACC,EAAgBR,KAC9CS,QAAQT,MAAM,GAAGQ,YAAkBR,GACnCU,MAAM,GAAGF,uCAGEG,EAAsB,EAAGrI,QAAOsI,aAC3C,EAAArG,EAAAI,KAACkG,EAAAC,GAAM,CAAApG,UACL,EAAAH,EAAAI,KAACkG,EAAAE,GAAM,CAAArG,UACL,EAAAH,EAAAI,KAACkG,EAAAG,GAAK,CAACC,KAAK,IAAIC,SAAS,EAAA3G,EAAAI,KAACwG,EAAS,CAAC7I,MAAOA,EAAO8I,QAASR,UAKpDS,EAAe,KH8GIxH,SCrJf,IAAIyH,gBAAgBpB,SAASqB,SAASC,QACvCC,IAAI,OAAS,WDsJlBrI,EAAOS,GAEdrB,OAAOC,KAAKW,GAAQN,QAAQI,UAAcE,EAAOF,KG7G/CiI,EAA8C,EAAG7I,QAAO8I,cAC5D,MAAOM,EAAcC,IAAmB,EAAAd,EAAAe,MAClC/H,EAAYgI,UAAUH,EAAaD,IAAI,OAAS,KAG/CK,EAAaC,IAAkB,EAAA7E,EAAAC,WAAS,IACxC9C,EAAUC,IAAe,EAAA4C,EAAAC,UAA2B,QACpD6E,EAAcC,IAAmB,EAAA/E,EAAAC,UAAwB,MAC1D+E,GAAgB,EAAAhF,EAAAiF,QAAsB,MACtCC,GAAgB,EAAAlF,EAAAiF,QAAO,GAGvBpI,EHpCgB,EAACzB,EAAY8I,EAAcvH,KACjD,GAAIT,EAAOS,GACT,OAAOT,EAAOS,GAGhB,MAAME,ED4EiB,EAACzB,EAAc8I,EAAkBiB,KAEzD,MAAMC,EAAW,IAAIC,IACfC,EAAY,IAAID,IAEhBE,EAAeC,IAChBC,MAAMC,QAAQF,EAAGG,gBACpBH,EAAGG,cAAc/J,QAAQgK,IACxBN,EAAUO,IAAID,EAAIE,GAAIF,MAoCzB,GA9BAxK,EAAMA,MAAM2K,QAAU3K,EAAMA,MAAM2K,OAAOnK,QAAS4J,IACjDJ,EAASS,IAAIL,EAAGM,GAAIN,GAChBC,MAAMC,QAAQF,EAAGG,gBACpBH,EAAGG,cAAc/J,QAAQgK,IACxBN,EAAUO,IAAID,EAAIE,GAAIF,OAKzBxK,EAAMA,MAAM4K,iBAAmB5K,EAAMA,MAAM4K,gBAAgBpK,QAAS4J,IACnEJ,EAASS,IAAIL,EAAGM,GAAIN,GACpBD,EAAYC,GAERC,MAAMC,QAAQF,EAAGS,aACpBT,EAAGS,WAAWrK,QAASsK,IACtBA,EAAIC,OAASX,EACbJ,EAASS,IAAIK,EAAIJ,GAAII,GACrBX,EAAYW,GACRT,MAAMC,QAAQQ,EAAIE,aACrBF,EAAIE,WAAWxK,QAASyK,IACvBA,EAAIF,OAASD,EACbd,EAASS,IAAIQ,EAAIP,GAAIO,GACrBd,EAAYc,SAQbjL,EAAMA,MAAMkL,gBAAiB,CAChC,MAAMC,EAAsBf,IAC3BA,EAAGe,oBAAsBf,EAAGe,mBAAmB3K,QAAS4K,IACvD,MAAMN,EAAM,IAAId,EAASb,IAAIiC,EAAKC,aAAcX,GAAIU,EAAKV,IACzDV,EAASS,IAAIK,EAAIJ,GAAII,GACrBA,EAAIC,OAASX,EACbD,EAAYiB,MAIRE,EAAc,CAAClB,EAAaW,KACjCX,EAAGW,OAASA,EACZf,EAASS,IAAIL,EAAGM,GAAIN,GACpBD,EAAYC,GACZe,EAAmBf,GACnBA,EAAGhI,UAAYgI,EAAGhI,SAAS5B,QAASsK,GAAiBQ,EAAYR,EAAKV,IACtEA,EAAGmB,qBAAuBnB,EAAGmB,oBAAoB/K,QAASsK,GAAiBQ,EAAYR,EAAKV,KAG7FpK,EAAMA,MAAMkL,gBAAgB1K,QAAS4J,GAAgBkB,EAAYlB,EAAI,MACtE,CAGA,MAAMpH,KAACA,EAAI1C,QAAEA,GA+Md,SAAiBN,EAAc+J,GAC9B,IAAI/G,EAAa,KAAM1C,EAAkB,GAUzC,OATAJ,OAAOC,KAAKH,EAAMI,OAAOC,OAAOI,GAAKA,EAAEF,SAAS,UAAUiL,KAAM/K,GACtDT,EAAMI,MAAcK,GAAI+K,KAAM9K,IACtC,GAAIA,EAAEE,KAAOmJ,EAGZ,OAFA/G,EAAOtC,EACPJ,EAAUG,GACH,KAIH,CAACuC,OAAM1C,UACf,CA3NyBmL,CAAQzL,EAAO+J,GAEvC,IAAK/G,EAAM,OAAO,KAElB,MAAMvB,EAAQ,IAAIwE,EAAAyF,GAAU1I,EAAKpC,IAAKoC,EAAKnC,OAASmC,EAAKpC,KACnD+K,EAAgB3I,EAAK4I,iBAAiBD,cAC5ClK,EAAMoK,gBAAkBF,EAAgBjM,EAAiBiM,QAAiBG,EAC1E,MAAMC,EAAqB,CAACC,KAAMvK,EAAMuK,KAAMC,YAAajJ,EAAKiJ,YAAaC,QAASlM,EAAMkM,QAASlC,SAAU,IAG/G,GAFAvI,EAAMsK,SAAWA,GAEZ/I,EAAKgH,SAAU,OAAOvI,EAG3B,MAAM0K,EAA0C,CAAC,EACjD,GAAe,mBAAX7L,GAA2C,kBAAXA,EACnC0C,EAAKgH,SAASxJ,QAAQ4L,IACrB,MAAMhC,EAAKJ,EAASb,IAAIiD,EAAI1B,IACxBN,GAAIW,SACPoB,EAAY/B,EAAGW,OAAOL,KAAM,UAGxB,GAAI1H,EAAKqJ,iBAEVrJ,EAAKgH,SAASsC,KAAKF,GAAOA,EAAI1B,IAAM1H,EAAKqJ,oBAC7CF,EAAYnJ,EAAKqJ,mBAAoB,QAChC,GAAe,wBAAX/L,EAAmC,CAE7C,MAAMiM,EAAa,CAAC7B,GAAI,oBAAqB1K,EAAMA,MAAMwM,YACzDxC,EAASS,IAAI8B,EAAE7B,GAAI6B,GACfvM,EAAMA,MAAM2K,QAAQ3K,EAAMA,MAAM2K,OAAOtK,OAAO+J,GAAqB,YAAfA,EAAGnB,UAAwBzI,QAAQ4J,GAAMA,EAAGW,OAASwB,GACzGvM,EAAMA,MAAM4K,iBAAiB5K,EAAMA,MAAM4K,gBAAgBvK,OAAO+J,GAAqB,YAAfA,EAAGnB,UAAwBzI,QAAQ4J,GAAMA,EAAGW,OAASwB,GAC/HJ,EAAYI,EAAE7B,KAAM,CACrB,CAEA,MAAM+B,EAASzM,EAAMI,MAAMqM,OAGrBC,EAAgBC,GAAgBA,EAAIC,cAAc1L,QAAQ,cAAe,KAE3EuL,GAAQzC,UACXyC,EAAOzC,SAASxJ,QAAQC,IACvB,GAAIA,EAAEkM,IAAK,CACV,MAAME,EAAY,SAASH,EAAajM,EAAEkM,OACtClM,EAAEqM,YAAYrL,EAAMsL,cAActC,IAAIhK,EAAEqM,WAAsB,GAAGD,QACjEpM,EAAEuM,OAAOvL,EAAMsL,cAActC,IAAIhK,EAAEuM,MAAiB,GAAGH,WACvDpM,EAAEwM,QAAQxL,EAAMsL,cAActC,IAAIhK,EAAEwM,OAAkB,GAAGJ,WAC9D,IAIEJ,GAAQlC,eACXkC,EAAOlC,cAAc/J,QAAQC,IAC5B,GAAIA,EAAEkM,IAAK,CACV,MAAME,EAAY,aAAaH,EAAajM,EAAEkM,OAC1ClM,EAAEuM,OAAOvL,EAAMsL,cAActC,IAAIhK,EAAEuM,MAAiB,GAAGH,UAC5D,IAKF7J,EAAKgH,SAASxJ,QAAS4L,IAEtB,GAAID,EAAYC,EAAI1B,IAAK,OAEzB,MAAMN,EAAKJ,EAASb,IAAIiD,EAAI1B,IACtBwC,EAAiB9C,EA6JzB,SAAgCpK,EAAcqM,GAC7C,MAAMrJ,EAAOhD,EAAMI,MAAM+M,gBAAgBb,KAAKc,GAAaA,EAAUf,kBAAoBA,GACzF,OAAOrJ,GAAMpC,GACd,CAhK8ByM,CAAuBrN,EAAOoK,EAAGM,SAAMoB,EAEnE,IAAIwB,EAAM,GACNrK,EAAQ,CAAC,EACb,GAAImH,EAAI,CACP,MAAMmD,EAAOnD,EAAGmD,KAAKtM,MAAM,KAC3BqM,EAAMC,EAAKA,EAAK/K,OAAS,GACrB4H,EAAGoD,aACNF,GAAO,KAAOlD,EAAGoD,YAElBD,EAAK/M,QAAQmM,IACZ,MAAMlM,EAAIgM,GAAUA,EAAOzC,UAAYyC,EAAOzC,SAASsC,KAAK7L,GAAKA,EAAEkM,KAAOA,GAC1ElM,IAAMwC,EAAQ,IAAIA,KAAUxC,KAE9B,CAEAgB,EAAMgM,QACLrB,EAAI1B,GACJN,GAAMA,EAAG4B,MAAkBI,EAAI1B,GAC/B4C,EACClD,GAAMA,EAAG6B,YAAe7B,EAAG6B,YAAc,GAC1ChJ,EAuGH,SAAkB2F,EAA8BsE,GAC/C,GAAIA,EAAgB,CACnB,MAAMQ,EAAiBC,mBAAmBT,GAC1C,MAAO,CACNU,KAAM,OAAOF,IACbG,WAAY,GAAGH,QAEjB,CACA,GAAI9E,GAASkF,IACZ,MAAO,CACNF,KAAMhF,EAAQkF,IACdD,WAAYjF,EAAQkF,IAIvB,CArHGC,CAAS3D,EAAI8C,IAEd9C,GAAM2B,EAAS/B,SAASrJ,KAAK,CAC5B+J,GAAIN,EAAGM,GACP6C,KAAMnD,EAAGmD,KACTtE,SAAUmB,EAAGnB,SACb+E,WAAY5D,EAAG4D,WACfd,iBACAM,WAAYpD,EAAGoD,WACfM,IAAK1D,EAAG0D,QAINzD,MAAMC,QAAQtH,EAAKuH,gBACtBvH,EAAKuH,cAAc/J,QAAQ4L,IAC1B,MAAM5B,EAAMN,EAAUf,IAAIiD,EAAI1B,IAC9B,IAAKF,EAAK,OAEV,IAAK/I,EAAMwM,SAASC,IAAI1D,EAAI2D,UAAW,CACtC,GAAInE,EAASkE,IAAI1D,EAAI2D,UAAW,CAC/B,MAAM/D,EAAKJ,EAASb,IAAIqB,EAAI2D,UAC5BhG,QAAQiG,KAAK,mCAAoChE,EAAGM,GAAIN,EAAG4B,KAC5D,MACC7D,QAAQiG,KAAK,sBAAuB5D,EAAI2D,UAEzC,MACD,CACA,IAAK1M,EAAMwM,SAASC,IAAI1D,EAAI6D,eAAgB,CAC3C,GAAIrE,EAASkE,IAAI1D,EAAI6D,eAAgB,CACpC,MAAMjE,EAAKJ,EAASb,IAAIqB,EAAI6D,eAC5BlG,QAAQiG,KAAK,mCAAoChE,EAAGM,GAAIN,EAAG4B,KAC5D,MACC7D,QAAQiG,KAAK,sBAAuB5D,EAAI6D,eAEzC,MACD,CACA,IAAIpL,EAAa,CAAC,EAClBuH,EAAI+C,KAAKtM,MAAM,KAAKT,QAAQmM,IAC3B,MAAMlM,EAAIgM,GAAUA,EAAOlC,eAAiBkC,EAAOlC,cAAc+B,KAAK7L,GAAKA,EAAEkM,KAAOA,GACpFlM,IAAMwC,EAAQ,IAAIA,KAAUxC,MAEzB2L,EAAIkC,UAASrL,EAAMqL,QAAUlC,EAAIkC,SAErC7M,EAAM8M,QAAQ/D,EAAIE,GAAIF,EAAI2D,SAAU3D,EAAI6D,cAAe7D,EAAIyB,YAAaG,EAAIoC,SAAUvL,KAMxF,MAAMwL,EAASrE,IACd,IAAIsE,EAAI,EACR,IAAK,IAAInC,EAAInC,EAAGW,OAAQwB,EAAGA,EAAIA,EAAExB,OAAQ2D,IACzC,OAAOA,GA+CR,OA7CkBxO,OAAOC,KAAKgM,GAC5BpJ,IAAI2H,GAAMV,EAASb,IAAIuB,IACvBiE,KAAK,CAACC,EAAGC,IAAMJ,EAAMG,GAAKH,EAAMI,IAAM,EAAI,GAElCrO,QAAQuK,IACjB,IAAI9H,EAAQ,CAAC,EACE,mBAAX3C,GACQ0J,EAASb,IAAI4B,EAAOL,IACf6C,KAAKtM,MAAM,KACtBT,QAAQmM,IACZ,MAAMlM,EAAIgM,GAAUA,EAAOzC,UAAYyC,EAAOzC,SAASsC,KAAK7L,GAAKA,EAAEkM,KAAOA,GAC1ElM,IAAMwC,EAAQ,IAAIA,KAAUxC,MAK9B,MAAMqO,EAAe9L,EAAKgH,SACxBjH,IAAIqJ,GAAOpC,EAASb,IAAIiD,EAAI1B,KAC5BrK,OAAO+J,MACFA,GAAMA,EAAGW,SAAWA,GAGT,yBAAZzK,GAAoD,mBAAdyK,EAAOL,IAEzB,aAAhBN,EAAGnB,WAMXlG,IAAIqH,GAAMA,EAAGM,IAGXoE,EAAatM,OAAS,GACzBf,EAAMsN,SACLhE,EAAOL,GACPK,EAAOiB,KACP8C,EACA7L,KAMHxB,EAAMuN,KAAKlG,EAAQrH,EAAMiJ,KAClBjJ,GCzUQwN,CAAUjP,EAAO8I,EAASvH,GAKxC,OAJIE,IACFX,EAAOS,GAAaE,GAGfA,GG0BOyN,CAASlP,EAAO8I,EAASvH,IAGjCO,UAAEA,EAASqN,iBAAEA,GHzBS1N,KAC5B,MAAOK,EAAWsN,IAAgB,EAAAxK,EAAAC,WAAS,GAe3C,MAAO,CAAE/C,YAAWqN,kBAbK,EAAAvK,EAAAyK,aAAYC,UACnCF,GAAa,GACb,IACE,MAAMG,EAAyB,CAC7BC,UAAW/N,EAAMoK,iBAAmB,UAChC4D,GAAQ,CAAC,SAEThO,EAAMiO,WAAWH,EACzB,CAAC,QACCH,GAAa,EACf,GACC,CAAC3N,MGWoCkO,CAAclO,GAAU,CAAC,IAC3DI,OAAEA,EAAM+N,WAAEA,GHNK,EAACnO,EAAkBF,KACxC,MAAOM,EAAQgO,IAAa,EAAAjL,EAAAC,WAAS,GAqBrC,MAAO,CAAEhD,SAAQ+N,YAnBE,EAAAhL,EAAAyK,aAAYC,UAC7BO,GAAU,GAEV,IACE,MAAMC,QAAiBC,MAAM,gBAAkBpC,mBAAmBpM,GAAY,CAC5EyO,OAAQ,OACRC,KAAMxO,EAAMyO,cAGd,GAAwB,MAApBJ,EAASrI,OAAgB,CAC3B,MAAM0I,SAAgBL,EAASM,QAAQC,OACvC,MAAM,IAAIC,MAAMH,GAAU,yBAAyBL,EAASrI,SAC9D,CACAhG,EAAM8O,UACR,CAAC,QACCV,GAAU,EACZ,GACC,CAACpO,EAAOF,MGdoBiP,CAAQ/O,GAAU,CAAC,EAAiBF,GAEnE,IAAKE,EACH,OAAO,EAAAQ,EAAAI,KAACoO,EAAY,CAACzQ,MAAOA,IAG9B,MAAM0Q,GAAmB,EAAA9L,EAAAyK,aAAY,KACnC5F,GAAgBD,IACf,CAACA,IAEEmH,GAAmB,EAAA/L,EAAAyK,aAAY,KACnC1F,EAAgBpI,IACf,CAACA,IAEEqP,GAA8B,EAAAhM,EAAAyK,aAAY,KACzCF,IAAmB0B,MAAMnJ,GAASO,EAAuB,SAAUP,KACvE,CAACyH,IAEE2B,GAAwB,EAAAlM,EAAAyK,aAAY,KACnCO,IAAaiB,MAAMnJ,GAASO,EAAuB,OAAQP,KAC/D,CAACkI,KAGJ,EAAAhL,EAAAM,WAAU,KACJzD,GAASA,EAAMuK,OACjBpE,SAAS/G,MAAQ,GAAGY,EAAMuK,iBAE3B,CAACvK,KAGJ,EAAAmD,EAAAM,WAAU,KACR,MAAM6L,EAAS7Q,OAAO8Q,YAAY5H,EAAa6H,WACzCC,EAA0B,MAAnBH,EAAa,MAAgC,SAAnBA,EAAa,KAC9CI,EAA0B,MAAnBJ,EAAa,MAAgC,SAAnBA,EAAa,KACpD,IAAKG,IAASC,EAIZ,OAHAvH,EAAcwH,QAAU,KACxBtH,EAAcsH,eACd5J,EAAoB,MAGtB,GAAIkC,IAAiBnI,EACnB,OAGF,MAAMX,EAAM,GAAGW,KAAa6H,EAAaiI,aACzC,GAAIzH,EAAcwH,UAAYxQ,EAC5B,OAEFgJ,EAAcwH,QAAUxQ,EACxB,MAAM0Q,IAAQxH,EAAcsH,QAC5B5J,EAAoB,WAEpB,MAAMgI,GAAauB,EAAkB,WAAK,IAAI3P,cACxCmQ,EAAgC,MAAtBR,EAAgB,SAAmC,SAAtBA,EAAgB,QAGvDS,EAA4B,CAAC,EADQ,CAAC,KAAM,OAAQ,OAAQ,SAE9CC,SAASjC,KAC3BgC,EAAWhC,UAAYA,GAErB+B,IACFC,EAAWE,eAAgB,GAG7B,WACE,IACMR,SACI/B,EAAiBqC,GAErBL,SACIvB,IAEJ9F,EAAcsH,UAAYE,GAC5B9J,EAAoB,WAExB,CAAE,MAAOE,GACP,MAAMiK,EAjHQjK,IACpBA,aAAiB4I,MAAQ5I,EAAMiK,QAAUC,OAAOlK,GAgH1BmK,CAAanK,GAC7BS,QAAQT,MAAM,qBAAsBA,GAChCoC,EAAcsH,UAAYE,GAC5B9J,EAAoB,QAASmK,EAEjC,CACD,EAlBD,IAmBC,CAACpQ,EAAWE,EAAO0N,EAAkBS,EAAYlG,EAAcN,IH/DhC,EAClC0I,EACAC,EACAtQ,EACAM,EACAC,EACAN,MAEA,EAAAkD,EAAAM,WAAU,KACR,MAAM8M,EAAiBtP,IACrB,MAAMuP,GAAW,EAAAC,EAAAC,IAAazP,GAG1BuP,GACFvP,EAAE0P,iBAGa,SAAbH,EACFH,IACsB,SAAbG,EACTF,IACSE,IAAaC,EAAAG,IAAoBrQ,GAAeD,EACzDC,EAAyB,QAAbD,EAAqB,SAAW,OACnCN,IAELwQ,IAAaC,EAAAI,GACf7Q,EAAM4C,kBACG4N,IAAaC,EAAAK,GACtB9Q,EAAM6C,kBACG2N,IAAaC,EAAAM,GACtB/Q,EAAM+C,uBACGyN,IAAaC,EAAAO,GACtBhR,EAAMgD,uBACGwN,IAAaC,EAAAQ,IAAehR,EACrCA,IACSuQ,IAAaC,EAAAS,GACtBlR,EAAMmR,YACGX,IAAaC,EAAAW,GACtBpR,EAAM8D,aACG0M,IAAaC,EAAAY,GACtBrR,EAAM+D,mBACGyM,IAAaC,EAAAa,GACtBtR,EAAMgE,gBACGwM,IAAaC,EAAAc,GACtBvR,EAAMwR,cAAcxR,EAAMyR,cAAe,GAChCjB,IAAaC,EAAAiB,GACtB1R,EAAMwR,cAAc,EAAG,GAAG,GACjBhB,IAAaC,EAAAkB,GACtB3R,EAAMwR,aAAaxR,EAAMyR,cAAe,GAC/BjB,IAAaC,EAAAmB,GACtB5R,EAAMwR,aAAa,EAAG,GAAG,GAChBhB,IAAaC,EAAAoB,GACtB7R,EAAMwR,aAAa,GAAIxR,EAAMyR,eACpBjB,IAAaC,EAAAqB,GACtB9R,EAAMwR,aAAa,GAAI,GAAG,GACjBhB,IAAaC,EAAAsB,GACtB/R,EAAMwR,aAAa,EAAGxR,EAAMyR,eACnBjB,IAAaC,EAAAuB,IACtBhS,EAAMwR,aAAa,EAAG,GAAG,KAM/B,OADA7N,OAAOC,iBAAiB,UAAW2M,GAC5B,IAAM5M,OAAOE,oBAAoB,UAAW0M,IAClD,CAACF,EAAYC,EAAYtQ,EAAOM,EAAUC,EAAaN,KGC1DgS,CACEhD,EACAI,EACArP,EACAM,EACAC,EACA4O,GAGF,MAAM+C,GAAmB,EAAA/O,EAAAyK,aAAa3E,IACpCrB,EAAgB,CAAEqB,GAAIiD,mBAAmBjD,MACxC,CAACrB,IAEEuK,GAAe,EAAAhP,EAAAyK,aAAa3E,IAChC,GAAIA,EAAI,CACN,MAAM9B,EAAUnH,EAAMsK,SAAS/B,SAASsC,KAAMuH,GAAWA,EAAEnJ,KAAOA,GAClEvC,QAAQ2L,KF5KmBC,EE4KEnL,EF3K1BoL,KAAKC,MAAMD,KAAKE,UAAUH,KE4K/B,CF7KG,IAA0BA,GE8K5B,CAACtS,IAEL,OACC,EAAAQ,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,EACC,EAAAH,EAAAI,KAACf,EAAO,CACPtB,MAAOA,EACPuB,UAAWA,EACXC,aAAcmS,EACdlS,MAAOA,EACPC,aAAckP,EACdjP,OAAQmP,EACRlP,aAAc8O,EACd7O,OAAQA,EACRC,UAAWA,EACXC,SAAUA,EACVC,YAAaA,KAEd,EAAAC,EAAAI,KAACuC,EAAAuP,SAAQ,CAACC,UAAU,EAAAnS,EAAAI,KAAA,OAAAD,SAAK,qBAAuBA,UAC/C,EAAAH,EAAAI,KAACkF,EAAK,CAEL8M,KAAM5S,EACN6S,SAAUV,EACVW,QAAS5D,EACT5O,SAAUA,GAJLR,KAONiI,IACA,EAAAvH,EAAAI,KAACuC,EAAAuP,SAAQ,CAACC,UAAU,EAAAnS,EAAAI,KAAA,OAAAD,SAAK,oBAAsBA,UAC9C,EAAAH,EAAAI,KAACyE,EAAI,UAOJ2J,EAAmC,EAAGzQ,YAC1C,MAAMI,EAAQL,EAAUC,GAWxB,OATA4E,EAAAM,UAAgB,KAEd0C,SAAS/G,MAAQ,wCAEbT,EAAMoC,OAAS,IACjBoF,SAASqB,SAAS2E,KAAO,OAASxN,EAAM,GAAGQ,MAE5C,CAACR,IAEAA,EAAMoC,OAAS,GACV,EAAAP,EAAAC,MAAAD,EAAAiC,SAAA,CAAA9B,SAAA,CAAE,kBAAgBhC,EAAM,GAAGS,UAE7B,EAAAoB,EAAAI,KAAAJ,EAAAiC,SAAA,CAAA9B,SAAE,4GClOJ,MCkDMoS,EAAc,CAACpE,EAAcqE,EAAeC,KACxD,MAAMjB,EAnDa,MACnB,MAAMkB,EAAM/M,SAASgN,gBAAgB,6BAA8B,OAGnE,OAFAhN,SAASqI,KAAK4E,YAAYF,GAEnB,CACNG,QAAS,CAAC1E,EAAcsE,KACvB,MAAMK,EAAOnN,SAASgN,gBAAgB,6BAA8B,QACpEG,EAAKC,aAAa,IAAK,KACvBD,EAAKC,aAAa,IAAK,KACvB,IAAK,IAAIC,KAAQP,EAChBK,EAAKC,aAAaC,EAAMP,EAAMO,IAE/BF,EAAKF,YAAYjN,SAASsN,eAAe9E,IAEzCuE,EAAIE,YAAYE,GAChB,MAAMN,MAACA,EAAKU,OAAEA,GAAUJ,EAAKK,UAE7B,OADAT,EAAIU,YAAYN,GACT,CAACN,QAAOU,WAEhBG,MAAO,KACN1N,SAASqI,KAAKoF,YAAYV,MA+BjBY,GACX,IAAIC,EAAO,EAEX,MAAMC,EAAMrF,EAAKC,OAAOpP,MAAM,MAAM8B,IAAIqN,IAEvC,MAAMsF,EAAQtF,EAAKC,OAAOpP,MAAM,OAChC,IAAI0U,EAAkB,GAClBC,EAAwB,GAuC5B,OArCAF,EAAMlV,QAAQqV,IAGb,GADiBpC,EAAGqB,QAAQe,EAAMnB,GACrBD,MAAQA,EAAO,CAEvBmB,EAAYpT,OAAS,IACxBmT,EAAMhV,KAAKiV,EAAYE,KAAK,MAC5BF,EAAc,IAGf,MAAMG,EA5CY,EAACF,EAAcG,EAAkBtB,EAAkCjB,KACxF,MAAMwC,EAAkB,GACxB,IAAIC,EAAc,GAElB,IAAK,IAAIxH,EAAI,EAAGA,EAAImH,EAAKrT,OAAQkM,IAAK,CACrC,MAAMyH,EAAWD,EAAcL,EAAKnH,GACvB+E,EAAGqB,QAAQqB,EAAUzB,GAEzBD,MAAQuB,GAAYE,EAAY1T,OAAS,GACjDyT,EAAMtV,KAAKuV,GACXA,EAAcL,EAAKnH,IAEnBwH,EAAcC,CAEhB,CAMA,OAJID,EAAY1T,OAAS,GACxByT,EAAMtV,KAAKuV,GAGLD,GAwBgBG,CAAcP,EAAMpB,EAAOC,EAAOjB,GAEtD,IAAK,IAAI/E,EAAI,EAAGA,EAAIqH,EAAYvT,OAAS,EAAGkM,IAC3CiH,EAAMhV,KAAKoV,EAAYrH,IACvB8G,EAAOzP,KAAKQ,IAAIiP,EAAM/B,EAAGqB,QAAQiB,EAAYrH,GAAIgG,GAAOD,OAGrDsB,EAAYvT,OAAS,IACxBoT,EAAc,CAACG,EAAYA,EAAYvT,OAAS,IAElD,KAAO,CAEN,MAAM6T,EAAU,IAAIT,EAAaC,GAC3BS,EAAO7C,EAAGqB,QAAQuB,EAAQP,KAAK,KAAMpB,GACvC4B,EAAK7B,MAAQA,GAASmB,EAAYpT,OAAS,GAC9CmT,EAAMhV,KAAKiV,EAAYE,KAAK,MAC5BF,EAAc,CAACC,KAEfL,EAAOzP,KAAKQ,IAAIiP,EAAMc,EAAK7B,OAC3BmB,EAAcS,EAEhB,IAGGT,EAAYpT,OAAS,GACxBmT,EAAMhV,KAAKiV,EAAYE,KAAK,MAEtBH,IACLY,OAAO,CAAC3H,EAAGlO,IAAMkO,EAAE4H,OAAO9V,GAAI,IAGjC,OADA+S,EAAG6B,QACI,CAACK,MAAOF,EAAKD,SCnGRiB,EAAS,CACrB,OAAA7N,CAAQ8N,EAAchC,EAAyC,CAAC,EAAGvS,GAClE,MAAMiI,EAAKxC,SAASgN,gBAAgB,6BAA8B8B,GAGlE,OAFAxW,OAAO+Q,QAAQyD,GAAOlU,QAAQ,EAAEmW,EAAGjW,KAAO0J,EAAG4K,aAAa2B,EAAG/E,OAAOlR,KAChEyB,GAAWiI,EAAGwM,UAAUC,IAAI1U,GACzBiI,CACR,EAEA,GAAA0M,CAAIpM,EAAYgK,EAAyC,CAAC,GACzD,MAAMtK,EAAK2M,KAAKnO,QAAQ,MAAO8L,GAE/B,OADAtK,EAAG4M,eAAe,+BAAgC,aAAc,IAAMtM,GAC/DN,CACR,EAEA,IAAAzB,CAAKA,EAAc+L,EAAyC,CAAC,EAAGvS,GAE/D,OADU4U,KAAKnO,QAAQ,OAAQ,IAAI8L,EAAOuC,EAAGtO,GAAOxG,EAErD,EAEA,IAAAiO,CAAKA,EAAcsE,EAAyC,CAAC,GAC5D,MAAMwC,EAAIH,KAAKnO,QAAQ,OAAQ8L,GAE/B,OADItE,IAAM8G,EAAEC,YAAc/G,GACnB8G,CACR,EAEA,QAAAE,CAAShH,EAAcqE,EAAe4C,EAAkBC,EAAeC,EAAI,EAAGC,EAAI,EAAGC,EAAS,IAC7F,MAAM/C,EAAgC,CACrC,YAAa,GAAG2C,MAChB,cAAeC,EAAO,OAAS,WAE1B3B,MAACA,EAAKH,KAAEA,GAAQhB,EAAYpE,EAAMqE,EAAOC,GACzCgD,EAAMX,KAAK3G,KAAK,GAAI,CAACmH,EAAG,EAAGC,IAAG,cAAeC,QAAU3L,IAQ7D,OANA6J,EAAMnV,QAAQ,CAACmX,EAAMjJ,KACpB,MAAMkJ,EAAOb,KAAKnO,QAAQ,QAAS,CAAC2O,IAAGM,GAAI,GAAGR,EAAW,SAAU3C,IACnEkD,EAAKT,YAAcQ,EACnBD,EAAII,OAAOF,KAGL,CAACF,MAAKG,IAAKlC,EAAMnT,OAAS,IAAM6U,EAAW,GAAI7B,OACvD,EAEA,IAAAuC,CAAKtD,EAAeU,EAAgBoC,EAAI,EAAGC,EAAI,EAAGQ,EAAI,EAAG7V,GACxD,OAAO4U,KAAKnO,QAAQ,OAAQ,CAAC2O,IAAGC,IAAGS,GAAID,EAAGE,GAAIF,EAAGvD,QAAOU,UAAShT,EAClE,EAEA,IAAAgW,CAAKA,EAAcZ,EAAI,EAAGC,EAAI,GAC7B,OAAOT,KAAKD,IAAIqB,EAAM,CAACZ,IAAGC,KAC3B,EAEA,MAAAY,CAAOb,EAAWC,EAAWa,GAC5B,MAAMC,EAAIvB,KAAKnO,QAAQ,IAAK,CAACrE,UAAW,aAAagT,KAAKC,MAAO,UAKjE,OAJAc,EAAER,OACDf,KAAKgB,KAAK,GAAI,GAAI,EAAG,EAAG,GACxBhB,KAAK3G,KAAKiI,EAAW,IAAM,IAAK,CAACd,EAAG,GAAIC,EAAG,GAAI,cAAe,YAExDc,CACR,GAGM,SAASC,EAAYD,EAAgBf,EAAWC,GACtDc,EAAEtD,aAAa,YAAa,aAAauC,KAAKC,KAC/C,CCrDO,SAASgB,EAAUjM,EAAUsC,EAAS4J,GAAc,GAC1D,OAAOA,EACLlM,EAAEgL,EAAI1I,EAAE0I,EAAI1I,EAAE4F,MAAQ,GAAKlI,EAAEgL,EAAI1I,EAAE0I,EAAI1I,EAAE4F,MAAQ,GAAKlI,EAAEiL,EAAI3I,EAAE2I,EAAI3I,EAAEsG,OAAS,GAAK5I,EAAEiL,EAAI3I,EAAE2I,EAAI3I,EAAEsG,OAAS,EACzG5I,EAAEgL,EAAI1I,EAAE0I,GAAKhL,EAAEgL,EAAI1I,EAAE0I,EAAI1I,EAAE4F,OAASlI,EAAEiL,EAAI3I,EAAE2I,GAAKjL,EAAEiL,EAAI3I,EAAE2I,EAAI3I,EAAEsG,MAClE,CAMO,SAASuD,EAAY7J,GAC3B,MAAO,CAAC0I,EAAG1I,EAAE0I,EAAI1I,EAAE4F,MAAQ,EAAG+C,EAAG3I,EAAE2I,EAAI3I,EAAEsG,OAAS,EAAGV,MAAO5F,EAAE4F,MAAOU,OAAQtG,EAAEsG,OAChF,CA6CO,SAASwD,EAAkBC,EAAWC,EAAWC,GACvD,MAAMC,EAAID,EAAIrE,MAAQ,EAChBuE,EAAIF,EAAI3D,OAAS,EAOvB,MANuC,CACtC,CAAC5I,EAAG,CAACgL,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,GAAIC,EAAG,CAAC1B,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,IAC/D,CAACzM,EAAG,CAACgL,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,GAAIC,EAAG,CAAC1B,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,IAC/D,CAACzM,EAAG,CAACgL,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,GAAIC,EAAG,CAAC1B,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,IAC/D,CAACzM,EAAG,CAACgL,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,GAAIC,EAAG,CAAC1B,EAAGuB,EAAIvB,EAAIwB,EAAGvB,EAAGsB,EAAItB,EAAIwB,KAEpDjW,IAAItC,GA7CjB,SAA6BmY,EAAWM,EAAWL,EAAWM,GAC7D,IAAIC,EAAaxK,EAAGC,EAAGwK,EAAYC,EAClCC,EAAuE,CACtEhC,EAAG,KACHC,EAAG,KACHgC,SAAS,EACTC,SAAS,GAGX,OADAL,GAAeD,EAAG3B,EAAIqB,EAAGrB,IAAM0B,EAAG3B,EAAIqB,EAAGrB,IAAM4B,EAAG5B,EAAIsB,EAAGtB,IAAM2B,EAAG1B,EAAIoB,EAAGpB,GACtD,GAAf4B,IAGJxK,EAAIgK,EAAGpB,EAAIqB,EAAGrB,EACd3I,EAAI+J,EAAGrB,EAAIsB,EAAGtB,EACd8B,GAAeF,EAAG5B,EAAIsB,EAAGtB,GAAK3I,GAAOuK,EAAG3B,EAAIqB,EAAGrB,GAAK3I,EACpDyK,GAAeJ,EAAG3B,EAAIqB,EAAGrB,GAAK3I,GAAOsK,EAAG1B,EAAIoB,EAAGpB,GAAK3I,EACpDD,EAAIyK,EAAaD,EACjBvK,EAAIyK,EAAaF,EAGjBG,EAAOhC,EAAIqB,EAAGrB,EAAK3I,GAAKsK,EAAG3B,EAAIqB,EAAGrB,GAClCgC,EAAO/B,EAAIoB,EAAGpB,EAAK5I,GAAKsK,EAAG1B,EAAIoB,EAAGpB,GAG9B5I,EAAI,GAAKA,EAAI,IAChB2K,EAAOC,SAAU,GAGd3K,GAAK,GAAKA,GAAK,IAClB0K,EAAOE,SAAU,IAnBVF,CAuBT,CAYsBG,CAAoBd,EAAIC,EAAIpY,EAAE8L,EAAG9L,EAAEwY,IAAI5Y,OAAOoV,GAAOA,EAAI+D,SAAW/D,EAAIgE,QAC9F,CAGO,SAASE,EAAcb,EAAWvM,GACxC,OAAIiM,EAAUjM,EAAGuM,GAAa,CAACvB,EAAGuB,EAAIvB,EAAGC,EAAGsB,EAAItB,GACzCmB,EAAkBG,EAAKvM,EAAGuM,GAAK,IAAM,CAACvB,EAAGuB,EAAIvB,EAAGC,EAAGsB,EAAItB,EAC/D,CAEO,SAASoC,EAAiBC,EAAkB5B,EAAYC,EAAY4B,EAAmBC,GAG7F,MAAMnB,EAAK,CAACrB,EAAGwC,EAAMxC,EAAIsC,EAAUtC,EAAGC,EAAGuC,EAAMvC,EAAIqC,EAAUrC,GACvDqB,EAASiB,EAAWvC,EAAIsC,EAAUtC,EAAlCsB,EAAwCiB,EAAWtC,EAAIqC,EAAUrC,EAEnEqB,GAAQD,EAAGrB,IACdqB,EAAGrB,GAAK,MAGT,MAAM9W,GAAKoY,EAAOD,EAAGpB,IAAMqB,EAAOD,EAAGrB,GAC/ByC,EAAKnB,EAAQpY,EAAIoY,EACjBjK,EAAKsJ,EAAKA,EAAOD,EAAKA,EAAKxX,EAAIA,EAC/BoO,EAAI,EAAIoJ,EAAKA,EAAK+B,EAAKvZ,EACvBwZ,EAAIhC,EAAKA,EAAK+B,EAAKA,EAAK/B,EAAKA,EAAKC,EAAKA,EAEvCgC,EAAgBnU,KAAKoU,KAAMtL,EAAIA,EAAM,EAAID,EAAIqL,GAC7C1C,EAAIqB,EAAGrB,EAAIsB,IACdhK,EAAIqL,IAAkB,EAAItL,KAC1BC,EAAIqL,IAAkB,EAAItL,GACvBwL,EAAM,CACX7C,EAAGA,EACHC,EAAG/W,EAAI8W,EAAIyC,GAMZ,OAHAI,EAAI7C,GAAKsC,EAAUtC,EACnB6C,EAAI5C,GAAKqC,EAAUrC,EAEZ4C,CACR,CAuCO,SAASC,EAAQ9N,EAAUqC,EAAUC,GAC3C,IAAIyL,EAAWzL,EAAE0I,EAAI3I,EAAE2I,EAAnB+C,EAAyBzL,EAAE2I,EAAI5I,EAAE4I,EAEjC+C,EAAMD,EAASA,EAASA,EAASA,EACjCE,GAFWjO,EAAEgL,EAAI3I,EAAE2I,GAEJ+C,GAFU/N,EAAEiL,EAAI5I,EAAE4I,GAEA8C,EACjCpD,EAAInR,KAAKS,IAAI,EAAGT,KAAKQ,IAAI,EAAGiU,EAAMD,IACtC,MAAO,CACNhD,EAAG3I,EAAE2I,EAAI+C,EAASpD,EAClBM,EAAG5I,EAAE4I,EAAI8C,EAASpD,EAEpB,CAEO,SAASuD,EAAY7B,EAAWC,GACtC,OAAO9S,KAAK2U,IAAI7B,EAAGtB,EAAIqB,EAAGrB,GAAKxR,KAAK2U,IAAI7B,EAAGrB,EAAIoB,EAAGpB,EACnD,CCxJA,SAASmD,EAAgBlG,GACxB,OAAOA,EAAQ,GAAK,IAAMA,EAAQ,GACnC,CAEO,SAASmG,EAAkBC,EAAepG,EAAeU,GAC/D,OAAQ0F,EAAMjO,eACb,IAAK,WACJ,OAAO,EAAI+N,EAAgBlG,GAC5B,IAAK,SACJ,MAAgB,GAATU,EACR,IAAK,SACJ,OAAOV,EAAQ,GAChB,IAAK,QACJ,MAAgB,IAATU,EACR,IAAK,aACJ,OAAOA,EAAS,EACjB,QACC,OAAO,EAEV,CAEA,MAAM2F,EACYC,IAEjB,WAAAC,CAAY5Q,GACX2M,KAAKgE,IAAM3Q,CACZ,CAEA,IAAA2K,GACC,OAAOgC,KAAKgE,GACb,CAEA,IAAA9F,CAAKjJ,EAAcpJ,GAElB,OADAmU,KAAKgE,IAAI/F,aAAahJ,EAAM4F,OAAOhP,IAC5BmU,IACR,CAEA,MAAAkE,CAAOvE,EAAc0D,GACpB,MAAMhQ,EAAKxC,SAASgN,gBAAgB,6BAA8B8B,GAC5DzL,EAAM8L,KAAKgE,IAAIG,aAAa9Q,EAAI2M,KAAKgE,IAAII,cAAcf,IAC7D,OAAO,IAAIU,EAAU7P,EACtB,EAID,SAAS8M,EAAKhN,EAAmBqQ,EAAYrG,EAAcsG,GAAU,GACpE,MAAMC,EAAWvQ,EAAOkQ,OAAO,OAAQ,gBACrChG,KAAK,KAAMoG,EAAUtG,EAAKN,MAAQ,EAAI,GACtCQ,KAAK,KAAMoG,EAAUtG,EAAKN,MAAQ,EAAI,GACtCQ,KAAK,KAAMmG,EAAK3G,MAAQ,GACxBQ,KAAK,KAAMmG,EAAKjG,OAAS,GACzBF,KAAK,QAASmG,EAAK3G,OACnBQ,KAAK,SAAUmG,EAAKjG,QAMtB,OAJAJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc5E,EAAMgF,EAC5B,EAEOuB,CACR,CA0DA,SAASE,EAASzQ,EAAmBqQ,EAAYrG,EAAckD,EAAYC,GAC1E,MAAMoD,EAAWvQ,EAAOkQ,OAAO,UAAW,gBACxChG,KAAK,KAAM,GACXA,KAAK,KAAM,GACXA,KAAK,KAAMgD,GACXhD,KAAK,KAAMiD,GACXjD,KAAK,QAASF,EAAKN,OACnBQ,KAAK,SAAUF,EAAKI,QAKtB,OAHAJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOH,EAAiB7E,EAAMkD,EAAIC,EAAInD,EAAMgF,EAC7C,EACOuB,CACR,CA0GA,SAASG,EAAqB1Q,EAAmBqQ,EAAYrG,GAC5D,MAAM8C,EAAK9C,EAAKN,MAAQ,EAClBuD,EAAIjD,EAAKN,MAAQ,GACjB6G,EAAWvQ,EAAOkQ,OAAO,IAAK,gBAwBpC,OAvBAK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,IAAK,KAAKF,EAAKN,MAAQ,MAAMM,EAAKI,OAAS,MAAMJ,EAAKN,aAAaM,EAAKN,MAAQ,KAAKM,EAAKI,OAAS,MAAMJ,EAAKN,WACrH6G,EAASL,OAAO,SAAU,gBACxBhG,KAAK,KAAM,GACXA,KAAK,KAAMF,EAAKI,OAAS,EAAI0C,EAAK,GAClC5C,KAAK,IAAS,GAAJ+C,GACZsD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM+C,GACX/C,KAAK,KAAMF,EAAKI,OAAS,EAAI0C,EAAK,EAAQ,GAAJG,GACtC/C,KAAK,QAAa,EAAJ+C,GACd/C,KAAK,SAAc,GAAJ+C,GACjBsD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM+C,GACX/C,KAAK,KAAM+C,GACX/C,KAAK,KAAMmG,EAAK3G,MAAQ,GACxBQ,KAAK,KAAMmG,EAAKjG,OAAS,EAAI0C,GAC7B5C,KAAK,QAASmG,EAAK3G,OACnBQ,KAAK,SAAUmG,EAAKjG,OAAS,EAAI0C,GAEnC9C,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc,CAACpC,EAAGxC,EAAKwC,EAAGC,EAAGzC,EAAKyC,EAAG/C,MAAOM,EAAKN,MAAOU,OAAQJ,EAAKI,OAAS,EAAI0C,GAAKkC,EAC/F,EAEOuB,CACR,CAmKO,MAAMI,EAA8E,CAC1F5C,IAAK,CAAC/N,EAAoBgK,IAAiBgD,EAAK,IAAI+C,EAAU/P,GAASgK,EAAMA,GAAMA,OACnF4G,WAAY,CAAC5Q,EAAoBgK,IAAiBgD,EAAK,IAAI+C,EAAU/P,GAASgK,EAAMA,GAAM,GAAMA,OAChG6G,UAAW,CAAC7Q,EAAoBgK,IAlRjC,SAAmBhK,EAAmBqQ,EAAYrG,GACjD,MAAM8G,EAAK9G,EAAKN,MAAQ,GAClB6G,EAAWvQ,EAAOkQ,OAAO,IAAK,gBAwBpC,OAvBAK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAMF,EAAKN,MAAQ,EAAIoH,GAC5B5G,KAAK,KAAMF,EAAKI,OAAS,EAAI0G,GAC7B5G,KAAK,QAAc,EAAL4G,GACd5G,KAAK,SAAU4G,GACjBP,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAMF,EAAKN,MAAQ,EAAIoH,GAC5B5G,KAAK,KAAMF,EAAKI,OAAS,EAAS,IAAL0G,GAC7B5G,KAAK,QAAc,EAAL4G,GACd5G,KAAK,SAAU4G,GACjBP,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAMF,EAAKN,MAAQ,GACxBQ,KAAK,KAAMF,EAAKI,OAAS,GACzBF,KAAK,QAASF,EAAKN,OACnBQ,KAAK,SAAUF,EAAKI,QAEtBJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc,CAACpC,EAAGxC,EAAKwC,EAAIsE,EAAK,EAAGrE,EAAGzC,EAAKyC,EAAG/C,MAAOM,EAAKN,MAAQoH,EAAI1G,OAAQJ,EAAKI,QAAS4E,EACpG,EAEOuB,CACR,CAuPkDM,CAAU,IAAId,EAAU/P,GAASgK,EAAMA,GAAMA,OAC9F+G,SAAU,CAAC/Q,EAAoBgK,IAjXhC,SAAkBhK,EAAmBqQ,EAAYrG,GAChD,MAAMgE,EAAIqC,EAAK3G,MACTwD,EAAKc,EAAI,EACTb,EAAKyC,EAAgB5B,GACrBC,EAAIoC,EAAKjG,OAET0F,EACL,OAAO3C,MAAOD,KAAMC,WAAYa,SAASd,KAAMC,YAAaa,WAAWC,EAAI,EAAId,OAAQD,KAAMC,WAAYa,WAAgB,EAAIb,EAARc,IAEhHsC,EAAWvQ,EACfkK,KAAK,iBAAkB2F,EAAkB,WAAY7B,EAAGC,IACxDiC,OAAO,OAAQ,gBACfhG,KAAK,IAAK4F,GACV5F,KAAK,YAAa,cAAgB8D,EAAI,EAAI,KAAQC,EAAI,EAAK,KAe7D,OAbAjE,EAAKwG,UAAY,SAAUxB,GAC1B,MAAMK,EAAMT,EAAc5E,EAAMgF,GAChC,IAAIgC,EAAKhH,EAAKyC,EAAIzC,EAAKI,OAAS,EAAI+C,EACpC,OAAIkC,EAAI5C,EAAIuE,EACJnC,EAAiB,CAACrC,EAAGxC,EAAKwC,EAAGC,EAAGuE,GAAK9D,EAAIC,EAAInD,EAAMgF,IAE3DgC,EAAKhH,EAAKyC,EAAIzC,EAAKI,OAAS,EAAI+C,EAC5BkC,EAAI5C,EAAIuE,EACJnC,EAAiB,CAACrC,EAAGxC,EAAKwC,EAAGC,EAAGuE,GAAK9D,EAAIC,EAAInD,EAAMgF,GAEpDK,EACR,EAEOkB,CACR,CAoViDQ,CAAS,IAAIhB,EAAU/P,GAASgK,EAAMA,GAAMA,OAC5FiH,OAAQ,CAACjR,EAAoBgK,IAnV9B,SAAgBhK,EAAmBqQ,EAAYrG,GAC9C,MAAMgE,EAAIqC,EAAK3G,MACTuE,EAAIoC,EAAKjG,OAET0F,EACL,KAAK,IAAM9B,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,EAAWvQ,EACfkK,KAAK,iBAAkB2F,EAAkB,SAAU7B,EAAGC,IACtDiC,OAAO,OAAQ,gBACfhG,KAAK,IAAK4F,GACV5F,KAAK,YAAa,cAAgB8D,EAAI,EAAI,KAAQC,EAAI,EAAK,KAO7D,OALAjE,EAAKwG,UAAY,SAAUxB,GAE1B,OADYJ,EAAc5E,EAAMgF,EAEjC,EAEOuB,CACR,CA6T+CU,CAAO,IAAIlB,EAAU/P,GAASgK,EAAMA,GAAMA,OACxFkH,OAAQ,CAAClR,EAAoBgK,IA7S9B,SAAgBhK,EAAmBqQ,EAAYrG,GAC9C,OAAOyG,EAASzQ,EAAQqQ,EAAMrG,EAAMA,EAAKN,MAAQ,EAAGM,EAAKN,MAAQ,EAClE,CA2S+CwH,CAAO,IAAInB,EAAU/P,GAASgK,EAAMA,GAAMA,OACxFmH,QAAS,CAACnR,EAAoBgK,IA1S/B,SAAiBhK,EAAmBqQ,EAAYrG,GAC/C,OAAOyG,EAASzQ,EAAQqQ,EAAMrG,EAAmB,IAAbA,EAAKN,MAA0B,IAAbM,EAAKN,MAC5D,CAwSgDyH,CAAQ,IAAIpB,EAAU/P,GAASgK,EAAMA,GAAMA,OAC1FoH,QAAS,CAACpR,EAAoBgK,IAvS/B,SAAiBhK,EAAmBqQ,EAAYrG,GAC/C,MAAMqH,EAAKrH,EAAKN,MAAQ,EAGlB6G,EAAWvQ,EAAOkQ,OAAO,UAAW,gBACxChG,KAAK,SACL,CAAC,GAAQ,KAAQ,EAAQ,EAAQ,IAAS,MAAS,IAAS,MAAS,GAAS,GAAS,GAAQ,KAAQ,GAAQ,MAAQlS,IAAIsZ,GAAKA,EAAID,GAAItG,KAAK,MAC7Ib,KAAK,QAASF,EAAKN,OACnBQ,KAAK,SAAUF,EAAKI,QAKtB,OAHAJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOH,EAAiB7E,EAAMA,EAAKN,MAAQ,EAAGM,EAAKN,MAAQ,EAAGM,EAAMgF,EACrE,EACOuB,CACR,CAyRgDa,CAAQ,IAAIrB,EAAU/P,GAASgK,EAAMA,GAAMA,OAC1FuH,OAAQ,CAACvR,EAAoBgK,IA3P9B,SAAgBhK,EAAmBqQ,EAAYrG,GAC9C,MAAM8C,EAAK9C,EAAKN,MAAQ,GAClB6G,EAAWvQ,EACfkK,KAAK,iBAAkB2F,EAAkB,SAAU7F,EAAKN,MAAOM,EAAKI,SACpE8F,OAAO,IAAK,gBAcd,OAbAK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAMF,EAAKN,MAAQ,GACxBQ,KAAK,KAAMF,EAAKI,OAAS,EAAS,EAAL0C,GAC7B5C,KAAK,QAASF,EAAKN,OACnBQ,KAAK,SAAUF,EAAKI,OAAc,EAAL0C,GAC/ByD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,IAAK,OAAOF,EAAKI,OAAS,EAAI,EAAI0C,MAAOA,MAAO,EAAIA,MAAO9C,EAAKN,MAAQ,EAAS,EAALoD,MAAgB,EAALA,KAE9F9C,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc,CAACpC,EAAGxC,EAAKwC,EAAGC,EAAGzC,EAAKyC,EAAIK,EAAK,EAAGpD,MAAOM,EAAKN,MAAOU,OAAQJ,EAAKI,OAAS0C,GAAKkC,EACpG,EAEOuB,CACR,CAwO+CgB,CAAO,IAAIxB,EAAU/P,GAASgK,EAAMA,GAAMA,OACxFwH,sBAAuB,CAACxR,EAAoBgK,IAvO7C,SAA+BhK,EAAmBqQ,EAAYrG,GAC7D,MAAM8G,EAAK9G,EAAKN,MAAQ,EAClBuD,EAAIjD,EAAKN,MAAQ,GACjB6G,EAAWvQ,EAAOkQ,OAAO,IAAK,gBAwBpC,OAvBAK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,IAAK,KAAKF,EAAKN,MAAQ,MAAMM,EAAKI,OAAS,QAAQJ,EAAKI,WAAWJ,EAAKN,MAAQ,MAAMM,EAAKI,OAAS,QAAQJ,EAAKI,UACxHmG,EAASL,OAAO,SAAU,gBACxBhG,KAAK,MAAOF,EAAKN,MAAQ,EAAIoH,EAAK,GAClC5G,KAAK,KAAM,GACXA,KAAK,IAAS,GAAJ+C,GACZsD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,IAAKF,EAAKN,MAAQ,EAAIoH,EAAK,EAAQ,GAAJ7D,GACpC/C,KAAK,KAAM+C,GACX/C,KAAK,QAAa,GAAJ+C,GACd/C,KAAK,SAAc,EAAJ+C,GACjBsD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM+C,GACX/C,KAAK,KAAM+C,GACX/C,KAAK,KAAMmG,EAAK3G,MAAQ,EAAIoH,GAC5B5G,KAAK,KAAMmG,EAAKjG,OAAS,GACzBF,KAAK,QAASmG,EAAK3G,MAAQ,EAAIoH,GAC/B5G,KAAK,SAAUmG,EAAKjG,QAEtBJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc,CAACpC,EAAGxC,EAAKwC,EAAGC,EAAGzC,EAAKyC,EAAG/C,MAAOM,EAAKN,MAAQ,EAAIoH,EAAI1G,OAAQJ,EAAKI,QAAS4E,EAC/F,EAEOuB,CACR,CA2M8DiB,CAAsB,IAAIzB,EAAU/P,GAASgK,EAAMA,GAAMA,OACtH0G,qBAAsB,CAAC1Q,EAAoBgK,IAAiB0G,EAAqB,IAAIX,EAAU/P,GAASgK,EAAMA,GAAMA,OACpHyH,aAAc,CAACzR,EAAoBgK,IAAiB0G,EAAqB,IAAIX,EAAU/P,GAASgK,EAAMA,GAAMA,OAC5G0H,KAAM,CAAC1R,EAAoBgK,IA9K5B,SAAchK,EAAmBqQ,EAAYrG,GAC5C,MAAMgE,EAAIhE,EAAKN,MACTuE,EAAIjE,EAAKI,OACT+C,EAAKc,EAAI,EACTf,EAAKC,GAAM,IAAMa,EAAI,IAErB8B,EACL,KAAK5C,aACFA,KAAMC,aAAcc,WACpBf,KAAMC,cAAec,WACrBD,aACAd,KAAMC,aAAcc,YACnBD,MAECuC,EAAWvQ,EACfkQ,OAAO,OAAQ,gBACfhG,KAAK,IAAK4F,GACV5F,KAAK,YAAa,cAAgB8D,EAAI,EAAI,KAAQC,EAAI,EAAK,KAM7D,OAJAjE,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc,CAACpC,EAAGxC,EAAKwC,EAAIU,EAAIT,EAAGzC,EAAKyC,EAAG/C,MAAOM,EAAKN,MAAQ,EAAIwD,EAAI9C,OAAQJ,EAAKI,QAAS4E,EACpG,EAEOuB,CACR,CAsJ6CmB,CAAK,IAAI3B,EAAU/P,GAASgK,EAAMA,GAAMA,OACpF2H,MAAO,CAAC3R,EAAoBgK,IArJ7B,SAAehK,EAAmBqQ,EAAYrG,GAC7C,MAAMgE,EAAIhE,EAAKN,MACTuE,EAAIjE,EAAKI,OAGTwH,EAAW5W,KAAKS,IAAQ,IAAJuS,EAAc,IAAJC,GAC9B4D,EAAmB,GAAXD,EACRE,EAAsB,IAAXF,EACXG,EAAsB,IAAXH,EAGXI,EAAkB,IAAXJ,EACPK,EAAwB,IAAXL,EAGbM,EAAkB,IAAXN,EACPO,EAAkB,GAAXP,EAGPQ,EAAQpE,EACRqE,GAAWpE,EAAI,EAAI6D,EAAWF,EAC9BU,EAAQrE,EAAI6D,EAAWF,EAGvBrB,EAAWvQ,EACfkK,KAAK,iBAAkB2F,EAAkB,QAAS7B,EAAGC,IACrDiC,OAAO,IAAK,gBAGdK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KARO,GASZA,KAAK,KATO,GAUZA,KAAK,KAAMkI,EAAQ,GACnBlI,KAAK,IAAKmI,GACVnI,KAAK,QAASkI,GACdlI,KAAK,SAAUoI,GAGjB,MAAMC,GAAWtE,EAAI,EAAI6D,EACzBvB,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM2H,GACX3H,KAAK,KAAM2H,GACX3H,KAAK,KAAM0H,EAAW,GACtB1H,KAAK,IAAKqI,GACVrI,KAAK,QAAS0H,GACd1H,KAAK,SAAU0H,GAGjBrB,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,QAAS,iBACdA,KAAK,KAAM,GACXA,KAAK,KAAMqI,GACXrI,KAAK,KAAM,GACXA,KAAK,MAAO+D,EAAI,EAAe,EAAX8D,GACpB7H,KAAK,eAA2B,GAAX6H,GACrB7H,KAAK,iBAAkB,SAGzBqG,EAASL,OAAO,SAAU,gBACxBhG,KAAK,QAAS,sBACdA,KAAK,KAAM,GACXA,KAAK,MAAO+D,EAAI,EAAe,EAAX8D,GACpB7H,KAAK,IAAgB,IAAX6H,GAGZ,MAAMS,EAAOD,EAAqB,GAAXX,EACvBrB,EAASL,OAAO,SAAU,gBACxBhG,KAAK,QAAS,aACdA,KAAK,MAAO+H,GACZ/H,KAAK,KAAMsI,GACXtI,KAAK,IAAK8H,GACZzB,EAASL,OAAO,SAAU,gBACxBhG,KAAK,QAAS,aACdA,KAAK,KAAM+H,GACX/H,KAAK,KAAMsI,GACXtI,KAAK,IAAK8H,GAGZ,MAAMS,EAASF,EAAqB,GAAXX,EACnBc,EAAoB,IAAXd,EA4Bf,OA3BArB,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,QAAS,eACdA,KAAK,IAAK,KAAKwI,EAAS,KAAKD,QAAaA,EAAkB,GAATC,KAAgBA,EAAS,KAAKD,KACjFvI,KAAK,OAAQ,QACbA,KAAK,eAA2B,GAAX6H,GACrB7H,KAAK,iBAAkB,SAGzBqG,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAa,IAAPgI,GACXhI,KAAK,KAAa,IAAPgI,GACXhI,KAAK,KAAM0H,EAAW,EAAIM,EAAO,GACjChI,KAAK,IAAKsI,EAAOL,EAAO,GACxBjI,KAAK,QAASgI,GACdhI,KAAK,SAAUiI,GACjB5B,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAa,IAAPgI,GACXhI,KAAK,KAAa,IAAPgI,GACXhI,KAAK,IAAK0H,EAAW,EAAI,GACzB1H,KAAK,IAAKsI,EAAOL,EAAO,GACxBjI,KAAK,QAASgI,GACdhI,KAAK,SAAUiI,GAEjBnI,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc5E,EAAMgF,EAC5B,EAEOuB,CACR,CAyC8CoB,CAAM,IAAI5B,EAAU/P,GAASgK,EAAMA,GAAMA,OACtF2I,WAAY,CAAC3S,EAAoBgK,IAxClC,SAAoBhK,EAAmBqQ,EAAYrG,GAClD,MAAM8C,EAAK9C,EAAKI,OAAS,EACnBmG,EAAWvQ,EACfkK,KAAK,iBAAkB2F,EAAkB,aAAc7F,EAAKN,MAAOM,EAAKI,SACxE8F,OAAO,IAAK,gBAkBd,OAjBAK,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,IAAK,aACNF,EAAKN,MAAQ,MAAMM,EAAKI,OAAS,EAAI0C,MAAO9C,EAAKN,kBACjDM,EAAKN,MAAQ,EAAIoD,EAAK,MAAM9C,EAAKI,OAAS,EAAI0C,EAAK,MAAMA,EAAK,MAAMA,EAAK,OAAOA,EAAK,gBACrF9C,EAAKN,MAAQ,EAAIoD,MAAO9C,EAAKI,OAAS,EAAI0C,EAAK,MAAM9C,EAAKN,MAAQoD,EAAKA,EAAK,MAAMA,EAAK,OAAO9C,EAAKN,MAAQoD,EAAKA,EAAK,aAE3HyD,EAASL,OAAO,OAAQ,gBACtBhG,KAAK,KAAM,GAAGA,KAAK,KAAM,GACzBA,KAAK,KAAMF,EAAKN,MAAQ,GACxBQ,KAAK,KAAMF,EAAKI,OAAS,GACzBF,KAAK,QAASF,EAAKN,OACnBQ,KAAK,SAAUF,EAAKI,QAEtBJ,EAAKwG,UAAY,SAAUxB,GAC1B,OAAOJ,EAAc5E,EAAMgF,EAC5B,EAEOuB,CACR,CAiBmDoC,CAAW,IAAI5C,EAAU/P,GAASgK,EAAMA,GAAMA,qBCnc1F,MAAM4I,EACKC,SAAkB,GAC3BxD,IAAc,EACdyD,aAAuB,EACdC,UACAC,UACjBC,OACQC,iBAA+B,KAEvC,WAAAjD,CAAYtQ,EAAYoT,EAAsBC,GAC7ChH,KAAK+G,UAAYA,EACjB/G,KAAKgH,UAAYA,EACjBhH,KAAKiH,OA+DP,SAAkBE,GACjB,IAAIC,EACJ,OAAO,WACN,MAAMC,EAAUrH,KAKhBsH,aAAaF,GACbA,EAAUG,WALI,WACbH,EAAU,KACVD,EAAKK,MAAMH,EACZ,EAtE6C,IAyE9C,CACD,CA1EgBI,CAAS,IAAMzH,KAAK0H,UACnC,CAGA,YAAAC,GACM3H,KAAKkH,mBACTlH,KAAKkH,iBAAmBlH,KAAK4H,UAAU5H,KAAK+G,aAE9C,CAEA,MAAAtb,GACC,OAAOuU,KAAK6G,SAASpb,MACtB,CAEA,YAAAoc,GACC,OAAO7H,KAAK4H,UAAU5H,KAAK6G,SAAS7G,KAAKqD,IAAM,GAChD,CAEQ,OAAAqE,GACP,IAAK1H,KAAKkH,iBACT,MAAM3N,MAAM,4EAGbyG,KAAK6G,SAAS7G,KAAKqD,KAAOrD,KAAK4H,UAAU5H,KAAK+G,aAC9C/G,KAAK6G,SAAS7G,KAAKqD,IAAM,GAAKrD,KAAKkH,iBACnClH,KAAKkH,iBAAmB,KACxBlH,KAAKqD,KAAO,EAGZrD,KAAK6G,SAASiB,OAAO9H,KAAKqD,IAC3B,CAEQ,SAAAuE,CAAUG,GAEjB,MAA+B,oBAApBC,gBACHA,gBAAgBD,GAEjB9K,KAAKC,MAAMD,KAAKE,UAAU4K,GAClC,CAEA,IAAA3a,GACC,GAAI4S,KAAKqD,IAAM,EAAG,OAClBrD,KAAKqD,KAAO,EACZ,MAAM0E,EAAM/H,KAAK6G,SAAS7G,KAAKqD,IAAM,GACrCrD,KAAKgH,UAAUhH,KAAK4H,UAAUG,GAC/B,CAEA,IAAA1a,GACC,GAAI2S,KAAKqD,IAAMrD,KAAK6G,SAASpb,OAAS,EAAG,OACzC,MAAMsc,EAAM/H,KAAK6G,SAAS7G,KAAKqD,KAC/BrD,KAAKgH,UAAUhH,KAAK4H,UAAUG,IAC9B/H,KAAKqD,KAAO,CACb,CAEA,OAAAvT,GACC,OAAOkQ,KAAKqD,MAAQrD,KAAK8G,YAC1B,CAEA,QAAAtN,GACCwG,KAAK8G,aAAe9G,KAAKqD,GAC1B,eCpBM,MAoBM4E,EAMF,CACT,cAAe,uDACf/R,OAAQ,QAgCGgS,EAAa,CAAC7U,EAAgBnH,KAC1C/C,OAAOC,KAAK8C,GAAOzC,QAAQI,IAC1B,MAAMgC,EAAQK,EAAMrC,GACC,iBAAVgC,EACVwH,EAAGnH,MAAMic,YAAYte,EAAKgC,EAAMyO,YAEhCjH,EAAGnH,MAAMic,YAAYte,EAAKgC,MAMhBuc,EAAoB,CAACvG,EAAWC,IACrC9S,KAAKoU,MAAMtB,EAAGtB,EAAIqB,EAAGrB,IAAMsB,EAAGtB,EAAIqB,EAAGrB,IAAMsB,EAAGrB,EAAIoB,EAAGpB,IAAMqB,EAAGrB,EAAIoB,EAAGpB,IC5G7E,SAAS4H,EACRhP,EACAqE,EACA4C,EACAC,EACA+H,EACAC,GAEA,MAAM5K,EAAQ,CACb,cAAe9C,OAAOoN,EAAoB,gBAC1C,YAAa,GAAG3H,MAChB,cAAeC,EAAO,OAAS,UAE1BiI,EAAU/K,EAAYpE,EAAMqE,EAAOC,GAGzC,MAAO,CACNiB,MAHa4J,EAAQ5J,MAAMnT,OAAS,EAAI+c,EAAQ5J,MAAQ,CAAC,IAIzD0B,WACAmI,WAAYnI,EAAW,EACvBC,OACAgI,QACAD,WAEF,CC8CA,MAAMI,EFnCuC,CAC5CC,UAAW,EACX1S,MAAO,OACP2S,QAAS,EACTtI,SAAU,GACVuI,QAAQ,GE+BHC,EF5BuC,CAC5CpL,MAAO,IACPU,OAAQ,IACRrI,WAAY,0BACZE,MAAO,OACP2S,QAAS,GACT1S,OAAQ,OACRoK,SAAU,GACVwD,MAAO,OEmDD,MAAMiF,EACZpV,GACAsB,KACAiC,SACA8R,MACAC,aACAC,UACAlU,SACAF,gBACAkB,cAAqC,IAAI9C,IACjCiW,MACAC,cAAwB,EACxBC,aAAuB,EACvBC,UAAoB,GACpBC,cAAwB,EAEhC,WAAAtF,CAAYtQ,EAAasB,GACxB+K,KAAKrM,GAAKA,EACVqM,KAAK/K,KAAOA,EAEZ+K,KAAKgJ,MAAQ,GACbhJ,KAAKiJ,aAAe,IAAI/V,IACxB8M,KAAK9I,SAAW,IAAIhE,IACpB8M,KAAKkJ,UAAY,IAAIhW,IAErB8M,KAAKmJ,MAAQ,IAAIvC,EAChB5G,KAAKrM,GACL,IAAMqM,KAAKwJ,cAAa,GACvBC,GAAOzJ,KAAK0J,aAAaD,GAAI,IAI/Bpb,OAAO3D,MAAQsV,IAChB,CAGA,IAAA/H,CAAK1G,GACJA,GAAUyO,KAAK0J,aAAanY,GAC5ByO,KAAKmJ,MAAQ,IAAIvC,EAChB5G,KAAKrM,GACL,IAAMqM,KAAKwJ,cAAa,GACvBC,GAAOzJ,KAAK0J,aAAaD,GAAI,IAE3BzJ,KAAKmJ,MAAM1d,UACduU,KAAK0J,aAAa1J,KAAKmJ,MAAMtB,gBAI9B7H,KAAKmJ,MAAMxB,eACX3H,KAAKmJ,MAAMlC,QACZ,CAEA,OAAAvQ,CAAQ/C,EAAYgW,EAAepT,EAAarB,EAAqBhJ,EAAkB0d,GACtF,GAAI5J,KAAK9I,SAASC,IAAIxD,GAAK,MAAM4F,MAAM,mBAAqB5F,GAC5D,MAAMkW,EAAY,IAAIf,KAAqB5c,GACrC4X,GAAS+F,EAAU/F,OAAS,OAAOjO,cAEnCiU,EAA0B,WAAVhG,EAAqB,IAAM,IAC3CpG,EAAQ1O,KAAKQ,IAFE,IAEgBqa,EAAUnM,OAAS,GAElDqM,EDxID,SACNjgB,EACAkgB,EACA9U,EACA+U,EACA3J,GAEA,MAAM4J,EAAYlb,KAAKQ,IAAIya,EAAYE,GAAwB,IACzDC,EAAS,CACd/B,EAAUve,EAAOogB,EAAW5J,GAAU,EAtCtB,EAsCuC,QACvD+H,EAAU,IAAI2B,KAAaE,EAAsB,IAAX5J,GAAiB,EAtCpC,IAuCnB+H,EAAUnT,EAAagV,EAAWlb,KAAKS,IAAe,GAAX6Q,EAAgB,KAAK,EAAO,EAAG,gBAErE+J,EAAaD,EAAO5K,OACzB,CAACpB,EAAQkM,IAAUlM,EAASkM,EAAM1L,MAAMnT,OAAS6e,EAAM7B,WAAa6B,EAAMhC,SAC1E,GAGD,MAAO,CACN8B,SACAC,aACAP,cAAeO,EAAaE,GAE9B,CCiHwBC,CAAkBb,EAAOpT,EAAKrB,EAAawI,EADhDmM,EAAUvJ,UAAY,IAEvC,IAAIlC,EAASpP,KAAKQ,IAAIsa,EAAeD,EAAUzL,QAAU,EAAG2L,EAAcD,eAI1E,IAAK,IAAInS,EAAI,EAAGA,EAAI,GAAIA,IAAK,CAC5B,MAAM8S,EAAiBV,EAAcD,cACpC9a,KAAK2U,IAAIE,EAAkBC,EAAOpG,EAAOU,IAC1C,GAAIqM,GAAkBrM,EAAS,GAC9B,MAEDA,EAASqM,CACV,CAEA,MAAMnF,EAAU,CACf3R,KAAI7J,MAAO6f,EAAOpT,MAAKrB,cAAahJ,MAAO2d,EAC3CrJ,EAAG,EAAGC,EAAG,EAAG/C,QAAOU,SAAQoG,UAAW,KAAMoF,OAAMG,iBAEnD/J,KAAK9I,SAASxD,IAAI4R,EAAE3R,GAAI2R,EACzB,CAEA,KAAAoF,GACC,OAAOpX,MAAMqX,KAAK3K,KAAK9I,SAAS0T,SACjC,CAEA,OAAApT,CAAQ7D,EAAYkX,EAAkBC,EAAgBnB,EAAelS,EAAmBvL,GACvFuL,GAAYA,EAAShO,QAAQ,CAAC+L,EAAGmC,KAChC,MAAMhO,EAAI6L,EACV7L,EAAEgK,GAAK,KAAKA,KAAMgE,IAClBqI,KAAKiJ,aAAavV,IAAI/J,EAAEgK,GAAIhK,KAO7B,MAsBMohB,EAAO,CACZpX,KACAgX,KAAM3K,KAAK9I,SAAS9E,IAAIyY,GACxBG,GAAIhL,KAAK9I,SAAS9E,IAAI0Y,GACtBnB,QACAlS,SAAU,KACVvL,MAAO,IAAIwc,KAAqBxc,GAChC+e,WAhBmBzV,IACnB,MAAM7L,EAAI6L,EAMV,OALK7L,EAAEgK,KACNhK,EAAEgK,GARmB,EAACuX,EAAgB1V,IAGhC,KAAK0V,OAXIC,KAChB,IAAIlJ,EAAI,WACR,IAAK,IAAItK,EAAI,EAAGA,EAAIwT,EAAM1f,OAAQkM,IACjCsK,GAAKkJ,EAAMC,WAAWzT,GACtBsK,EAAIjT,KAAKqc,KAAKpJ,EAAG,UAElB,OAAQA,IAAM,GAAG3H,SAAS,KAKFgR,CAAQ,GAAGJ,KAFxB1V,EAAUgL,KACVhL,EAAUiL,OAMb8K,CAAeR,EAAKpX,GAAI6B,GAC/BwK,KAAKiJ,aAAavV,IAAI/J,EAAEgK,GAAIhK,IAE7BA,EAAEohB,KAAOA,EACFvV,GAUPgW,qBAAqB,GAEtBxL,KAAKgJ,MAAMpf,KAAKmhB,GACZtT,IACHsT,EAAKtT,SAAWA,EAASzL,IAAIwJ,GAAKuV,EAAKE,WAAWzV,IAEpD,CAEA,QAAAwC,CAASrE,EAAYsB,EAAcwW,EAAyBvf,GAC3D,GAAI8T,KAAKkJ,UAAU/R,IAAIxD,GAEtB,YADAvC,QAAQT,MAAM,iBAAiBgD,KAAMsB,KAGtC,MAAMyW,EAAe,CACpB/X,KAAIsB,OAAMuL,EAAG,KAAMC,EAAG,KAAM/C,MAAO,KAAMU,OAAQ,KACjDsM,MAAOe,EAAczf,IAAI4T,IACxB,MAAM0F,EAAItF,KAAK9I,SAAS9E,IAAIwN,IAAMI,KAAKkJ,UAAU9W,IAAIwN,GAErD,OADK0F,GAAGlU,QAAQT,MAAM,iBAAiBiP,yBAAyBjM,MAAOsB,MAChEqQ,IACLhc,OAAOqiB,SACVzf,SAED8T,KAAKkJ,UAAUxV,IAAIC,EAAI+X,EACxB,CAWA,eAAAE,CAAgB5N,EAAY6N,GAC3B7N,EAAK6N,SAAWA,EAChBA,EACC7N,EAAK3I,IAAIwK,UAAUC,IAAI,YACvB9B,EAAK3I,IAAIwK,UAAUiM,OAAO,YAC3B9L,KAAK+L,gBACN,CAEQ,cAAAA,GACP/L,KAAKgJ,MAAMvf,QAAQkC,IACdA,EAAEqf,GAAGa,UAAYlgB,EAAEgf,KAAKkB,SAC3BlgB,EAAE0J,IAAIwK,UAAUC,IAAI,YAEpBnU,EAAE0J,IAAIwK,UAAUiM,OAAO,aAG1B,CAEA,QAAAE,CAAS1G,EAAS9E,EAAWC,EAAWwL,GAAuB,EAAOC,GAAoB,GACzF,GAAK5G,EAAL,CAGA,GAAItF,KAAKqJ,cAAgB4C,EAAa,CACrC,MAAME,EAAUnM,KAAKhS,WAAWwS,EAAGC,GACnCD,EAAI2L,EAAQ3L,EACZC,EAAI0L,EAAQ1L,CACb,CAEI6E,EAAE9E,GAAKA,GAAK8E,EAAE7E,GAAKA,IAElByL,GACJlM,KAAKmJ,MAAMxB,eAEZrC,EAAE9E,EAAIA,EACN8E,EAAE7E,EAAIA,EACNe,EAAY8D,EAAEjQ,IAAKmL,EAAGC,GACtBT,KAAKoM,YAAY9G,GACjBtF,KAAKqM,aAAa/G,GACb4G,GACJlM,KAAKmJ,MAAMlC,SApBJ,CAsBT,CAEA,cAAAqF,CAAe3iB,EAAe6W,EAAWC,EAAWwL,GAAuB,EAAOC,GAAoB,GAErG,GAAIlM,KAAKqJ,cAAgB4C,EAAa,CACrC,MAAME,EAAUnM,KAAKhS,WAAWwS,EAAGC,GACnCD,EAAI2L,EAAQ3L,EACZC,EAAI0L,EAAQ1L,CACb,CAGI9W,EAAE6W,GAAKA,GAAK7W,EAAE8W,GAAKA,IAClByL,GACJlM,KAAKmJ,MAAMxB,eAEZhe,EAAE6W,EAAIA,EACN7W,EAAE8W,EAAIA,EACNT,KAAKuM,WAAW5iB,EAAEohB,MACbmB,GACJlM,KAAKmJ,MAAMlC,SAEb,CAEA,YAAA/K,CAAa4I,EAAYhE,EAAYmL,GAAuB,GAC3DjM,KAAK0K,QAAQjhB,QAAQ6b,GAAKA,EAAEuG,UAAY7L,KAAKgM,SAAS1G,EAAGA,EAAE9E,EAAIsE,EAAIQ,EAAE7E,EAAIK,EAAImL,GAAa,IAC1FjM,KAAKiJ,aAAaxf,QAAQE,GAAKA,EAAEkiB,UAAY7L,KAAKsM,eAAe3iB,EAAGA,EAAE6W,EAAIsE,EAAInb,EAAE8W,EAAIK,EAAImL,GAAa,GACtG,CAEA,gBAAAO,CAAiBzB,EAAYvV,EAAU6N,EAAaoJ,GACnDzM,KAAKmJ,MAAMxB,eACX,MAAMhe,EAAIohB,EAAKE,WAAWzV,GAC1B7L,EAAEkiB,UAAW,EACTY,IACH1B,EAAKtT,SAAShO,QAAQE,GAAKA,EAAEggB,OAAQ,GACrChgB,EAAEggB,OAAQ,GAEXoB,EAAKtT,SAASqQ,OAAOzE,EAAM,EAAG,EAAG1Z,GACjCqW,KAAKuM,WAAWxB,GAChB/K,KAAKmJ,MAAMlC,QACZ,CAEA,gBAAAyF,CAAiB/iB,GAChBqW,KAAKmJ,MAAMxB,eAEX,MAAMgF,EAAQhjB,EAAEohB,KAAKtT,SAASmV,QAAQjjB,GAClCgjB,GAAS,IACZhjB,EAAEohB,KAAKtT,SAASqQ,OAAO6E,EAAO,GAC9B3M,KAAKiJ,aAAa4D,OAAOljB,EAAEgK,IAG3BhK,EAAEohB,KAAKS,qBAAsB,GAG9BxL,KAAKuM,WAAW5iB,EAAEohB,MAClB/K,KAAKmJ,MAAMlC,QACZ,CAEA,OAAAnX,GACC,OAAOkQ,KAAKmJ,MAAMrZ,SACnB,CAEA,IAAA1C,GACC4S,KAAKmJ,MAAM/b,MACZ,CAEA,IAAAC,GACC2S,KAAKmJ,MAAM9b,MACZ,CAIA,YAAAyf,GACC,MAAMC,EAAgB/M,KAAKgN,yBAGrBC,EAFU,IAECF,EAAcvM,EACzB0M,EAHU,IAGCH,EAActM,EAG/BT,KAAKuJ,cAAe,EAEpBvJ,KAAKmJ,MAAMxB,eAEX3H,KAAK9I,SAASzN,QAAQuU,IACrBgC,KAAKgM,SAAShO,EAAMA,EAAKwC,EAAIyM,EAASjP,EAAKyC,EAAIyM,GAAS,GAAM,KAG/DlN,KAAKiJ,aAAaxf,QAAQ0jB,IACzBnN,KAAKsM,eAAea,EAAQA,EAAO3M,EAAIyM,EAASE,EAAO1M,EAAIyM,GAAS,GAAM,KAG3ElN,KAAKmJ,MAAMlC,QAGZ,CAGA,iBAAAmG,GACC,MAAMre,EAAcse,IACdC,EAAY1P,EAAIwG,cAAc,UAChCkJ,IACHA,EAAUrP,aAAa,YAAa,SAASlP,sBAgyDhD,SAAgCwe,GAC/B,MAAMC,EAAKD,EAAUP,yBACfpe,EAAOye,IACPrL,EAAIhT,KAAKQ,IAAIoO,EAAI6P,cAAcC,YAAc9e,EAAM4e,EAAGhN,EAAIgN,EAAG9P,MAAQ,IACrEuE,EAAIjT,KAAKQ,IAAIoO,EAAI6P,cAAcE,aAAe/e,EAAM4e,EAAG/M,EAAI+M,EAAGpP,OAAS,IAC7ER,EAAIK,aAAa,QAASpD,OAAOmH,EAAIpT,IACrCgP,EAAIK,aAAa,SAAUpD,OAAOoH,EAAIrT,GACvC,CAtyDGgf,CAAuB5N,OAIxB6N,GAAe7N,KAAKrM,IAGpBqM,KAAKuJ,cAAe,CACrB,CAGA,iBAAAuE,GACC,OAAO9N,KAAKuJ,YACb,CAGA,SAAA1N,GACC,MAAMyR,EAAY1P,EAAIwG,cAAc,UAChCkJ,IAEHA,EAAUrP,aAAa,YAAa,4BACpC8P,KAIDF,GAAe7N,KAAKrM,GACrB,CAGQ,WAAAyY,CAAY9G,GACnBtF,KAAKgJ,MAAMvf,QAAQkC,IAAM2Z,GAAK3Z,EAAEgf,MAAQrF,GAAK3Z,EAAEqf,KAAOhL,KAAKuM,WAAW5gB,IACtEqU,KAAK+L,gBACN,CAEA,UAAAQ,CAAW5gB,GACV,MAAM6J,EAAI7J,EAAE0J,IAAIoY,cAChBjY,EAAE8I,YAAY3S,EAAE0J,KAChB1J,EAAE0J,IAAM2Y,EAAUhO,KAAMrU,GACxB6J,EAAEuL,OAAOpV,EAAE0J,IACZ,CAEQ,YAAAgX,CAAarO,GACpBgC,KAAKkJ,UAAUzf,QAAQiiB,IAEtB,MAAMlW,EAAIkW,EAAMrW,IAAIoY,cACpBjY,EAAE8I,YAAYoN,EAAMrW,KACpB4Y,EAAWvC,GACXlW,EAAEuL,OAAO2K,EAAMrW,MAEjB,CAEA,SAAA8D,GAEC,MAAM+U,EAA6Brd,SAASuT,cAAc,aAIpD+J,GAHUD,EAAY9J,cAAc,gBAGxB8J,EAAYE,WAAU,IAGxCD,EAAUE,iBAAiB,gCAAgC5kB,QAAQmgB,IAClEA,EAAK3L,aAAa,OAAQ2L,EAAK0E,aAAa,qBAAuB,IACnE1E,EAAK2E,gBAAgB,sBAItB,MAAMC,EAAgBL,EAAU/J,cAAc,gBAC1CoK,GACHA,EAAc1C,SAIf,MAAMiB,EAAgB/M,KAAKgN,yBAMrByB,EAAc1B,EAAcrP,MAASgR,IACrCC,EAAe5B,EAAc3O,OAAUsQ,IAGvCzB,EAPU,GAOCF,EAAcvM,EACzB0M,EARU,GAQCH,EAActM,EAGzBmO,EAAkBT,EAAU/J,cAAc,UAC5CwK,GAEHA,EAAgB3Q,aAAa,YAAa,sBAAsBgP,MAAYC,MAI7EiB,EAAUlQ,aAAa,UAAW,OAAOwQ,KAAeE,KACxDR,EAAUlQ,aAAa,QAASpD,OAAO4T,IACvCN,EAAUlQ,aAAa,SAAUpD,OAAO8T,IAGxCR,EAAUlQ,aAAa,QAAS,8BAGhC+B,KAAK6O,gCAAgCV,GAGrC,MAAMW,EAASje,SAASke,cAAc,UAUtC,OATAD,EAAO7Q,aAAa,OAAQ,oBAC5B+B,KAAKhL,SAASzD,OAASyO,KAAKwJ,eAC5BsF,EAAO/N,OAAO,YAA0B9D,KAAKE,UAAU6C,KAAKhL,SAAU,KAAM,GAwjBjE7K,QAAQ,OAAQ,mBAxjBuD,OAClFgkB,EAAUhK,aAAa2K,EAAQX,EAAUa,YAG7Bb,EAAUc,SAIvB,CAIQ,+BAAAJ,CAAgCjR,GACP,IAA5BoC,KAAKhK,cAAcuJ,OAGvB3B,EAAIyQ,iBAAiB,UAAU5kB,QAAQ4J,IACtC,MAAM6b,EAAO7b,EAAGib,aAAa,QACzBY,GAAQlP,KAAKhK,cAAcmB,IAAI+X,IAClC7b,EAAG4K,aAAa,OAAQ,OAAO+B,KAAKhK,cAAc5D,IAAI8c,OAAUA,QAKlEtR,EAAIyQ,iBAAiB,YAAY5kB,QAAQ4J,IACxC,MAAM6C,EAAS7C,EAAGib,aAAa,UAC3BpY,GAAU8J,KAAKhK,cAAcmB,IAAIjB,IACpC7C,EAAG4K,aAAa,SAAU,OAAO+B,KAAKhK,cAAc5D,IAAI8D,OAAYA,QAGvE,CAGA,sBAAA8W,GACC,IAAImC,EAAOC,IAAUC,EAAOD,IAAUE,GAAO,IAAWC,GAAO,IAqE/D,OAlEAvP,KAAK0K,QAAQjhB,QAAQuU,IACpB,MAAMwR,EAAOxR,EAAKwC,EAAIxC,EAAKN,MAAQ,EAC7B+R,EAAQzR,EAAKwC,EAAIxC,EAAKN,MAAQ,EAC9BgS,EAAM1R,EAAKyC,EAAIzC,EAAKI,OAAS,EAC7BuR,EAAS3R,EAAKyC,EAAIzC,EAAKI,OAAS,EAEtC+Q,EAAOngB,KAAKS,IAAI0f,EAAMK,GACtBF,EAAOtgB,KAAKQ,IAAI8f,EAAMG,GACtBJ,EAAOrgB,KAAKS,IAAI4f,EAAMK,GACtBH,EAAOvgB,KAAKQ,IAAI+f,EAAMI,KAIvB3P,KAAKiJ,aAAaxf,QAAQ0jB,IACzBgC,EAAOngB,KAAKS,IAAI0f,EAAMhC,EAAO3M,EAAI,GACjC8O,EAAOtgB,KAAKQ,IAAI8f,EAAMnC,EAAO3M,EAAI,GACjC6O,EAAOrgB,KAAKS,IAAI4f,EAAMlC,EAAO1M,EAAI,GACjC8O,EAAOvgB,KAAKQ,IAAI+f,EAAMpC,EAAO1M,EAAI,KAIlCT,KAAKkJ,UAAUzf,QAAQiiB,IACtB,MAAM8D,EAAO9D,EAAMlL,EAAIkL,EAAMhO,MAAQ,EAC/B+R,EAAQ/D,EAAMlL,EAAIkL,EAAMhO,MAAQ,EAChCgS,EAAMhE,EAAMjL,EAAIiL,EAAMtN,OAAS,EAC/BuR,EAASjE,EAAMjL,EAAIiL,EAAMtN,OAAS,EAExC+Q,EAAOngB,KAAKS,IAAI0f,EAAMK,GACtBF,EAAOtgB,KAAKQ,IAAI8f,EAAMG,GACtBJ,EAAOrgB,KAAKS,IAAI4f,EAAMK,GACtBH,EAAOvgB,KAAKQ,IAAI+f,EAAMI,KAIvB3P,KAAKgJ,MAAMvf,QAAQshB,IAkBlB,GAhBAoE,EAAOngB,KAAKS,IAAI0f,EAAMpE,EAAKJ,KAAKnK,EAAI,GAAIuK,EAAKC,GAAGxK,EAAI,IACpD8O,EAAOtgB,KAAKQ,IAAI8f,EAAMvE,EAAKJ,KAAKnK,EAAI,GAAIuK,EAAKC,GAAGxK,EAAI,IACpD6O,EAAOrgB,KAAKS,IAAI4f,EAAMtE,EAAKJ,KAAKlK,EAAI,GAAIsK,EAAKC,GAAGvK,EAAI,IACpD8O,EAAOvgB,KAAKQ,IAAI+f,EAAMxE,EAAKJ,KAAKlK,EAAI,GAAIsK,EAAKC,GAAGvK,EAAI,IAGhDsK,EAAKtT,UACRsT,EAAKtT,SAAShO,QAAQ0jB,IACrBgC,EAAOngB,KAAKS,IAAI0f,EAAMhC,EAAO3M,EAAI,IACjC8O,EAAOtgB,KAAKQ,IAAI8f,EAAMnC,EAAO3M,EAAI,IACjC6O,EAAOrgB,KAAKS,IAAI4f,EAAMlC,EAAO1M,EAAI,IACjC8O,EAAOvgB,KAAKQ,IAAI+f,EAAMpC,EAAO1M,EAAI,MAK/BsK,EAAKpB,OAASoB,EAAKpB,MAAMrQ,OAAQ,CAEpC,MAAMsW,GAAW7E,EAAKJ,KAAKnK,EAAIuK,EAAKC,GAAGxK,GAAK,EACtCqP,GAAW9E,EAAKJ,KAAKlK,EAAIsK,EAAKC,GAAGvK,GAAK,EACtCqP,EAAsC,GAApB/E,EAAKpB,MAAMle,OAAc,GAEjD0jB,EAAOngB,KAAKS,IAAI0f,EAAMS,EAAUE,GAChCR,EAAOtgB,KAAKQ,IAAI8f,EAAMM,EAAUE,GAChCT,EAAOrgB,KAAKS,IAAI4f,EAAMQ,EAAU,IAChCN,EAAOvgB,KAAKQ,IAAI+f,EAAMM,EAAU,GACjC,IAIGV,IAASC,IACL,CAAE5O,EAAG,EAAGC,EAAG,EAAG/C,MAAO,IAAKU,OAAQ,KAGnC,CACNoC,EAAG2O,EACH1O,EAAG4O,EACH3R,MAAO4R,EAAOH,EACd/Q,OAAQmR,EAAOF,EAEjB,CAQA,YAAA7F,CAAauG,GAAO,GACnB,MAAMrR,EAAc,CAAC,EAmBrB,OAlBAsB,KAAK0K,QAAQjhB,QAAQ6b,GAAK5G,EAAI4G,EAAE3R,IAAM,CAAC6M,EAAG8E,EAAE9E,EAAGC,EAAG6E,EAAE7E,IACpDT,KAAKgJ,MAAMvf,QAAQkC,IAClB,IAAKA,EAAE8L,SAAU,OAEjB,MAAMuY,EAAMrkB,EAAE8L,SAASzL,IAAIrC,IAAC,CAC3B6W,EAAG7W,EAAE6W,EACLC,EAAG9W,EAAE8W,EACLkJ,MAAOhgB,EAAEggB,MACTxP,KAAMxQ,EAAEwQ,SAEL6V,EAAIvkB,QAAUskB,KACjBrR,EAAI,KAAK/S,EAAEgI,MAAQqc,GAGhBrkB,EAAE6f,sBACL9M,EAAI,KAAK/S,EAAEgI,eAAgB,KAGtB+K,CACR,CAEA,QAAAlF,GACCwG,KAAKmJ,MAAM3P,UACZ,CAEA,YAAAkQ,CAAanY,EAAgC0e,GAAW,GAEvD,MAAMC,EAA6C,GAEnD/mB,OAAO+Q,QAAQ3I,GAAQ9H,QAAQ,EAAEmW,EAAGjW,MAC9BiW,EAAEuQ,WAAW,YAAiBpb,IAARpL,EAAE6W,QAA2BzL,IAARpL,EAAE8W,EAGvCb,EAAEuQ,WAAW,OAAS7c,MAAMC,QAAQ5J,IAE9CA,EAAEF,QAAS0jB,SACOpY,IAAboY,EAAO3M,QAAgCzL,IAAboY,EAAO1M,GACpCyP,EAAYtmB,KAAK,CAAC4W,EAAG2M,EAAO3M,EAAGC,EAAG0M,EAAO1M,MAL3CyP,EAAYtmB,KAAK,CAAC4W,EAAG7W,EAAE6W,EAAGC,EAAG9W,EAAE8W,MAYjC,IAAIwM,EAAU,EACVC,EAAU,EAEd,GAAIgD,EAAYzkB,OAAS,EAAG,CAC3B,MAAM0jB,EAAOngB,KAAKS,OAAOygB,EAAYlkB,IAAIkX,GAAKA,EAAE1C,IAC1C6O,EAAOrgB,KAAKS,OAAOygB,EAAYlkB,IAAIkX,GAAKA,EAAEzC,IAGhD,GAAI0O,GAAQ,KAAOE,GAAQ,KAAOrgB,KAAKQ,OAAO0gB,EAAYlkB,IAAIkX,GAAKA,EAAE1C,IAAM,KAAQxR,KAAKQ,OAAO0gB,EAAYlkB,IAAIkX,GAAKA,EAAEzC,IAAM,IAAM,CACjI,MAAMiO,EAAU,GAChBzB,GAAWkC,EAAOT,EAClBxB,GAAWmC,EAAOX,CACnB,CACD,CAGAvlB,OAAO+Q,QAAQ3I,GAAQ9H,QAAQ,EAAEmW,EAAGjW,MAEnC,MAAM2b,EAAItF,KAAK9I,SAAS9E,IAAIwN,GAC5B,GAAI0F,EACHA,EAAE9E,EAAI7W,EAAE6W,EAAIyM,EACZ3H,EAAE7E,EAAI9W,EAAE8W,EAAIyM,OAGb,GAAItN,EAAEuQ,WAAW,QAAUvQ,EAAEpW,SAAS,YAAa,CAClD,MAAMuhB,EAAO/K,KAAKgJ,MAAMzT,KAAK5J,GAAKA,EAAEgI,IAAMiM,EAAEtV,MAAM,IAClD,IAAKygB,EAAM,OAgBX,OAfAA,EAAKtT,UAAYsT,EAAKtT,SAAShO,QAAQE,GAAKqW,KAAKiJ,aAAa4D,OAAOljB,EAAEgK,UACvEoX,EAAKtT,SAAW9N,EAAEqC,IAAKwJ,IACtB,MAAM4a,EAAkB,CACvB5P,EAAGhL,EAAEgL,EAAIyM,EACTxM,EAAGjL,EAAEiL,EAAIyM,GAGV/jB,OAAOknB,OAAOD,EAAiB5a,EAAG,CAAEgL,EAAGhL,EAAEgL,EAAIyM,EAASxM,EAAGjL,EAAEiL,EAAIyM,IAC/D,MAAMC,EAASpC,EAAKE,WAAWmF,GAK/B,OAHK5a,EAAU2E,OACdgT,EAAOhT,MAAO,GAERgT,IAGT,CACA,GAAIvN,EAAEpW,SAAS,YAAa,CAC3B,MAAM8mB,EAAS1Q,EAAEtV,MAAM,GAAI,GACrBygB,EAAO/K,KAAKgJ,MAAMzT,KAAK5J,GAAKA,EAAEgI,IAAM2c,GAI1C,YAHIvF,IAAc,IAANphB,IACXohB,EAAKS,qBAAsB,GAG7B,IAEGyE,IACHjQ,KAAK0K,QAAQjhB,QAAQ6b,GAAK9D,EAAY8D,EAAEjQ,IAAKiQ,EAAE9E,EAAG8E,EAAE7E,IACpDT,KAAKgJ,MAAMvf,QAAQkC,GAAKqU,KAAKuM,WAAW5gB,IACxCqU,KAAK+L,iBACL/L,KAAKqM,aAAa,MAEpB,CAEA,gBAAM1T,CAAWH,GAChB,IACC,MAAM2B,QAAa,EAAA5I,EAAAqO,GAAWI,KAAMxH,GAEpCwH,KAAKmJ,MAAMxB,eAGXxN,EAAKuQ,MAAMjhB,QAAQ8mB,IAClB,MAAMjL,EAAItF,KAAK9I,SAAS9E,IAAIme,EAAG5c,IAC3B2R,GACHtF,KAAKgM,SAAS1G,EAAGiL,EAAG/P,EAAG+P,EAAG9P,GAAG,GAAO,KAKtCtG,EAAK6O,MAAMvf,QAAQ+mB,IAClB,MAAMzF,EAAO/K,KAAKgJ,MAAMzT,KAAK5J,GAAKA,EAAEgI,IAAM6c,EAAG7c,IAC7C,GAAIoX,EAAM,CAsBT,GApBIA,EAAKtT,UACRsT,EAAKtT,SAAShO,QAAQE,IACjBA,EAAEgK,IACLqM,KAAKiJ,aAAa4D,OAAOljB,EAAEgK,MAI9BoX,EAAKtT,SAAW,GAChBsT,EAAKS,qBAAsB,EAGvBgF,EAAG/Y,UAAY+Y,EAAG/Y,SAAShM,OAAS,IACvCsf,EAAKtT,SAAW+Y,EAAG/Y,SAASzL,IAAIwJ,IAC/B,MAAM2X,EAASpC,EAAKE,WAAWzV,GAE/B,OADA2X,EAAOhT,MAAO,EACPgT,KAKLqD,EAAG7G,MAAO,CAGToB,EAAKtT,WACRsT,EAAKtT,SAAShO,QAAQE,IACjBA,EAAEggB,OACL3J,KAAKiJ,aAAa4D,OAAOljB,EAAEgK,MAG7BoX,EAAKtT,SAAWsT,EAAKtT,SAASnO,OAAOK,IAAMA,EAAEggB,QAI9C,MAAM8G,EAAc1F,EAAKE,WAAWuF,EAAG7G,OACvC8G,EAAY9G,OAAQ,EACpB8G,EAAYtW,MAAO,EAInB4Q,EAAKtT,SAAWsT,EAAKtT,UAAY,GACjC,MAAMiZ,EAkhDZ,SAAkCjZ,EAAmBkZ,EAAiB9F,EAAiBC,GAEtF,GAAwB,IAApBrT,EAAShM,OACZ,OAAO,EAIR,MAAMmlB,EAAW,CAAC/F,KAAapT,EAAUqT,GAGzC,IAAI+F,EAAczB,IACd0B,EAAmB,EAEvB,IAAK,IAAInZ,EAAI,EAAGA,EAAIiZ,EAASnlB,OAAS,EAAGkM,IAAK,CAC7C,MAIMoZ,EAAWC,GAAkBL,EAJdC,EAASjZ,GACXiZ,EAASjZ,EAAI,IAK5BoZ,EAAWF,IACdA,EAAcE,EACdD,EAAmBnZ,EAErB,CAMA,OAAOmZ,CACR,CAjjDwBG,CAAyBlG,EAAKtT,SAAU+Y,EAAG7G,MAAOoB,EAAKJ,KAAMI,EAAKC,IAG9EkG,EAwlDZ,SAAiCzZ,EAAmBkZ,EAAiBD,EAAmB7F,EAAiBC,GAExG,MAAM8F,EAAW,CAAC/F,KAAapT,EAAUqT,GAOzC,OAMD,SAAiC9H,EAAcmO,EAAqBC,GACnE,MAAMC,EAAIrO,EAAMxC,EAAI2Q,EAAa3Q,EAC3B8Q,EAAItO,EAAMvC,EAAI0Q,EAAa1Q,EAC3B8Q,EAAIH,EAAW5Q,EAAI2Q,EAAa3Q,EAChCgR,EAAIJ,EAAW3Q,EAAI0Q,EAAa1Q,EAEhCgD,EAAM4N,EAAIE,EAAID,EAAIE,EAClBC,EAAQF,EAAIA,EAAIC,EAAIA,EAE1B,GAAc,IAAVC,EAEH,MAAO,CAAEjR,EAAG2Q,EAAa3Q,EAAGC,EAAG0Q,EAAa1Q,GAG7C,IAAIiR,EAAQjO,EAAMgO,EAKlB,OAFAC,EAAQ1iB,KAAKQ,IAAI,EAAGR,KAAKS,IAAI,EAAGiiB,IAEzB,CACNlR,EAAG2Q,EAAa3Q,EAAIkR,EAAQH,EAC5B9Q,EAAG0Q,EAAa1Q,EAAIiR,EAAQF,EAE9B,CA7BQG,CAAwBhB,EAJVC,EAASF,GACXE,EAASF,EAAY,GAIzC,CAlmD2BkB,CAAwB7G,EAAKtT,SAAU+Y,EAAG7G,MAAO+G,EAAW3F,EAAKJ,KAAMI,EAAKC,IACjGyF,EAAYjQ,EAAI0Q,EAAa1Q,EAC7BiQ,EAAYhQ,EAAIyQ,EAAazQ,EAE7BsK,EAAKtT,SAASqQ,OAAO4I,EAAW,EAAGD,GACnCzQ,KAAKiJ,aAAavV,IAAI+c,EAAY9c,GAAI8c,EAEvC,CAGAzQ,KAAKuM,WAAWxB,EACjB,IAID/K,KAAKtQ,YAELsQ,KAAKmJ,MAAMlC,QAEZ,CAAE,MAAOtW,GACRS,QAAQT,MAAM,sBAAuBA,EAEtC,CACD,CAEA,eAAApD,GACC,MAAMyiB,EAAehQ,KAAK0K,QAAQphB,OAAOgc,GAAKA,EAAEuG,UAChDmE,EAAIpmB,QAAQ0J,MAAMqX,KAAK3K,KAAKiJ,aAAa2B,UAAUthB,OAAOK,GAAKA,EAAEkiB,WACjE,IAAIwD,EAAOrgB,KAAKS,OAAOugB,EAAIhkB,IAAIwJ,GAAKA,EAAEiL,IACtCT,KAAK9I,SAASzN,QAAQ6b,GAAKA,EAAEuG,UAAY7L,KAAKgM,SAAS1G,EAAGA,EAAE9E,EAAG6O,GAAM,GAAO,IAC5ErP,KAAKiJ,aAAaxf,QAAQE,GAAKA,EAAEkiB,UAAY7L,KAAKsM,eAAe3iB,EAAGA,EAAE6W,EAAG6O,GAAM,GAAO,GACvF,CAEA,eAAA/hB,GACC,MAAM0iB,EAAehQ,KAAK0K,QAAQphB,OAAOgc,GAAKA,EAAEuG,UAChDmE,EAAIpmB,QAAQ0J,MAAMqX,KAAK3K,KAAKiJ,aAAa2B,UAAUthB,OAAOK,GAAKA,EAAEkiB,WACjE,IAAIsD,EAAOngB,KAAKS,OAAOugB,EAAIhkB,IAAIwJ,GAAKA,EAAEgL,IACtCR,KAAK9I,SAASzN,QAAQ6b,GAAKA,EAAEuG,UAAY7L,KAAKgM,SAAS1G,EAAG6J,EAAM7J,EAAE7E,GAAG,GAAO,IAC5ET,KAAKiJ,aAAaxf,QAAQE,GAAKA,EAAEkiB,UAAY7L,KAAKsM,eAAe3iB,EAAGwlB,EAAMxlB,EAAE8W,GAAG,GAAO,GACvF,CAEA,oBAAAhT,GACC,MAAMokB,EAAgB7R,KAAK0K,QAAQphB,OAAOgc,GAAKA,EAAEuG,UAC3CiG,EAAmBxe,MAAMqX,KAAK3K,KAAKiJ,aAAa2B,UAAUthB,OAAOK,GAAKA,EAAEkiB,UAE9E,GAAIgG,EAAcpmB,OAASqmB,EAAiBrmB,OAAS,EAAG,OAExDuU,KAAKmJ,MAAMxB,eAGX,MAAMoK,EAAc,IAAIF,KAAkBC,GAC1CC,EAAYna,KAAK,CAACC,EAAGC,IAAMD,EAAE2I,EAAI1I,EAAE0I,GAEnC,MAAM2O,EAAO4C,EAAY,GAAGvR,EAEtBwR,GADOD,EAAYA,EAAYtmB,OAAS,GAAG+U,EACzB2O,IAAS4C,EAAYtmB,OAAS,GAGtDsmB,EAAYtoB,QAAQ,CAACoI,EAAS8a,KAC7B,MAAMsF,EAAO9C,EAAQxC,EAAQqF,EACzB,UAAWngB,EAEdmO,KAAKgM,SAASna,EAAiBogB,EAAMpgB,EAAQ4O,GAAG,GAAO,GAGvDT,KAAKsM,eAAeza,EAAuBogB,EAAMpgB,EAAQ4O,GAAG,GAAO,KAIrET,KAAKmJ,MAAMlC,QACZ,CAEA,oBAAAvZ,GACC,MAAMmkB,EAAgB7R,KAAK0K,QAAQphB,OAAOgc,GAAKA,EAAEuG,UAC3CiG,EAAmBxe,MAAMqX,KAAK3K,KAAKiJ,aAAa2B,UAAUthB,OAAOK,GAAKA,EAAEkiB,UAE9E,GAAIgG,EAAcpmB,OAASqmB,EAAiBrmB,OAAS,EAAG,OAExDuU,KAAKmJ,MAAMxB,eAGX,MAAMoK,EAAc,IAAIF,KAAkBC,GAC1CC,EAAYna,KAAK,CAACC,EAAGC,IAAMD,EAAE4I,EAAI3I,EAAE2I,GAEnC,MAAM4O,EAAO0C,EAAY,GAAGtR,EAEtBuR,GADOD,EAAYA,EAAYtmB,OAAS,GAAGgV,EACzB4O,IAAS0C,EAAYtmB,OAAS,GAGtDsmB,EAAYtoB,QAAQ,CAACoI,EAAS8a,KAC7B,MAAMuF,EAAO7C,EAAQ1C,EAAQqF,EACzB,UAAWngB,EAEdmO,KAAKgM,SAASna,EAAiBA,EAAQ2O,EAAG0R,GAAM,GAAO,GAGvDlS,KAAKsM,eAAeza,EAAuBA,EAAQ2O,EAAG0R,GAAM,GAAO,KAIrElS,KAAKmJ,MAAMlC,QACZ,CAGA,eAAAkL,CAAgBpH,EAAYc,GAEvBA,IACH7L,KAAK4L,gBAAgBb,EAAKJ,MAAM,GAChC3K,KAAK4L,gBAAgBb,EAAKC,IAAI,IAG/BhL,KAAK+L,gBACN,CAGA,SAAArc,GACC,MAAMqd,EAAgB/M,KAAKgN,yBAG3B,GAA4B,IAAxBD,EAAcrP,OAAwC,IAAzBqP,EAAc3O,OAC9C,OAID,MAAMgU,EAAgBxU,EAAI6P,eAAeC,aAAe,IAClD2E,EAAiBzU,EAAI6P,eAAeE,cAAgB,IAMpD2E,GAASF,EAAgB1D,IAAe3B,EAAcrP,MACtD6U,GAASF,EAAiB3D,IAAe3B,EAAc3O,OACvDoU,EAAcxjB,KAAKS,IAAI6iB,EAAOC,GAG9BE,EAAYzjB,KAAKQ,IAAIR,KAAKS,IAAI+iB,EAAa,GAAI,IAY/CE,EALkBN,EAAgB,GAJjBrF,EAAcvM,EAAIuM,EAAcrP,MAAQ,GASR+U,EACjDE,EALkBN,EAAiB,GAJlBtF,EAActM,EAAIsM,EAAc3O,OAAS,GASTqU,EAGjDnF,EAAY1P,EAAIwG,cAAc,UAChCkJ,GACHA,EAAUrP,aAAa,YAAa,aAAayU,MAAeC,YAAqBF,MAItF1E,IAGA6E,EAAc5S,KAAKrM,GACpB,CAGQ,eAAAkf,GACP,OAAO7S,KAAKwJ,cAAa,EAC1B,CAGQ,kBAAAsJ,CAAmBC,GAC1B/S,KAAK0J,aAAaqJ,GAAO,EAC1B,CAGA,aAAAhlB,GACC,OAAOiS,KAAKoJ,YACb,CAEA,YAAAlb,GACC,OAAO8R,KAAKqJ,WACb,CAEA,WAAAlN,GACC,OAAO6D,KAAKsJ,SACb,CAEA,UAAA9a,GACCwR,KAAKoJ,cAAgBpJ,KAAKoJ,aAC1BpJ,KAAKgT,oBAEL3kB,OAAO4kB,cAAc,IAAIC,YAAY,oBACtC,CAEA,gBAAAzkB,GACCuR,KAAKqJ,aAAerJ,KAAKqJ,YAEzBhb,OAAO4kB,cAAc,IAAIC,YAAY,oBACtC,CAEA,aAAAxkB,GACMsR,KAAKqJ,cAEVrJ,KAAKmJ,MAAMxB,eACX3H,KAAK0K,QAAQjhB,QAAQuU,IACpB,MAAMmV,EAAWnkB,KAAKC,MAAM+O,EAAKwC,EAAIR,KAAKsJ,WAAatJ,KAAKsJ,UACtD8J,EAAWpkB,KAAKC,MAAM+O,EAAKyC,EAAIT,KAAKsJ,WAAatJ,KAAKsJ,UAC5DtJ,KAAKgM,SAAShO,EAAMmV,EAAUC,GAAU,GAAO,KAEhDpT,KAAKmJ,MAAMlC,SACZ,CAGQ,UAAAjZ,CAAWwS,EAAWC,GAC7B,MAAO,CACND,EAAGxR,KAAKC,MAAMuR,EAAIR,KAAKsJ,WAAatJ,KAAKsJ,UACzC7I,EAAGzR,KAAKC,MAAMwR,EAAIT,KAAKsJ,WAAatJ,KAAKsJ,UAE3C,CAEA,iBAAA0J,GACC,IAAKpV,EAAK,OAGV,MAAMyV,EAAezV,EAAIwG,cAAc,iBACnCiP,GACHA,EAAavH,SAGd,MAAMwH,EAAmB1V,EAAIwG,cAAc,oBAK3C,GAJIkP,GACHA,EAAiBxH,UAGb9L,KAAKoJ,aAAc,OAGxB,IAAImK,EAAO3V,EAAIwG,cAAc,QACxBmP,IACJA,EAAO1iB,SAASgN,gBAAgB,6BAA8B,QAC9DD,EAAIuG,aAAaoP,EAAM3V,EAAIoR,aAG5B,MAAMwE,EAAU3iB,SAASgN,gBAAgB,6BAA8B,WACvE2V,EAAQ7f,GAAK,eACb6f,EAAQvV,aAAa,QAAS+B,KAAKsJ,UAAUhP,YAC7CkZ,EAAQvV,aAAa,SAAU+B,KAAKsJ,UAAUhP,YAC9CkZ,EAAQvV,aAAa,eAAgB,kBAErC,MAAMrM,EAAOf,SAASgN,gBAAgB,6BAA8B,QACpEjM,EAAKqM,aAAa,IAAK,KAAK+B,KAAKsJ,uBAAuBtJ,KAAKsJ,aAC7D1X,EAAKqM,aAAa,OAAQ,QAC1BrM,EAAKqM,aAAa,SAAU,WAC5BrM,EAAKqM,aAAa,eAAgB,KAClCrM,EAAKqM,aAAa,UAAW,OAE7BuV,EAAQ1V,YAAYlM,GACpB2hB,EAAKzV,YAAY0V,GAGjB,MAAMxS,EAAOnQ,SAASgN,gBAAgB,6BAA8B,QACpEmD,EAAKrN,GAAK,kBACVqN,EAAK/C,aAAa,IAAK,UACvB+C,EAAK/C,aAAa,IAAK,UACvB+C,EAAK/C,aAAa,QAAS,SAC3B+C,EAAK/C,aAAa,SAAU,SAC5B+C,EAAK/C,aAAa,OAAQ,sBAC1B+C,EAAK/C,aAAa,iBAAkB,QAGpC,MAAMqP,EAAY1P,EAAIwG,cAAc,UAChCkJ,GACHA,EAAUnJ,aAAanD,EAAMsM,EAAU0B,WAEzC,EASD,IAUIyE,EAVA7V,EAAqB/M,SAASuT,cAAc,aAC3CxG,IACJA,EAAM/M,SAASgN,gBAAgB,6BAA8B,OAC7DD,EAAIK,aAAa,KAAM,SACvBL,EAAItP,iBAAiB,QAAS3C,GAAK8nB,EAAc9nB,KAGlDiS,EAAIK,aAAa,QAAS,QAC1BL,EAAIK,aAAa,SAAU,QAG3B,IACIyV,EADAC,GAAW,EAIR,MAAMC,EAAa,CAACtW,EAAiBuW,EAAiC7oB,KAE5E4S,EAAIkW,URtmCe,uqBQumCnBjjB,SAASqI,KAAK6H,OAAOnD,GAErBA,EAAImW,OAASzW,EAEboW,EAAiBG,EAGjBJ,EAAgB9nB,MAOhBqoB,EAAY1W,GACZ,MAAM2W,EAAYvU,EAAOsB,KAAK,IAAK,IAAK,GAAI,GAAI,EAAG,WASnD,OARApD,EAAImD,OAAOkT,GAGX3W,EAAK0V,oBAGLkB,EAAqBtW,EAAK5S,GAEnB,CACN4S,MACAuW,YAWIH,EAAe1W,IAEpB,MAAM8W,EAAQ1U,EAAO7N,QAAQ,IAAK,CAAC,EAAG,QAChCwiB,EAAS3U,EAAO7N,QAAQ,IAAK,CAAC,EAAG,SACjCyiB,EAAS5U,EAAO7N,QAAQ,IAAK,CAAC,EAAG,SACjC0iB,EAAU7U,EAAO7N,QAAQ,IAAK,CAAC,EAAG,UACxCuiB,EAAMrT,OAAOwT,EAASD,EAAQD,GAG9B/W,EAAKpG,SAASzN,QAAS6b,KA+MxB,SAAmBA,EAAShI,GAE3BjP,OAAOmmB,MAAQlX,EAEf,MAAMiE,EAAI7B,EAAO7N,QAAQ,IAAK,CAAC,EAAG,QAClC0P,EAAEtD,aAAa,KAAMqH,EAAE3R,IACvB2R,EAAEuG,UAAYtK,EAAE1B,UAAUC,IAAI,YAC9B0B,EAAYD,EAAG+D,EAAE9E,EAAG8E,EAAE7E,GACtB,MAAMmJ,EAAOtE,EAAEsE,KACZlK,EAAO7N,QAAQ,IAAK,CACrBgF,KAAMyO,EAAEsE,KAAK/S,KACb,mBAAoByO,EAAEsE,KAAK9S,WAC3B,aAAc,QAAQwO,EAAExb,SACtB,YACD,KACG2qB,EAAU7K,GAAQrI,EACpBqI,IACHrI,EAAE1B,UAAUC,IAAI,UAChByB,EAAER,OAAO6I,IAIV,MAAM8K,EAAYpP,EAAEpZ,MAAM4X,OAAS,MAE7BA,GADUa,EAAO+P,EAAU7e,gBAAkB8O,EAAO5C,KACxB0S,EAASnP,GAE3CxB,EAAMjE,UAAUC,IAAI,cAGpBoI,EAAWpE,EAAOpO,EAAOif,YAEzB7Q,EAAM7F,aAAa,OAAQqH,EAAEpZ,MAAM6J,YACnC+N,EAAM7F,aAAa,SAAUqH,EAAEpZ,MAAMgK,QAErC4N,EAAM7F,aAAa,eAAgB,KACnC6F,EAAM7F,aAAa,UAAWpD,OAAOyK,EAAEpZ,MAAM0c,UAC7CgM,EAAe9Q,EAAOwB,EAAEpZ,MAAM2oB,QAE9B,MAAMC,ED/zCA,SAA0BvjB,EAA2B0E,GAC3D,MAAMyV,EAAQhM,EAAO7N,QAAQ,KAC7B,IAAI6d,GAAOne,EAAO8Y,WAAa,EA2B/B,OAzBA9Y,EAAO6Y,OAAO3gB,QAAS6gB,IACtB,MAAMjR,EAAOqG,EAAOrG,KAAK,GAAI,CAAC,cAAe,WAC7C6O,EAAW7O,EAAM4O,GACbhS,GACHoD,EAAK4E,aAAa,OAAQhI,GAEvBqU,EAAM/B,OACTlP,EAAK4E,aAAa,aAAcqM,EAAM/B,OAGvC+B,EAAM1L,MAAMnV,QAAQ,CAACmX,EAAM+L,KAC1B,MAAM9L,EAAOnB,EAAO7N,QAAQ,QAAS,CACpC2O,EAAG,EACHC,EAAGiP,EAAMpF,EAAMhK,SAAWqM,EAAQrC,EAAM7B,WACxC,YAAa,GAAG6B,EAAMhK,aACtB,cAAegK,EAAM/J,KAAO,OAAS,WAEtCM,EAAKT,YAAcQ,EACnBvH,EAAK0H,OAAOF,KAGb6K,EAAM3K,OAAO1H,GACbqW,GAAOpF,EAAM1L,MAAMnT,OAAS6e,EAAM7B,WAAa6B,EAAMhC,WAG/CoD,CACR,CCiyCYqJ,CAAiBzP,EAAEyE,cAAezE,EAAEpZ,MAAM+J,OAErDuL,EAAYsT,EAAI,GADKE,OAAOP,EAAQnG,aAAa,oBAAsB,GACrC,GAClCmG,EAAQ1T,OAAO+T,GAGfvT,EAAEwS,OAASzO,EACXA,EAAEjQ,IAAMkM,CAGT,CA9PE0T,CAAU3P,EAAGhI,GACb+W,EAAOtT,OAAOuE,EAAEjQ,OAGjBiI,EAAK0L,MAAMvf,QAAQshB,IAClBA,EAAKmK,iBAAcngB,IAEpBuI,EAAK0L,MAAMvf,QAAQkC,IAClBqiB,EAAU1Q,EAAM3R,GAChB2oB,EAAOvT,OAAOpV,EAAE0J,OAGjBiI,EAAK4L,UAAUzf,QAASiiB,IACvBuC,EAAWvC,GACX6I,EAAQxT,OAAO2K,EAAMrW,OAGtBuI,EAAImD,OAAOqT,IAGZ,SAASpG,EAAU1Q,EAAiByN,GACnC,MAAMoK,EAAKpK,EAAKJ,KAAMyK,EAAKrK,EAAKC,GAE1BzJ,EAAI7B,EAAO7N,QAAQ,IAAK,CAAC,EAAG,QAClC0P,EAAEtD,aAAa,KAAM8M,EAAKpX,IAC1B4N,EAAEtD,aAAa,YAAa8M,EAAKJ,KAAKhX,IACtC4N,EAAEtD,aAAa,UAAW8M,EAAKC,GAAGrX,IAElC,MAAM0hB,GAAYtK,EAAK7e,MAAMmpB,UAAY,IAAM,IAGzC5d,EC5mCA,SAA+BsT,EAAYzN,GACjD,MAAM6X,EAAKpK,EAAKJ,KAAMyK,EAAKrK,EAAKC,GAIhC,IAAIvT,EAAoBsT,EAAKtT,SAAWsT,EAAKtT,SAASgI,SAAW,GAKjE,GAAuB,GAAnBhI,EAAShM,SAAgBsf,EAAKS,oBAAqB,CAItD,MAAM8J,EAAYhY,EAAK0L,MAAM1f,OAAOqC,GAAKA,EAAEgf,MAAQI,EAAKJ,MAAQhf,EAAEqf,IAAMD,EAAKC,IAC7E,IAAIuK,EAAY,EAChB,GAAID,EAAU7pB,OAAS,EAAG,CAEzB8pB,EADYD,EAAU1I,QAAQ7B,IACXuK,EAAU7pB,OAAS,GAAK,EAE3C,IAAI+pB,EAAU,EAAGC,EAAU,EACvBzmB,KAAK2U,IAAIwR,EAAG3U,EAAI4U,EAAG5U,GAAKxR,KAAK2U,IAAIwR,EAAG1U,EAAI2U,EAAG3U,GAC9CgV,EAAsB,GAAZF,EAEVC,EAAsB,IAAZD,EAEX,MAAM5rB,EAAIohB,EAAKE,WAAW,CACzBzK,GAAI2U,EAAG3U,EAAI4U,EAAG5U,GAAK,EAAIgV,EACvB/U,GAAI0U,EAAG1U,EAAI2U,EAAG3U,GAAK,EAAIgV,IAExB9rB,EAAEggB,OAAQ,EACVhgB,EAAEwQ,MAAO,EACT1C,EAAS7N,KAAKD,EACf,CAMD,CAEA8N,EAASie,QAAQP,GACjB1d,EAAS7N,KAAKwrB,GAId,IAAIO,EAAqBle,EAASA,EAAShM,OAAS,GACpD,IAAK,IAAIkM,EAAI,EAAGA,EAAIF,EAAShM,OAAS,EAAGkM,IACxC,IAAMF,EAASE,GAAWgS,MAAO,CAChCgM,EAAqBle,EAASE,GAC9B,KACD,CAID,IAAIie,EAAoBne,EAAS,GACjC,IAAK,IAAIE,EAAIF,EAAShM,OAAS,EAAGkM,EAAI,EAAGA,IACxC,IAAMF,EAASE,GAAWgS,MAAO,CAChCiM,EAAoBne,EAASE,GAC7B,KACD,CAQD,MAAMke,EAA4B,CAAC7X,EAAW8X,KAC7C,MAAMC,EAAY/X,EAAK9R,OAAO4X,OAAOjO,eAAiB,MAChDiP,EAAKgR,EAAYtV,EAAIxC,EAAKwC,EAC1BM,EAAKgV,EAAYrV,EAAIzC,EAAKyC,EAIhC,GAHwBzC,EAAKwC,EAAMxC,EAAKyC,EAGpCzR,KAAK2U,IAAImB,GAAM,KAAQ9V,KAAK2U,IAAI7C,GAAM,IACzC,MAAO,CAAEN,EAAGxC,EAAKwC,EAAIxC,EAAKN,MAAQ,EAAG+C,EAAGzC,EAAKyC,GAG9C,GAAkB,aAAdsV,EAA0B,CAE7B,MAAM/T,EAAIhE,EAAKN,MACTwD,EAAKc,EAAI,EACTb,EAAKD,GAAM,IAAMc,EAAI,IACrBgU,EAAahY,EAAKI,OAAS,EAG3B6X,EAAQjnB,KAAKknB,MAAMpV,EAAIgE,GACvBqR,EAAMnnB,KAAKmnB,IAAIF,GACfG,EAAMpnB,KAAKonB,IAAIH,GAGrB,IAAI9V,EAAIiP,IACJpgB,KAAK2U,IAAIwS,GAAO,MACnBhW,EAAInR,KAAKS,IAAI0Q,EAAGnR,KAAK2U,IAAIzC,EAAKiV,KAE3BnnB,KAAK2U,IAAIyS,GAAO,MACnBjW,EAAInR,KAAKS,IAAI0Q,EAAGnR,KAAK2U,IAAIqS,EAAaI,KAGvC,MAAMC,EAAQrY,EAAKwC,EAAI2V,EAAMhW,EACvBmW,EAAQtY,EAAKyC,EAAI2V,EAAMjW,EAGvBoW,EAAYvY,EAAKyC,EAAIuV,EAAa7U,EAClCqV,EAAexY,EAAKyC,EAAIuV,EAAa7U,EAE3C,GAAImV,EAAQC,GAAaD,EAAQE,EAAc,CAE9C,MAAMC,EAAWH,EAAQC,EAAYvY,EAAKyC,EAAIuV,EAAa7U,EAAKnD,EAAKyC,EAAIuV,EAAa7U,EAEhFtJ,EAAI,GAAKqJ,EAAKA,GACdpJ,GAAK,EAAIkG,EAAKwC,GAAKU,EAAKA,GAGxBwV,EAAe5e,EAAIA,EAAI,EAAID,GAFtBmG,EAAKwC,EAAIxC,EAAKwC,GAAMU,EAAKA,IAAQuV,EAAWzY,EAAKyC,IAAMgW,EAAWzY,EAAKyC,IAAOU,EAAKA,GAAM,GAGpG,GAAIuV,GAAgB,EAAG,CACtB,MAAMC,EAAS3nB,KAAKoU,KAAKsT,GACnBE,IAAO9e,EAAI6e,IAAW,EAAI9e,GAC1Bgf,IAAO/e,EAAI6e,IAAW,EAAI9e,GAIhC,MAAO,CAAE2I,EADUsE,EAAK,EAAI9V,KAAKQ,IAAIonB,EAAIC,GAAM7nB,KAAKS,IAAImnB,EAAIC,GACpCpW,EAAGgW,EAC5B,CACD,CAEA,MAAO,CAAEjW,EAAG6V,EAAO5V,EAAG6V,EAEvB,CAAO,GAAkB,WAAdP,EAAwB,CAClC,MAAMe,EAAS9Y,EAAKN,MAAQ,EACtBuY,EAAQjnB,KAAKknB,MAAMpV,EAAIgE,GAC7B,MAAO,CACNtE,EAAGxC,EAAKwC,EAAIxR,KAAKmnB,IAAIF,GAASa,EAC9BrW,EAAGzC,EAAKyC,EAAIzR,KAAKonB,IAAIH,GAASa,EAGhC,CAAO,GAAkB,YAAdf,EAAyB,CACnC,MAAM7U,EAAkB,IAAblD,EAAKN,MACVyD,EAAkB,IAAbnD,EAAKN,MACVuY,EAAQjnB,KAAKknB,MAAMpV,EAAIgE,GACvBqR,EAAMnnB,KAAKmnB,IAAIF,GACfG,EAAMpnB,KAAKonB,IAAIH,GAGf9V,EAAInR,KAAKoU,KAAMlC,EAAKA,EAAKkV,EAAMA,EAAQjV,EAAKA,EAAKgV,EAAMA,GAC7D,MAAO,CACN3V,EAAGxC,EAAKwC,EAAKU,EAAKiV,EAAMhV,EAAMhB,EAC9BM,EAAGzC,EAAKyC,EAAKU,EAAKiV,EAAMlV,EAAMf,EAGhC,CAAO,CAEN,MAAM4W,EAAY/Y,EAAKN,MAAQ,EACzBsY,EAAahY,EAAKI,OAAS,EAC3B6X,EAAQjnB,KAAKknB,MAAMpV,EAAIgE,GACvBqR,EAAMnnB,KAAKmnB,IAAIF,GACfG,EAAMpnB,KAAKonB,IAAIH,GAGrB,IAAI9V,EAAIiP,IAQR,OAPIpgB,KAAK2U,IAAIwS,GAAO,MACnBhW,EAAInR,KAAKS,IAAI0Q,EAAGnR,KAAK2U,IAAIoT,EAAYZ,KAElCnnB,KAAK2U,IAAIyS,GAAO,MACnBjW,EAAInR,KAAKS,IAAI0Q,EAAGnR,KAAK2U,IAAIqS,EAAaI,KAGhC,CACN5V,EAAGxC,EAAKwC,EAAI2V,EAAMhW,EAClBM,EAAGzC,EAAKyC,EAAI2V,EAAMjW,EAEpB,GAOD,IAAI6W,EAAoBnB,EAA0BV,EAAIQ,GAGlDsB,EAAkBpB,EAA0BT,EAAIQ,GAQpD,OAHAne,EAAS,GAAKuf,EACdvf,EAASA,EAAShM,OAAS,GAAKwrB,EAEzBxf,CACR,CD66BkByf,CAAsBnM,EAAMzN,GAEvC6Z,ECx6BA,SACN1f,EACA4d,EACAhY,GAEA,IACI+Z,EADApU,EAAQ,CAACxC,EAAGnD,EAASmD,EAAGC,EAAGpD,EAASoD,GAExC,MAAM4W,EAAa5f,EAAS6f,UAAUnK,GAAWA,EAAsBxD,OACvE,IAAI4N,GAAU,EAEd,GAAIF,GAAc,EAAG,CACpB,MAAM5G,EAAchZ,EAAS4f,GAC7BrU,EAAQyN,EACR8G,GAA+B,IAArB9G,EAAYtW,KACtB,MAAMqd,EAA8B,GAChCH,EAAa,GAChBG,EAAiB5tB,KAAK,CAAC4L,EAAGiC,EAAS4f,EAAa,GAAInV,EAAGc,IAEpDqU,EAAa5f,EAAShM,OAAS,GAClC+rB,EAAiB5tB,KAAK,CAAC4L,EAAGwN,EAAOd,EAAGzK,EAAS4f,EAAa,KAE3DD,EAAUI,EAAiBhY,OAA4B,CAACiY,EAASphB,IAC3DohB,EAGErP,EAAkB/R,EAAUb,EAAGa,EAAU6L,GAC/CkG,EAAkBqP,EAAQjiB,EAAGiiB,EAAQvV,GACnC7L,EACAohB,EALKphB,OAMNtB,EACJ,KAAO,CACN,MAIM2iB,EAJcjgB,EAASnN,MAAM,GAAGkV,OACrC,CAACmY,EAAKxK,EAAQR,IAAUgL,EAAMvP,EAAkB3Q,EAASkV,GAAQQ,GACjE,GAEkCkI,EACnC,IAAIuC,EAAY,EAChB,IAAK,IAAIjL,EAAQ,EAAGA,EAAQlV,EAAShM,OAAQkhB,IAAS,CACrD,MAAMtW,EAAY,CAACb,EAAGiC,EAASkV,EAAQ,GAAIzK,EAAGzK,EAASkV,IACjDlhB,EAAS2c,EAAkB/R,EAAUb,EAAGa,EAAU6L,GACxD,GAAIzW,EAAS,GAAKmsB,EAAYnsB,GAAUisB,EAAc,CACrD,MAAMG,GAAmBH,EAAeE,GAAansB,EACrDuX,EAAQ,CACPxC,EAAGnK,EAAUb,EAAEgL,GAAKnK,EAAU6L,EAAE1B,EAAInK,EAAUb,EAAEgL,GAAKqX,EACrDpX,EAAGpK,EAAUb,EAAEiL,GAAKpK,EAAU6L,EAAEzB,EAAIpK,EAAUb,EAAEiL,GAAKoX,GAEtDT,EAAU/gB,EACV,KACD,CACAuhB,GAAansB,CACd,CACD,CAEA,MAAMqsB,EAAqBV,EAAUpoB,KAAK2U,IAAIyT,EAAQlV,EAAE1B,EAAI4W,EAAQ5hB,EAAEgL,GAAK,EACrEuX,EAAmBX,EAAUpoB,KAAK2U,IAAIyT,EAAQlV,EAAEzB,EAAI2W,EAAQ5hB,EAAEiL,GAAK,EACzE,MAAO,IACHuC,EACHgV,YAAaD,EAAmBD,EAAqB,WAAa,aAClEV,UACAG,UAEF,CD22BwBU,CAAwBxgB,EAAU4d,EAAUF,IAE7D+C,GAACA,EAAEvX,IAAEA,EAAG0D,KAAEA,GAyCjB,SAAwB8T,EAA+BpN,EAAYzN,GAClE,MAEMgD,EAAWyK,EAAK7e,MAAMoU,SAC5B,IAAIK,IAACA,EAAGG,GAAEA,EAAErC,KAAEA,GAAQiB,EAAOW,SAAS0K,EAAKpB,MAAO,IAAKrJ,GAAU,EAAO6X,EAAU3X,EAAG2X,EAAU1X,EAAG,UAClGK,GAAMR,EAAW,EACjB7B,GAAQ6B,EAER,MAAM8X,EAAmB,CAAC,CAAC5X,EAAG2X,EAAU3X,EAAGC,EAAG0X,EAAU1X,IACxD,GAAI0X,EAAUZ,SAAWY,EAAUf,QAClC,IAAK,MAAMiB,IAAY,CAAC,GAAK,IAAM,IAAM,GAAK,IAAM,CACnD,MAAM3X,EAAS,CACdF,EAAG2X,EAAUf,QAAQ5hB,EAAEgL,GACrB2X,EAAUf,QAAQlV,EAAE1B,EAAI2X,EAAUf,QAAQ5hB,EAAEgL,GAAK6X,EACnD5X,EAAG0X,EAAUf,QAAQ5hB,EAAEiL,GACrB0X,EAAUf,QAAQlV,EAAEzB,EAAI0X,EAAUf,QAAQ5hB,EAAEiL,GAAK4X,GAE/CD,EAAQ3jB,KAAK6jB,GACjBtpB,KAAK2U,IAAI2U,EAAS9X,EAAIE,EAAOF,GAAK,IAClCxR,KAAK2U,IAAI2U,EAAS7X,EAAIC,EAAOD,GAAK,KAElC2X,EAAQxuB,KAAK8W,EAEf,CAGD,MAAM6X,EAAW,IACbjb,EAAKoN,QAAQ1e,IAAIgS,GAAQwa,EAAU7W,EAAY3D,GAzB1B,OA0BrBV,EAAK0L,MACN1f,OAAOmvB,GAASA,IAAU1N,GAAQ0N,EAAMvD,aACxClpB,IAAIysB,GAASD,EAAUC,EAAMvD,YA5BP,KAoDnBrJ,EAtBauM,EAAQM,QAAQhY,GACJ,aAA1ByX,EAAUH,YACN,CAAC,GAAI,GAAGhsB,IAAI2sB,GAAQC,EAC1BlY,EAAOF,EAAImY,GAAQla,EAAO,EAlCZ,IAmCdiC,EAAOD,EACPhC,EACAqC,EACAJ,EACAyX,EACAI,IAGK,EAAE,EAAG,GAAGvsB,IAAI2sB,GAAQC,EAC1BlY,EAAOF,EACPE,EAAOD,EAAIkY,GAAQ7X,EAAK,EA7CT,IA8CfrC,EACAqC,EACAJ,EACAyX,EACAI,KAG0B/Y,OAAO,CAACqZ,EAAMxiB,IACzCA,EAAUyiB,MAAQD,EAAKC,MAAQziB,EAAYwiB,IAEtCjJ,QAACA,EAAOC,QAAEA,EAAOkJ,OAAEA,GAAUlN,EACnClL,EAAI0N,iBAAiB,SAAS5kB,QAASoX,IACtCA,EAAK5C,aAAa,IAAKpD,OAAO+U,MAE/BjP,EAAI1C,aAAa,IAAKpD,OAAOgV,EAAU/O,EAAK,IAE5CoH,EAAWvH,EAAKjL,EAAOsjB,UACvBrY,EAAI1C,aAAa,SAAU,QAC3B0C,EAAI1C,aAAa,YAAapD,OAAOkQ,EAAK7e,MAAMoU,WAChDK,EAAI1C,aAAa,OAAQ8M,EAAK7e,MAAM+J,OAEpC,MAAMoO,EAAO,IAAI0U,GACXb,EAAKxY,EAAOsB,KAAKqD,EAAK3G,MAAO2G,EAAKjG,OAAQiG,EAAK7D,EAAG6D,EAAK5D,GAO7D,OANAyH,EAAWgQ,EAAIxiB,EAAOujB,UACtBtY,EAAI1C,aAAa,aAAc,SAC/B8M,EAAKmK,YAAc6D,EAEnB1U,EAAK7D,GAAK6D,EAAK3G,MAAQ,EACvB2G,EAAK5D,GAAK4D,EAAKjG,OAAS,EACjB,CAAC8Z,KAAIvX,MAAK0D,OAClB,CAtHyB6U,CAAe/B,EAAgBpM,EAAMzN,GAC7DiE,EAAER,OAAOmX,EAAIvX,GAGb,MAAMwY,SAACA,EAAQvnB,KAAEA,GC52BX,SAA4B6F,EAAmB4M,EAAY8Q,EAAWC,GAC5E,MAAM+D,EAAsB,GAC5B,IAAK,IAAIxhB,EAAI,EAAGA,EAAIF,EAAShM,OAAQkM,IACpCwhB,EAASvvB,KAAK,CAAC4L,EAAGiC,EAASE,EAAI,GAAIuK,EAAGzK,EAASE,KAWhD,IAAI/F,EAKJ,GAZIunB,EAAS1tB,OAAS,GACD0tB,EAASA,EAAS1tB,OAAS,GN9N1C,SAA8B0tB,EAAqBpX,GACzD,IAAK,IAAIpK,EAAI,EAAGA,EAAIwhB,EAAS1tB,OAAQkM,IAAK,CACzC,MAAMjO,EAAIyvB,EAASxhB,GACnB,GAAI8J,EAAU/X,EAAE8L,EAAGuM,GACdN,EAAU/X,EAAEwY,EAAGH,IAClBoX,EAASrR,OAAOnQ,EAAG,GACnBA,GAAK,GAELjO,EAAE8L,EAAIoM,EAAkBlY,EAAE8L,EAAG9L,EAAEwY,EAAGH,GAAK,QAGxC,GAAIN,EAAU/X,EAAEwY,EAAGH,GAClBrY,EAAEwY,EAAIN,EAAkBlY,EAAE8L,EAAG9L,EAAEwY,EAAGH,GAAK,OACjC,CACN,MAAMrD,EAAMkD,EAAkBlY,EAAE8L,EAAG9L,EAAEwY,EAAGH,GACxC,GAAkB,GAAdrD,EAAIjT,OAAa,CAEPuD,KAAK2U,IAAIjF,EAAI,GAAG8B,EAAI9W,EAAE8L,EAAEgL,GAAKxR,KAAK2U,IAAIjF,EAAI,GAAG+B,EAAI/W,EAAE8L,EAAEiL,GACrDzR,KAAK2U,IAAIjF,EAAI,GAAG8B,EAAI9W,EAAE8L,EAAEgL,GAAKxR,KAAK2U,IAAIjF,EAAI,GAAG+B,EAAI/W,EAAE8L,EAAEiL,IACjD/B,EAAI0a,UAErB,MAAMC,EAAK,CAAC7jB,EAAGkJ,EAAI,GAAIwD,EAAGxY,EAAEwY,GAC5BxY,EAAEwY,EAAIxD,EAAI,GACVya,EAASrR,OAAOnQ,EAAI,EAAG,EAAG0hB,GAC1B1hB,GAAK,CACN,CACD,CAEF,CACD,CMoMC2hB,CAAqBH,EAAU9U,GAQ3B8U,EAAS1tB,OAAS,EAAG,CACxBmG,EAAO,IAAIunB,EAAS,GAAG3jB,EAAEgL,KAAK2Y,EAAS,GAAG3jB,EAAEiL,IAC5C,IAAK,IAAI9I,EAAI,EAAGA,EAAIwhB,EAAS1tB,OAAQkM,IAAK,CACzC,MAAMjO,EAAIyvB,EAASxhB,GAGnB/F,GAAQ,KAAKlI,EAAEwY,EAAE1B,KAAK9W,EAAEwY,EAAEzB,GAC3B,CACD,MAIC7O,EAAO,IAAIujB,EAAG3U,KAAK2U,EAAG1U,MAAM2U,EAAG5U,KAAK4U,EAAG3U,IAGxC,MAAO,CAAE0Y,WAAUvnB,OACpB,CDy0B0B2nB,CAAmB9hB,EAAU4M,EAAM8Q,EAAIC,GAE1D5f,EAAIkK,EAAO9N,KAAKA,EAAM,CAAC,aAAc,eAAgB,QAgC3D,OA/BA4D,EAAEyI,aAAa,OAAQ,QACvBzI,EAAEyI,aAAa,SAAU8M,EAAK7e,MAAM+J,OACpCT,EAAEyI,aAAa,eAAgBpD,OAAOkQ,EAAK7e,MAAMyc,YACjDnT,EAAEyI,aAAa,iBAAkB,SACjC8M,EAAK7e,MAAM2c,QAAUrT,EAAEyI,aAAa,mBAAoB,KACxDsD,EAAER,OAAOvL,GAKTuV,EAAKtT,SAAWA,EAASnN,MAAM,GAAI,GAAG0B,IAAIwJ,IAEzC,GAAI,OAAQA,GAAK,SAAUA,EAAG,CAE7B,MAAM7L,EAAI6L,EAEV,OADA7L,EAAEohB,KAAOA,EACFphB,CACR,CAEC,OAAOohB,EAAKE,WAAWzV,KAGzBuV,EAAKtT,SAAShO,QAAQ,CAAC+L,EAAGmC,KACzB,MAAMhO,EAAI6L,EACV7L,EAAE0L,IAAMqK,EAAO7N,QAAQ,SAAU,CAAC8B,GAAIhK,EAAEgK,GAAI6lB,GAAIhkB,EAAEgL,EAAGwE,GAAIxP,EAAEiL,EAAGQ,EAAG,EAAGiO,KAAM,QAAS,SACnFvlB,EAAEkiB,UAAYliB,EAAE0L,IAAIwK,UAAUC,IAAI,YAClCnW,EAAEwQ,MAAQxQ,EAAE0L,IAAIwK,UAAUC,IAAI,QAC9ByB,EAAER,OAAOpX,EAAE0L,OAGZ0V,EAAK1V,IAAMkM,EACJA,CACR,CAiFA,SAASqX,EACRhJ,EACAC,EACAnS,EACAU,EACAsC,EACAyX,EACAI,GAEA,MAAMQ,EAAS,CACdvY,EAAGoP,EAAUlS,EAAQ,EACrB+C,EAAGoP,EAAUzR,EAAS,EACtBV,QACAU,UAEKqb,EAAUlB,EAAS/Y,OACxB,CAACka,EAAO3X,KAAQ2X,UAoBMC,EApBiBZ,EAoBJa,EApBY7X,EAqBlC/S,KAAKQ,IAClB,EACAR,KAAKS,IAAIkqB,EAAMnZ,EAAImZ,EAAMjc,MAAOkc,EAAOpZ,EAAIoZ,EAAOlc,OACjD1O,KAAKQ,IAAImqB,EAAMnZ,EAAGoZ,EAAOpZ,IAEZxR,KAAKQ,IACnB,EACAR,KAAKS,IAAIkqB,EAAMlZ,EAAIkZ,EAAMvb,OAAQwb,EAAOnZ,EAAImZ,EAAOxb,QAClDpP,KAAKQ,IAAImqB,EAAMlZ,EAAGmZ,EAAOnZ,KAT5B,IAAwBkZ,EAAaC,GAnBnC,GAED,MAAO,CACNhK,UACAC,UACAkJ,SACAD,MAAiB,IAAVW,EAAiBrR,EAAkB1H,EAAQyX,GAEpD,CAEA,SAASK,EAAUzW,EAAW2M,GAC7B,MAAO,CACNlO,EAAGuB,EAAIvB,EAAIkO,EACXjO,EAAGsB,EAAItB,EAAIiO,EACXhR,MAAOqE,EAAIrE,MAAkB,EAAVgR,EACnBtQ,OAAQ2D,EAAI3D,OAAmB,EAAVsQ,EAEvB,CAoEA,SAAST,EAAWvC,GACnB,GAA0B,GAAtBA,EAAMhB,MAAMjf,OACf,OAED,MAAM8V,EAAI7B,EAAO7N,QAAQ,IAAK,CAAC,EAAG,SAElC,IAAIgoB,EAAY,CAACrZ,EAAG,MAAOC,EAAG,OAAQoB,EAAY,CAACrB,EAAG,EAAGC,EAAG,GAC5DiL,EAAMhB,MAAMjhB,QAAQ6b,IAEnB,MAAMxB,EAASwB,EAAWpZ,OAAO4X,OAAOjO,eAAiB,MAEzD,IAAIikB,EAAexU,EAAElH,OAAS,EAC1B2b,EAAkBzU,EAAElH,OAAS,EAEjC,GAAc,UAAV0F,EAAmB,CAGtB,MAAMgC,EAAsB,IAAXR,EAAElH,OACnB0b,EAAexU,EAAElH,OAAS,EAAI0H,EAC9BiU,EAAkBzU,EAAElH,OAAS,CAC9B,MAAO,GAAc,YAAV0F,EAAqB,CAG/B,MAAMkW,EAAY1U,EAAE5H,MAAQ,EAAI,KAChCoc,EAAeE,EACfD,EAAkBC,CACnB,CAEA,MAAMliB,EAAI,CACT0I,EAAG8E,EAAE9E,EAAI8E,EAAE5H,MAAQ,EACnB+C,EAAG6E,EAAE7E,EAAIqZ,EACTpc,MAAO4H,EAAE5H,MACTU,OAAQ0b,EAAeC,GAExBF,EAAGrZ,EAAIxR,KAAKS,IAAIoqB,EAAGrZ,EAAG1I,EAAE0I,GACxBqZ,EAAGpZ,EAAIzR,KAAKS,IAAIoqB,EAAGpZ,EAAG3I,EAAE2I,GACxBoB,EAAGrB,EAAIxR,KAAKQ,IAAIqS,EAAGrB,EAAG1I,EAAE0I,EAAI1I,EAAE4F,OAC9BmE,EAAGpB,EAAIzR,KAAKQ,IAAIqS,EAAGpB,EAAG3I,EAAE2I,EAAI3I,EAAEsG,UAE/B,MAEM4D,EAAIhT,KAAKQ,IAAIqS,EAAGrB,EAAIqZ,EAAGrZ,EAAG,KAC1ByB,EAAIJ,EAAGpB,EAAIoZ,EAAGpZ,EACd+M,EAAK,CACVhN,EAAGqZ,EAAGrZ,EALK,GAMXC,EAAGoZ,EAAGpZ,EANK,GAOX/C,MAAOsE,EAAIiY,GACX7b,OAAQ6D,EAAIgY,GAPO,IASdhZ,EAAIvB,EAAOsB,KAAKwM,EAAG9P,MAAO8P,EAAGpP,OAAQoP,EAAGhN,EAAGgN,EAAG/M,GACpDiL,EAAMlL,EAAIgN,EAAGhN,EAAIgN,EAAG9P,MAAQ,EAC5BgO,EAAMjL,EAAI+M,EAAG/M,EAAI+M,EAAGpP,OAAS,EAC7BsN,EAAMhO,MAAQ8P,EAAG9P,MACjBgO,EAAMtN,OAASoP,EAAGpP,OAClB8J,EAAWjH,EAAGvL,EAAOwkB,WACrBxO,EAAMxf,MAAMgK,QAAU+K,EAAEhD,aAAa,SAAUyN,EAAMxf,MAAMgK,QAC3DwV,EAAMxf,MAAM6J,YAAckL,EAAEhD,aAAa,OAAQyN,EAAMxf,MAAM6J,YAE7D,MAAM4K,EAAMjB,EAAOrG,KAAKqS,EAAMzW,KAAM,CAACuL,EAAGqZ,EAAGrZ,EAAGC,EAAG+M,EAAG/M,EAAI+M,EAAGpP,OAAS1I,EAAOykB,UAAU,eACrFjS,EAAWvH,EAAKjL,EAAOykB,WACvBzO,EAAMxf,MAAM+J,OAAS0K,EAAI1C,aAAa,OAAQyN,EAAMxf,MAAM+J,OAE1DsL,EAAER,OAAOE,EAAGN,GACZ+K,EAAMrW,IAAMkM,CACb,CAEA,SAAS6Y,EAAmB1vB,EAAkB8K,GAE7C,IAAI6kB,EAAM,CAACC,IAAKtF,OAAOuF,kBAAmBlX,KAAM,EAAG0H,KAAM,KAAcyP,IAAK,MAa5E,OAZA9vB,EAAMse,MAAMvf,QAAQshB,IACnB,MAAMtT,EAAWsT,EAAKtT,UAAY,GAC5BgjB,EAAM,CAAC1P,EAAKJ,QAASlT,EAAUsT,EAAKC,IAC1C,IAAK,IAAIrT,EAAI,EAAGA,EAAI8iB,EAAIhvB,OAAQkM,IAAK,CACpC,MAAM6iB,EAAMlX,EAAQ9N,EAAGilB,EAAI9iB,EAAI,GAAI8iB,EAAI9iB,IACjC2iB,EAAM5W,EAAYlO,EAAGglB,GACvBF,EAAM,IACNA,EAAMD,EAAIC,MACbD,EAAM,CAACC,MAAKjX,IAAK1L,EAAG6iB,MAAKzP,QAE3B,IAEMsP,EAAItP,KAAOsP,EAAM,IACzB,CAEA,SAASK,EAAe/uB,GAEvB,MAAMmM,EAAI8F,EAAI+c,wBACRC,EAAIvN,IAGJwN,EAAmBC,IAIzB,MAAO,CACNta,GAAI7U,EAAEovB,QAAUjjB,EAAE0I,EAAIqa,EAAiBra,GAAKoa,EAC5Cna,GAAI9U,EAAEqvB,QAAUljB,EAAE2I,EAAIoa,EAAiBpa,GAAKma,EAE9C,CASA,SAASK,EAA2Brd,EAAoBsd,EAUrDlwB,GACF,IAAImwB,EAA6C,GAC7CC,EAAe,KACfC,GAAY,EACZC,EAAY,EACZC,EAAY,EACZC,EAAmB,CAAEhb,EAAG,EAAGC,EAAG,GAC9Bgb,EAAqE,KACrEC,EAAmC,KACnCC,GAAa,EACbC,GAAoB,EAGxB,MAAMC,EAA8F,GAgRpG,SAAS9uB,EAAQpB,GAChB,MAAMC,EAASD,EAAEC,OACXA,aAAkBkwB,SAAalwB,EAAOmwB,QAAQ,gBACpDpwB,EAAE0P,iBACGugB,IACLjwB,EAAEqwB,kBACFJ,GAAoB,GACrB,CAyDA,OAtDA,SAAgB/pB,GACf,IAAIoqB,EAAwC,KAE5C,SAASC,EAAavwB,GACrB,MAAI,mBAAoBA,GAAKA,EAAEwwB,eACvBxwB,EAAEwwB,eAAe,GAElBxwB,CACR,CAEA,SAASywB,EAAmBzwB,GACtBswB,GAjHP,SAAqBtwB,EAAemZ,EAAYhE,GAG/C,IAAK6a,IAAe3sB,KAAK2U,IAAImB,GADP,GAC8B9V,KAAK2U,IAAI7C,GADvC,KAErB6a,GAAa,EACbD,EAAoB,KAGhBD,GAAwB,CAC3B,MAAM/Q,EAAQwQ,EAAKmB,eACnB3R,EAAMjf,OAAS,EACfif,EAAM9gB,KAAK6xB,EAAuBzd,MAClCkd,EAAKoB,aAAa5R,GAElByQ,EAAM,CAAC,CAAE3a,EAAGib,EAAuBzd,KAAKwC,EAAGC,EAAGgb,EAAuBzd,KAAKyC,EAAG6E,EAAGmW,EAAuBzd,OACvGyd,EAAyB,IAC1B,CAGD,GAAIJ,GA9GL,SAAsB7a,EAAWC,GAChC,MAAM6M,EAAY1P,EAAIwG,cAAc,UACpC,IAAKkJ,EAAW,OAEhB,MAAM1e,EAAOye,IACbC,EAAUrP,aAAa,YAAa,aAAauC,MAAMC,YAAY7R,KACpE,CA6GE2tB,CAFaf,EAAiBhb,EAAIsE,EACrB0W,EAAiB/a,EAAIK,QAE5B,GAAIqa,EAAI1vB,OAAS,GAAKkwB,EAAY,CAGxC,MAAM/sB,EAAOssB,EAAK7N,UACZmP,EAAY1X,EAAKlW,EACjB6tB,EAAY3b,EAAKlS,EACvBusB,EAAI1xB,QAAQ4K,IAGX6mB,EAAKlP,SAAS3X,EAAKiR,EAAGjR,EAAKmM,EAAIgc,EAAWnoB,EAAKoM,EAAIgc,KAEpDvB,EAAKwB,aAAY,EAClB,MAAWtB,IAEVA,EAAQuB,OAAOhxB,GACfuvB,EAAKwB,aAAY,GAEnB,CAyEEE,CADAjxB,EAAIuwB,EAAavwB,GACFA,EAAEovB,QAAUkB,EAAGY,GAAIlxB,EAAEqvB,QAAUiB,EAAGa,GAClD,CASA,SAASC,EAAiBpxB,GANzBkF,SAAStC,oBAAoB,YAAa6tB,GAC1CvrB,SAAStC,oBAAoB,YAAa6tB,GAC1CvrB,SAAStC,oBAAoB,UAAWwuB,GACxClsB,SAAStC,oBAAoB,WAAYwuB,GA9E3C,SAAmBpxB,GAClBuvB,EAAKwB,aAAY,GACjB,MAAMM,EAAarB,EAAa,KAAOD,EASvC,GARIC,IACHC,GAAoB,EACpBvtB,OAAOkZ,WAAW,KACjBqU,GAAoB,GAClB,IAIAH,IAA2BE,EAAY,CAC1C,MAAMjR,EAAQwQ,EAAKmB,eACnB3R,EAAMjf,OAAS,EACfif,EAAM9gB,KAAK6xB,EAAuBzd,MAClCkd,EAAKoB,aAAa5R,EACnB,CAEA,GAAI0Q,EAAS,CACZ,MAAMrZ,EAAMqZ,EAAQ6B,MAChBlb,EACHmZ,EAAKgC,aAAanb,EAAKpW,EAAEwxB,UACdhC,EAAI1vB,QAEfyvB,EAAKoB,aAAa,IAEnBlB,EAAU,IACX,CAGA,GAAIC,GAAaM,EAAY,CAC5B,MAAMpO,EAAa3P,EAAYmW,OAC3BxG,GAAaA,EAAU5Z,IAC1Bif,EAAcrF,EAAU5Z,GAE1B,CAGA8nB,EAAyB,KACzBC,EAAoB,KACpBC,GAAa,EACbN,GAAY,EACZH,EAAKnN,gBACDiP,IACH3uB,OAAO6D,SAAS2E,KAAOmmB,EAEzB,CAqCEI,CAAUlB,EAAavwB,IACvBswB,EAAK,IACN,CAEA,SAASoB,EAAmB1xB,GAC3BA,EAAIuwB,EAAavwB,GACjBswB,EAAK,CAAEY,GAAIlxB,EAAEovB,QAAS+B,GAAInxB,EAAEqvB,SAxN9B,SAAqBrvB,GACpBA,EAAE0P,iBACFsgB,GAAa,EACbF,EAAyB,KACzB,MAAM7vB,EAASD,EAAEC,OACXge,EAAOhe,aAAkBkwB,QAAUlwB,EAAOmwB,QAAQ,cAAgB,KACxEL,EAAoB/vB,EAAEwxB,SAAW,KAAOvT,GAAM0E,aAAa,SAAW,KAEtE,MAAMtQ,EAAOkd,EAAKoC,cAAc3xB,GAG1B4xB,EAAgB5xB,EAAEwxB,SAAyB,QAAbnyB,EAAqB,SAAW,MAASA,EAE7E,GAAKgT,EAsBL,GAAsB,QAAlBuf,EAAyB,CAE5BlC,GAAY,EACZD,EAAU,KAEV,MAAM1Q,EAAQwQ,EAAKmB,eACfnB,EAAKsC,WAAWxf,GAEnBmd,EAAMzQ,EAAM1e,IAAIsZ,IAAC,CAAO9E,EAAG8E,EAAE9E,EAAGC,EAAG6E,EAAE7E,EAAG6E,QAIxC4V,EAAKoB,aAAa,CAACte,IACnBmd,EAAM,CAAC,CAAE3a,EAAGxC,EAAKwC,EAAGC,EAAGzC,EAAKyC,EAAG6E,EAAGtH,IAEpC,KAAO,CAENqd,GAAY,EACZD,EAAU,KACV,MAAM1Q,EAAQwQ,EAAKmB,eAEnB,GAAI1wB,EAAEwxB,UAAyB,WAAbnyB,EAAuB,CAExC,GAAIkwB,EAAKsC,WAAWxf,GAAO,CAC1B,MAAM2O,EAAQjC,EAAM4M,UAAUhS,GAAKA,EAAE3R,KAAOqK,EAAKrK,IAC7CgZ,GAAS,GAAGjC,EAAM5C,OAAO6E,EAAO,EACrC,MACCjC,EAAM9gB,KAAKoU,GAEZkd,EAAKoB,aAAa5R,GAClByQ,EAAMzQ,EAAM1e,IAAIsZ,IAAC,CAAO9E,EAAG8E,EAAE9E,EAAGC,EAAG6E,EAAE7E,EAAG6E,MACzC,MAEK4V,EAAKsC,WAAWxf,IAEnBmd,EAAMzQ,EAAM1e,IAAIsZ,IAAC,CAAO9E,EAAG8E,EAAE9E,EAAGC,EAAG6E,EAAE7E,EAAG6E,OAExCmW,EAAyB,OAGzBA,EAAyB,CAAEzd,OAAMmf,SAAUxxB,EAAEwxB,UAE7ChC,EAAM,CAAC,CAAE3a,EAAGxC,EAAKwC,EAAGC,EAAGzC,EAAKyC,EAAG6E,EAAGtH,IAGrC,KAlEuB,QAAlBuf,GAEHlC,GAAY,EACZD,EAAU,KACVE,EAAY3vB,EAAEovB,QACdQ,EAAY5vB,EAAEqvB,QACdQ,EA3CH,WACC,MAAMlO,EAAY1P,EAAIwG,cAAc,UACpC,IAAKkJ,EAAW,MAAO,CAAE9M,EAAG,EAAGC,EAAG,GAElC,MACMgd,GADYnQ,EAAUgB,aAAa,cAAgB,IACxBoP,MAAM,gCACvC,OAAID,EACI,CACNjd,EAAGmd,WAAWF,EAAe,KAAO,EACpChd,EAAGkd,WAAWF,EAAe,KAAO,GAG/B,CAAEjd,EAAG,EAAGC,EAAG,EACnB,CA8BsBmd,GACnBzC,EAAM,GAEND,EAAKoB,aAAa,MAGlBjB,GAAY,EACZD,EAzHH,WACC,IAAIyC,EAAgB,EAAGC,EAAgB,EAAG9c,EAA8B,KAExE,MAAO,CACN,GAAAma,CAAIxvB,GAEH,MAAMoyB,EAAKrD,EAAe/uB,GAC1BkyB,EAAgBE,EAAGvd,EACnBsd,EAAgBC,EAAGtd,EAEnBO,EAAOnQ,SAASgN,gBAAgB,6BAA8B,QAC9DmD,EAAK/C,aAAa,OAAQ,0BAC1B+C,EAAK/C,aAAa,SAAU,0BAC5B+C,EAAK/C,aAAa,eAAgB,KAClC+C,EAAK/C,aAAa,mBAAoB,OACtC+C,EAAK/C,aAAa,IAAKpD,OAAOgjB,IAC9B7c,EAAK/C,aAAa,IAAKpD,OAAOijB,IAC9B9c,EAAK/C,aAAa,QAAS,KAC3B+C,EAAK/C,aAAa,SAAU,KAG5B,MAAMqP,EAAY1P,EAAIwG,cAAc,UAChCkJ,EACHA,EAAUxP,YAAYkD,GAEtBpD,EAAIE,YAAYkD,EAElB,EACA,MAAA2b,CAAOhxB,GACN,IAAKqV,EAAM,OAGX,MAAMgd,EAAYtD,EAAe/uB,GAC3BsyB,EAAkBD,EAAUxd,EAC5B0d,EAAkBF,EAAUvd,EAG5BD,EAAIxR,KAAKS,IAAIouB,EAAeI,GAC5Bxd,EAAIzR,KAAKS,IAAIquB,EAAeI,GAC5BxgB,EAAQ1O,KAAK2U,IAAIsa,EAAkBJ,GACnCzf,EAASpP,KAAK2U,IAAIua,EAAkBJ,GAE1C9c,EAAK/C,aAAa,IAAKpD,OAAO2F,IAC9BQ,EAAK/C,aAAa,IAAKpD,OAAO4F,IAC9BO,EAAK/C,aAAa,QAASpD,OAAO6C,IAClCsD,EAAK/C,aAAa,SAAUpD,OAAOuD,GACpC,EACA,GAAA6e,GACC,IAAKjc,EAAM,OAAO,KAGlB,MAAMR,EAAImd,WAAW3c,EAAKsN,aAAa,MAAQ,KACzC7N,EAAIkd,WAAW3c,EAAKsN,aAAa,MAAQ,KACzC5Q,EAAQigB,WAAW3c,EAAKsN,aAAa,UAAY,KACjDlQ,EAASuf,WAAW3c,EAAKsN,aAAa,WAAa,KAMzD,OAJAtN,EAAK8K,SACL9K,EAAO,KAGHtD,EAAQ,GAAKU,EAAS,EAClB,CACNoC,EAAGA,EAAGC,EAAGA,EAAG/C,MAAOA,EAAOU,OAAQA,EAClCoR,KAAMhP,EAAGkP,IAAKjP,EAAGgP,MAAOjP,EAAI9C,EAAOiS,OAAQlP,EAAIrC,GAG1C,IACR,EAEF,CAoDa+f,GACN/C,GAASA,EAAQD,IAAIxvB,GACzBwvB,EAAM,GAoDT,CAwIEiD,CAAYzyB,GACZkF,SAASvC,iBAAiB,YAAa8tB,GACvCvrB,SAASvC,iBAAiB,YAAa8tB,GACvCvrB,SAASvC,iBAAiB,UAAWyuB,GACrClsB,SAASvC,iBAAiB,WAAYyuB,EACvC,CAEAlrB,EAAQvD,iBAAiB,YAAa+uB,GACtCxrB,EAAQvD,iBAAiB,aAAc+uB,GAGvCxB,EAAejyB,KACd,CAAEiI,UAASwsB,MAAO,YAAaC,QAASjB,GACxC,CAAExrB,UAASwsB,MAAO,aAAcC,QAASjB,GAE3C,CAEAkB,CAAO3gB,GACPA,EAAItP,iBAAiB,QAASvB,GAC9B8uB,EAAejyB,KAAK,CAAEiI,QAAS+L,EAAKygB,MAAO,QAASC,QAASvxB,IAGtD,KACN8uB,EAAepyB,QAAQ,EAAGoI,UAASwsB,QAAOC,cACzCzsB,EAAQtD,oBAAoB8vB,EAAOC,KAGtC,CAEO,SAASpK,EAAqBtW,EAAoB5S,GAExD,MAAMwzB,EAAmB5gB,EAAY6gB,2BAKrC,SAASC,EAAQrrB,GAEhB,OAAOA,EAAG0gB,MACX,CAPIyK,GACHA,IAQD,MAAMG,EAAK,IAAOD,EAAQ9gB,GAGpBie,EAA8F,GAE9F+C,EAAuBjzB,IACvBgzB,IAAK7uB,YACVnE,EAAE0P,iBACF1P,EAAEkzB,YAAc,KAKjB,SAASC,EAAe5e,EAAW2L,GAClC3L,EAAE2L,SAAWA,EACb,MAAMkT,EAAQnhB,EAAIwG,cAAc,IAAMlE,EAAEvM,IACxCuM,EAAE2L,SAAWkT,EAAMlf,UAAUC,IAAI,YAAcif,EAAMlf,UAAUiM,OAAO,WACvE,CAPAzd,OAAOC,iBAAiB,eAAgBswB,GACxC/C,EAAejyB,KAAK,CAAEiI,QAASxD,OAAQgwB,MAAO,eAAgBC,QAASM,IASvE,MAAMI,EAAoBrzB,IACzB,IAAKA,EAAEszB,OAAQ,OACf,MAAM5E,EAAMD,EAAmBuE,IAAMjE,EAAe/uB,IACpD,GAAI0uB,EAAK,CACR,MAAMG,IAACA,GAAOH,EACRrmB,EAAS4J,EAAIwG,cAAc,WACjC,IAAIX,EAAMzP,EAAOoQ,cAAc,QAC1BX,IACJA,EAAM/D,EAAO7N,QAAQ,SAAU,CAAC8B,GAAI,MAAO6lB,GAAIgB,EAAIha,EAAGwE,GAAIwV,EAAI/Z,EAAGQ,EAAG,IACpEjN,EAAO+M,OAAO0C,IAEfA,EAAIxF,aAAa,KAAMpD,OAAO2f,EAAIha,IAClCiD,EAAIxF,aAAa,KAAMpD,OAAO2f,EAAI/Z,GACnC,MACCye,KAGFthB,EAAItP,iBAAiB,YAAa0wB,GAClCnD,EAAejyB,KAAK,CAAEiI,QAAS+L,EAAKygB,MAAO,YAAaC,QAASU,IAEjE,MAAMG,EAAgBxzB,IACrB,MAAM9B,GAAM,EAAAsR,EAAAC,IAAazP,GAAG,GACxB9B,GAAOsR,EAAAikB,IAAcv1B,GAAOsR,EAAAkkB,IAChCH,KAKD,SAASA,IACR,MAAM7rB,EAAKuK,EAAIwG,cAAc,gBAC7B/Q,GAAMA,EAAGoa,cAAcnP,YAAYjL,EACpC,CANAhF,OAAOC,iBAAiB,QAAS6wB,GACjCtD,EAAejyB,KAAK,CAAEiI,QAASxD,OAAQgwB,MAAO,QAASC,QAASa,IAOhE,MAAMG,EAAgB3zB,IACrB,MAAM9B,GAAM,EAAAsR,EAAAC,IAAazP,GAAG,GAC5B,GAAI9B,GAAOsR,EAAAkkB,IAAoBx1B,GAAOsR,EAAAikB,GAAY,OAClD,MAAM/E,EAAMD,EAAmBuE,IAAMjE,EAAe/uB,IACpD,GAAI0uB,EAAK,CACR,MAAMtP,KAACA,EAAI1H,IAAEA,EAAGmX,IAAEA,GAAOH,EAEzBsE,IAAKnS,iBAAiBzB,EAAMyP,EAAKnX,EAAKxZ,GAAOsR,EAAAkkB,IAC7CH,GACD,GAEDthB,EAAItP,iBAAiB,QAASgxB,GAC9BzD,EAAejyB,KAAK,CAAEiI,QAAS+L,EAAKygB,MAAO,QAASC,QAASgB,IAE7D,MAAMC,EAAgB5zB,IAGrB,MAAM6zB,EAA8B,GAAtBxwB,KAAKywB,KAAK9zB,EAAE+zB,QACpB3wB,EAAcse,IACdsS,EAAU3wB,KAAKQ,IAAI,GAAKR,KAAKS,IAAI,EAAGV,EAAcywB,IAExD,GAAIG,IAAY5wB,EAAa,CAE5B,MAAMiS,EAAOpD,EAAI+c,wBAGjBiF,EAAgBD,EAFHh0B,EAAEovB,QAAU/Z,EAAKwO,KACjB7jB,EAAEqvB,QAAUha,EAAK0O,KAE9B/jB,EAAE0P,iBAGF,MAAMkS,EAAa3P,EAAYmW,OAC3BxG,GAAaA,EAAU5Z,IAC1Bif,EAAcrF,EAAU5Z,GAE1B,GAEDiK,EAAItP,iBAAiB,QAASixB,GAC9B1D,EAAejyB,KAAK,CAAEiI,QAAS+L,EAAKygB,MAAO,QAASC,QAASiB,IAE7D,MAAMM,EAAkBl0B,IACvB,MAAMuP,GAAW,EAAAC,EAAAC,IAAazP,GAO9B,OAJIuP,GACHvP,EAAE0P,iBAGKH,GACP,KAAKC,EAAA2kB,GACqBxsB,MAAMqX,KAAKgU,IAAK1V,aAAa2B,UAAUthB,OAAOK,GAAKA,EAAEkiB,UAC7DpiB,QAAQE,IACxBg1B,IAAKjS,iBAAiB/iB,KAEvB,MACD,IAAK,OACJg1B,IAAKvxB,OACL,MACD,IAAK,OACJuxB,IAAKtxB,OACL,MACD,KAAK8N,EAAA4kB,GAGJH,EAFkB5wB,KAAKS,IAAI,EAAe,IAAZ4d,MAG9BuF,EAAc+L,IAAKhrB,IACnB,MACD,KAAKwH,EAAA6kB,GAGJJ,EAFmB5wB,KAAKQ,IAAI,GAAK6d,IAAY,MAG7CuF,EAAc+L,IAAKhrB,IACnB,MACD,KAAKwH,EAAA8kB,GAEJL,EAAgB,GAChBhN,EAAc+L,IAAKhrB,IACnB,MACD,KAAKwH,EAAA+kB,GACJvB,IAAKjvB,YAEL,MACD,KAAKyL,EAAAglB,EACJxB,IAAKjU,QAAQjhB,QAAQ6b,GAAKqZ,IAAK/S,gBAAgBtG,GAAG,IAClDqZ,IAAK1V,aAAaxf,QAAQE,GAAKm1B,EAAen1B,GAAG,IACjD,MACD,KAAKwR,EAAAilB,GACJzB,IAAKjU,QAAQjhB,QAAQ6b,GAAKqZ,IAAK/S,gBAAgBtG,GAAG,IAClDqZ,IAAK1V,aAAaxf,QAAQE,GAAKm1B,EAAen1B,GAAG,MAIpD0E,OAAOC,iBAAiB,UAAWuxB,GACnChE,EAAejyB,KAAK,CAAEiI,QAASxD,OAAQgwB,MAAO,UAAWC,QAASuB,IAGlE,MAAMQ,EAA2BpF,EAA2Brd,EAAK,CAChE,aAAA0f,CAAc3xB,GACbA,EAAE0P,iBAEF,IAAIhI,EAAM1H,EAAEC,OAAsBmwB,QAAQ,kBAC1C,OAAI1oB,EAAWqrB,EAAQrrB,IAEvBA,EAAM1H,EAAEC,OAAsBmwB,QAAQ,yBAClC1oB,EACIsrB,IAAK1V,aAAa7W,IAAIiB,EAAGM,IAE1B,KACR,EACA,YAAA2oB,CAAagE,GAEZ3B,IAAKjU,QAAQjhB,QAAQ6b,GAAKqZ,IAAK/S,gBAAgBtG,EAAGgb,EAAQ7rB,KAAKwN,GAAKA,EAAEtO,IAAM2R,EAAE3R,MAE9EgrB,IAAK1V,aAAaxf,QAAQyW,GAAK4e,EAAe5e,EAAGogB,EAAQ7rB,KAAKwN,GAAKA,EAAEtO,IAAMuM,EAAEvM,MAC7E+f,EAAeiL,IAAKjU,QAAQnV,KAAK+P,GAAKA,EAAEuG,UACzC,EACA,WAAA6Q,CAAYxc,GACXyT,EAAWzT,CACZ,EACAsd,WAAW+C,GACHA,EAAO1U,SAEf,YAAAwQ,GACC,MAAM3d,EAAgBigB,IAAKjU,QAAQphB,OAAOgc,GAAKA,EAAEuG,UAEjD,OADA8S,IAAK1V,aAAaxf,QAAQyW,GAAKA,EAAE2L,UAAYnN,EAAI9U,KAAKsW,IAC/CxB,CACR,EACA2O,QAASA,EACT,QAAArB,CAAS/J,EAAWzB,EAAWC,GAC1Bke,IAAKznB,SAASC,IAAI8K,EAAEtO,IACvBgrB,IAAK3S,SAAS/J,EAAWzB,EAAGC,IAE3BwB,EAAiB9H,MAAO,EACzBwkB,IAAKrS,eAAerK,EAAiBzB,EAAGC,GAE1C,EACA,YAAAyc,CAAanb,EAAcjC,GAG1B6e,IAAKznB,SAASzN,QAAQ6b,IL1iElB,IAAsBkb,EAAUC,EAAVD,EK2iEE7e,EAAY2D,GL3iEJmb,EK2iEQ1e,EL1iEvCye,EAAGhgB,EAAIigB,EAAGjgB,EAAIigB,EAAG/iB,OAAS8iB,EAAG/f,EAAIggB,EAAGhgB,EAAIggB,EAAGriB,QAAUoiB,EAAGhgB,EAAIggB,EAAG9iB,MAAQ+iB,EAAGjgB,GAAKggB,EAAG/f,EAAI+f,EAAGpiB,OAASqiB,EAAGhgB,EK6iExGke,IAAK/S,gBAAgBtG,GAAIA,EAAEuG,UAChB/L,GAEX6e,IAAK/S,gBAAgBtG,GAAG,KAI1BqZ,IAAK1V,aAAaxf,QAAQyW,IACXuB,EAAUvB,EAAG6B,GAAK,GAG/B+c,EAAe5e,GAAIA,EAAE2L,UACV/L,GAEXgf,EAAe5e,GAAG,KAIpBwT,EAAeiL,IAAKjU,QAAQnV,KAAK+P,GAAKA,EAAEuG,UACzC,EACAkC,cAAeA,GACb/iB,GAaD4S,EAAY6gB,2BAVE,KACf5C,EAAepyB,QAAQ,EAAGoI,UAASwsB,QAAOC,cACzCzsB,EAAQtD,oBAAoB8vB,EAAOC,KAEhC+B,GACHA,IAMH,CAEO,SAAShT,IACf,IAAKzP,EAAK,OAAO,EACjB,MAAMvK,EAAKuK,EAAIwG,cAAc,UAC7B,IAAK/Q,EAAI,OAAO,EAGhB,MACMqtB,GADYrtB,EAAGib,aAAa,cAAgB,IACrBoP,MAAM,oBACnC,OAAIgD,GACI/C,WAAW+C,EAAW,KAEvB,CACR,CAIO,SAASvM,EAAQvlB,GACvB,IAAKgP,EAAK,OACV,MAAMvK,EAAKuK,EAAIwG,cAAc,UAC7B,IAAK/Q,EAAI,OAGT,MAAMwnB,EAAmBC,IACzBznB,EAAG4K,aAAa,YAAa,aAAa4c,EAAiBra,MAAMqa,EAAiBpa,YAAY7R,MAG9Fmf,GACD,CAEO,SAAS6R,EAAgBD,EAAiB/P,EAAkBC,GAClE,MAAMxc,EAAKuK,EAAIwG,cAAc,UACvBuc,EAAUtT,IAGhB,QAAgBtY,IAAZ6a,QAAqC7a,IAAZ8a,EAAuB,CAGnD,MAAM+Q,EAAYhjB,EAAI6P,cAClBmT,GACHhR,EAAUgR,EAAUlT,YAAc,EAClCmC,EAAU+Q,EAAUjT,aAAe,IAGnCiC,EAAUhS,EAAI8P,YAAc,EAC5BmC,EAAUjS,EAAI+P,aAAe,EAE/B,CAGA,MAAMkN,EAAmBC,IAYnB+F,EAAgBjR,GANJA,EAAUiL,EAAiBra,GAAKmgB,EAMNhB,EACtCmB,EAAgBjR,GANJA,EAAUgL,EAAiBpa,GAAKkgB,EAMNhB,EAG5CtsB,EAAG4K,aAAa,YAAa,aAAa4iB,MAAkBC,YAAwBnB,MAGpF5R,GACD,CAEA,SAAS+M,IACR,IAAKld,EAAK,MAAO,CAAE4C,EAAG,EAAGC,EAAG,GAC5B,MAAMpN,EAAKuK,EAAIwG,cAAc,UAC7B,IAAK/Q,EAAI,MAAO,CAAEmN,EAAG,EAAGC,EAAG,GAE3B,MACMgd,GADYpqB,EAAGib,aAAa,cAAgB,IACjBoP,MAAM,gCACvC,OAAID,EACI,CACNjd,EAAGmd,WAAWF,EAAe,KAAO,EACpChd,EAAGkd,WAAWF,EAAe,KAAO,GAG/B,CAAEjd,EAAG,EAAGC,EAAG,EACnB,CAEA,SAASsN,IACR,IAAKnQ,EAAK,OACV,MAAMvK,EAAKuK,EAAIwG,cAAc,UAC7B,IAAK/Q,EAAI,OACT,MAAMma,EAAKna,EAAGgL,UACRzP,EAAOye,IACb,IAAKzP,EAAI6P,cAAe,OACxB,MAAMzL,EAAIhT,KAAKQ,IAAIoO,EAAI6P,cAAcC,YAAc9e,EAAM4e,EAAGhN,EAAIgN,EAAG9P,MAAQ,IACrEuE,EAAIjT,KAAKQ,IAAIoO,EAAI6P,cAAcE,aAAe/e,EAAM4e,EAAG/M,EAAI+M,EAAGpP,OAAS,IAC7ER,EAAIK,aAAa,QAASpD,OAAOmH,EAAIpT,IACrCgP,EAAIK,aAAa,SAAUpD,OAAOoH,EAAIrT,GAIvC,CAYO,MAwBDgmB,EAAiB,CAACvhB,EAAgBnH,KAC1B,UAATA,EAAmBmH,EAAG4K,aAAa,mBAAoB,KACzC,UAAT/R,GAAmBmH,EAAG4K,aAAa,mBAAoB,MAG3DvI,EAAS,CAEdif,WAAY,CAEXrrB,OAAQ,gBAETy3B,SAAU,CACT,cAAe,oBACf7qB,OAAQ,QAIT8iB,SAAU,CACT,cAAe,oBACf9iB,OAAQ,QAGT+iB,SAAU,CACT/J,KAAM,OACNhZ,OAAQ,QAITgkB,UAAW,CAEVhL,KAAM,sBACNhZ,OAAQ,OACR,eAAgB,EAChB,mBAAoB,GAErBikB,UAAW,CACV,cAAe,oBACfjL,KAAM,OACN,YAAa,GACb,cAAe,OACf8R,OAAQ,YAKJC,EAAiB,IAAI/tB,IAGpB,SAAS0f,EAAcsO,GAC7B,IAAKtjB,EAAK,OAEV,MAAMhP,EAAOye,IACP7f,EAAYstB,IAEZ/H,EAAQ,CACbnkB,OACApB,UAAW,CAAEgT,EAAGhT,EAAUgT,EAAGC,EAAGjT,EAAUiT,IAG3CwgB,EAAevtB,IAAIwtB,EAASnO,EAC7B,CAGO,SAASoO,EAAiBD,GAChC,IAAKtjB,IAAQqjB,EAAe9pB,IAAI+pB,GAC/B,OAAO,EAGR,MAAMnO,EAAQkO,EAAe7uB,IAAI8uB,GACjC,IAAKnO,EACJ,OAAO,EAIR,MAAMzF,EAAY1P,EAAIwG,cAAc,UAMpC,OALIkJ,IACHA,EAAUrP,aAAa,YAAa,SAAS8U,EAAMnkB,mBAAmBmkB,EAAMvlB,UAAUgT,MAAMuS,EAAMvlB,UAAUiT,MAC5GsN,MAGM,CACR,CAGO,SAASF,GAAeqT,GAC9BD,EAAepU,OAAOqU,EACvB,CA0CA,SAASlQ,GAAkBhO,EAAcmO,EAAqBC,GAC7D,MAAMC,EAAIrO,EAAMxC,EAAI2Q,EAAa3Q,EAC3B8Q,EAAItO,EAAMvC,EAAI0Q,EAAa1Q,EAC3B8Q,EAAIH,EAAW5Q,EAAI2Q,EAAa3Q,EAChCgR,EAAIJ,EAAW3Q,EAAI0Q,EAAa1Q,EAEhCgD,EAAM4N,EAAIE,EAAID,EAAIE,EAClBC,EAAQF,EAAIA,EAAIC,EAAIA,EAE1B,GAAc,IAAVC,EAEH,OAAOziB,KAAKoU,KAAKiO,EAAIA,EAAIC,EAAIA,GAG9B,IAEI8P,EAAIC,EAFJ3P,EAAQjO,EAAMgO,EAIdC,EAAQ,GACX0P,EAAKjQ,EAAa3Q,EAClB6gB,EAAKlQ,EAAa1Q,GACRiR,EAAQ,GAClB0P,EAAKhQ,EAAW5Q,EAChB6gB,EAAKjQ,EAAW3Q,IAEhB2gB,EAAKjQ,EAAa3Q,EAAIkR,EAAQH,EAC9B8P,EAAKlQ,EAAa1Q,EAAIiR,EAAQF,GAG/B,MAAM1M,EAAK9B,EAAMxC,EAAI4gB,EACftgB,EAAKkC,EAAMvC,EAAI4gB,EACrB,OAAOryB,KAAKoU,KAAK0B,EAAKA,EAAKhE,EAAKA,EACjC,sCEj3EA,SAASwgB,EACRC,EAA6B,CAAC,EAC9BC,GAAmB,GAGnB,MAAMC,EAAiC,CACtCC,YAAaH,EAAYG,aAdb,GAeZC,aAAcJ,EAAYI,cAdb,GAebC,iBAdiB,GAejBlT,QAdQ,GAeRmT,gBAdgB,KAqCjB,OAnBIL,IACHC,EAAgBC,YAAc1yB,KAAKQ,IAClCiyB,EAAgBC,YAAcD,EAAgBI,gBAC9C,IAEDJ,EAAgBE,aAAe3yB,KAAKQ,IACnCiyB,EAAgBE,aAAeF,EAAgBI,gBAC/C,IAEDJ,EAAgBG,iBAAmB5yB,KAAKQ,IACvCiyB,EAAgBG,iBAAmBH,EAAgBI,gBACnD,IAEDJ,EAAgB/S,QAAU1f,KAAKQ,IAC9BiyB,EAAgB/S,QAAU+S,EAAgBI,gBAC1C,KAIKJ,CACR,CAGA,SAASK,EACR9P,EACAuP,GAEA,MAAM9oB,UACLA,EAAY,OAAMkC,cAClBA,GAAgB,GACb4mB,EAEEQ,EAAsC,CAC3C,gBAAiB,UACjB,gBAAiBtpB,EACjB,uBAAwBuZ,EAAQ0P,YAAYpnB,WAC5C,iCAAkC0X,EAAQ4P,iBAAiBtnB,WAC3D,cAAe,QAAQ0X,EAAQtD,gBAAgBsD,EAAQtD,kBAAkBsD,EAAQtD,iBAAiBsD,EAAQtD,WAG1G,4CAA6CsD,EAAQ2P,aAAarnB,WAClE,4CAA6C,KAC7C,4CAA6C,KAG7C,kBAAmB,WACnB,oCAAqC,QAGrC,0CAA2C,kBAC3C,6CAA8C,IAC9C,sDAAuD,MAGvD,6CAA8C,OAC9C,iDAAkD,aAGlD,kCAAmC,OAGnC,qCAAsC,kBACtC,+CAAgD,OAGhD,4CAA6C,cAC7C,mDAAoD,OAGpD,wBAAyB,oBACzB,0CAA2C,OAG3C,2BAA4B,SAC5B,wBAAyB,OACzB,wBAAyB,IACzB,8BAA+B,QAC/B,oCAAqC,QACrC,uCAAwC,aASzC,OALIK,IACHonB,EAAY,wBAA0B/yB,KAAKQ,IAA0B,GAAtBwiB,EAAQ0P,YAAmB,IAAIpnB,WAC9EynB,EAAY,6CAA+C/yB,KAAKQ,IAA2B,GAAvBwiB,EAAQ2P,aAAoB,IAAIrnB,YAG9FynB,CACR,CAEOxpB,eAAeI,EAAWjO,EAAkB8N,EAAyB,CAAC,GAK5E,MACMwpB,EAAM,UADM5xB,EAAAzE,EAAA,KAAAwE,KAAAC,EAAA+P,EAAA9P,KAAAD,EAAA,SAAmCD,KAAKG,GAAUA,EAAOC,UAMrE0xB,EAAW,CAChBtuB,GAAI,OACJuuB,cAAeJ,EALIR,EAAoB9oB,GAAS,GAKNA,GAC1CnN,SAAU,GACV2d,MAAO,IAKFmZ,EAAU,IAAIjvB,IACdkvB,EAAW,IAAIlvB,IACrBxI,EAAMwM,SAASzN,QAAQuU,IACtB,IAAKA,EAAKrK,GAAI,OAEdwuB,EAAQzuB,IAAIsK,EAAKrK,GAAIqK,GAIrB,MAAMiM,EAAYjb,KAAKQ,IAAIwO,EAAKN,OAAS,IAAK,KACxC2kB,EAAarzB,KAAKQ,IAAIwO,EAAKI,QAAU,IAAK,KAMhDgkB,EAAS1uB,IAAIsK,EAAKrK,GAAI,CACrBA,GAAIqK,EAAKrK,GAET6M,EAAGxC,EAAKwC,EACRC,EAAGzC,EAAKyC,EACR/C,MAAOuM,EAAaqY,GACpBlkB,OAAQikB,EAAcC,GACtBJ,cAAe,CAEd,eAAgB,GAEhB,2BAA4B,oBAK/B,MAAMK,EAAkB,IAAIrvB,IACtBsvB,EAAc,IAAItvB,IAClBuvB,EAAgB,IAAIC,IAE1Bh4B,EAAMwe,UAAUzf,QAAQiiB,IACvBA,EAAMhB,MAAMjhB,QAAQk5B,IACnB,GAAInB,EAAQmB,GAKX,OAJKH,EAAYrrB,IAAIwrB,EAAOhvB,KAC3B6uB,EAAY9uB,IAAIivB,EAAOhvB,GAAI+X,EAAM/X,SAElC8uB,EAAc3iB,IAAI6iB,EAAOhvB,IAGrB4uB,EAAgBprB,IAAIwrB,EAAOhvB,KAC/B4uB,EAAgB7uB,IAAIivB,EAAOhvB,GAAI+X,EAAM/X,QAKxC,MAAMivB,EAAY,IAAI1vB,IAChB2vB,EAAiBnX,IACtB,MAAM4M,EAAWsK,EAAUxwB,IAAIsZ,EAAM/X,IACrC,GAAI2kB,EAAU,OAAOA,EAErB,MAAMjtB,EAAWqgB,EAAMhB,MAAMgO,QAAQiK,IACpC,GAAInB,EAAQmB,GACX,OAAOH,EAAYpwB,IAAIuwB,EAAOhvB,MAAQ+X,EAAM/X,GAAK,CAACkvB,EAAcF,IAAW,GAE5E,MAAM3kB,EAAOokB,EAAShwB,IAAIuwB,EAAOhvB,IACjC,OAAOqK,GAAQukB,EAAgBnwB,IAAIuwB,EAAOhvB,MAAQ+X,EAAM/X,GAAK,CAACqK,GAAQ,KAEjE8kB,EAAW,CAChBnvB,GAAI+X,EAAM/X,GACVtI,WACA2d,MAAO,GACPkZ,cAAeJ,EAAcR,EAAoB9oB,GAAS,GAAOA,IAGlE,OADAoqB,EAAUlvB,IAAIgY,EAAM/X,GAAImvB,GACjBA,GAGRp4B,EAAMwe,UAAUzf,QAAQiiB,IACvB,IAAK+W,EAActrB,IAAIuU,EAAM/X,IAAK,CACjC,MAAMmvB,EAAWD,EAAcnX,GAC3BoX,EAASz3B,SAASI,OAAS,GAC9Bw2B,EAAS52B,SAASzB,KAAKk5B,EAEzB,IAEDV,EAAS34B,QAAQ,CAACuU,EAAMrK,KAClB4uB,EAAgBprB,IAAIxD,IACxBsuB,EAAS52B,SAASzB,KAAKoU,KAIzB,MAAM+kB,EAAkBC,IACvB,MAAMC,EAAsB,GAC5B,IAAI5oB,EAAU2oB,EACd,KAAO3oB,GACN4oB,EAAUr5B,KAAKyQ,GACfA,EAAUmoB,EAAYpwB,IAAIiI,GAE3B,OAAO4oB,GAgDR,GAxCAv4B,EAAMse,MAAMvf,QAAQshB,IAEnB,IAAKA,EAAKpX,KAAOoX,EAAKJ,MAAMhX,KAAOoX,EAAKC,IAAIrX,GAAI,OAGhD,IAAKwuB,EAAQhrB,IAAI4T,EAAKJ,KAAKhX,MAAQwuB,EAAQhrB,IAAI4T,EAAKC,GAAGrX,IAEtD,YADAvC,QAAQiG,KAAK,iBAAiB0T,EAAKpX,cAAcoX,EAAKJ,KAAKhX,gBAAgBoX,EAAKC,GAAGrX,yBAKpF,MAAMuvB,EAAanY,EAAKpB,OAASoB,EAAKpB,MAAMrQ,OAC3CtK,KAAKS,IAAwB,EAApBsb,EAAKpB,MAAMle,OAAY,KAAO,EAGlC03B,EAAU,CACfxvB,GAAIoX,EAAKpX,GACTyvB,QAAS,CAACrY,EAAKJ,KAAKhX,IACpB0vB,QAAS,CAACtY,EAAKC,GAAGrX,IAElB2vB,OAAQvY,EAAKpB,OAASoB,EAAKpB,MAAMrQ,OAAS,CAAC,CAC1C3F,GAAI,GAAGoX,EAAKpX,WACZ0F,KAAM0R,EAAKpB,MAEXjM,MAAOwlB,EACP9kB,OAAQ,GACR8jB,cAAe,CACd,2BAA4B,SAC5B,wBAAyB,UAGtB,IAGAc,EAxCmB,EAACO,EAAkBC,KAC5C,MAAMC,EAAkBV,EAAeR,EAAgBnwB,IAAImxB,IACrDG,EAAuB,IAAIhB,IAAIK,EAAeR,EAAgBnwB,IAAIoxB,KACxE,OAAOC,EAAgBluB,KAAKytB,GAAWU,EAAqBvsB,IAAI6rB,KAqChDW,CAAkB5Y,EAAKJ,KAAKhX,GAAIoX,EAAKC,GAAGrX,KAClCqvB,EAAUJ,EAAUxwB,IAAI4wB,GAAWf,GAC3CjZ,MAAMpf,KAAKu5B,MAIrBlB,EAAStuB,KAAOsuB,EAAS52B,SAC7B,MAAM,IAAIkO,MAAM,+BAIjB,IACC,MAAMqqB,QAAsB5B,EAAIzwB,OAAO0wB,GAGjCvX,EAAmD,GACnD1B,EAAsG,GAItG6a,EAAe,CAACjD,EAAgB3T,EAAU,EAAGC,EAAU,KAC5D0T,EAAUv1B,UAAU5B,QAASq6B,IACxBA,EAAMz4B,SAETw4B,EAAaC,EAAO7W,GAAW6W,EAAMtjB,GAAK,GAAI0M,GAAW4W,EAAMrjB,GAAK,IAKpEiK,EAAM9gB,KAAK,CACV+J,GAAImwB,EAAMnwB,GACV6M,EAAGyM,GAAW6W,EAAMtjB,GAAK,IAAMsjB,EAAMpmB,OAAS,GAAK,EACnD+C,EAAGyM,GAAW4W,EAAMrjB,GAAK,IAAMqjB,EAAM1lB,QAAU,GAAK,OAOlD2lB,EAAsB,CAACnD,EAAgB3T,EAAU,EAAGC,EAAU,KACnE0T,EAAU5X,OAAOvf,QAAS05B,IACzB,MAAM1rB,EAA0C,GAChD,IAAIkS,EAGAwZ,EAAQa,UAAYb,EAAQa,SAASv4B,OAAS,GACjD03B,EAAQa,SAASv6B,QAASF,IAErBA,EAAQ06B,YACXxsB,EAAS7N,KAAK,CACb4W,EAAGyM,EAAU1jB,EAAQ06B,WAAWzjB,EAChCC,EAAGyM,EAAU3jB,EAAQ06B,WAAWxjB,IAK9BlX,EAAQ26B,YAAc36B,EAAQ26B,WAAWz4B,OAAS,GACrDlC,EAAQ26B,WAAWz6B,QAAS06B,IAC3B1sB,EAAS7N,KAAK,CACb4W,EAAGyM,EAAUkX,EAAG3jB,EAChBC,EAAGyM,EAAUiX,EAAG1jB,MAMflX,EAAQ66B,UACX3sB,EAAS7N,KAAK,CACb4W,EAAGyM,EAAU1jB,EAAQ66B,SAAS5jB,EAC9BC,EAAGyM,EAAU3jB,EAAQ66B,SAAS3jB,MAOlC,MAAM4jB,EAAe35B,EAAMse,MAAMzT,KAAK5J,GAAKA,EAAEgI,KAAOwvB,EAAQxvB,IAC5D,GAAI0wB,GAAc1a,OAAS0a,EAAa1a,MAAMrQ,OAC7C,GAAI6pB,EAAQG,QAAUH,EAAQG,OAAO73B,OAAS,EAAG,CAChD,MAAM64B,EAAWnB,EAAQG,OAAO,QACbvuB,IAAfuvB,EAAS9jB,QAAkCzL,IAAfuvB,EAAS7jB,IACxCkJ,EAAQ,CACPnJ,EAAGyM,EAAUqX,EAAS9jB,GAAK8jB,EAAS5mB,OAAS,GAAK,EAClD+C,EAAGyM,EAAUoX,EAAS7jB,GAAK6jB,EAASlmB,QAAU,GAAK,GAGtD,MAAO,GAAI3G,EAAShM,QAAU,EAAG,CAEhC,MAAM84B,EAAWv1B,KAAKw1B,MAAM/sB,EAAShM,OAAS,GAC9C,GAAIgM,EAAShM,OAAS,GAAM,EAAG,CAC9B,MAAMg5B,EAAKhtB,EAAS8sB,EAAW,GACzBG,EAAKjtB,EAAS8sB,GACpB5a,EAAQ,CAAEnJ,GAAIikB,EAAGjkB,EAAIkkB,EAAGlkB,GAAK,EAAGC,GAAIgkB,EAAGhkB,EAAIikB,EAAGjkB,GAAK,EACpD,MACCkJ,EAAQlS,EAAS8sB,EAEnB,CAIDvb,EAAMpf,KAAK,CACV+J,GAAIwvB,EAAQxvB,GACZ8D,WACAkS,YAKFiX,EAAUv1B,UAAU5B,QAASq6B,IACxBA,EAAM9a,OAAS8a,EAAM9a,MAAMvd,OAAS,GACvCs4B,EAAoBD,EAAO7W,GAAW6W,EAAMtjB,GAAK,GAAI0M,GAAW4W,EAAMrjB,GAAK,OAW9E,GALAojB,EAAaD,GACbG,EAAoBH,GAIhBlZ,EAAMjf,OAAS,EAAG,CAErB,MAAM0jB,EAAOngB,KAAKS,OAAOib,EAAM1e,IAAIsZ,GAAKA,EAAE9E,IACpC6O,EAAOrgB,KAAKS,OAAOib,EAAM1e,IAAIsZ,GAAKA,EAAE7E,IAGpCiO,EAAU,GACVzB,GAAWkC,EAAOT,EAClBxB,GAAWmC,EAAOX,EAGxBhE,EAAMjhB,QAAQuU,IACbA,EAAKwC,GAAKyM,EACVjP,EAAKyC,GAAKyM,IAIXlE,EAAMvf,QAAQshB,IACbA,EAAKtT,SAAShO,QAAQ0jB,IACrBA,EAAO3M,GAAKyM,EACZE,EAAO1M,GAAKyM,IAETnC,EAAKpB,QACRoB,EAAKpB,MAAMnJ,GAAKyM,EAChBlC,EAAKpB,MAAMlJ,GAAKyM,IAGnB,CAEA,MAAO,CAAExC,QAAO1B,QAEjB,CAAE,MAAOrY,GAER,OADAS,QAAQiG,KAAK,mDAAoD1G,GAMnE,SAA8BjG,GAI7B,MAAMggB,EAAmD,GACnD1B,EAAsE,GAG5E,IAAIxI,EAAI,EAAGC,EAAI,EACf,MACMkkB,EAAU31B,KAAK41B,KAAK51B,KAAKoU,KAAK1Y,EAAMwM,SAASqI,OAEnD,IAAIslB,EAAM,EA0BV,OAzBAn6B,EAAMwM,SAASzN,QAAQuU,IACtB0M,EAAM9gB,KAAK,CACV+J,GAAIqK,EAAKrK,GACT6M,EAAGA,EACHC,EAAGA,IAGJokB,IACIA,GAAOF,GACVE,EAAM,EACNrkB,EAAI,EACJC,GAfc,KAiBdD,GAjBc,MAsBhB9V,EAAMse,MAAMvf,QAAQshB,IACnB/B,EAAMpf,KAAK,CACV+J,GAAIoX,EAAKpX,GACT8D,SAAU,OAIL,CAAEiT,QAAO1B,QACjB,CA5CS8b,CAAqBp6B,EAC7B,CACD,CA4CA,SAAS82B,EAAQmB,GAChB,MAAO,UAAWA,CACnB,kKC/dAnqB,EAAA,GAEAA,EAAAusB,kBAA4BC,IAC5BxsB,EAAAysB,cAAwBC,IACxB1sB,EAAA0L,OAAiBihB,IAAA90B,KAAa,aAC9BmI,EAAA4sB,OAAiBC,IACjB7sB,EAAA8sB,mBAA6BC,IAEhBC,IAAIt5B,EAAAmlB,EAAO7Y,GAKFtM,EAAAmlB,GAAWnlB,EAAAmlB,EAAOoU,QAAUv5B,EAAAmlB,EAAOoU,cCXzD,MAAMC,EACYC,SACArH,QACTsH,SAAmB,EACnBC,UAAkD,KAE1D,WAAA5hB,CAAY0hB,GACX3lB,KAAK2lB,SAAWA,EAChB3lB,KAAKse,QAAU,KACdte,KAAK4lB,SAAU,EACf5lB,KAAK6lB,UAAY,KACjB7lB,KAAK2lB,WAEP,CAEA,KAAAG,CAAM1e,GACDpH,KAAK4lB,SACR5lB,KAAK+lB,OAEN/lB,KAAK6lB,UAAYte,WAAWvH,KAAKse,QAASlX,GAC1CpH,KAAK4lB,SAAU,CAChB,CAEA,IAAAG,GACK/lB,KAAK4lB,SAA8B,OAAnB5lB,KAAK6lB,YACxBve,aAAatH,KAAK6lB,WAClB7lB,KAAK4lB,SAAU,EACf5lB,KAAK6lB,UAAY,KAEnB,CAEA,SAAAG,GACC,OAAOhmB,KAAK4lB,OACb,EAMM,MAAMK,EACZC,uBAA6D,CAC5DC,SAAU,IACVC,SAAU,IACVC,iBAAkB,KAGnBH,4BAA+C,CAC9C,6CACA,sDAGgBI,IACA9tB,QACA+tB,kBAETC,OAA2B,KAC3BC,UACAC,mBAA6B,EAC7BC,oBAA8B,GAErBN,iBACAO,eAEjB,WAAA3iB,CAAYsiB,EAA2C/tB,GACtDwH,KAAKumB,kBAAoBA,EACzBvmB,KAAKxH,QAAU,IAAKytB,EAAiBY,mBAAoBruB,GACzDwH,KAAKsmB,IAAM,kCACXtmB,KAAKymB,UAAYzmB,KAAKxH,QAAQ2tB,SAE9BnmB,KAAKqmB,iBAAmB,IAAIX,EAAM,IAAM1lB,KAAK8mB,0BAC7C9mB,KAAK4mB,eAAiB,IAAIlB,EAAM,IAAM1lB,KAAK+mB,sBAC5C,CAEA,OAAAC,GACChnB,KAAK0mB,mBAAoB,EAErB1mB,KAAKinB,sBAITjnB,KAAKknB,uBACLlnB,KAAKmnB,kBACN,CAEA,UAAAC,GACCpnB,KAAK0mB,mBAAoB,EACzB1mB,KAAK4mB,eAAeb,OAEhB/lB,KAAKinB,sBACRjnB,KAAK2mB,oBAAsB,SAC3B3mB,KAAKwmB,OAAQa,QAEf,CAEQ,iBAAAJ,GACP,OAAuB,OAAhBjnB,KAAKwmB,QAAmBxmB,KAAKwmB,OAAOc,aAAeC,UAAUC,IACrE,CAEQ,oBAAAN,GACPlnB,KAAK4mB,eAAeb,OACpB/lB,KAAK2mB,oBAAsB,gBAC5B,CAEQ,eAAAQ,GACPnnB,KAAKwmB,OAAS,IAAIe,UAAUvnB,KAAKsmB,KACjCtmB,KAAKwmB,OAAOiB,OAAS,IAAMznB,KAAK0nB,aAChC1nB,KAAKwmB,OAAOmB,QAAU,IAAM3nB,KAAK4nB,cACjC5nB,KAAKwmB,OAAOqB,UAAaxJ,GAAUre,KAAK8nB,cAAczJ,GACtDre,KAAKwmB,OAAOuB,QAAU,IAAM/nB,KAAKgoB,aAClC,CAEQ,UAAAN,GACP1nB,KAAK2mB,oBAAsB,mBAC3B3mB,KAAKioB,gBACN,CAEQ,WAAAL,GACPx2B,QAAQ2L,IAAI,2BAA2BiD,KAAK2mB,iCAAiC3mB,KAAKymB,eAClFzmB,KAAKkoB,sBACN,CAEQ,aAAAJ,CAAczJ,GACrB,IACC,MAAMzjB,EAA6BqC,KAAKC,MAAMmhB,EAAM/gB,MACpD0C,KAAKmoB,eAAevtB,EACrB,CAAE,MAAOjK,GACRS,QAAQT,MAAM,qCAAsCA,EACrD,CACD,CAEQ,WAAAq3B,GACP,CAGO,cAAAG,CAAevtB,GACtB,OAAQA,EAAQwtB,SACf,IAAK,QACJpoB,KAAKqoB,qBACL,MACD,IAAK,SACJroB,KAAKsoB,oBAAoB1tB,GACzB,MACD,QACCxJ,QAAQ2L,IAAI,sCAAuCnC,GAEtD,CAEQ,kBAAAytB,GACProB,KAAKqmB,iBAAiBN,OACtB/lB,KAAKymB,UAAYzmB,KAAKxH,QAAQ2tB,QAC/B,CAEQ,mBAAAmC,CAAoB1tB,GAG3BoF,KAAK4mB,eAAeb,OACpB/lB,KAAKgnB,UAEDpsB,EAAQhJ,MACXoO,KAAKumB,kBAAkB3rB,EAAQhJ,KAEjC,CAEQ,cAAAq2B,GACP,MAAMM,EAAkC,CACvCH,QAAS,QACTI,UAAWvC,EAAiBwC,qBAC5BC,IAAK,SAGN1oB,KAAK2oB,YAAYJ,GACjBvoB,KAAKqmB,iBAAiBP,MAAM9lB,KAAKxH,QAAQ6tB,iBAC1C,CAEQ,sBAAAS,GACH9mB,KAAKinB,sBACRjnB,KAAK2mB,oBAAsB,oBAC3B3mB,KAAKwmB,OAAQa,QAEf,CAEQ,mBAAAN,GACH/mB,KAAK0mB,mBACR1mB,KAAKgnB,SAEP,CAEQ,oBAAAkB,GACFloB,KAAK0mB,oBAIL1mB,KAAK4mB,eAAeZ,cACxBhmB,KAAK4mB,eAAed,MAAM9lB,KAAKymB,WAC/BzmB,KAAKymB,UAAYz3B,KAAKS,IAAIuQ,KAAKxH,QAAQ4tB,SAA2B,EAAjBpmB,KAAKymB,YAExD,CAEQ,WAAAkC,CAAYP,GACfpoB,KAAKinB,qBACRjnB,KAAKwmB,OAAQoC,KAAK3rB,KAAKE,UAAUirB,GAEnC,eChND,MAAMS,GAAO,EAAAh7B,EAAAmC,MAAK,IAAMC,QAAAC,UAAAC,KAAAC,EAAAC,KAAAD,EAAA,MAAiBD,KAAKG,IAAM,CAAOC,QAASD,EAAOgB,SAarEw3B,EAAgB,KACrB,MAAO/V,EAAOgW,IAAY,EAAAl7B,EAAAC,UAAmB,CAC5CwP,KAAM,KACN3M,MAAO,KACPq4B,SAAS,IAGJC,EAAW1wB,UAChBwwB,EAASG,IAAI,IAAUA,EAAMF,SAAS,EAAMr4B,MAAO,QAEnD,IACC,MAAOw4B,EAAeC,SAAwBn5B,QAAQo5B,IAAI,CACzDrwB,MAAM,mBACNA,MAAM,sBAGP,IAAKmwB,EAAc52B,GAClB,MAAM,IAAIgH,MAAM,0BAA0B4vB,EAAcG,cAGzD,IAAKF,EAAe72B,GACnB,MAAM,IAAIgH,MAAM,2BAA2B6vB,EAAeE,cAG3D,MAAOrgC,EAAOsI,SAAgBtB,QAAQo5B,IAAI,CACzCF,EAAcI,OACdH,EAAeG,SAGhBR,EAAS,CACRzrB,KAAM,CAAErU,QAAOsI,UACfZ,MAAO,KACPq4B,SAAS,GAEX,CAAE,MAAOr4B,GACRS,QAAQT,MAAM,uBAAwBA,GACtCo4B,EAAS,CACRzrB,KAAM,KACN3M,MAAOA,aAAiB4I,MAAQ5I,EAAMiK,QAAU,yBAChDouB,SAAS,GAEX,GAGKQ,EAAoB53B,IACrBA,EAAKpI,SAAS,UAIlB4H,QAAQ2L,IAAI,gBAAiBnL,IAC7B,EAAAN,EAAAm4B,KACAR,MAiBD,OAdA,EAAAp7B,EAAAM,WAAU,KAEgB,IAAI83B,EAAiBuD,GAC7BxC,UAGjBiC,IAGO,QAGL,IAEClW,EAAMiW,SACF,EAAA99B,EAAAI,KAACo+B,EAAa,IAGlB3W,EAAMpiB,OACF,EAAAzF,EAAAI,KAACq+B,EAAW,CAACh5B,MAAOoiB,EAAMpiB,MAAOi5B,QAASX,IAG7ClW,EAAMzV,MAKV,EAAApS,EAAAI,KAACuC,EAAAuP,SAAQ,CAACC,UAAU,EAAAnS,EAAAI,KAACo+B,EAAa,IAAIr+B,UACrC,EAAAH,EAAAI,KAACu9B,EAAI,CAAC5/B,MAAO8pB,EAAMzV,KAAKrU,MAAOsI,OAAQwhB,EAAMzV,KAAK/L,YAL5C,EAAArG,EAAAI,KAACq+B,EAAW,CAACh5B,MAAM,oBAAoBi5B,QAASX,KAUnDS,EAA0B,KAC/B,EAAAx+B,EAAAI,KAAA,OAAKY,MAAO,CACXG,QAAS,OACTw9B,eAAgB,SAChBv9B,WAAY,SACZ8R,OAAQ,QACR0rB,WAAY,qBACXz+B,UACD,EAAAH,EAAAI,KAAA,OAAAD,SAAK,iBAIDs+B,EAAgE,EAAGh5B,QAAOi5B,cAC/E,EAAA1+B,EAAAC,MAAA,OAAKe,MAAO,CACXwiB,QAAS,OACTzY,MAAO,MACP6zB,WAAY,YACZC,WAAY,WACZ19B,QAAS,OACT29B,cAAe,SACf19B,WAAY,SACZu9B,eAAgB,SAChBzrB,OAAQ,SACP/S,SAAA,EACD,EAAAH,EAAAI,KAAA,MAAAD,SAAI,+BACJ,EAAAH,EAAAI,KAAA,KAAAD,SAAIsF,KACJ,EAAAzF,EAAAI,KAAA,UACCyB,QAAS68B,EACT19B,MAAO,CACNwiB,QAAS,YACTpO,SAAU,OACV0gB,OAAQ,UACRiJ,gBAAiB,UACjBh0B,MAAO,QACP4e,OAAQ,OACRqV,aAAc,OACb7+B,SACF,aAOGu1B,EAAY/vB,SAASs5B,eAAe,QAC1C,IAAKvJ,EACJ,MAAM,IAAIrnB,MAAM,6BAGJ,EAAA6wB,EAAAC,YAAWzJ,GACnB0J,QAAO,EAAAp/B,EAAAI,KAACw9B,EAAG,8CC3IT,MAIMyB,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,mBAE1B9wB,EAAkD,CACvD,CACClG,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GAnBgB,OAoBhBw4B,KAAM,sBACNC,aAAc,CACb,CAACviC,IAAK,IAAKwiC,OAAO,GAClB,CAACxiC,IAAK,KAAMwiC,OAAO,OAKvB,CACCp3B,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GA/DgB,OAgEhBw4B,KAAM,OACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,OAInC,CACCr3B,KAAM,UACNi3B,KAAM,CACL,CACCv4B,GAvEgB,OAwEhBw4B,KAAM,OACNC,aAAc,CACb,CAACE,MAAM,EAAMziC,IAAK,OAGpB,CACC8J,GA7EgB,OA8EhBw4B,KAAM,OACNC,aAAc,CACb,CAACE,MAAM,EAAMD,OAAO,EAAMxiC,IAAK,KAC/B,CAACyiC,MAAM,EAAMziC,IAAK,SAMtB,CACCoL,KAAM,uBACNi3B,KAAM,CACL,CACCv4B,GAAI42B,EACJ4B,KAAM,0BACNC,aAAc,CACb,CAACG,KAAK,EAAMC,OAAO,KAGrB,CACC74B,GAAI62B,EACJ2B,KAAM,uCACNC,aAAc,CACb,CAACG,KAAK,EAAMF,OAAO,EAAMG,OAAO,KAGlC,CACC74B,GAAI82B,EACJ0B,KAAM,6BACNC,aAAc,CACb,CAACviC,IAAK,UACN,CAACA,IAAK,iBAKV,CACCoL,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GAAI+2B,EACJyB,KAAM,UACNC,aAAc,CACb,CAACE,MAAM,EAAMziC,IAAK,OAGpB,CACC8J,GAAIg3B,EACJwB,KAAM,WACNC,aAAc,CACb,CAACE,MAAM,EAAMziC,IAAK,OAGpB,CACC8J,GAAIi3B,EACJuB,KAAM,aACNC,aAAc,CAAC,CAACE,MAAM,EAAMziC,IAAK,OAElC,CACC8J,GAAIk3B,EACJsB,KAAM,YACNC,aAAc,CAAC,CAACE,MAAM,EAAMziC,IAAK,OAElC,CACC8J,GAAI,aACJw4B,KAAM,+BACNC,aAAc,CAAC,CAACK,OAAO,OAI1B,CACCx3B,KAAM,qBACNi3B,KAAM,CACL,CACCv4B,GAlIoB,WAmIpBw4B,KAAM,8BACNC,aAAc,CAAC,CAACI,OAAO,KAExB,CACC74B,GAtI0B,iBAuI1Bw4B,KAAM,iBACNC,aAAc,CAAC,CAACI,OAAO,KAExB,CACC74B,GA1IwB,eA2IxBw4B,KAAM,4BACNC,aAAc,CAAC,CAACC,OAAO,EAAMG,OAAO,KAErC,CACC74B,GA9IsB,aA+ItBw4B,KAAM,mCACNC,aAAc,CAAC,CAACC,OAAO,EAAMG,OAAO,KAErC,CACC74B,GAlJyB,gBAmJzBw4B,KAAM,yBACNC,aAAc,CAAC,CAACI,OAAO,OAI1B,CACCv3B,KAAM,SACNi3B,KAAM,CACL,CACCv4B,GAAIm3B,EACJqB,KAAM,aACNC,aAAc,CAAC,CAACE,MAAM,EAAMziC,IAAK,OAElC,CACC8J,GAAIo3B,EACJoB,KAAM,WACNC,aAAc,CAAC,CAACviC,IAAK,WAIxB,CACCoL,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GAAIu3B,EACJiB,KAAM,2BACNC,aAAc,CAAC,CAACviC,IAAK,QAEtB,CACC8J,GAAI23B,EACJa,KAAM,oBACNC,aAAc,CAAC,CAACviC,IAAK,KAAMwiC,OAAO,KAEnC,CACC14B,GAAIs3B,EACJkB,KAAM,8BACNC,aAAc,CAAC,CAACviC,IAAK,WAEtB,CACC8J,GAAI03B,EACJc,KAAM,uBACNC,aAAc,CAAC,CAACviC,IAAK,QAASwiC,OAAO,KAEtC,CACC14B,GAAIw3B,EACJgB,KAAM,6BACNC,aAAc,CAAC,CAACviC,IAAK,UAEtB,CACC8J,GAAI43B,EACJY,KAAM,sBACNC,aAAc,CAAC,CAACviC,IAAK,OAAQwiC,OAAO,KAErC,CACC14B,GAAIq3B,EACJmB,KAAM,6BACNC,aAAc,CAAC,CAACviC,IAAK,UAEtB,CACC8J,GAAIy3B,EACJe,KAAM,sBACNC,aAAc,CAAC,CAACviC,IAAK,OAAQwiC,OAAO,OAIrC,CACAp3B,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GAAI63B,EACJW,KAAM,qCACNC,aAAc,CAAC,CAACviC,IAAK,OAEtB,CACC8J,GAAIm4B,EACJK,KAAM,0BACNC,aAAc,CAAC,CAACviC,IAAK,OAAQyiC,MAAM,OAIvC,CACCr3B,KAAM,YACNi3B,KAAM,CACL,CACCv4B,GAAI83B,EACJU,KAAM,uCACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMD,OAAO,KAE9C,CACC14B,GAAI+3B,EACJS,KAAM,qCACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMD,OAAO,KAE9C,CACC14B,GAAIg4B,EACJQ,KAAM,4CACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMC,KAAK,KAE5C,CACC54B,GAAIi4B,EACJO,KAAM,0CACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMC,KAAK,OAI9C,CACCt3B,KAAM,SACNi3B,KAAM,CACL,CACCv4B,GAAIk4B,EACJM,KAAM,2BACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,OAInC,CACCr3B,KAAM,OACNi3B,KAAM,CACL,CACCv4B,GAAIo4B,EACJI,KAAM,yBACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,KAEjC,CACC34B,GAAIq4B,EACJG,KAAM,sBACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMD,OAAO,KAE9C,CACC14B,GAAIs4B,EACJE,KAAM,4BACNC,aAAc,CAAC,CAACviC,IAAK,IAAKyiC,MAAM,EAAMC,KAAK,QAMzCG,EAAcvxB,EAClBqE,OAAO,CAACwQ,EAAKtmB,IAAMsmB,EAAIvQ,OAAO/V,EAAEwiC,MAAO,IACvC1sB,OAAkC,CAACxT,EAAKtC,KACxCsC,EAAItC,EAAEiK,IAAMjK,EACLsC,GACL,CAAC,GAqDC2gC,EAAazpB,GACX,CACNA,EAAEopB,OAAQ,EAAAM,EAAA1/B,MAAqB7C,cAC/B6Y,EAAEmpB,OAAS,QACXnpB,EAAEqpB,KAAO,MACTrpB,EAAErZ,MAAQqZ,EAAErZ,IAAI4B,OAAS,EAAIyX,EAAErZ,IAAM,IAAIqZ,EAAErZ,IAAIQ,kBAC/C6Y,EAAEspB,OAAS,QACXtpB,EAAEupB,OAAS,SACVnjC,OAAOqiB,SAAS5M,KAAK,mEAGA,KAChB,EAAA8tB,EAAA1hC,MAAA,OAAKC,UAAU,UAASC,SAAA,EAC9B,EAAAwhC,EAAAvhC,KAAA,MAAAD,SAAI,eACJ,EAAAwhC,EAAAvhC,KAAA,SAAAD,UACC,EAAAwhC,EAAAvhC,KAAA,SAAAD,SAEC8P,EAAUnP,IAAIzC,IAAW,EAAAsjC,EAAA1hC,MAAA0hC,EAAA1/B,SAAA,CAAA9B,SAAA,EACxB,EAAAwhC,EAAAvhC,KAAA,MAAAD,UACC,EAAAwhC,EAAAvhC,KAAA,MAAIwhC,QAAS,EAAEzhC,SAAE9B,EAAQ0L,SAEzB1L,EAAQ2iC,KAAKlgC,IAAIqI,IAAQ,EAAAw4B,EAAA1hC,MAAA,MAAAE,SAAA,EACzB,EAAAwhC,EAAAvhC,KAAA,MAAAD,SAAKgJ,EAAK+3B,aAAapgC,IAAI2gC,GAAW5tB,KAAK,SAC3C,EAAA8tB,EAAAvhC,KAAA,MAAAD,SAAKgJ,EAAK83B,mFAhDY,CAACxgC,EAA+B6gC,GAAQ,EAAOC,GAAQ,KAElF,MAAMM,EAAU5jC,OAAOC,KAAKsjC,GAAapjC,OAAOsW,GA5BhC,EAACjU,EAA+BuP,EAAoBsxB,EAAgBC,IAC7EvxB,EAASkxB,aAAa33B,KAAKyO,IACjC,GAAIyI,QAAQzI,EAAEmpB,QAAU1gC,EAAEwxB,SAAU,OAAO,EAE3C,GAAIja,EAAEopB,QACe,EAAAM,EAAAI,IAAuBrhC,GACzB,OAAO,EAE1B,GAAIggB,QAAQzI,EAAEqpB,MAAQ5gC,EAAEszB,OAAQ,OAAO,EACvC,GAAIuN,EAAO,OAAOtpB,EAAEspB,MACpB,GAAIC,EAAO,OAAOvpB,EAAEupB,MACpB,GAAIvpB,EAAErZ,IAAK,CACV,MAAMojC,EAAKthC,EACX,MAAa,UAATuX,EAAErZ,IAAkC,UAAVojC,EAAGpjC,IACpB,aAATqZ,EAAErZ,IAAqC,aAAVojC,EAAGpjC,IACvB,OAATqZ,EAAErZ,IAA+B,UAAVojC,EAAGpjC,IACjB,MAATqZ,EAAErZ,IAA8B,WAAVojC,EAAGpjC,IAChB,QAATqZ,EAAErZ,IAAgC,aAAVojC,EAAGpjC,IAClB,QAATqZ,EAAErZ,IAAgC,aAAVojC,EAAGpjC,IAClB,SAATqZ,EAAErZ,IAAiC,cAAVojC,EAAGpjC,IACzBqZ,EAAErZ,KAAOojC,EAAGpjC,KAAOqZ,EAAErZ,IAAIgM,eAAiBo3B,EAAGpjC,IAAIgM,aACzD,CACA,OAAO,IAM6Cq3B,CAASvhC,EAAG+gC,EAAY9sB,GAAI4sB,EAAOC,IAExF,GAAuB,IAAnBM,EAAQthC,OACZ,OAAuB,IAAnBshC,EAAQthC,OAAqBshC,EAAQ,GAInBA,EAAQn1B,KAAK,CAACC,EAAGC,KACtC,MAAMq1B,EAAYT,EAAY70B,GACxBu1B,EAAYV,EAAY50B,GAExBu1B,EAAaF,EAAUf,aAAa,GACpCkB,EAAaF,EAAUhB,aAAa,GAEpCmB,GAAUF,EAAWhB,MAAQ,EAAI,IAAMgB,EAAWf,KAAO,EAAI,IAAMe,EAAWd,IAAM,EAAI,GAG9F,OAFgBe,EAAWjB,MAAQ,EAAI,IAAMiB,EAAWhB,KAAO,EAAI,IAAMgB,EAAWf,IAAM,EAAI,GAE9EgB,IAGI,kKC1Xf,MAAMC,EAAQ,KACnB,GAAyB,oBAAdC,UAA2B,OAAO,EAG7C,GAAI,kBAAmBA,WAAcA,UAAkBC,cAAe,CACpE,MAAMzgC,EAAYwgC,UAAkBC,cAAczgC,SAClD,GAAIA,GAAYA,EAAS4I,cAAc6E,SAAS,OAC9C,OAAO,CAEX,CAGA,MAAMizB,EAAYF,UAAUE,UAAU93B,cACtC,GAAI83B,EAAUjzB,SAAS,WAAaizB,EAAUjzB,SAAS,aACrD,OAAO,EAIT,GAAI+yB,UAAUxgC,SAAU,CACtB,MAAMA,EAAWwgC,UAAUxgC,SAAS4I,cACpC,GAAI5I,EAASyN,SAAS,QAAUzN,EAASyN,SAAS,UAChD,OAAO,CAEX,CAGA,IAEE,QAA0B3F,IADR,IAAI64B,cAAc,UAAW,CAAEC,SAAS,IAC5CA,QAEZ,MAAO,mBAAmBC,KAAKL,UAAUE,UAE7C,CAAE,MAAOhiC,GACP,CAGF,OAAO,iBAa8B0yB,GAC9BmP,IAAUnP,EAAMwP,QAAUxP,EAAM0P,eARP,IACzBP,IAAU,MAAQ,oDChD3BQ,QAA8BC,GAA4BC,KAE1DF,EAAApkC,KAAA,CAAA0G,EAAAqD,GAAA,42SA4bC,IAAOwB,QAAA,EAAAiuB,QAAA,8BAAA+K,MAAA,GAAAC,SAAA,uhHAA0lHC,eAAA,82SAAm4SC,WAAA,MAEr+Z,MAAAC,EAAA","sources":["webpack://app/./src/parseModel.ts","webpack://app/./src/hooks.ts","webpack://app/./src/utils.ts","webpack://app/./src/components/Toolbar.tsx","webpack://app/./src/Root.tsx","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/constants.ts","webpack://app/./src/graph-view/node-content.ts","webpack://app/./src/graph-view/graph.ts","webpack://app/./src/graph-view/edge-utils.ts","webpack://app/./src/graph-view/layout.ts","webpack://app/./src/style.css?dd02","webpack://app/./src/websocket.ts","webpack://app/./src/index.tsx","webpack://app/./src/shortcuts.tsx","webpack://app/./src/utils/platform.ts","webpack://app/./src/style.css"],"sourcesContent":["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 { useState, useCallback, useEffect } from 'react';\nimport { GraphData } from './graph-view/graph';\nimport { parseView } from './parseModel';\nimport { LayoutOptions } from './graph-view/layout';\nimport { \n findShortcut, \n HELP, \n SAVE, \n TOGGLE_DRAG_MODE,\n ALIGN_HORIZONTAL,\n ALIGN_VERTICAL,\n DISTRIBUTE_HORIZONTAL,\n DISTRIBUTE_VERTICAL,\n AUTO_LAYOUT,\n RESET_POSITION,\n TOGGLE_GRID,\n TOGGLE_SNAP_TO_GRID,\n SNAP_ALL_TO_GRID,\n MOVE_LEFT,\n MOVE_RIGHT,\n MOVE_UP,\n MOVE_DOWN,\n MOVE_LEFT_FINE,\n MOVE_RIGHT_FINE,\n MOVE_UP_FINE,\n MOVE_DOWN_FINE\n} from './shortcuts';\n\n// Global state for graphs to preserve edits\nconst graphs: { [key: string]: GraphData } = {};\n\n// Custom hook for graph management\nexport const useGraph = (model: any, layouts: any, currentID: string): GraphData | null => {\n if (graphs[currentID]) {\n return graphs[currentID];\n }\n \n const graph = parseView(model, layouts, currentID);\n if (graph) {\n graphs[currentID] = graph;\n }\n \n return graph;\n};\n\n// Custom hook for auto layout functionality\nexport const useAutoLayout = (graph: GraphData) => {\n const [layouting, setLayouting] = useState(false);\n\n const handleAutoLayout = useCallback(async (opts?: LayoutOptions) => {\n setLayouting(true);\n try {\n const options: LayoutOptions = {\n direction: graph.layoutDirection || 'DOWN',\n ...(opts || {})\n };\n await graph.autoLayout(options);\n } finally {\n setLayouting(false);\n }\n }, [graph]);\n\n return { layouting, handleAutoLayout };\n};\n\n// Custom hook for save functionality\nexport const useSave = (graph: GraphData, currentID: string) => {\n const [saving, setSaving] = useState(false);\n\n const handleSave = useCallback(async () => {\n setSaving(true);\n \n try {\n const response = await fetch('data/save?id=' + encodeURIComponent(currentID), {\n method: 'post',\n body: graph.exportSVG()\n });\n \n if (response.status !== 202) {\n const detail = (await response.text()).trim();\n throw new Error(detail || `save failed with HTTP ${response.status}`);\n }\n graph.setSaved();\n } finally {\n setSaving(false);\n }\n }, [graph, currentID]);\n\n return { saving, handleSave };\n};\n\n// Custom hook for keyboard shortcuts\nexport const useKeyboardShortcuts = (\n toggleHelp: () => void,\n saveLayout: () => void,\n graph?: GraphData,\n dragMode?: 'pan' | 'select',\n setDragMode?: (mode: 'pan' | 'select') => void,\n onAutoLayout?: () => void\n) => {\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n const shortcut = findShortcut(e);\n \n // Prevent browser default for all recognized shortcuts\n if (shortcut) {\n e.preventDefault();\n }\n \n if (shortcut === HELP) {\n toggleHelp();\n } else if (shortcut === SAVE) {\n saveLayout();\n } else if (shortcut === TOGGLE_DRAG_MODE && setDragMode && dragMode) {\n setDragMode(dragMode === 'pan' ? 'select' : 'pan');\n } else if (graph) {\n // Graph-dependent shortcuts\n if (shortcut === ALIGN_HORIZONTAL) {\n graph.alignSelectionH();\n } else if (shortcut === ALIGN_VERTICAL) {\n graph.alignSelectionV();\n } else if (shortcut === DISTRIBUTE_HORIZONTAL) {\n graph.distributeSelectionH();\n } else if (shortcut === DISTRIBUTE_VERTICAL) {\n graph.distributeSelectionV();\n } else if (shortcut === AUTO_LAYOUT && onAutoLayout) {\n onAutoLayout();\n } else if (shortcut === RESET_POSITION) {\n graph.resetView();\n } else if (shortcut === TOGGLE_GRID) {\n graph.toggleGrid();\n } else if (shortcut === TOGGLE_SNAP_TO_GRID) {\n graph.toggleSnapToGrid();\n } else if (shortcut === SNAP_ALL_TO_GRID) {\n graph.snapAllToGrid();\n } else if (shortcut === MOVE_LEFT) {\n graph.moveSelected(-graph.getGridSize(), 0);\n } else if (shortcut === MOVE_LEFT_FINE) {\n graph.moveSelected(-1, 0, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_RIGHT) {\n graph.moveSelected(graph.getGridSize(), 0);\n } else if (shortcut === MOVE_RIGHT_FINE) {\n graph.moveSelected(1, 0, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_UP) {\n graph.moveSelected(0, -graph.getGridSize());\n } else if (shortcut === MOVE_UP_FINE) {\n graph.moveSelected(0, -1, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_DOWN) {\n graph.moveSelected(0, graph.getGridSize());\n } else if (shortcut === MOVE_DOWN_FINE) {\n graph.moveSelected(0, 1, true); // Disable snap for fine movement\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleHelp, saveLayout, graph, dragMode, setDragMode, onAutoLayout]);\n};\n\n// Utility function to clear graph cache\nexport const clearGraphCache = (currentID?: string) => {\n if (currentID) {\n delete graphs[currentID];\n } else {\n Object.keys(graphs).forEach(key => delete graphs[key]);\n }\n};","// Helper functions for the application\n\nexport function removeEmptyProps(obj: any) {\n return JSON.parse(JSON.stringify(obj));\n}\n\nexport function camelToWords(camel: string) {\n const split = camel.replace(/([A-Z])/g, \" $1\");\n return split.charAt(0).toUpperCase() + split.slice(1);\n}\n\nexport function getCurrentViewID() {\n const params = new URLSearchParams(document.location.search);\n return params.get('id') || '';\n} ","import React, { FC, useState, useEffect } from 'react';\nimport { getZoomAuto, GraphData, setZoom, getZoom, setZoomCentered } from '../graph-view/graph';\nimport { listViews } from '../parseModel';\nimport { camelToWords } from '../utils';\nimport { getModifierKeyName } from '../utils/platform';\n\n// Types\ninterface ToolbarProps {\n model: any;\n currentID: string;\n onViewChange: (id: string) => void;\n graph: GraphData;\n onAutoLayout: () => void;\n onSave: () => void;\n onToggleHelp: () => void;\n saving: boolean;\n layouting: boolean;\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}\n\nexport const Toolbar: FC = ({\n model, currentID, onViewChange, graph, \n onAutoLayout, onSave, onToggleHelp, saving, layouting,\n dragMode, setDragMode\n}) => {\n const views = listViews(model);\n \n return (\n
\n \n \n
\n );\n};\n\nconst ViewSelector: FC<{\n views: any[];\n currentID: string;\n onViewChange: (id: string) => void;\n}> = ({ views, currentID, onViewChange }) => (\n
\n View:\n {views.length > 1 ? (\n \n ) : (\n \n {views[0] ? camelToWords(views[0].section) + ': ' + views[0].title : 'No views available'}\n \n )}\n
\n);\n\nconst ToolbarActions: FC<{\n graph: GraphData;\n onAutoLayout: () => void;\n onSave: () => void;\n onToggleHelp: () => void;\n saving: boolean;\n layouting: boolean;\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}> = ({\n graph, onAutoLayout, onSave, onToggleHelp, saving, layouting,\n dragMode, setDragMode\n}) => (\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n);\n\nconst DragModeButton: FC<{\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}> = ({ dragMode, setDragMode }) => (\n \n);\n\nconst UndoRedoButtons: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n );\n};\n\nconst AlignmentButtons: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n \n \n );\n};\n\nconst LayoutControls: FC<{\n onAutoLayout: () => void;\n layouting: boolean;\n}> = ({ onAutoLayout, layouting }) => {\n const modKey = getModifierKeyName();\n return (\n \n );\n};\n\nconst GridControls: FC<{ graph: GraphData }> = ({ graph }) => {\n const [gridVisible, setGridVisible] = useState(graph.isGridVisible());\n const [snapToGrid, setSnapToGrid] = useState(graph.isSnapToGrid());\n const modKey = getModifierKeyName();\n \n // Update state when graph changes or when grid state changes via shortcuts\n React.useEffect(() => {\n const updateGridState = () => {\n setGridVisible(graph.isGridVisible());\n setSnapToGrid(graph.isSnapToGrid());\n };\n \n // Initial update\n updateGridState();\n \n // Listen for grid state changes from keyboard shortcuts\n window.addEventListener('gridStateChanged', updateGridState);\n \n return () => {\n window.removeEventListener('gridStateChanged', updateGridState);\n };\n }, [graph]);\n \n const handleToggleGrid = () => {\n graph.toggleGrid();\n setGridVisible(graph.isGridVisible());\n };\n \n const handleToggleSnap = () => {\n graph.toggleSnapToGrid();\n setSnapToGrid(graph.isSnapToGrid());\n };\n \n const handleSnapAll = () => {\n graph.snapAllToGrid();\n };\n \n return (\n <>\n \n \n \n \n );\n};\n\nconst ZoomDisplay: FC = () => {\n const [zoom, setZoomState] = useState(100);\n\n useEffect(() => {\n const updateZoom = () => {\n const currentZoom = Math.round(getZoom() * 100);\n setZoomState(currentZoom);\n };\n\n // Update zoom initially\n updateZoom();\n\n // Update zoom every 100ms to catch changes from wheel/keyboard/etc\n const interval = setInterval(updateZoom, 100);\n\n return () => clearInterval(interval);\n }, []);\n\n return (\n \n );\n};\n\nconst ZoomControls: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n \n \n );\n};\n\nconst SaveButton: FC<{\n onSave: () => void;\n saving: boolean;\n graph: GraphData;\n}> = ({ onSave, saving, graph }) => {\n const [hasChanges, setHasChanges] = useState(false);\n const modKey = getModifierKeyName();\n \n // Check for changes periodically\n useEffect(() => {\n const checkChanges = () => {\n setHasChanges(graph.changed());\n };\n \n // Initial check\n checkChanges();\n \n // Check every 100ms for changes\n const interval = setInterval(checkChanges, 100);\n \n return () => clearInterval(interval);\n }, [graph]);\n \n return (\n \n );\n};\n\nconst HelpButton: FC<{\n onToggleHelp: () => void;\n}> = ({ onToggleHelp }) => {\n return (\n \n );\n};","import React, { FC, useState, useCallback, useEffect, useRef, Suspense, lazy } from \"react\";\nimport { GraphData, LayoutDirection } from \"./graph-view/graph\";\nimport { LayoutOptions } from \"./graph-view/layout\";\nimport { BrowserRouter as Router, Routes, Route, useSearchParams } from 'react-router-dom';\nimport { listViews } from \"./parseModel\";\nimport { useGraph, useAutoLayout, useSave, useKeyboardShortcuts, clearGraphCache } from \"./hooks\";\nimport { Toolbar } from \"./components/Toolbar\";\nimport { removeEmptyProps, getCurrentViewID } from \"./utils\";\n\nconst Help = lazy(() => import(\"./shortcuts\").then(module => ({ default: module.Help })));\nconst Graph = lazy(() => import(\"./graph-view/graph-react\").then(module => ({ default: module.Graph })));\n\n// Types\ninterface ModelData {\n model: any;\n layout: any;\n}\n\ntype AutomationStatus = 'running' | 'complete' | 'error';\n\nconst setAutomationStatus = (status: AutomationStatus | null, error?: string) => {\n const root = document.documentElement;\n if (!status) {\n delete root.dataset.mdlAutomationStatus;\n delete root.dataset.mdlAutomationError;\n return;\n }\n root.dataset.mdlAutomationStatus = status;\n if (error) {\n root.dataset.mdlAutomationError = error;\n } else {\n delete root.dataset.mdlAutomationError;\n }\n};\n\nconst errorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nconst reportInteractiveError = (action: string, error: unknown) => {\n console.error(`${action} failed:`, error);\n alert(`${action} failed. See console for details.`);\n};\n\nexport const Root: FC = ({ model, layout }) => (\n \n \n } />\n \n \n);\n\nexport const refreshGraph = () => {\n const currentID = getCurrentViewID();\n clearGraphCache(currentID);\n};\n\nconst ModelPane: FC<{ model: any; layouts: any }> = ({ model, layouts }) => {\n const [searchParams, setSearchParams] = useSearchParams();\n const currentID = decodeURI(searchParams.get('id') || '');\n \n // UI State\n const [helpVisible, setHelpVisible] = useState(false);\n const [dragMode, setDragMode] = useState<'pan' | 'select'>('pan');\n const [readyGraphID, setReadyGraphID] = useState(null);\n const automationKey = useRef(null);\n const automationRun = useRef(0);\n \n // Get or create graph for current view\n const graph = useGraph(model, layouts, currentID);\n \n // Custom hooks for functionality\n const { layouting, handleAutoLayout } = useAutoLayout(graph || ({} as GraphData));\n const { saving, handleSave } = useSave(graph || ({} as GraphData), currentID);\n \n if (!graph) {\n return ;\n }\n\n const handleToggleHelp = useCallback(() => {\n setHelpVisible(!helpVisible);\n }, [helpVisible]);\n\n const handleGraphReady = useCallback(() => {\n setReadyGraphID(currentID);\n }, [currentID]);\n\n const handleInteractiveAutoLayout = useCallback(() => {\n void handleAutoLayout().catch(error => reportInteractiveError('Layout', error));\n }, [handleAutoLayout]);\n\n const handleInteractiveSave = useCallback(() => {\n void handleSave().catch(error => reportInteractiveError('Save', error));\n }, [handleSave]);\n\n // Update document title when view changes\n useEffect(() => {\n if (graph && graph.name) {\n document.title = `${graph.name} - Model`;\n }\n }, [graph]);\n\n // Headless automation: support query params to auto-layout and save\n useEffect(() => {\n const params = Object.fromEntries(searchParams.entries());\n const auto = params['auto'] === '1' || params['auto'] === 'true';\n const save = params['save'] === '1' || params['save'] === 'true';\n if (!auto && !save) {\n automationKey.current = null;\n automationRun.current++;\n setAutomationStatus(null);\n return;\n }\n if (readyGraphID !== currentID) {\n return;\n }\n\n const key = `${currentID}:${searchParams.toString()}`;\n if (automationKey.current === key) {\n return;\n }\n automationKey.current = key;\n const run = ++automationRun.current;\n setAutomationStatus('running');\n\n const direction = (params['direction'] || '').toUpperCase();\n const compact = params['compact'] === '1' || params['compact'] === 'true';\n\n const validDirections: LayoutDirection[] = ['UP', 'DOWN', 'LEFT', 'RIGHT'];\n const layoutOpts: LayoutOptions = {};\n if (validDirections.includes(direction as LayoutDirection)) {\n layoutOpts.direction = direction as LayoutDirection;\n }\n if (compact) {\n layoutOpts.compactLayout = true;\n }\n\n (async () => {\n try {\n if (auto) {\n await handleAutoLayout(layoutOpts);\n }\n if (save) {\n await handleSave();\n }\n if (automationRun.current === run) {\n setAutomationStatus('complete');\n }\n } catch (error) {\n const message = errorMessage(error);\n console.error('Automation failed:', error);\n if (automationRun.current === run) {\n setAutomationStatus('error', message);\n }\n }\n })();\n }, [currentID, graph, handleAutoLayout, handleSave, readyGraphID, searchParams]);\n\n // Setup keyboard shortcuts\n useKeyboardShortcuts(\n handleToggleHelp,\n handleInteractiveSave,\n graph,\n dragMode,\n setDragMode,\n handleInteractiveAutoLayout,\n );\n\n const handleViewChange = useCallback((id: string) => {\n setSearchParams({ id: encodeURIComponent(id) });\n }, [setSearchParams]);\n\n const handleSelect = useCallback((id: string | null) => {\n if (id) {\n const element = graph.metadata.elements.find((m: any) => m.id === id);\n console.log(removeEmptyProps(element));\n }\n }, [graph]);\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\tLoading graph...}>\n\t\t\t\t\n\t\t\t\n\t\t\t{helpVisible && (\n\t\t\t\tLoading help...}>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t)}\n\t\t\n\t);\n};\n\nconst ViewRedirect: FC<{ model: any }> = ({ model }) => {\n const views = listViews(model);\n \n React.useEffect(() => {\n // Set default title when no view is selected\n document.title = 'Model - Architecture Diagrams as Code';\n \n if (views.length > 0) {\n document.location.href = '?id=' + views[0].key;\n }\n }, [views]);\n\n if (views.length > 0) {\n return <>Redirecting to {views[0].title};\n }\n return <>No views available;\n};\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","const textMeasure = () => {\n\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\tdocument.body.appendChild(svg);\n\n\treturn {\n\t\tmeasure: (text: string, attrs: { [key: string]: string }) => {\n\t\t\tconst node = document.createElementNS('http://www.w3.org/2000/svg', 'text')\n\t\t\tnode.setAttribute('x', '0');\n\t\t\tnode.setAttribute('y', '0');\n\t\t\tfor (let attr in attrs) {\n\t\t\t\tnode.setAttribute(attr, attrs[attr]);\n\t\t\t}\n\t\t\tnode.appendChild(document.createTextNode(text));\n\n\t\t\tsvg.appendChild(node);\n\t\t\tconst {width, height} = node.getBBox();\n\t\t\tsvg.removeChild(node);\n\t\t\treturn {width, height};\n\t\t},\n\t\tclean: () => {\n\t\t\tdocument.body.removeChild(svg);\n\t\t}\n\t}\n}\n\n// Helper function to break long words that exceed width\nconst breakLongWord = (word: string, maxWidth: number, attrs: { [key: string]: string }, mt: any): string[] => {\n\tconst parts: string[] = [];\n\tlet currentPart = '';\n\t\n\tfor (let i = 0; i < word.length; i++) {\n\t\tconst testPart = currentPart + word[i];\n\t\tconst size = mt.measure(testPart, attrs);\n\t\t\n\t\tif (size.width > maxWidth && currentPart.length > 0) {\n\t\t\tparts.push(currentPart);\n\t\t\tcurrentPart = word[i];\n\t\t} else {\n\t\t\tcurrentPart = testPart;\n\t\t}\n\t}\n\t\n\tif (currentPart.length > 0) {\n\t\tparts.push(currentPart);\n\t}\n\t\n\treturn parts;\n}\n\n// split a text in lines wrapped at a certain width\nexport const svgTextWrap = (text: string, width: number, attrs: { [key: string]: string }) => {\n\tconst mt = textMeasure()\n\tlet maxW = 0;\n\t\n\tconst ret = text.trim().split('\\n').map(text => { //split paragraphs\n\t\t//do one paragraph\n\t\tconst words = text.trim().split(/\\s+/);\n\t\tlet lines: string[] = [];\n\t\tlet currentLine: string[] = [];\n\t\t\n\t\twords.forEach(word => {\n\t\t\t// First check if the single word exceeds the width\n\t\t\tconst wordSize = mt.measure(word, attrs);\n\t\t\tif (wordSize.width > width) {\n\t\t\t\t// If we have content in current line, finish it first\n\t\t\t\tif (currentLine.length > 0) {\n\t\t\t\t\tlines.push(currentLine.join(' '));\n\t\t\t\t\tcurrentLine = [];\n\t\t\t\t}\n\t\t\t\t// Break the long word into smaller parts\n\t\t\t\tconst brokenParts = breakLongWord(word, width, attrs, mt);\n\t\t\t\t// Add all but the last part as complete lines\n\t\t\t\tfor (let i = 0; i < brokenParts.length - 1; i++) {\n\t\t\t\t\tlines.push(brokenParts[i]);\n\t\t\t\t\tmaxW = Math.max(maxW, mt.measure(brokenParts[i], attrs).width);\n\t\t\t\t}\n\t\t\t\t// Start new line with the last part\n\t\t\t\tif (brokenParts.length > 0) {\n\t\t\t\t\tcurrentLine = [brokenParts[brokenParts.length - 1]];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Normal word processing\n\t\t\t\tconst newLine = [...currentLine, word];\n\t\t\t\tconst size = mt.measure(newLine.join(' '), attrs);\n\t\t\t\tif (size.width > width && currentLine.length > 0) {\n\t\t\t\t\tlines.push(currentLine.join(' '));\n\t\t\t\t\tcurrentLine = [word];\n\t\t\t\t} else {\n\t\t\t\t\tmaxW = Math.max(maxW, size.width)\n\t\t\t\t\tcurrentLine = newLine;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (currentLine.length > 0) {\n\t\t\tlines.push(currentLine.join(' '));\n\t\t}\n\t\treturn lines;\n\t}).reduce((a, v) => a.concat(v), []) //flatten\n\n\tmt.clean()\n\treturn {lines: ret, maxW};\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}","// 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 {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} 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\tcalculateDistance\n} from \"./constants\";\nimport {\n\tcalculateEdgeVertices,\n\tcalculateLabelPlacement,\n\tcreateEdgeSegments,\n\tEdgeLabelPlacement\n} from \"./edge-utils\";\nimport {\n\tbuildNodeContent,\n\tlayoutNodeContent,\n\tNodeContentLayout\n} from \"./node-content\";\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}\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\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\tlabelVertex?: EdgeVertex; // ELK-calculated label position (separate from routing vertices)\n\tlabelBounds?: 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 Layout {\n\t[k: string]: Point | (Point & { label: boolean })[] | 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\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\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\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\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\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\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(this, 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\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: { [key: string]: any }, rerender = false) {\n\t\t// First pass: collect all coordinate values to find bounds\n\t\tconst coordinates: Array<{x: number, y: number}> = [];\n\t\t\n\t\tObject.entries(layout).forEach(([k, v]) => {\n\t\t\tif (!k.startsWith('e-') && v.x !== undefined && v.y !== undefined) {\n\t\t\t\t// Node coordinates\n\t\t\t\tcoordinates.push({x: v.x, y: v.y});\n\t\t\t} else if (k.startsWith('e-') && Array.isArray(v)) {\n\t\t\t\t// Edge vertex coordinates\n\t\t\t\tv.forEach((vertex: any) => {\n\t\t\t\t\tif (vertex.x !== undefined && vertex.y !== undefined) {\n\t\t\t\t\t\tcoordinates.push({x: vertex.x, y: vertex.y});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\t\n\t\t// Calculate normalization offset if we have coordinates\n\t\tlet offsetX = 0;\n\t\tlet offsetY = 0;\n\t\t\n\t\tif (coordinates.length > 0) {\n\t\t\tconst minX = Math.min(...coordinates.map(c => c.x));\n\t\t\tconst minY = Math.min(...coordinates.map(c => c.y));\n\t\t\t\n\t\t\t// Only normalize if coordinates are problematic (negative or very large)\n\t\t\tif (minX < -100 || minY < -100 || Math.max(...coordinates.map(c => c.x)) > 3000 || Math.max(...coordinates.map(c => c.y)) > 2000) {\n\t\t\t\tconst padding = 50;\n\t\t\t\toffsetX = -minX + padding;\n\t\t\t\toffsetY = -minY + padding;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Second pass: apply coordinates with normalization\n\t\tObject.entries(layout).forEach(([k, v]) => {\n\t\t\t// nodes\n\t\t\tconst n = this.nodesMap.get(k)\n\t\t\tif (n) {\n\t\t\t\tn.x = v.x + offsetX\n\t\t\t\tn.y = v.y + offsetY\n\t\t\t} else\n\t\t\t\t// edge vertices\n\t\t\tif (k.startsWith('e-') && !k.endsWith('-deleted')) {\n\t\t\t\tconst edge = this.edges.find(e => e.id == k.slice(2))\n\t\t\t\tif (!edge) return;\n\t\t\t\tedge.vertices && edge.vertices.forEach(v => this.edgeVertices.delete(v.id))\n\t\t\t\tedge.vertices = v.map((p: Point) => {\n\t\t\t\t\tconst normalizedPoint = { \n\t\t\t\t\t\tx: p.x + offsetX, \n\t\t\t\t\t\ty: p.y + offsetY \n\t\t\t\t\t} as Point;\n\t\t\t\t\t// Preserve any additional properties like 'label' and 'auto'\n\t\t\t\t\tObject.assign(normalizedPoint, p, { x: p.x + offsetX, y: p.y + offsetY });\n\t\t\t\t\tconst vertex = edge.initVertex(normalizedPoint);\n\t\t\t\t\t// Ensure auto flag is preserved after initVertex\n\t\t\t\t\tif ((p as any).auto) {\n\t\t\t\t\t\tvertex.auto = true;\n\t\t\t\t\t}\n\t\t\t\t\treturn vertex;\n\t\t\t\t})\n\t\t\t\treturn;\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(e => e.id == edgeId)\n\t\t\t\tif (edge && v === true) {\n\t\t\t\t\tedge.userDeletedVertices = true\n\t\t\t\t}\n\t\t\t\treturn;\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\ttry {\n\t\t\tconst auto = await autoLayout(this, options)\n\t\t\t\n\t\t\tthis._undo.beforeChange()\n\t\t\t\n\t\t\t// Apply node positions\n\t\t\tauto.nodes.forEach(an => {\n\t\t\t\tconst n = this.nodesMap.get(an.id)\n\t\t\t\tif (n) {\n\t\t\t\t\tthis.moveNode(n, an.x, an.y, false, true) // Skip undo for individual moves\n\t\t\t\t}\n\t\t\t})\n\t\t\t\n\t\t\t// Apply edge routing from ELK layout\n\t\t\tauto.edges.forEach(ae => {\n\t\t\t\tconst edge = this.edges.find(e => e.id == ae.id)\n\t\t\t\tif (edge) {\n\t\t\t\t\t// Clear existing vertices for this edge only\n\t\t\t\t\tif (edge.vertices) {\n\t\t\t\t\t\tedge.vertices.forEach(v => {\n\t\t\t\t\t\t\tif (v.id) {\n\t\t\t\t\t\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tedge.vertices = []\n\t\t\t\t\tedge.userDeletedVertices = false\n\t\t\t\t\t\n\t\t\t\t\t// Add routing vertices from ELK (these are proper bend points, not nodes)\n\t\t\t\t\tif (ae.vertices && ae.vertices.length > 0) {\n\t\t\t\t\t\tedge.vertices = ae.vertices.map(p => {\n\t\t\t\t\t\t\tconst vertex = edge.initVertex(p)\n\t\t\t\t\t\t\tvertex.auto = true // Mark as auto-generated\n\t\t\t\t\t\t\treturn vertex\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Handle edge label positioning - create proper interactive label vertices\n\t\t\t\t\tif (ae.label) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Remove any existing label vertices (ELK or user-created)\n\t\t\t\t\t\tif (edge.vertices) {\n\t\t\t\t\t\t\tedge.vertices.forEach(v => {\n\t\t\t\t\t\t\t\tif (v.label) {\n\t\t\t\t\t\t\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tedge.vertices = edge.vertices.filter(v => !v.label)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Create a proper label vertex that behaves like a user-created vertex\n\t\t\t\t\t\tconst labelVertex = edge.initVertex(ae.label)\n\t\t\t\t\t\tlabelVertex.label = true\n\t\t\t\t\t\tlabelVertex.auto = true // Mark as auto-generated so it can be cleaned up\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Insert label vertex at the optimal position in the routing path\n\t\t\t\t\t\t// Find the best position to insert it and project it onto that line segment\n\t\t\t\t\t\tedge.vertices = edge.vertices || []\n\t\t\t\t\t\tconst insertPos = findOptimalLabelPosition(edge.vertices, ae.label, edge.from, edge.to)\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Project the label position onto the line segment where it will be inserted\n\t\t\t\t\t\tconst projectedPos = projectLabelOntoSegment(edge.vertices, ae.label, insertPos, edge.from, edge.to)\n\t\t\t\t\t\tlabelVertex.x = projectedPos.x\n\t\t\t\t\t\tlabelVertex.y = projectedPos.y\n\t\t\t\t\t\t\n\t\t\t\t\t\tedge.vertices.splice(insertPos, 0, labelVertex)\n\t\t\t\t\t\tthis.edgeVertices.set(labelVertex.id, labelVertex)\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Redraw the edge with new routing\n\t\t\t\t\tthis.redrawEdge(edge)\n\t\t\t\t}\n\t\t\t})\n\t\t\t\n\t\t\t// Fit the layout to the viewport with optimal positioning\n\t\t\tthis.fitToView()\n\t\t\t\n\t\t\tthis._undo.change()\n\t\t\t\n\t\t} catch (error) {\n\t\t\tconsole.error('Auto layout failed:', error)\n\t\t\t// Could show user notification here\n\t\t}\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\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(data, 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(data: GraphData, edge: Edge) {\n\tconst n1 = edge.from, n2 = edge.to;\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\t// Calculate edge vertices using utility function\n\tconst vertices = calculateEdgeVertices(edge, data)\n\n\tconst labelPlacement = calculateLabelPlacement(vertices, position, n1)\n\n\tconst {bg, txt, bbox} = buildEdgeLabel(labelPlacement, edge, data)\n\tg.append(bg, txt)\n\n\t// Create edge segments and path using utility function\n\tconst {segments, path} = createEdgeSegments(vertices, bbox, n1, n2)\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\tedge.vertices = vertices.slice(1, -1).map(p => {\n\t\t// Preserve existing EdgeVertex objects to maintain IDs and selection state\n\t\tif ('id' in p && 'edge' in p) {\n\t\t\t// This is already an EdgeVertex, preserve it\n\t\t\tconst v = p as EdgeVertex;\n\t\t\tv.edge = edge; // Ensure edge reference is correct\n\t\t\treturn v;\n\t\t} else {\n\t\t\t// This is a new Point, convert to EdgeVertex\n\t\t\treturn edge.initVertex(p);\n\t\t}\n\t})\n\tedge.vertices.forEach((p, i) => {\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\nfunction buildEdgeLabel(placement: EdgeLabelPlacement, edge: Edge, data: GraphData) {\n\tconst labelGap = 12;\n\tconst collisionPadding = 8;\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\n\tconst anchors: Point[] = [{x: placement.x, y: placement.y}]\n\tif (placement.movable && placement.segment) {\n\t\tfor (const fraction of [0.5, 0.35, 0.65, 0.2, 0.8]) {\n\t\t\tconst anchor = {\n\t\t\t\tx: placement.segment.p.x +\n\t\t\t\t\t(placement.segment.q.x - placement.segment.p.x) * fraction,\n\t\t\t\ty: placement.segment.p.y +\n\t\t\t\t\t(placement.segment.q.y - placement.segment.p.y) * fraction,\n\t\t\t}\n\t\t\tif (!anchors.some(existing =>\n\t\t\t\tMath.abs(existing.x - anchor.x) < 0.1 &&\n\t\t\t\tMath.abs(existing.y - anchor.y) < 0.1\n\t\t\t)) {\n\t\t\t\tanchors.push(anchor)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst occupied = [\n\t\t...data.nodes().map(node => expandBox(uncenterBox(node), collisionPadding)),\n\t\t...data.edges\n\t\t\t.filter(other => other !== edge && other.labelBounds)\n\t\t\t.map(other => expandBox(other.labelBounds, collisionPadding)),\n\t]\n\tconst candidates = anchors.flatMap(anchor => {\n\t\tif (placement.orientation === 'vertical') {\n\t\t\treturn [1, -1].map(side => edgeLabelCandidate(\n\t\t\t\tanchor.x + side * (maxW / 2 + labelGap),\n\t\t\t\tanchor.y,\n\t\t\t\tmaxW,\n\t\t\t\tdy,\n\t\t\t\tanchor,\n\t\t\t\tplacement,\n\t\t\t\toccupied,\n\t\t\t))\n\t\t}\n\t\treturn [-1, 1].map(side => edgeLabelCandidate(\n\t\t\tanchor.x,\n\t\t\tanchor.y + side * (dy / 2 + labelGap),\n\t\t\tmaxW,\n\t\t\tdy,\n\t\t\tanchor,\n\t\t\tplacement,\n\t\t\toccupied,\n\t\t))\n\t})\n\tconst selected = candidates.reduce((best, candidate) =>\n\t\tcandidate.score < best.score ? candidate : best\n\t)\n\tconst {centerX, centerY, bounds} = selected\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 bbox = {...bounds}\n\tconst bg = create.rect(bbox.width, bbox.height, bbox.x, bbox.y)\n\tapplyStyle(bg, styles.edgeRect)\n\ttxt.setAttribute('data-field', 'label')\n\tedge.labelBounds = bounds\n\n\tbbox.x += bbox.width / 2\n\tbbox.y += bbox.height / 2\n\treturn {bg, txt, bbox}\n}\n\nfunction edgeLabelCandidate(\n\tcenterX: number,\n\tcenterY: number,\n\twidth: number,\n\theight: number,\n\tanchor: Point,\n\tplacement: EdgeLabelPlacement,\n\toccupied: BBox[],\n) {\n\tconst bounds = {\n\t\tx: centerX - width / 2,\n\t\ty: centerY - height / 2,\n\t\twidth,\n\t\theight,\n\t}\n\tconst overlap = occupied.reduce(\n\t\t(total, box) => total + boxOverlapArea(bounds, box),\n\t\t0,\n\t)\n\treturn {\n\t\tcenterX,\n\t\tcenterY,\n\t\tbounds,\n\t\tscore: overlap * 1000 + calculateDistance(anchor, placement),\n\t}\n}\n\nfunction expandBox(box: BBox, padding: number): BBox {\n\treturn {\n\t\tx: box.x - padding,\n\t\ty: box.y - padding,\n\t\twidth: box.width + padding * 2,\n\t\theight: box.height + padding * 2,\n\t}\n}\n\nfunction boxOverlapArea(first: BBox, second: BBox): number {\n\tconst width = Math.max(\n\t\t0,\n\t\tMath.min(first.x + first.width, second.x + second.width) -\n\t\t\tMath.max(first.x, second.x),\n\t)\n\tconst height = Math.max(\n\t\t0,\n\t\tMath.min(first.y + first.height, second.y + second.height) -\n\t\t\tMath.max(first.y, second.y),\n\t)\n\treturn width * height\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\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}\n\n/**\n * Find the optimal position to insert a label vertex into the routing path\n * to minimize disruption to the existing route\n */\nfunction findOptimalLabelPosition(vertices: Point[], labelPos: Point, fromNode: Point, toNode: Point): number {\n\t// If no existing vertices, insert at the beginning\n\tif (vertices.length === 0) {\n\t\treturn 0;\n\t}\n\t\n\t// Build the full routing path including start/end nodes\n\tconst fullPath = [fromNode, ...vertices, toNode];\n\t\n\t// Find the closest point on the path to the label position\n\tlet minDistance = Infinity;\n\tlet bestSegmentIndex = 0;\n\t\n\tfor (let i = 0; i < fullPath.length - 1; i++) {\n\t\tconst segmentStart = fullPath[i];\n\t\tconst segmentEnd = fullPath[i + 1];\n\t\t\n\t\t// Calculate distance from label position to this segment\n\t\tconst distance = distanceToSegment(labelPos, segmentStart, segmentEnd);\n\t\t\n\t\tif (distance < minDistance) {\n\t\t\tminDistance = distance;\n\t\t\tbestSegmentIndex = i;\n\t\t}\n\t}\n\t\n\t// Convert full path index to vertices array index\n\t// bestSegmentIndex 0 means between fromNode and vertices[0] -> insert at 0\n\t// bestSegmentIndex 1 means between vertices[0] and vertices[1] -> insert at 1\n\t// etc.\n\treturn bestSegmentIndex;\n}\n\n/**\n * Calculate distance from a point to a line segment\n */\nfunction distanceToSegment(point: Point, segmentStart: Point, segmentEnd: Point): number {\n\tconst A = point.x - segmentStart.x;\n\tconst B = point.y - segmentStart.y;\n\tconst C = segmentEnd.x - segmentStart.x;\n\tconst D = segmentEnd.y - segmentStart.y;\n\t\n\tconst dot = A * C + B * D;\n\tconst lenSq = C * C + D * D;\n\t\n\tif (lenSq === 0) {\n\t\t// Segment is actually a point\n\t\treturn Math.sqrt(A * A + B * B);\n\t}\n\t\n\tlet param = dot / lenSq;\n\t\n\tlet xx, yy;\n\t\n\tif (param < 0) {\n\t\txx = segmentStart.x;\n\t\tyy = segmentStart.y;\n\t} else if (param > 1) {\n\t\txx = segmentEnd.x;\n\t\tyy = segmentEnd.y;\n\t} else {\n\t\txx = segmentStart.x + param * C;\n\t\tyy = segmentStart.y + param * D;\n\t}\n\t\n\tconst dx = point.x - xx;\n\tconst dy = point.y - yy;\n\treturn Math.sqrt(dx * dx + dy * dy);\n}\n\n/**\n * Project a label position onto the line segment where it will be inserted\n */\nfunction projectLabelOntoSegment(vertices: Point[], labelPos: Point, insertPos: number, fromNode: Point, toNode: Point): Point {\n\t// Build the full routing path including start/end nodes\n\tconst fullPath = [fromNode, ...vertices, toNode];\n\t\n\t// The segment where we're inserting is between fullPath[insertPos] and fullPath[insertPos + 1]\n\tconst segmentStart = fullPath[insertPos];\n\tconst segmentEnd = fullPath[insertPos + 1];\n\t\n\t// Project the label position onto this line segment\n\treturn projectPointOntoSegment(labelPos, segmentStart, segmentEnd);\n}\n\n/**\n * Project a point onto a line segment (closest point on the segment)\n */\nfunction projectPointOntoSegment(point: Point, segmentStart: Point, segmentEnd: Point): Point {\n\tconst A = point.x - segmentStart.x;\n\tconst B = point.y - segmentStart.y;\n\tconst C = segmentEnd.x - segmentStart.x;\n\tconst D = segmentEnd.y - segmentStart.y;\n\t\n\tconst dot = A * C + B * D;\n\tconst lenSq = C * C + D * D;\n\t\n\tif (lenSq === 0) {\n\t\t// Segment is actually a point, return that point\n\t\treturn { x: segmentStart.x, y: segmentStart.y };\n\t}\n\t\n\tlet param = dot / lenSq;\n\t\n\t// Clamp to segment (don't extend beyond endpoints)\n\tparam = Math.max(0, Math.min(1, param));\n\t\n\treturn {\n\t\tx: segmentStart.x + param * C,\n\t\ty: segmentStart.y + param * D\n\t};\n}","import { Point, BBox, calculateDistance } from './constants';\nimport { intersectPolylineBox, 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\ninterface GraphData {\n\tid: string;\n\tname: string;\n\tnodesMap: Map;\n\tedges: Edge[];\n\tedgeVertices: Map;\n\tgroupsMap: Map;\n\tmetadata: any;\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, data: GraphData): 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\tconst tmp = (vertices as EdgeVertex[]);\n\n\tif (vertices.length == 0 && !edge.userDeletedVertices) {\n\t\t// Only create auto vertices if user hasn't explicitly deleted them\n\t\t// for edges with same \"from\" and \"to\", we must spread the labels so they don't overlap\n\t\t// lookup the other \"same\" edges\n\t\tconst sameEdges = data.edges.filter(e => e.from == edge.from && e.to == edge.to)\n\t\tlet spreadPos = 0\n\t\tif (sameEdges.length > 1) {\n\t\t\tconst idx = sameEdges.indexOf(edge) // my index in the list of same edges\n\t\t\tspreadPos = idx - (sameEdges.length - 1) / 2\n\n\t\t\tlet spreadX = 0, spreadY = 0;\n\t\t\tif (Math.abs(n1.x - n2.x) > Math.abs(n1.y - n2.y)) {\n\t\t\t\tspreadY = spreadPos * 70\n\t\t\t} else {\n\t\t\t\tspreadX = spreadPos * 200\n\t\t\t}\n\t\t\tconst v = edge.initVertex({\n\t\t\t\tx: (n1.x + n2.x) / 2 + spreadX,\n\t\t\t\ty: (n1.y + n2.y) / 2 + spreadY\n\t\t\t})\n\t\t\tv.label = true\n\t\t\tv.auto = true\n\t\t\tvertices.push(v)\n\t\t} else {\n\t\t\t// If there are no user-defined vertices and not a multi-edge scenario,\n\t\t\t// we don't create any auto-vertices here. AutoLayout will provide them.\n\t\t\t// The path will be a straight line between n1 and n2 (after intersection points are calculated).\n\t\t\t// ELK/autoLayout is responsible for providing bend points for non-straight lines.\n\t\t}\n\t}\n\n\tvertices.unshift(n1)\n\tvertices.push(n2)\n\n\t// Calculate intersection points with node boundaries\n\t// Find first non-label vertex for start intersection\n\tlet firstRoutingVertex = vertices[vertices.length - 1]; // Default to end node\n\tfor (let i = 1; i < vertices.length - 1; i++) {\n\t\tif (!(vertices[i] as any).label) {\n\t\t\tfirstRoutingVertex = vertices[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t// Find last non-label vertex for end intersection \n\tlet lastRoutingVertex = vertices[0]; // Default to start node\n\tfor (let i = vertices.length - 2; i > 0; i--) {\n\t\tif (!(vertices[i] as any).label) {\n\t\t\tlastRoutingVertex = vertices[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t// For connections without routing vertices, ensure we have proper direction\n\t// The defaults are already correct: firstRoutingVertex = n2, lastRoutingVertex = n1\n\t\n\t\n\t// Calculate proper node boundary intersection\n\tconst calculateNodeIntersection = (node: any, targetPoint: Point): Point => {\n\t\tconst nodeShape = node.style?.shape?.toLowerCase() || 'box';\n\t\tconst dx = targetPoint.x - node.x;\n\t\tconst dy = targetPoint.y - node.y;\n\t\tconst nodeCenter = { x: node.x, y: node.y };\n\t\t\n\t\t// If target is at center, default to right edge\n\t\tif (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01) {\n\t\t\treturn { x: node.x + node.width / 2, y: node.y };\n\t\t}\n\t\t\n\t\tif (nodeShape === 'cylinder') {\n\t\t\t// Cylinder shape intersection (same as shapes.ts)\n\t\t\tconst w = node.width;\n\t\t\tconst rx = w / 2;\n\t\t\tconst ry = rx / (5.5 + w / 70);\n\t\t\tconst halfHeight = node.height / 2;\n\t\t\t\n\t\t\t// First calculate rectangular bounds intersection\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Check intersection with rectangular bounds\n\t\t\tlet t = Infinity;\n\t\t\tif (Math.abs(cos) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(rx / cos));\n\t\t\t}\n\t\t\tif (Math.abs(sin) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfHeight / sin));\n\t\t\t}\n\t\t\t\n\t\t\tconst rectX = node.x + cos * t;\n\t\t\tconst rectY = node.y + sin * t;\n\t\t\t\n\t\t\t// Check if we need elliptical intersection for top/bottom curves\n\t\t\tconst topCurveY = node.y - halfHeight + ry;\n\t\t\tconst bottomCurveY = node.y + halfHeight - ry;\n\t\t\t\n\t\t\tif (rectY < topCurveY || rectY > bottomCurveY) {\n\t\t\t\t// Use ellipse intersection for curved parts\n\t\t\t\tconst ellipseY = rectY < topCurveY ? node.y - halfHeight + ry : node.y + halfHeight - ry;\n\t\t\t\t// Solve for ellipse intersection\n\t\t\t\tconst a = 1 / (rx * rx);\n\t\t\t\tconst b = -2 * node.x / (rx * rx);\n\t\t\t\tconst c = (node.x * node.x) / (rx * rx) + ((ellipseY - node.y) * (ellipseY - node.y)) / (ry * ry) - 1;\n\t\t\t\t\n\t\t\t\tconst discriminant = b * b - 4 * a * c;\n\t\t\t\tif (discriminant >= 0) {\n\t\t\t\t\tconst sqrt_d = Math.sqrt(discriminant);\n\t\t\t\t\tconst x1 = (-b + sqrt_d) / (2 * a);\n\t\t\t\t\tconst x2 = (-b - sqrt_d) / (2 * a);\n\t\t\t\t\t\n\t\t\t\t\t// Choose the intersection in the direction of the target\n\t\t\t\t\tconst intersectX = dx > 0 ? Math.max(x1, x2) : Math.min(x1, x2);\n\t\t\t\t\treturn { x: intersectX, y: ellipseY };\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn { x: rectX, y: rectY };\n\t\t\t\n\t\t} else if (nodeShape === 'circle') {\n\t\t\tconst radius = node.width / 2;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\treturn {\n\t\t\t\tx: node.x + Math.cos(angle) * radius,\n\t\t\t\ty: node.y + Math.sin(angle) * radius\n\t\t\t};\n\t\t\t\n\t\t} else if (nodeShape === 'ellipse') {\n\t\t\tconst rx = node.width * 0.55;\n\t\t\tconst ry = node.width * 0.45;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Parametric ellipse intersection\n\t\t\tconst t = Math.sqrt((rx * rx * sin * sin) + (ry * ry * cos * cos));\n\t\t\treturn {\n\t\t\t\tx: node.x + (rx * cos * ry) / t,\n\t\t\t\ty: node.y + (ry * sin * rx) / t\n\t\t\t};\n\t\t\t\n\t\t} else {\n\t\t\t// Default rectangular intersection\n\t\t\tconst halfWidth = node.width / 2;\n\t\t\tconst halfHeight = node.height / 2;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Calculate which edge we hit first\n\t\t\tlet t = Infinity;\n\t\t\tif (Math.abs(cos) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfWidth / cos));\n\t\t\t}\n\t\t\tif (Math.abs(sin) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfHeight / sin));\n\t\t\t}\n\t\t\t\n\t\t\treturn {\n\t\t\t\tx: node.x + cos * t,\n\t\t\t\ty: node.y + sin * t\n\t\t\t};\n\t\t}\n\t};\n\t\n\t// Calculate intersections, but use the direction from center to NEXT vertex in sequence\n\t// This ensures the line exits the node in the direction it needs to go\n\t\n\t// For start intersection: use direction from node center to first routing vertex\n\tlet startIntersection = calculateNodeIntersection(n1, firstRoutingVertex);\n\t\n\t// For end intersection: use direction from node center to last routing vertex \n\tlet endIntersection = calculateNodeIntersection(n2, lastRoutingVertex);\n\t\n\t// Start intersection: NO offset needed - intersection already gives perfect boundary point\n\t// End intersection: NO offset needed - the arrow marker now has refX=\"0\" so the tip is at the endpoint\n\t\n\tvertices[0] = startIntersection;\n\tvertices[vertices.length - 1] = endIntersection;\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\tfallback: Point,\n): EdgeLabelPlacement {\n\tlet point = {x: fallback.x, y: fallback.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/**\n * Create edge segments and generate SVG path\n */\nexport function createEdgeSegments(vertices: Point[], bbox: BBox, n1: Point, n2: Point): { segments: Segment[], path: string } {\n\tconst segments: Segment[] = []\n\tfor (let i = 1; i < vertices.length; i++) {\n\t\tsegments.push({p: vertices[i - 1], q: vertices[i]})\n\t}\n\t\n\t// Debug the final segment that will have the arrow\n\tif (segments.length > 0) {\n\t\tconst lastSegment = segments[segments.length - 1];\n\t}\n\t// splice edge over label box\n\tintersectPolylineBox(segments, bbox)\n\n\t// Generate path based on routing style - SIMPLIFIED\n\tlet path: string\n\t\n\t// Always draw a polyline through the segments.\n\t// If segments.length is 1 (meaning direct connection or only label vertex), it will be a straight line.\n\t// If autoLayout provided bend points, those will be in `segments`.\n\tif (segments.length > 0) {\n\t\tpath = `M${segments[0].p.x},${segments[0].p.y}`\n\t\tfor (let i = 0; i < segments.length; i++) {\n\t\t\tconst s = segments[i]\n\t\t\t// For polylines, we just draw line segments to each vertex point.\n\t\t\t// The ELK 'POLYLINE' routing should give us the necessary bend points.\n\t\t\tpath += ` L${s.q.x},${s.q.y}`\n\t\t}\n\t} else {\n\t\t// Fallback for edges with no segments (should ideally not happen if n1 and n2 are defined)\n\t\t// Draw a straight line between n1 and n2 directly if no vertices/segments exist.\n\t\t// Note: Intersection points are calculated before this, so n1/n2 are already adjusted.\n\t\tpath = `M${n1.x},${n1.y} L${n2.x},${n2.y}`\n\t}\n\t\n\treturn { segments, path };\n}","import {GraphData, Node, Group, LayoutDirection} from \"./graph\";\n\nexport interface LayoutOptions {\n\tdirection?: LayoutDirection;\n\tnodeSpacing?: number;\n\tlayerSpacing?: number;\n\tcompactLayout?: boolean;\n}\n\n// Simplified spacing configuration\ninterface SpacingConfig {\n\tnodeSpacing: number;\n\tlayerSpacing: number;\n\tcomponentSpacing: number;\n\tpadding: number;\n\tgroupMultiplier: number;\n}\n\n// Spacing configuration - balanced for readability\nconst DEFAULT_SPACING: SpacingConfig = {\n\tnodeSpacing: 80, // Comfortable vertical spacing between nodes in same layer\n\tlayerSpacing: 60, // Layer spacing (between nodes in flow direction)\n\tcomponentSpacing: 80, // Separation between disconnected components\n\tpadding: 40, // Padding around the entire layout (for group labels)\n\tgroupMultiplier: 0.65, // Moderate compaction within groups\n};\n\n// Helper function to get effective spacing for a context\nfunction getEffectiveSpacing(\n\tuserOptions: LayoutOptions = {},\n\tisGroup: boolean = false\n): SpacingConfig {\n\t// Apply user overrides to base config\n\tconst effectiveConfig: SpacingConfig = {\n\t\tnodeSpacing: userOptions.nodeSpacing ?? DEFAULT_SPACING.nodeSpacing,\n\t\tlayerSpacing: userOptions.layerSpacing ?? DEFAULT_SPACING.layerSpacing,\n\t\tcomponentSpacing: DEFAULT_SPACING.componentSpacing,\n\t\tpadding: DEFAULT_SPACING.padding,\n\t\tgroupMultiplier: DEFAULT_SPACING.groupMultiplier,\n\t};\n\t\n\t// Apply group multiplier if in group context\n\tif (isGroup) {\n\t\teffectiveConfig.nodeSpacing = Math.max(\n\t\t\teffectiveConfig.nodeSpacing * effectiveConfig.groupMultiplier,\n\t\t\t30 // Minimum 30px spacing within groups\n\t\t);\n\t\teffectiveConfig.layerSpacing = Math.max(\n\t\t\teffectiveConfig.layerSpacing * effectiveConfig.groupMultiplier,\n\t\t\t35 // Minimum 35px layer spacing within groups\n\t\t);\n\t\teffectiveConfig.componentSpacing = Math.max(\n\t\t\teffectiveConfig.componentSpacing * effectiveConfig.groupMultiplier,\n\t\t\t25 // Minimum 25px component spacing within groups\n\t\t);\n\t\teffectiveConfig.padding = Math.max(\n\t\t\teffectiveConfig.padding * effectiveConfig.groupMultiplier,\n\t\t\t15 // Minimum 15px padding within groups\n\t\t);\n\t}\n\t\n\treturn effectiveConfig;\n}\n\n// Simplified ELK layout options builder\nfunction getELKOptions(\n\tspacing: SpacingConfig,\n\tuserOptions: LayoutOptions\n): Record {\n\tconst {\n\t\tdirection = 'DOWN',\n\t\tcompactLayout = false\n\t} = userOptions;\n\t\n\tconst baseOptions: Record = {\n\t\t'elk.algorithm': 'layered', // Back to layered for better orthogonal routing\n\t\t'elk.direction': direction,\n\t\t'elk.spacing.nodeNode': spacing.nodeSpacing.toString(),\n\t\t'elk.spacing.componentComponent': spacing.componentSpacing.toString(),\n\t\t'elk.padding': `[top=${spacing.padding},left=${spacing.padding},bottom=${spacing.padding},right=${spacing.padding}]`,\n\t\t\n\t\t// Layer spacing for compact layout\n\t\t'elk.layered.spacing.nodeNodeBetweenLayers': spacing.layerSpacing.toString(),\n\t\t'elk.layered.spacing.edgeNodeBetweenLayers': '10', // Minimal spacing around nodes\n\t\t'elk.layered.spacing.edgeEdgeBetweenLayers': '10', // Minimal space between edges\n\t\t\n\t\t// ORTHOGONAL edge routing for cleaner layout\n\t\t'elk.edgeRouting': 'POLYLINE',\n\t\t'elk.layered.unnecessaryBendpoints': 'false',\n\t\t\n\t\t// Minimal edge routing - straight lines where possible\n\t\t'elk.layered.edgeRouting.orthogonal.mode': 'DIRECTION_BASED',\n\t\t'elk.layered.edgeRouting.orthogonal.spacing': '5', // Minimal edge spacing\n\t\t'elk.layered.edgeRouting.orthogonal.nodeOverlapRatio': '0.1',\n\t\t\n\t\t// Compaction options\n\t\t'elk.layered.compaction.connectedComponents': 'true',\n\t\t'elk.layered.compaction.postCompaction.strategy': 'LEFT_RIGHT',\n\t\t\n\t\t// Separate components to reduce complexity\n\t\t'elk.separateConnectedComponents': 'true',\n\t\t\n\t\t// Node placement strategy for consistent vertical spacing\n\t\t'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',\n\t\t'elk.layered.nodePlacement.favorStraightEdges': 'true',\n\t\t\n\t\t// Crossing minimization - respect model order for consistent layout\n\t\t'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',\n\t\t'elk.layered.crossingMinimization.semiInteractive': 'true',\n\t\t\n\t\t// Flatten hierarchy for better edge routing\n\t\t'elk.hierarchyHandling': 'SEPARATE_CHILDREN',\n\t\t'elk.layered.considerModelOrder.strategy': 'NONE', // Ignore model ordering constraints\n\t\t\n\t\t// Edge label handling - minimal space, labels positioned above edges\n\t\t'elk.edgeLabels.placement': 'CENTER',\n\t\t'elk.edgeLabels.inline': 'true',\n\t\t'elk.spacing.edgeLabel': '5', // Minimal spacing for labels\n\t\t'elk.edgeLabels.avoidOverlap': 'false', // Disable collision avoidance\n\t\t'elk.edgeLabels.considerModelOrder': 'false',\n\t\t'elk.layered.edgeLabels.sideSelection': 'ALWAYS_UP', // Labels above edges\n\t};\n\t\n\t// Additional compact layout options if requested\n\tif (compactLayout) {\n\t\tbaseOptions['elk.spacing.nodeNode'] = Math.max(spacing.nodeSpacing * 0.7, 30).toString();\n\t\tbaseOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = Math.max(spacing.layerSpacing * 0.7, 30).toString();\n\t}\n\t\n\treturn baseOptions;\n}\n\nexport async function autoLayout(graph: GraphData, options: LayoutOptions = {}): Promise<{\n\tnodes: Array<{id: string, x: number, y: number}>,\n\tedges: Array<{id: string, vertices: Array<{x: number, y: number}>, label?: {x: number, y: number}}>\n}> {\n\t// Dynamically import ELK only when auto-layout is used\n\tconst ELK = await import('elkjs/lib/elk.bundled.js').then(module => module.default);\n\tconst elk = new ELK();\n\t// Get systematic spacing configuration\n\tconst rootSpacing = getEffectiveSpacing(options, false);\n\t\n\t// Build ELK graph structure\n\tconst elkGraph = {\n\t\tid: \"root\",\n\t\tlayoutOptions: getELKOptions(rootSpacing, options),\n\t\tchildren: [] as any[],\n\t\tedges: [] as any[]\n\t};\n\n\t// Build actual ELK nodes first. Groups below take ownership of their direct\n\t// members so ELK reserves non-overlapping space for every boundary.\n\tconst nodeMap = new Map();\n\tconst elkNodes = new Map();\n\tgraph.nodesMap.forEach(node => {\n\t\tif (!node.id) return; // Skip nodes without IDs\n\t\t\n\t\tnodeMap.set(node.id, node);\n\t\t\n\t\t// Ensure minimum dimensions and validate node size data\n\t\t// Use larger height to account for shapes like Robot that extend above the center\n\t\tconst nodeWidth = Math.max(node.width || 200, 150); // Min width 150px\n\t\tconst nodeHeight = Math.max(node.height || 100, 250); // Min height 250px to account for robot shape\n\t\t\n\t\t// Add padding to node dimensions for ELK to account for arrow size\n\t\t// This makes ELK route edges to a slightly larger boundary so arrow tips stay outside\n\t\tconst arrowPadding = 25; // Padding for arrow clearance\n\t\t\n\t\telkNodes.set(node.id, {\n\t\t\tid: node.id,\n\t\t\t// Provide current position as hint to ELK\n\t\t\tx: node.x,\n\t\t\ty: node.y,\n\t\t\twidth: nodeWidth + (arrowPadding * 2),\n\t\t\theight: nodeHeight + (arrowPadding * 2),\n\t\t\tlayoutOptions: {\n\t\t\t\t// Allow ELK to move nodes but consider current positions\n\t\t\t\t'elk.position': '',\n\t\t\t\t// Force ELK to use our exact dimensions\n\t\t\t\t'elk.nodeSize.constraints': '[FIXED_SIZE]'\n\t\t\t}\n\t\t});\n\t});\n\n\tconst nodeParentGroup = new Map();\n\tconst groupParent = new Map();\n\tconst childGroupIDs = new Set();\n\n\tgraph.groupsMap.forEach(group => {\n\t\tgroup.nodes.forEach(member => {\n\t\t\tif (isGroup(member)) {\n\t\t\t\tif (!groupParent.has(member.id)) {\n\t\t\t\t\tgroupParent.set(member.id, group.id);\n\t\t\t\t}\n\t\t\t\tchildGroupIDs.add(member.id);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!nodeParentGroup.has(member.id)) {\n\t\t\t\tnodeParentGroup.set(member.id, group.id);\n\t\t\t}\n\t\t});\n\t});\n\n\tconst elkGroups = new Map();\n\tconst buildELKGroup = (group: Group): any => {\n\t\tconst existing = elkGroups.get(group.id);\n\t\tif (existing) return existing;\n\n\t\tconst children = group.nodes.flatMap(member => {\n\t\t\tif (isGroup(member)) {\n\t\t\t\treturn groupParent.get(member.id) === group.id ? [buildELKGroup(member)] : [];\n\t\t\t}\n\t\t\tconst node = elkNodes.get(member.id);\n\t\t\treturn node && nodeParentGroup.get(member.id) === group.id ? [node] : [];\n\t\t});\n\t\tconst elkGroup = {\n\t\t\tid: group.id,\n\t\t\tchildren,\n\t\t\tedges: [] as any[],\n\t\t\tlayoutOptions: getELKOptions(getEffectiveSpacing(options, true), options),\n\t\t};\n\t\telkGroups.set(group.id, elkGroup);\n\t\treturn elkGroup;\n\t};\n\n\tgraph.groupsMap.forEach(group => {\n\t\tif (!childGroupIDs.has(group.id)) {\n\t\t\tconst elkGroup = buildELKGroup(group);\n\t\t\tif (elkGroup.children.length > 0) {\n\t\t\t\telkGraph.children.push(elkGroup);\n\t\t\t}\n\t\t}\n\t});\n\telkNodes.forEach((node, id) => {\n\t\tif (!nodeParentGroup.has(id)) {\n\t\t\telkGraph.children.push(node);\n\t\t}\n\t});\n\n\tconst groupAncestors = (groupID?: string) => {\n\t\tconst ancestors: string[] = [];\n\t\tlet current = groupID;\n\t\twhile (current) {\n\t\t\tancestors.push(current);\n\t\t\tcurrent = groupParent.get(current);\n\t\t}\n\t\treturn ancestors;\n\t};\n\tconst lowestCommonGroup = (sourceID: string, destinationID: string) => {\n\t\tconst sourceAncestors = groupAncestors(nodeParentGroup.get(sourceID));\n\t\tconst destinationAncestors = new Set(groupAncestors(nodeParentGroup.get(destinationID)));\n\t\treturn sourceAncestors.find(groupID => destinationAncestors.has(groupID));\n\t};\n\n\tgraph.edges.forEach(edge => {\n\t\t// Skip edges without proper IDs\n\t\tif (!edge.id || !edge.from?.id || !edge.to?.id) return;\n\t\t\n\t\t// Verify source and target nodes exist in our node map\n\t\tif (!nodeMap.has(edge.from.id) || !nodeMap.has(edge.to.id)) {\n\t\t\tconsole.warn(`Skipping edge ${edge.id}: source ${edge.from.id} or target ${edge.to.id} not found in nodes`);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// Calculate more accurate label dimensions for ELK\n\t\tconst labelWidth = edge.label && edge.label.trim() ? \n\t\t\tMath.min(edge.label.length * 7, 200) : 0; // More realistic width estimate\n\t\t\n\t\t\n\t\tconst elkEdge = {\n\t\t\tid: edge.id,\n\t\t\tsources: [edge.from.id],\n\t\t\ttargets: [edge.to.id],\n\t\t\t// Include label information with much smaller dimensions\n\t\t\tlabels: edge.label && edge.label.trim() ? [{\n\t\t\t\tid: `${edge.id}-label`,\n\t\t\t\ttext: edge.label,\n\t\t\t\t// Much more conservative label size estimates\n\t\t\t\twidth: labelWidth,\n\t\t\t\theight: 20, // More realistic label height\n\t\t\t\tlayoutOptions: {\n\t\t\t\t\t'elk.edgeLabels.placement': 'CENTER',\n\t\t\t\t\t'elk.edgeLabels.inline': 'true'\n\t\t\t\t\t// Remove the FIXED_SIZE constraint that might be forcing detours\n\t\t\t\t}\n\t\t\t}] : []\n\t\t};\n\n\t\tconst groupID = lowestCommonGroup(edge.from.id, edge.to.id);\n\t\tconst edgeContainer = groupID ? elkGroups.get(groupID) : elkGraph;\n\t\tedgeContainer.edges.push(elkEdge);\n\t});\n\n\t// Enhanced validation - ensure ELK gets complete data\n\tif (!elkGraph.id || !elkGraph.children) {\n\t\tthrow new Error('Invalid ELK graph structure');\n\t}\n\t\n\n\ttry {\n\t\tconst layoutedGraph = await elk.layout(elkGraph);\n\t\t\n\t\t// Extract results\n\t\tconst nodes: Array<{id: string, x: number, y: number}> = [];\n\t\tconst edges: Array<{id: string, vertices: Array<{x: number, y: number}>, label?: {x: number, y: number}}> = [];\n\n\n\t\t// Extract nodes from layout result\n\t\tconst extractNodes = (container: any, offsetX = 0, offsetY = 0) => {\n\t\t\tcontainer.children?.forEach((child: any) => {\n\t\t\t\tif (child.children) {\n\t\t\t\t\t// This is a group, recurse\n\t\t\t\t\textractNodes(child, offsetX + (child.x || 0), offsetY + (child.y || 0));\n\t\t\t\t} else {\n\t\t\t\t\t// This is a node\n\t\t\t\t\t// Adjust for the padding we added - ELK positioned based on padded size\n\t\t\t\t\t// So we need to shift by the padding amount to get the real center\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\tx: offsetX + (child.x || 0) + (child.width || 0) / 2,\n\t\t\t\t\t\ty: offsetY + (child.y || 0) + (child.height || 0) / 2\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\t// Process edges from ELK layout result to get routing information\n\t\tconst processEdgesFromELK = (container: any, offsetX = 0, offsetY = 0) => {\n\t\t\tcontainer.edges?.forEach((elkEdge: any) => {\n\t\t\t\tconst vertices: Array<{x: number, y: number}> = [];\n\t\t\t\tlet label: {x: number, y: number} | undefined;\n\n\t\t\t\t// Process edge sections to get bend points\n\t\t\t\tif (elkEdge.sections && elkEdge.sections.length > 0) {\n\t\t\t\t\telkEdge.sections.forEach((section: any) => {\n\t\t\t\t\t\t// Add start point if it exists\n\t\t\t\t\t\tif (section.startPoint) {\n\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\tx: offsetX + section.startPoint.x, \n\t\t\t\t\t\t\t\ty: offsetY + section.startPoint.y\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Add bend points (this is where ELK puts the routing vertices!)\n\t\t\t\t\t\tif (section.bendPoints && section.bendPoints.length > 0) {\n\t\t\t\t\t\t\tsection.bendPoints.forEach((bp: any) => {\n\t\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\t\tx: offsetX + bp.x, \n\t\t\t\t\t\t\t\t\ty: offsetY + bp.y\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Add end point if it exists\n\t\t\t\t\t\tif (section.endPoint) {\n\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\tx: offsetX + section.endPoint.x, \n\t\t\t\t\t\t\t\ty: offsetY + section.endPoint.y\n\t\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\n\t\t\t\t// Extract ELK's calculated label positions (respect collision avoidance!)\n\t\t\t\tconst originalEdge = graph.edges.find(e => e.id === elkEdge.id);\n\t\t\t\tif (originalEdge?.label && originalEdge.label.trim()) {\n\t\t\t\t\tif (elkEdge.labels && elkEdge.labels.length > 0) {\n\t\t\t\t\t\tconst elkLabel = elkEdge.labels[0]; // Get first label\n\t\t\t\t\t\tif (elkLabel.x !== undefined && elkLabel.y !== undefined) {\n\t\t\t\t\t\t\tlabel = {\n\t\t\t\t\t\t\t\tx: offsetX + elkLabel.x + (elkLabel.width || 0) / 2, // Center of label\n\t\t\t\t\t\t\t\ty: offsetY + elkLabel.y + (elkLabel.height || 0) / 2\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (vertices.length >= 2) {\n\t\t\t\t\t\t// Fallback: use middle of edge if ELK didn't provide label position\n\t\t\t\t\t\tconst midIndex = Math.floor(vertices.length / 2);\n\t\t\t\t\t\tif (vertices.length % 2 === 0) {\n\t\t\t\t\t\t\tconst v1 = vertices[midIndex - 1];\n\t\t\t\t\t\t\tconst v2 = vertices[midIndex];\n\t\t\t\t\t\t\tlabel = { x: (v1.x + v2.x) / 2, y: (v1.y + v2.y) / 2 };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlabel = vertices[midIndex];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\n\t\t\t\tedges.push({\n\t\t\t\t\tid: elkEdge.id,\n\t\t\t\t\tvertices,\n\t\t\t\t\tlabel\n\t\t\t\t});\n\t\t\t});\n\t\t\t\n\t\t\t// Also process edges in child containers (groups)\n\t\t\tcontainer.children?.forEach((child: any) => {\n\t\t\t\tif (child.edges && child.edges.length > 0) {\n\t\t\t\t\tprocessEdgesFromELK(child, offsetX + (child.x || 0), offsetY + (child.y || 0));\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\n\t\textractNodes(layoutedGraph);\n\t\tprocessEdgesFromELK(layoutedGraph);\n\n\t\t// Normalize coordinates to start near (0,0) to prevent huge canvas sizes\n\t\t// while preserving relative positioning between elements\n\t\tif (nodes.length > 0) {\n\t\t\t// Find the minimum coordinates across all elements\n\t\t\tconst minX = Math.min(...nodes.map(n => n.x));\n\t\t\tconst minY = Math.min(...nodes.map(n => n.y));\n\t\t\t\n\t\t\t// Add some padding so content doesn't start at exact (0,0)\n\t\t\tconst padding = 50;\n\t\t\tconst offsetX = -minX + padding;\n\t\t\tconst offsetY = -minY + padding;\n\t\t\t\n\t\t\t// Normalize all node positions\n\t\t\tnodes.forEach(node => {\n\t\t\t\tnode.x += offsetX;\n\t\t\t\tnode.y += offsetY;\n\t\t\t});\n\t\t\t\n\t\t\t// Normalize all edge positions\n\t\t\tedges.forEach(edge => {\n\t\t\t\tedge.vertices.forEach(vertex => {\n\t\t\t\t\tvertex.x += offsetX;\n\t\t\t\t\tvertex.y += offsetY;\n\t\t\t\t});\n\t\t\t\tif (edge.label) {\n\t\t\t\t\tedge.label.x += offsetX;\n\t\t\t\t\tedge.label.y += offsetY;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn { nodes, edges };\n\n\t} catch (error) {\n\t\tconsole.warn('ELK layout failed, using fallback layout. Error:', error);\n\t\treturn createFallbackLayout(graph);\n\t}\n}\n\n// Simplified fallback layout\nfunction createFallbackLayout(graph: GraphData): {\n\tnodes: Array<{id: string, x: number, y: number}>,\n\tedges: Array<{id: string, vertices: Array<{x: number, y: number}>}>\n} {\n\tconst nodes: Array<{id: string, x: number, y: number}> = [];\n\tconst edges: Array<{id: string, vertices: Array<{x: number, y: number}>}> = [];\n\n\t// Simple grid layout for nodes\n\tlet x = 0, y = 0;\n\tconst spacing = 300;\n\tconst maxCols = Math.ceil(Math.sqrt(graph.nodesMap.size));\n\n\tlet col = 0;\n\tgraph.nodesMap.forEach(node => {\n\t\tnodes.push({\n\t\t\tid: node.id,\n\t\t\tx: x,\n\t\t\ty: y\n\t\t});\n\n\t\tcol++;\n\t\tif (col >= maxCols) {\n\t\t\tcol = 0;\n\t\t\tx = 0;\n\t\t\ty += spacing;\n\t\t} else {\n\t\t\tx += spacing;\n\t\t}\n\t});\n\n\t// Simple straight line edges\n\tgraph.edges.forEach(edge => {\n\t\tedges.push({\n\t\t\tid: edge.id,\n\t\t\tvertices: []\n\t\t});\n\t});\n\n\treturn { nodes, edges };\n}\n\nfunction isGroup(member: Node | Group): member is Group {\n\treturn \"nodes\" in member;\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","interface LiveReloadOptions {\n\tminDelay: number;\n\tmaxDelay: number;\n\thandshakeTimeout: number;\n}\n\ninterface LiveReloadMessage {\n\tcommand: string;\n\tprotocols?: string[];\n\tver?: string;\n\tpath?: string;\n}\n\nclass Timer {\n\tprivate readonly callback: () => void;\n\tprivate readonly handler: () => void;\n\tprivate running: boolean = false;\n\tprivate timeoutId: ReturnType | null = null;\n\n\tconstructor(callback: () => void) {\n\t\tthis.callback = callback;\n\t\tthis.handler = () => {\n\t\t\tthis.running = false;\n\t\t\tthis.timeoutId = null;\n\t\t\tthis.callback();\n\t\t};\n\t}\n\n\tstart(timeout: number): void {\n\t\tif (this.running) {\n\t\t\tthis.stop();\n\t\t}\n\t\tthis.timeoutId = setTimeout(this.handler, timeout);\n\t\tthis.running = true;\n\t}\n\n\tstop(): void {\n\t\tif (this.running && this.timeoutId !== null) {\n\t\t\tclearTimeout(this.timeoutId);\n\t\t\tthis.running = false;\n\t\t\tthis.timeoutId = null;\n\t\t}\n\t}\n\n\tisRunning(): boolean {\n\t\treturn this.running;\n\t}\n}\n\n/**\n * RefreshConnector implements the livereload protocol to listen for file changes via WebSocket\n */\nexport class RefreshConnector {\n\tprivate static readonly DEFAULT_OPTIONS: LiveReloadOptions = {\n\t\tminDelay: 1000,\n\t\tmaxDelay: 60000,\n\t\thandshakeTimeout: 5000\n\t};\n\n\tprivate static readonly LIVERELOAD_PROTOCOLS = [\n\t\t'http://livereload.com/protocols/official-9',\n\t\t'http://livereload.com/protocols/2.x-remote-control'\n\t];\n\n\tprivate readonly uri: string;\n\tprivate readonly options: LiveReloadOptions;\n\tprivate readonly fileChangeHandler: (path: string) => void;\n\t\n\tprivate socket: WebSocket | null = null;\n\tprivate nextDelay: number;\n\tprivate connectionDesired: boolean = false;\n\tprivate disconnectionReason: string = '';\n\t\n\tprivate readonly handshakeTimeout: Timer;\n\tprivate readonly reconnectTimer: Timer;\n\n\tconstructor(fileChangeHandler: (file: string) => void, options?: Partial) {\n\t\tthis.fileChangeHandler = fileChangeHandler;\n\t\tthis.options = { ...RefreshConnector.DEFAULT_OPTIONS, ...options };\n\t\tthis.uri = 'ws://localhost:35729/livereload';\n\t\tthis.nextDelay = this.options.minDelay;\n\n\t\tthis.handshakeTimeout = new Timer(() => this.handleHandshakeTimeout());\n\t\tthis.reconnectTimer = new Timer(() => this.attemptReconnection());\n\t}\n\n\tconnect(): void {\n\t\tthis.connectionDesired = true;\n\n\t\tif (this.isSocketConnected()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.prepareForConnection();\n\t\tthis.createWebSocket();\n\t}\n\n\tdisconnect(): void {\n\t\tthis.connectionDesired = false;\n\t\tthis.reconnectTimer.stop();\n\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.disconnectionReason = 'manual';\n\t\t\tthis.socket!.close();\n\t\t}\n\t}\n\n\tprivate isSocketConnected(): boolean {\n\t\treturn this.socket !== null && this.socket.readyState === WebSocket.OPEN;\n\t}\n\n\tprivate prepareForConnection(): void {\n\t\tthis.reconnectTimer.stop();\n\t\tthis.disconnectionReason = 'cannot-connect';\n\t}\n\n\tprivate createWebSocket(): void {\n\t\tthis.socket = new WebSocket(this.uri);\n\t\tthis.socket.onopen = () => this.handleOpen();\n\t\tthis.socket.onclose = () => this.handleClose();\n\t\tthis.socket.onmessage = (event) => this.handleMessage(event);\n\t\tthis.socket.onerror = () => this.handleError();\n\t}\n\n\tprivate handleOpen(): void {\n\t\tthis.disconnectionReason = 'handshake-failed';\n\t\tthis.startHandshake();\n\t}\n\n\tprivate handleClose(): void {\n\t\tconsole.log(`WebSocket disconnected: ${this.disconnectionReason}. Retry in ${this.nextDelay}ms`);\n\t\tthis.scheduleReconnection();\n\t}\n\n\tprivate handleMessage(event: MessageEvent): void {\n\t\ttry {\n\t\t\tconst message: LiveReloadMessage = JSON.parse(event.data);\n\t\t\tthis.processMessage(message);\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to parse WebSocket message:', error);\n\t\t}\n\t}\n\n\tprivate handleError(): void {\n\t\t// Error handling is done in onclose\n\t}\n\n\tprivate processMessage(message: LiveReloadMessage): void {\n\t\tswitch (message.command) {\n\t\t\tcase 'hello':\n\t\t\t\tthis.handleHelloMessage();\n\t\t\t\tbreak;\n\t\t\tcase 'reload':\n\t\t\t\tthis.handleReloadMessage(message);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tconsole.log('Unknown WebSocket message received:', message);\n\t\t}\n\t}\n\n\tprivate handleHelloMessage(): void {\n\t\tthis.handshakeTimeout.stop();\n\t\tthis.nextDelay = this.options.minDelay;\n\t}\n\n\tprivate handleReloadMessage(message: LiveReloadMessage): void {\n\t\t// The livereload server closes connection after sending reload\n\t\t// We must reconnect\n\t\tthis.reconnectTimer.stop();\n\t\tthis.connect();\n\n\t\tif (message.path) {\n\t\t\tthis.fileChangeHandler(message.path);\n\t\t}\n\t}\n\n\tprivate startHandshake(): void {\n\t\tconst helloMessage: LiveReloadMessage = {\n\t\t\tcommand: 'hello',\n\t\t\tprotocols: RefreshConnector.LIVERELOAD_PROTOCOLS,\n\t\t\tver: '3.3.1'\n\t\t};\n\t\t\n\t\tthis.sendCommand(helloMessage);\n\t\tthis.handshakeTimeout.start(this.options.handshakeTimeout);\n\t}\n\n\tprivate handleHandshakeTimeout(): void {\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.disconnectionReason = 'handshake-timeout';\n\t\t\tthis.socket!.close();\n\t\t}\n\t}\n\n\tprivate attemptReconnection(): void {\n\t\tif (this.connectionDesired) {\n\t\t\tthis.connect();\n\t\t}\n\t}\n\n\tprivate scheduleReconnection(): void {\n\t\tif (!this.connectionDesired) {\n\t\t\treturn; // Don't reconnect after manual disconnection\n\t\t}\n\n\t\tif (!this.reconnectTimer.isRunning()) {\n\t\t\tthis.reconnectTimer.start(this.nextDelay);\n\t\t\tthis.nextDelay = Math.min(this.options.maxDelay, this.nextDelay * 2);\n\t\t}\n\t}\n\n\tprivate sendCommand(command: LiveReloadMessage): void {\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.socket!.send(JSON.stringify(command));\n\t\t}\n\t}\n}","import { createRoot } from 'react-dom/client';\nimport React, { Suspense, lazy, useEffect, useState } from 'react';\nimport { refreshGraph } from \"./Root\";\nimport './style.css';\nimport '@fortawesome/fontawesome-free/css/all.css';\nimport { RefreshConnector } from \"./websocket\";\n\nconst Root = lazy(() => import('./Root').then(module => ({ default: module.Root })));\n\ninterface ModelData {\n\tmodel: any;\n\tlayout: any;\n}\n\ninterface AppState {\n\tdata: ModelData | null;\n\terror: string | null;\n\tloading: boolean;\n}\n\nconst App: React.FC = () => {\n\tconst [state, setState] = useState({\n\t\tdata: null,\n\t\terror: null,\n\t\tloading: true\n\t});\n\n\tconst loadData = async () => {\n\t\tsetState(prev => ({ ...prev, loading: true, error: null }));\n\t\t\n\t\ttry {\n\t\t\tconst [modelResponse, layoutResponse] = await Promise.all([\n\t\t\t\tfetch('data/model.json'),\n\t\t\t\tfetch('data/layout.json')\n\t\t\t]);\n\n\t\t\tif (!modelResponse.ok) {\n\t\t\t\tthrow new Error(`Failed to fetch model: ${modelResponse.statusText}`);\n\t\t\t}\n\t\t\t\n\t\t\tif (!layoutResponse.ok) {\n\t\t\t\tthrow new Error(`Failed to fetch layout: ${layoutResponse.statusText}`);\n\t\t\t}\n\n\t\t\tconst [model, layout] = await Promise.all([\n\t\t\t\tmodelResponse.json(),\n\t\t\t\tlayoutResponse.json()\n\t\t\t]);\n\n\t\t\tsetState({\n\t\t\t\tdata: { model, layout },\n\t\t\t\terror: null,\n\t\t\t\tloading: false\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to load data:', error);\n\t\t\tsetState({\n\t\t\t\tdata: null,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error occurred',\n\t\t\t\tloading: false\n\t\t\t});\n\t\t}\n\t};\n\n\tconst handleFileChange = (path: string) => {\n\t\tif (path.endsWith('.svg')) {\n\t\t\treturn; // Ignore SVG changes to avoid infinite loops\n\t\t}\n\t\t\n\t\tconsole.log('File changed:', path);\n\t\trefreshGraph();\n\t\tloadData();\n\t};\n\n\tuseEffect(() => {\n\t\t// Setup refresh connector\n\t\tconst refreshConnector = new RefreshConnector(handleFileChange);\n\t\trefreshConnector.connect();\n\n\t\t// Initial data load\n\t\tloadData();\n\n\t\t// Cleanup function\n\t\treturn () => {\n\t\t\t// RefreshConnector cleanup would go here if it had a disconnect method\n\t\t};\n\t}, []);\n\n\tif (state.loading) {\n\t\treturn ;\n\t}\n\n\tif (state.error) {\n\t\treturn ;\n\t}\n\n\tif (!state.data) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t}>\n\t\t\t\n\t\t\n\t);\n};\n\nconst LoadingScreen: React.FC = () => (\n\t
\n\t\t
Loading...
\n\t
\n);\n\nconst ErrorScreen: React.FC<{ error: string; onRetry: () => void }> = ({ error, onRetry }) => (\n\t
\n\t\t

Error loading application

\n\t\t

{error}

\n\t\t\n\t
\n);\n\n// Initialize the application\nconst container = document.getElementById('root');\nif (!container) {\n\tthrow new Error('Root container not found');\n}\n\nconst root = createRoot(container);\nroot.render();","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\";\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"],"names":["layoutDirections","TopBottom","BottomTop","LeftRight","RightLeft","listViews","model","viewsList","Object","keys","views","filter","section","endsWith","forEach","s","v","push","key","title","graphs","camelToWords","camel","split","replace","charAt","toUpperCase","slice","Toolbar","currentID","onViewChange","graph","onAutoLayout","onSave","onToggleHelp","saving","layouting","dragMode","setDragMode","jsx_runtime","jsxs","className","children","jsx","ViewSelector","ToolbarActions","length","onChange","e","target","value","disabled","hidden","map","view","style","marginLeft","fontWeight","display","alignItems","DragModeButton","UndoRedoButtons","AlignmentButtons","LayoutControls","GridControls","ZoomControls","SaveButton","HelpButton","onClick","modKey","platform","sy","Fragment","undo","redo","alignSelectionH","alignSelectionV","transform","distributeSelectionH","distributeSelectionV","gridVisible","setGridVisible","react","useState","isGridVisible","snapToGrid","setSnapToGrid","isSnapToGrid","useEffect","updateGridState","window","addEventListener","removeEventListener","toggleGrid","toggleSnapToGrid","snapAllToGrid","ZoomDisplay","zoom","setZoomState","updateZoom","currentZoom","Math","round","graph_view_graph","IX","interval","setInterval","clearInterval","a_","max","min","fitToView","hasChanges","setHasChanges","checkChanges","changed","Help","lazy","Promise","resolve","then","__webpack_require__","bind","module","default","Graph","setAutomationStatus","status","error","root","document","documentElement","dataset","mdlAutomationStatus","mdlAutomationError","reportInteractiveError","action","console","alert","Root","layout","chunk_62JRHF6Z","Kd","BV","qh","path","element","ModelPane","layouts","refreshGraph","URLSearchParams","location","search","get","searchParams","setSearchParams","ok","decodeURI","helpVisible","setHelpVisible","readyGraphID","setReadyGraphID","automationKey","useRef","automationRun","viewKey","elements","Map","relations","collectRels","el","Array","isArray","relationships","rel","set","id","people","softwareSystems","containers","el1","parent","components","el2","deploymentNodes","containerInstances","item","containerId","recAddNodes","infrastructureNodes","some","getView","jg","rankDirection","automaticLayout","layoutDirection","undefined","metadata","name","description","version","groupingIDs","ref","softwareSystemId","find","p","enterprise","styles","cssClassName","tag","toLowerCase","varPrefix","background","colorToVarMap","color","stroke","elementViewKey","containerViews","candidate","lookupContainerViewKey","sub","tags","technology","addNode","encodedViewKey","encodeURIComponent","href","exportHref","url","nodeLink","properties","nodesMap","has","sourceId","warn","destinationId","routing","addEdge","vertices","level","i","sort","a","b","groupMembers","addGroup","init","parseView","useGraph","handleAutoLayout","setLayouting","useCallback","async","options","direction","opts","autoLayout","useAutoLayout","handleSave","setSaving","response","fetch","method","body","exportSVG","detail","text","trim","Error","setSaved","useSave","ViewRedirect","handleToggleHelp","handleGraphReady","handleInteractiveAutoLayout","catch","handleInteractiveSave","params","fromEntries","entries","auto","save","current","toString","run","compact","layoutOpts","includes","compactLayout","message","String","errorMessage","toggleHelp","saveLayout","handleKeyDown","shortcut","shortcuts","Yp","preventDefault","aX","t9","Jk","DE","Vy","Hd","_t","resetView","Op","hZ","OE","Gg","moveSelected","getGridSize","J8","b3","iD","uK","l8","rB","mt","useKeyboardShortcuts","handleViewChange","handleSelect","m","log","obj","JSON","parse","stringify","Suspense","fallback","data","onSelect","onReady","svgTextWrap","width","attrs","svg","createElementNS","appendChild","measure","node","setAttribute","attr","createTextNode","height","getBBox","removeChild","clean","textMeasure","maxW","ret","words","lines","currentLine","word","join","brokenParts","maxWidth","parts","currentPart","testPart","breakLongWord","newLine","size","reduce","concat","create","type","k","classList","add","use","this","setAttributeNS","d","t","textContent","textArea","fontSize","bold","x","y","anchor","txt","line","span","dy","append","rect","r","rx","ry","icon","expand","expanded","g","setPosition","insideBox","centeredBox","uncenterBox","intersectRectFull","p1","p2","box","w","h","q","q1","q2","denominator","numerator1","numerator2","result","onLine1","onLine2","segmentIntersection","intersectRect","intersectEllipse","ellCenter","nodeCenter","point","si","c","radicand_sqrt","sqrt","pos","project","atob","len","dot","cabDistance","abs","cylinderRadiusY","shapeLabelOffsetY","shape","D3Element","_el","constructor","insert","insertBefore","querySelector","bbox","rounded","shapeSvg","intersect","_ellipse","mobiledeviceportrait","shapes","roundedbox","component","dx","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","SVG_STYLES","applyStyle","setProperty","calculateDistance","textBlock","gapAfter","field","wrapped","lineHeight","defaultEdgeStyle","thickness","opacity","dashed","defaultNodeStyle","GraphData","edges","edgeVertices","groupsMap","_undo","_gridVisible","_snapToGrid","_gridSize","_skipAutoFit","exportLayout","lo","importLayout","label","link","nodeStyle","minimumHeight","contentLayout","subtitle","nodeWidth","textWidth","HORIZONTAL_PADDING","blocks","textHeight","block","VERTICAL_PADDING","layoutNodeContent","requiredHeight","nodes","from","values","fromNode","toNode","edge","to","initVertex","edgeID","input","charCodeAt","imul","fnv1a36","stableVertexID","userDeletedVertices","nodesOrGroups","group","Boolean","setNodeSelected","selected","remove","updateEdgesSel","moveNode","disableSnap","skipUndo","snapped","redrawEdges","redrawGroups","moveEdgeVertex","redrawEdge","insertEdgeVertex","isLabel","deleteEdgeVertex","index","indexOf","delete","alignTopLeft","contentBounds","calculateContentBounds","offsetX","offsetY","vertex","resetPanTransform","getZoom","zoomGroup","graphData","bb","parentElement","clientWidth","clientHeight","updatePanningOptimized","clearViewState","shouldSkipAutoFit","updatePanning","buildEdge","buildGroup","originalSvg","exportSvg","cloneNode","querySelectorAll","getAttribute","removeAttribute","exportElastic","exportWidth","padding","exportHeight","exportZoomGroup","convertStylesToCustomProperties","script","createElement","firstChild","outerHTML","fill","minX","Infinity","minY","maxX","maxY","left","right","top","bottom","centerX","centerY","approxLabelSize","full","lst","rerender","coordinates","startsWith","normalizedPoint","assign","edgeId","an","ae","labelVertex","insertPos","labelPos","fullPath","minDistance","bestSegmentIndex","distance","distanceToSegment","findOptimalLabelPosition","projectedPos","segmentStart","segmentEnd","A","B","C","D","lenSq","param","projectPointOntoSegment","projectLabelOntoSegment","selectedNodes","selectedVertices","allElements","spacing","newX","newY","setEdgeSelected","viewportWidth","viewportHeight","zoomX","zoomY","optimalZoom","finalZoom","translateX","translateY","saveViewState","saveLayoutState","restoreLayoutState","state","updateGridDisplay","dispatchEvent","CustomEvent","snappedX","snappedY","existingGrid","existingGridRect","defs","pattern","clickListener","selectListener","dragging","buildGraph","onNodeSelect","innerHTML","__data","_buildGraph","elasticEl","addCursorInteraction","setZoom","zoomG","nodesG","edgesG","groupsG","gdata","content","shapeType","nodeBorder","setBorderStyle","border","tg","buildNodeContent","Number","buildNode","labelBounds","n1","n2","position","sameEdges","spreadPos","spreadX","spreadY","unshift","firstRoutingVertex","lastRoutingVertex","calculateNodeIntersection","targetPoint","nodeShape","halfHeight","angle","atan2","cos","sin","rectX","rectY","topCurveY","bottomCurveY","ellipseY","discriminant","sqrt_d","x1","x2","radius","halfWidth","startIntersection","endIntersection","calculateEdgeVertices","labelPlacement","segment","labelIndex","findIndex","movable","adjacentSegments","longest","targetLength","sum","traversed","segmentPosition","horizontalDistance","verticalDistance","orientation","calculateLabelPlacement","bg","placement","anchors","fraction","existing","occupied","expandBox","other","flatMap","side","edgeLabelCandidate","best","score","bounds","edgeText","edgeRect","buildEdgeLabel","segments","reverse","s2","intersectPolylineBox","createEdgeSegments","cx","overlap","total","first","second","p0","topExtension","bottomExtension","hexHeight","pad","groupRect","groupText","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","Element","closest","stopPropagation","md","convertEvent","changedTouches","onMouseMoveHandler","getSelection","setSelection","setTransform","drawingDx","drawingDy","setDragging","update","onMouseMove","ex","ey","onMouseUpHandler","navigation","end","boxSelection","shiftKey","onMouseUp","onMouseDownHandler","nodeFromEvent","effectiveMode","isSelected","translateMatch","match","parseFloat","getCurrentTransformLocal","startDrawingX","startDrawingY","pt","currentPt","currentDrawingX","currentDrawingY","createElastic","onMouseDown","event","handler","addDnd","existingCleanup","__cursorInteractionCleanup","getData","gd","beforeUnloadHandler","returnValue","setDotSelected","dotEl","mouseMoveHandler","altKey","removePrjDot","keyUpHandler","Zj","_s","clickHandler","wheelHandler","delta","sign","deltaY","newZoom","setZoomCentered","keyDownHandler","bl","Ur","hU","i1","mD","F","Gn","customInteractionCleanup","handles","handle","b1","b2","scaleMatch","oldZoom","container","newTranslateX","newTranslateY","nodeText","cursor","viewStateCache","graphId","restoreViewState","xx","yy","getEffectiveSpacing","userOptions","isGroup","effectiveConfig","nodeSpacing","layerSpacing","componentSpacing","groupMultiplier","getELKOptions","baseOptions","elk","elkGraph","layoutOptions","nodeMap","elkNodes","nodeHeight","arrowPadding","nodeParentGroup","groupParent","childGroupIDs","Set","member","elkGroups","buildELKGroup","elkGroup","groupAncestors","groupID","ancestors","labelWidth","elkEdge","sources","targets","labels","sourceID","destinationID","sourceAncestors","destinationAncestors","lowestCommonGroup","layoutedGraph","extractNodes","child","processEdgesFromELK","sections","startPoint","bendPoints","bp","endPoint","originalEdge","elkLabel","midIndex","floor","v1","v2","maxCols","ceil","col","createFallbackLayout","styleTagTransform","styleTagTransform_default","setAttributes","setAttributesWithoutAttributes_default","insertBySelector_default","domAPI","styleDomAPI_default","insertStyleElement","insertStyleElement_default","injectStylesIntoStyleTag_default","locals","Timer","callback","running","timeoutId","start","stop","isRunning","RefreshConnector","static","minDelay","maxDelay","handshakeTimeout","uri","fileChangeHandler","socket","nextDelay","connectionDesired","disconnectionReason","reconnectTimer","DEFAULT_OPTIONS","handleHandshakeTimeout","attemptReconnection","connect","isSocketConnected","prepareForConnection","createWebSocket","disconnect","close","readyState","WebSocket","OPEN","onopen","handleOpen","onclose","handleClose","onmessage","handleMessage","onerror","handleError","startHandshake","scheduleReconnection","processMessage","command","handleHelloMessage","handleReloadMessage","helloMessage","protocols","LIVERELOAD_PROTOCOLS","ver","sendCommand","send","src_Root","App","setState","loading","loadData","prev","modelResponse","layoutResponse","all","statusText","json","handleFileChange","S","LoadingScreen","ErrorScreen","onRetry","justifyContent","fontFamily","whiteSpace","flexDirection","backgroundColor","borderRadius","getElementById","client","createRoot","render","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__","react_jsx_runtime__WEBPACK_IMPORTED_MODULE_2__","colSpan","matches","SA","ke","checkKey","aShortcut","bShortcut","aModifiers","bModifiers","aCount","isMac","navigator","userAgentData","userAgent","KeyboardEvent","metaKey","test","ctrlKey","___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","names","mappings","sourcesContent","sourceRoot","__WEBPACK_DEFAULT_EXPORT__"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"main.js","mappings":"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","sources":["webpack://app/./src/parseModel.ts","webpack://app/./src/hooks.ts","webpack://app/./src/utils.ts","webpack://app/./src/components/Toolbar.tsx","webpack://app/./src/Root.tsx","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/constants.ts","webpack://app/./src/graph-view/node-content.ts","webpack://app/./src/graph-view/graph.ts","webpack://app/./src/graph-view/edge-utils.ts","webpack://app/./src/graph-view/layout.ts","webpack://app/./src/style.css?dd02","webpack://app/./src/websocket.ts","webpack://app/./src/index.tsx","webpack://app/./src/shortcuts.tsx","webpack://app/./src/utils/platform.ts","webpack://app/./src/style.css"],"sourcesContent":["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 { useState, useCallback, useEffect } from 'react';\nimport { GraphData } from './graph-view/graph';\nimport { parseView } from './parseModel';\nimport { LayoutOptions } from './graph-view/layout';\nimport { \n findShortcut, \n HELP, \n SAVE, \n TOGGLE_DRAG_MODE,\n ALIGN_HORIZONTAL,\n ALIGN_VERTICAL,\n DISTRIBUTE_HORIZONTAL,\n DISTRIBUTE_VERTICAL,\n AUTO_LAYOUT,\n RESET_POSITION,\n TOGGLE_GRID,\n TOGGLE_SNAP_TO_GRID,\n SNAP_ALL_TO_GRID,\n MOVE_LEFT,\n MOVE_RIGHT,\n MOVE_UP,\n MOVE_DOWN,\n MOVE_LEFT_FINE,\n MOVE_RIGHT_FINE,\n MOVE_UP_FINE,\n MOVE_DOWN_FINE\n} from './shortcuts';\n\n// Global state for graphs to preserve edits\nconst graphs: { [key: string]: GraphData } = {};\n\n// Custom hook for graph management\nexport const useGraph = (model: any, layouts: any, currentID: string): GraphData | null => {\n if (graphs[currentID]) {\n return graphs[currentID];\n }\n \n const graph = parseView(model, layouts, currentID);\n if (graph) {\n graphs[currentID] = graph;\n }\n \n return graph;\n};\n\n// Custom hook for auto layout functionality\nexport const useAutoLayout = (graph: GraphData) => {\n const [layouting, setLayouting] = useState(false);\n\n const handleAutoLayout = useCallback(async (opts?: LayoutOptions) => {\n setLayouting(true);\n try {\n const options: LayoutOptions = {\n direction: graph.layoutDirection || 'DOWN',\n ...(opts || {})\n };\n await graph.autoLayout(options);\n } finally {\n setLayouting(false);\n }\n }, [graph]);\n\n return { layouting, handleAutoLayout };\n};\n\n// Custom hook for save functionality\nexport const useSave = (graph: GraphData, currentID: string) => {\n const [saving, setSaving] = useState(false);\n\n const handleSave = useCallback(async () => {\n setSaving(true);\n \n try {\n const response = await fetch('data/save?id=' + encodeURIComponent(currentID), {\n method: 'post',\n body: graph.exportSVG()\n });\n \n if (response.status !== 202) {\n const detail = (await response.text()).trim();\n throw new Error(detail || `save failed with HTTP ${response.status}`);\n }\n graph.setSaved();\n } finally {\n setSaving(false);\n }\n }, [graph, currentID]);\n\n return { saving, handleSave };\n};\n\n// Custom hook for keyboard shortcuts\nexport const useKeyboardShortcuts = (\n toggleHelp: () => void,\n saveLayout: () => void,\n graph?: GraphData,\n dragMode?: 'pan' | 'select',\n setDragMode?: (mode: 'pan' | 'select') => void,\n onAutoLayout?: () => void\n) => {\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n const shortcut = findShortcut(e);\n \n // Prevent browser default for all recognized shortcuts\n if (shortcut) {\n e.preventDefault();\n }\n \n if (shortcut === HELP) {\n toggleHelp();\n } else if (shortcut === SAVE) {\n saveLayout();\n } else if (shortcut === TOGGLE_DRAG_MODE && setDragMode && dragMode) {\n setDragMode(dragMode === 'pan' ? 'select' : 'pan');\n } else if (graph) {\n // Graph-dependent shortcuts\n if (shortcut === ALIGN_HORIZONTAL) {\n graph.alignSelectionH();\n } else if (shortcut === ALIGN_VERTICAL) {\n graph.alignSelectionV();\n } else if (shortcut === DISTRIBUTE_HORIZONTAL) {\n graph.distributeSelectionH();\n } else if (shortcut === DISTRIBUTE_VERTICAL) {\n graph.distributeSelectionV();\n } else if (shortcut === AUTO_LAYOUT && onAutoLayout) {\n onAutoLayout();\n } else if (shortcut === RESET_POSITION) {\n graph.resetView();\n } else if (shortcut === TOGGLE_GRID) {\n graph.toggleGrid();\n } else if (shortcut === TOGGLE_SNAP_TO_GRID) {\n graph.toggleSnapToGrid();\n } else if (shortcut === SNAP_ALL_TO_GRID) {\n graph.snapAllToGrid();\n } else if (shortcut === MOVE_LEFT) {\n graph.moveSelected(-graph.getGridSize(), 0);\n } else if (shortcut === MOVE_LEFT_FINE) {\n graph.moveSelected(-1, 0, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_RIGHT) {\n graph.moveSelected(graph.getGridSize(), 0);\n } else if (shortcut === MOVE_RIGHT_FINE) {\n graph.moveSelected(1, 0, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_UP) {\n graph.moveSelected(0, -graph.getGridSize());\n } else if (shortcut === MOVE_UP_FINE) {\n graph.moveSelected(0, -1, true); // Disable snap for fine movement\n } else if (shortcut === MOVE_DOWN) {\n graph.moveSelected(0, graph.getGridSize());\n } else if (shortcut === MOVE_DOWN_FINE) {\n graph.moveSelected(0, 1, true); // Disable snap for fine movement\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleHelp, saveLayout, graph, dragMode, setDragMode, onAutoLayout]);\n};\n\n// Utility function to clear graph cache\nexport const clearGraphCache = (currentID?: string) => {\n if (currentID) {\n delete graphs[currentID];\n } else {\n Object.keys(graphs).forEach(key => delete graphs[key]);\n }\n};","// Helper functions for the application\n\nexport function removeEmptyProps(obj: any) {\n return JSON.parse(JSON.stringify(obj));\n}\n\nexport function camelToWords(camel: string) {\n const split = camel.replace(/([A-Z])/g, \" $1\");\n return split.charAt(0).toUpperCase() + split.slice(1);\n}\n\nexport function getCurrentViewID() {\n const params = new URLSearchParams(document.location.search);\n return params.get('id') || '';\n} ","import React, { FC, useState, useEffect } from 'react';\nimport { getZoomAuto, GraphData, setZoom, getZoom, setZoomCentered } from '../graph-view/graph';\nimport { listViews } from '../parseModel';\nimport { camelToWords } from '../utils';\nimport { getModifierKeyName } from '../utils/platform';\n\n// Types\ninterface ToolbarProps {\n model: any;\n currentID: string;\n onViewChange: (id: string) => void;\n graph: GraphData;\n onAutoLayout: () => void;\n onSave: () => void;\n onToggleHelp: () => void;\n saving: boolean;\n layouting: boolean;\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}\n\nexport const Toolbar: FC = ({\n model, currentID, onViewChange, graph, \n onAutoLayout, onSave, onToggleHelp, saving, layouting,\n dragMode, setDragMode\n}) => {\n const views = listViews(model);\n \n return (\n
\n \n \n
\n );\n};\n\nconst ViewSelector: FC<{\n views: any[];\n currentID: string;\n onViewChange: (id: string) => void;\n}> = ({ views, currentID, onViewChange }) => (\n
\n View:\n {views.length > 1 ? (\n \n ) : (\n \n {views[0] ? camelToWords(views[0].section) + ': ' + views[0].title : 'No views available'}\n \n )}\n
\n);\n\nconst ToolbarActions: FC<{\n graph: GraphData;\n onAutoLayout: () => void;\n onSave: () => void;\n onToggleHelp: () => void;\n saving: boolean;\n layouting: boolean;\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}> = ({\n graph, onAutoLayout, onSave, onToggleHelp, saving, layouting,\n dragMode, setDragMode\n}) => (\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n);\n\nconst DragModeButton: FC<{\n dragMode: 'pan' | 'select';\n setDragMode: (mode: 'pan' | 'select') => void;\n}> = ({ dragMode, setDragMode }) => (\n \n);\n\nconst UndoRedoButtons: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n );\n};\n\nconst AlignmentButtons: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n \n \n );\n};\n\nconst LayoutControls: FC<{\n onAutoLayout: () => void;\n layouting: boolean;\n}> = ({ onAutoLayout, layouting }) => {\n const modKey = getModifierKeyName();\n return (\n \n );\n};\n\nconst GridControls: FC<{ graph: GraphData }> = ({ graph }) => {\n const [gridVisible, setGridVisible] = useState(graph.isGridVisible());\n const [snapToGrid, setSnapToGrid] = useState(graph.isSnapToGrid());\n const modKey = getModifierKeyName();\n \n // Update state when graph changes or when grid state changes via shortcuts\n React.useEffect(() => {\n const updateGridState = () => {\n setGridVisible(graph.isGridVisible());\n setSnapToGrid(graph.isSnapToGrid());\n };\n \n // Initial update\n updateGridState();\n \n // Listen for grid state changes from keyboard shortcuts\n window.addEventListener('gridStateChanged', updateGridState);\n \n return () => {\n window.removeEventListener('gridStateChanged', updateGridState);\n };\n }, [graph]);\n \n const handleToggleGrid = () => {\n graph.toggleGrid();\n setGridVisible(graph.isGridVisible());\n };\n \n const handleToggleSnap = () => {\n graph.toggleSnapToGrid();\n setSnapToGrid(graph.isSnapToGrid());\n };\n \n const handleSnapAll = () => {\n graph.snapAllToGrid();\n };\n \n return (\n <>\n \n \n \n \n );\n};\n\nconst ZoomDisplay: FC = () => {\n const [zoom, setZoomState] = useState(100);\n\n useEffect(() => {\n const updateZoom = () => {\n const currentZoom = Math.round(getZoom() * 100);\n setZoomState(currentZoom);\n };\n\n // Update zoom initially\n updateZoom();\n\n // Update zoom every 100ms to catch changes from wheel/keyboard/etc\n const interval = setInterval(updateZoom, 100);\n\n return () => clearInterval(interval);\n }, []);\n\n return (\n \n );\n};\n\nconst ZoomControls: FC<{ graph: GraphData }> = ({ graph }) => {\n const modKey = getModifierKeyName();\n return (\n <>\n \n \n \n \n \n );\n};\n\nconst SaveButton: FC<{\n onSave: () => void;\n saving: boolean;\n graph: GraphData;\n}> = ({ onSave, saving, graph }) => {\n const [hasChanges, setHasChanges] = useState(false);\n const modKey = getModifierKeyName();\n \n // Check for changes periodically\n useEffect(() => {\n const checkChanges = () => {\n setHasChanges(graph.changed());\n };\n \n // Initial check\n checkChanges();\n \n // Check every 100ms for changes\n const interval = setInterval(checkChanges, 100);\n \n return () => clearInterval(interval);\n }, [graph]);\n \n return (\n \n );\n};\n\nconst HelpButton: FC<{\n onToggleHelp: () => void;\n}> = ({ onToggleHelp }) => {\n return (\n \n );\n};","import React, { FC, useState, useCallback, useEffect, useRef, Suspense, lazy } from \"react\";\nimport { GraphData, LayoutDirection } from \"./graph-view/graph\";\nimport { LayoutOptions } from \"./graph-view/layout\";\nimport { BrowserRouter as Router, Routes, Route, useSearchParams } from 'react-router-dom';\nimport { listViews } from \"./parseModel\";\nimport { useGraph, useAutoLayout, useSave, useKeyboardShortcuts, clearGraphCache } from \"./hooks\";\nimport { Toolbar } from \"./components/Toolbar\";\nimport { removeEmptyProps, getCurrentViewID } from \"./utils\";\n\nconst Help = lazy(() => import(\"./shortcuts\").then(module => ({ default: module.Help })));\nconst Graph = lazy(() => import(\"./graph-view/graph-react\").then(module => ({ default: module.Graph })));\n\n// Types\ninterface ModelData {\n model: any;\n layout: any;\n}\n\ntype AutomationStatus = 'running' | 'complete' | 'error';\n\nconst setAutomationStatus = (status: AutomationStatus | null, error?: string) => {\n const root = document.documentElement;\n if (!status) {\n delete root.dataset.mdlAutomationStatus;\n delete root.dataset.mdlAutomationError;\n return;\n }\n root.dataset.mdlAutomationStatus = status;\n if (error) {\n root.dataset.mdlAutomationError = error;\n } else {\n delete root.dataset.mdlAutomationError;\n }\n};\n\nconst errorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nconst reportInteractiveError = (action: string, error: unknown) => {\n console.error(`${action} failed:`, error);\n alert(`${action} failed. See console for details.`);\n};\n\nexport const Root: FC = ({ model, layout }) => (\n \n \n } />\n \n \n);\n\nexport const refreshGraph = () => {\n const currentID = getCurrentViewID();\n clearGraphCache(currentID);\n};\n\nconst ModelPane: FC<{ model: any; layouts: any }> = ({ model, layouts }) => {\n const [searchParams, setSearchParams] = useSearchParams();\n const currentID = decodeURI(searchParams.get('id') || '');\n \n // UI State\n const [helpVisible, setHelpVisible] = useState(false);\n const [dragMode, setDragMode] = useState<'pan' | 'select'>('pan');\n const [readyGraphID, setReadyGraphID] = useState(null);\n const automationKey = useRef(null);\n const automationRun = useRef(0);\n \n // Get or create graph for current view\n const graph = useGraph(model, layouts, currentID);\n \n // Custom hooks for functionality\n const { layouting, handleAutoLayout } = useAutoLayout(graph || ({} as GraphData));\n const { saving, handleSave } = useSave(graph || ({} as GraphData), currentID);\n \n if (!graph) {\n return ;\n }\n\n const handleToggleHelp = useCallback(() => {\n setHelpVisible(!helpVisible);\n }, [helpVisible]);\n\n const handleGraphReady = useCallback(() => {\n setReadyGraphID(currentID);\n }, [currentID]);\n\n const handleInteractiveAutoLayout = useCallback(() => {\n void handleAutoLayout().catch(error => reportInteractiveError('Layout', error));\n }, [handleAutoLayout]);\n\n const handleInteractiveSave = useCallback(() => {\n void handleSave().catch(error => reportInteractiveError('Save', error));\n }, [handleSave]);\n\n // Update document title when view changes\n useEffect(() => {\n if (graph && graph.name) {\n document.title = `${graph.name} - Model`;\n }\n }, [graph]);\n\n // Headless automation: support query params to auto-layout and save\n useEffect(() => {\n const params = Object.fromEntries(searchParams.entries());\n const auto = params['auto'] === '1' || params['auto'] === 'true';\n const save = params['save'] === '1' || params['save'] === 'true';\n if (!auto && !save) {\n automationKey.current = null;\n automationRun.current++;\n setAutomationStatus(null);\n return;\n }\n if (readyGraphID !== currentID) {\n return;\n }\n\n const key = `${currentID}:${searchParams.toString()}`;\n if (automationKey.current === key) {\n return;\n }\n automationKey.current = key;\n const run = ++automationRun.current;\n setAutomationStatus('running');\n\n const direction = (params['direction'] || '').toUpperCase();\n const compact = params['compact'] === '1' || params['compact'] === 'true';\n\n const validDirections: LayoutDirection[] = ['UP', 'DOWN', 'LEFT', 'RIGHT'];\n const layoutOpts: LayoutOptions = {};\n if (validDirections.includes(direction as LayoutDirection)) {\n layoutOpts.direction = direction as LayoutDirection;\n }\n if (compact) {\n layoutOpts.compactLayout = true;\n }\n\n (async () => {\n try {\n if (auto) {\n await handleAutoLayout(layoutOpts);\n }\n if (save) {\n await handleSave();\n }\n if (automationRun.current === run) {\n setAutomationStatus('complete');\n }\n } catch (error) {\n const message = errorMessage(error);\n console.error('Automation failed:', error);\n if (automationRun.current === run) {\n setAutomationStatus('error', message);\n }\n }\n })();\n }, [currentID, graph, handleAutoLayout, handleSave, readyGraphID, searchParams]);\n\n // Setup keyboard shortcuts\n useKeyboardShortcuts(\n handleToggleHelp,\n handleInteractiveSave,\n graph,\n dragMode,\n setDragMode,\n handleInteractiveAutoLayout,\n );\n\n const handleViewChange = useCallback((id: string) => {\n setSearchParams({ id: encodeURIComponent(id) });\n }, [setSearchParams]);\n\n const handleSelect = useCallback((id: string | null) => {\n if (id) {\n const element = graph.metadata.elements.find((m: any) => m.id === id);\n console.log(removeEmptyProps(element));\n }\n }, [graph]);\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\tLoading graph...}>\n\t\t\t\t\n\t\t\t\n\t\t\t{helpVisible && (\n\t\t\t\tLoading help...}>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t)}\n\t\t\n\t);\n};\n\nconst ViewRedirect: FC<{ model: any }> = ({ model }) => {\n const views = listViews(model);\n \n React.useEffect(() => {\n // Set default title when no view is selected\n document.title = 'Model - Architecture Diagrams as Code';\n \n if (views.length > 0) {\n document.location.href = '?id=' + views[0].key;\n }\n }, [views]);\n\n if (views.length > 0) {\n return <>Redirecting to {views[0].title};\n }\n return <>No views available;\n};\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","const textMeasure = () => {\n\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n\tdocument.body.appendChild(svg);\n\n\treturn {\n\t\tmeasure: (text: string, attrs: { [key: string]: string }) => {\n\t\t\tconst node = document.createElementNS('http://www.w3.org/2000/svg', 'text')\n\t\t\tnode.setAttribute('x', '0');\n\t\t\tnode.setAttribute('y', '0');\n\t\t\tfor (let attr in attrs) {\n\t\t\t\tnode.setAttribute(attr, attrs[attr]);\n\t\t\t}\n\t\t\tnode.appendChild(document.createTextNode(text));\n\n\t\t\tsvg.appendChild(node);\n\t\t\tconst {width, height} = node.getBBox();\n\t\t\tsvg.removeChild(node);\n\t\t\treturn {width, height};\n\t\t},\n\t\tclean: () => {\n\t\t\tdocument.body.removeChild(svg);\n\t\t}\n\t}\n}\n\n// Helper function to break long words that exceed width\nconst breakLongWord = (word: string, maxWidth: number, attrs: { [key: string]: string }, mt: any): string[] => {\n\tconst parts: string[] = [];\n\tlet currentPart = '';\n\t\n\tfor (let i = 0; i < word.length; i++) {\n\t\tconst testPart = currentPart + word[i];\n\t\tconst size = mt.measure(testPart, attrs);\n\t\t\n\t\tif (size.width > maxWidth && currentPart.length > 0) {\n\t\t\tparts.push(currentPart);\n\t\t\tcurrentPart = word[i];\n\t\t} else {\n\t\t\tcurrentPart = testPart;\n\t\t}\n\t}\n\t\n\tif (currentPart.length > 0) {\n\t\tparts.push(currentPart);\n\t}\n\t\n\treturn parts;\n}\n\n// split a text in lines wrapped at a certain width\nexport const svgTextWrap = (text: string, width: number, attrs: { [key: string]: string }) => {\n\tconst mt = textMeasure()\n\tlet maxW = 0;\n\t\n\tconst ret = text.trim().split('\\n').map(text => { //split paragraphs\n\t\t//do one paragraph\n\t\tconst words = text.trim().split(/\\s+/);\n\t\tlet lines: string[] = [];\n\t\tlet currentLine: string[] = [];\n\t\t\n\t\twords.forEach(word => {\n\t\t\t// First check if the single word exceeds the width\n\t\t\tconst wordSize = mt.measure(word, attrs);\n\t\t\tif (wordSize.width > width) {\n\t\t\t\t// If we have content in current line, finish it first\n\t\t\t\tif (currentLine.length > 0) {\n\t\t\t\t\tlines.push(currentLine.join(' '));\n\t\t\t\t\tcurrentLine = [];\n\t\t\t\t}\n\t\t\t\t// Break the long word into smaller parts\n\t\t\t\tconst brokenParts = breakLongWord(word, width, attrs, mt);\n\t\t\t\t// Add all but the last part as complete lines\n\t\t\t\tfor (let i = 0; i < brokenParts.length - 1; i++) {\n\t\t\t\t\tlines.push(brokenParts[i]);\n\t\t\t\t\tmaxW = Math.max(maxW, mt.measure(brokenParts[i], attrs).width);\n\t\t\t\t}\n\t\t\t\t// Start new line with the last part\n\t\t\t\tif (brokenParts.length > 0) {\n\t\t\t\t\tcurrentLine = [brokenParts[brokenParts.length - 1]];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Normal word processing\n\t\t\t\tconst newLine = [...currentLine, word];\n\t\t\t\tconst size = mt.measure(newLine.join(' '), attrs);\n\t\t\t\tif (size.width > width && currentLine.length > 0) {\n\t\t\t\t\tlines.push(currentLine.join(' '));\n\t\t\t\t\tcurrentLine = [word];\n\t\t\t\t} else {\n\t\t\t\t\tmaxW = Math.max(maxW, size.width)\n\t\t\t\t\tcurrentLine = newLine;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (currentLine.length > 0) {\n\t\t\tlines.push(currentLine.join(' '));\n\t\t}\n\t\treturn lines;\n\t}).reduce((a, v) => a.concat(v), []) //flatten\n\n\tmt.clean()\n\treturn {lines: ret, maxW};\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}","// 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 {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} 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\tcalculateDistance\n} from \"./constants\";\nimport {\n\tcalculateEdgeVertices,\n\tcalculateLabelPlacement,\n\tcreateEdgeSegments,\n\tEdgeLabelPlacement\n} from \"./edge-utils\";\nimport {\n\tbuildNodeContent,\n\tlayoutNodeContent,\n\tNodeContentLayout\n} from \"./node-content\";\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}\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\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\tlabelVertex?: EdgeVertex; // ELK-calculated label position (separate from routing vertices)\n\tlabelBounds?: 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 Layout {\n\t[k: string]: Point | (Point & { label: boolean })[] | 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\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\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\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\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\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\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(this, 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\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: { [key: string]: any }, rerender = false) {\n\t\t// First pass: collect all coordinate values to find bounds\n\t\tconst coordinates: Array<{x: number, y: number}> = [];\n\t\t\n\t\tObject.entries(layout).forEach(([k, v]) => {\n\t\t\tif (!k.startsWith('e-') && v.x !== undefined && v.y !== undefined) {\n\t\t\t\t// Node coordinates\n\t\t\t\tcoordinates.push({x: v.x, y: v.y});\n\t\t\t} else if (k.startsWith('e-') && Array.isArray(v)) {\n\t\t\t\t// Edge vertex coordinates\n\t\t\t\tv.forEach((vertex: any) => {\n\t\t\t\t\tif (vertex.x !== undefined && vertex.y !== undefined) {\n\t\t\t\t\t\tcoordinates.push({x: vertex.x, y: vertex.y});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t\t\n\t\t// Calculate normalization offset if we have coordinates\n\t\tlet offsetX = 0;\n\t\tlet offsetY = 0;\n\t\t\n\t\tif (coordinates.length > 0) {\n\t\t\tconst minX = Math.min(...coordinates.map(c => c.x));\n\t\t\tconst minY = Math.min(...coordinates.map(c => c.y));\n\t\t\t\n\t\t\t// Only normalize if coordinates are problematic (negative or very large)\n\t\t\tif (minX < -100 || minY < -100 || Math.max(...coordinates.map(c => c.x)) > 3000 || Math.max(...coordinates.map(c => c.y)) > 2000) {\n\t\t\t\tconst padding = 50;\n\t\t\t\toffsetX = -minX + padding;\n\t\t\t\toffsetY = -minY + padding;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Second pass: apply coordinates with normalization\n\t\tObject.entries(layout).forEach(([k, v]) => {\n\t\t\t// nodes\n\t\t\tconst n = this.nodesMap.get(k)\n\t\t\tif (n) {\n\t\t\t\tn.x = v.x + offsetX\n\t\t\t\tn.y = v.y + offsetY\n\t\t\t} else\n\t\t\t\t// edge vertices\n\t\t\tif (k.startsWith('e-') && !k.endsWith('-deleted')) {\n\t\t\t\tconst edge = this.edges.find(e => e.id == k.slice(2))\n\t\t\t\tif (!edge) return;\n\t\t\t\tedge.vertices && edge.vertices.forEach(v => this.edgeVertices.delete(v.id))\n\t\t\t\tedge.vertices = v.map((p: Point) => {\n\t\t\t\t\tconst normalizedPoint = { \n\t\t\t\t\t\tx: p.x + offsetX, \n\t\t\t\t\t\ty: p.y + offsetY \n\t\t\t\t\t} as Point;\n\t\t\t\t\t// Preserve any additional properties like 'label' and 'auto'\n\t\t\t\t\tObject.assign(normalizedPoint, p, { x: p.x + offsetX, y: p.y + offsetY });\n\t\t\t\t\tconst vertex = edge.initVertex(normalizedPoint);\n\t\t\t\t\t// Ensure auto flag is preserved after initVertex\n\t\t\t\t\tif ((p as any).auto) {\n\t\t\t\t\t\tvertex.auto = true;\n\t\t\t\t\t}\n\t\t\t\t\treturn vertex;\n\t\t\t\t})\n\t\t\t\treturn;\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(e => e.id == edgeId)\n\t\t\t\tif (edge && v === true) {\n\t\t\t\t\tedge.userDeletedVertices = true\n\t\t\t\t}\n\t\t\t\treturn;\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\ttry {\n\t\t\tconst auto = await autoLayout(this, options)\n\t\t\t\n\t\t\tthis._undo.beforeChange()\n\t\t\t\n\t\t\t// Apply node positions\n\t\t\tauto.nodes.forEach(an => {\n\t\t\t\tconst n = this.nodesMap.get(an.id)\n\t\t\t\tif (n) {\n\t\t\t\t\tthis.moveNode(n, an.x, an.y, false, true) // Skip undo for individual moves\n\t\t\t\t}\n\t\t\t})\n\t\t\t\n\t\t\t// Apply edge routing from ELK layout\n\t\t\tauto.edges.forEach(ae => {\n\t\t\t\tconst edge = this.edges.find(e => e.id == ae.id)\n\t\t\t\tif (edge) {\n\t\t\t\t\t// Clear existing vertices for this edge only\n\t\t\t\t\tif (edge.vertices) {\n\t\t\t\t\t\tedge.vertices.forEach(v => {\n\t\t\t\t\t\t\tif (v.id) {\n\t\t\t\t\t\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tedge.vertices = []\n\t\t\t\t\tedge.userDeletedVertices = false\n\t\t\t\t\t\n\t\t\t\t\t// Add routing vertices from ELK (these are proper bend points, not nodes)\n\t\t\t\t\tif (ae.vertices && ae.vertices.length > 0) {\n\t\t\t\t\t\tedge.vertices = ae.vertices.map(p => {\n\t\t\t\t\t\t\tconst vertex = edge.initVertex(p)\n\t\t\t\t\t\t\tvertex.auto = true // Mark as auto-generated\n\t\t\t\t\t\t\treturn vertex\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Handle edge label positioning - create proper interactive label vertices\n\t\t\t\t\tif (ae.label) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Remove any existing label vertices (ELK or user-created)\n\t\t\t\t\t\tif (edge.vertices) {\n\t\t\t\t\t\t\tedge.vertices.forEach(v => {\n\t\t\t\t\t\t\t\tif (v.label) {\n\t\t\t\t\t\t\t\t\tthis.edgeVertices.delete(v.id)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tedge.vertices = edge.vertices.filter(v => !v.label)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Create a proper label vertex that behaves like a user-created vertex\n\t\t\t\t\t\tconst labelVertex = edge.initVertex(ae.label)\n\t\t\t\t\t\tlabelVertex.label = true\n\t\t\t\t\t\tlabelVertex.auto = true // Mark as auto-generated so it can be cleaned up\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Insert label vertex at the optimal position in the routing path\n\t\t\t\t\t\t// Find the best position to insert it and project it onto that line segment\n\t\t\t\t\t\tedge.vertices = edge.vertices || []\n\t\t\t\t\t\tconst insertPos = findOptimalLabelPosition(edge.vertices, ae.label, edge.from, edge.to)\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Project the label position onto the line segment where it will be inserted\n\t\t\t\t\t\tconst projectedPos = projectLabelOntoSegment(edge.vertices, ae.label, insertPos, edge.from, edge.to)\n\t\t\t\t\t\tlabelVertex.x = projectedPos.x\n\t\t\t\t\t\tlabelVertex.y = projectedPos.y\n\t\t\t\t\t\t\n\t\t\t\t\t\tedge.vertices.splice(insertPos, 0, labelVertex)\n\t\t\t\t\t\tthis.edgeVertices.set(labelVertex.id, labelVertex)\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Redraw the edge with new routing\n\t\t\t\t\tthis.redrawEdge(edge)\n\t\t\t\t}\n\t\t\t})\n\t\t\t\n\t\t\t// Fit the layout to the viewport with optimal positioning\n\t\t\tthis.fitToView()\n\t\t\t\n\t\t\tthis._undo.change()\n\t\t\t\n\t\t} catch (error) {\n\t\t\tconsole.error('Auto layout failed:', error)\n\t\t\t// Could show user notification here\n\t\t}\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\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(data, 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(data: GraphData, edge: Edge) {\n\tconst n1 = edge.from, n2 = edge.to;\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\t// Calculate edge vertices using utility function\n\tconst vertices = calculateEdgeVertices(edge, data)\n\n\tconst labelPlacement = calculateLabelPlacement(vertices, position, n1)\n\n\tconst {bg, txt, bbox} = buildEdgeLabel(labelPlacement, edge, data)\n\tg.append(bg, txt)\n\n\t// Create edge segments and path using utility function\n\tconst {segments, path} = createEdgeSegments(vertices, bbox, n1, n2)\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\tedge.vertices = vertices.slice(1, -1).map(p => {\n\t\t// Preserve existing EdgeVertex objects to maintain IDs and selection state\n\t\tif ('id' in p && 'edge' in p) {\n\t\t\t// This is already an EdgeVertex, preserve it\n\t\t\tconst v = p as EdgeVertex;\n\t\t\tv.edge = edge; // Ensure edge reference is correct\n\t\t\treturn v;\n\t\t} else {\n\t\t\t// This is a new Point, convert to EdgeVertex\n\t\t\treturn edge.initVertex(p);\n\t\t}\n\t})\n\tedge.vertices.forEach((p, i) => {\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\nfunction buildEdgeLabel(placement: EdgeLabelPlacement, edge: Edge, data: GraphData) {\n\tconst labelGap = 12;\n\tconst collisionPadding = 8;\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\n\tconst anchors: Point[] = [{x: placement.x, y: placement.y}]\n\tif (placement.movable && placement.segment) {\n\t\tfor (const fraction of [0.5, 0.35, 0.65, 0.2, 0.8]) {\n\t\t\tconst anchor = {\n\t\t\t\tx: placement.segment.p.x +\n\t\t\t\t\t(placement.segment.q.x - placement.segment.p.x) * fraction,\n\t\t\t\ty: placement.segment.p.y +\n\t\t\t\t\t(placement.segment.q.y - placement.segment.p.y) * fraction,\n\t\t\t}\n\t\t\tif (!anchors.some(existing =>\n\t\t\t\tMath.abs(existing.x - anchor.x) < 0.1 &&\n\t\t\t\tMath.abs(existing.y - anchor.y) < 0.1\n\t\t\t)) {\n\t\t\t\tanchors.push(anchor)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst occupied = [\n\t\t...data.nodes().map(node => expandBox(uncenterBox(node), collisionPadding)),\n\t\t...data.edges\n\t\t\t.filter(other => other !== edge && other.labelBounds)\n\t\t\t.map(other => expandBox(other.labelBounds, collisionPadding)),\n\t]\n\tconst candidates = anchors.flatMap(anchor => {\n\t\tif (placement.orientation === 'vertical') {\n\t\t\treturn [1, -1].map(side => edgeLabelCandidate(\n\t\t\t\tanchor.x + side * (maxW / 2 + labelGap),\n\t\t\t\tanchor.y,\n\t\t\t\tmaxW,\n\t\t\t\tdy,\n\t\t\t\tanchor,\n\t\t\t\tplacement,\n\t\t\t\toccupied,\n\t\t\t))\n\t\t}\n\t\treturn [-1, 1].map(side => edgeLabelCandidate(\n\t\t\tanchor.x,\n\t\t\tanchor.y + side * (dy / 2 + labelGap),\n\t\t\tmaxW,\n\t\t\tdy,\n\t\t\tanchor,\n\t\t\tplacement,\n\t\t\toccupied,\n\t\t))\n\t})\n\tconst selected = candidates.reduce((best, candidate) =>\n\t\tcandidate.score < best.score ? candidate : best\n\t)\n\tconst {centerX, centerY, bounds} = selected\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 bbox = {...bounds}\n\tconst bg = create.rect(bbox.width, bbox.height, bbox.x, bbox.y)\n\tapplyStyle(bg, styles.edgeRect)\n\ttxt.setAttribute('data-field', 'label')\n\tedge.labelBounds = bounds\n\n\tbbox.x += bbox.width / 2\n\tbbox.y += bbox.height / 2\n\treturn {bg, txt, bbox}\n}\n\nfunction edgeLabelCandidate(\n\tcenterX: number,\n\tcenterY: number,\n\twidth: number,\n\theight: number,\n\tanchor: Point,\n\tplacement: EdgeLabelPlacement,\n\toccupied: BBox[],\n) {\n\tconst bounds = {\n\t\tx: centerX - width / 2,\n\t\ty: centerY - height / 2,\n\t\twidth,\n\t\theight,\n\t}\n\tconst overlap = occupied.reduce(\n\t\t(total, box) => total + boxOverlapArea(bounds, box),\n\t\t0,\n\t)\n\treturn {\n\t\tcenterX,\n\t\tcenterY,\n\t\tbounds,\n\t\tscore: overlap * 1000 + calculateDistance(anchor, placement),\n\t}\n}\n\nfunction expandBox(box: BBox, padding: number): BBox {\n\treturn {\n\t\tx: box.x - padding,\n\t\ty: box.y - padding,\n\t\twidth: box.width + padding * 2,\n\t\theight: box.height + padding * 2,\n\t}\n}\n\nfunction boxOverlapArea(first: BBox, second: BBox): number {\n\tconst width = Math.max(\n\t\t0,\n\t\tMath.min(first.x + first.width, second.x + second.width) -\n\t\t\tMath.max(first.x, second.x),\n\t)\n\tconst height = Math.max(\n\t\t0,\n\t\tMath.min(first.y + first.height, second.y + second.height) -\n\t\t\tMath.max(first.y, second.y),\n\t)\n\treturn width * height\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\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}\n\n/**\n * Find the optimal position to insert a label vertex into the routing path\n * to minimize disruption to the existing route\n */\nfunction findOptimalLabelPosition(vertices: Point[], labelPos: Point, fromNode: Point, toNode: Point): number {\n\t// If no existing vertices, insert at the beginning\n\tif (vertices.length === 0) {\n\t\treturn 0;\n\t}\n\t\n\t// Build the full routing path including start/end nodes\n\tconst fullPath = [fromNode, ...vertices, toNode];\n\t\n\t// Find the closest point on the path to the label position\n\tlet minDistance = Infinity;\n\tlet bestSegmentIndex = 0;\n\t\n\tfor (let i = 0; i < fullPath.length - 1; i++) {\n\t\tconst segmentStart = fullPath[i];\n\t\tconst segmentEnd = fullPath[i + 1];\n\t\t\n\t\t// Calculate distance from label position to this segment\n\t\tconst distance = distanceToSegment(labelPos, segmentStart, segmentEnd);\n\t\t\n\t\tif (distance < minDistance) {\n\t\t\tminDistance = distance;\n\t\t\tbestSegmentIndex = i;\n\t\t}\n\t}\n\t\n\t// Convert full path index to vertices array index\n\t// bestSegmentIndex 0 means between fromNode and vertices[0] -> insert at 0\n\t// bestSegmentIndex 1 means between vertices[0] and vertices[1] -> insert at 1\n\t// etc.\n\treturn bestSegmentIndex;\n}\n\n/**\n * Calculate distance from a point to a line segment\n */\nfunction distanceToSegment(point: Point, segmentStart: Point, segmentEnd: Point): number {\n\tconst A = point.x - segmentStart.x;\n\tconst B = point.y - segmentStart.y;\n\tconst C = segmentEnd.x - segmentStart.x;\n\tconst D = segmentEnd.y - segmentStart.y;\n\t\n\tconst dot = A * C + B * D;\n\tconst lenSq = C * C + D * D;\n\t\n\tif (lenSq === 0) {\n\t\t// Segment is actually a point\n\t\treturn Math.sqrt(A * A + B * B);\n\t}\n\t\n\tlet param = dot / lenSq;\n\t\n\tlet xx, yy;\n\t\n\tif (param < 0) {\n\t\txx = segmentStart.x;\n\t\tyy = segmentStart.y;\n\t} else if (param > 1) {\n\t\txx = segmentEnd.x;\n\t\tyy = segmentEnd.y;\n\t} else {\n\t\txx = segmentStart.x + param * C;\n\t\tyy = segmentStart.y + param * D;\n\t}\n\t\n\tconst dx = point.x - xx;\n\tconst dy = point.y - yy;\n\treturn Math.sqrt(dx * dx + dy * dy);\n}\n\n/**\n * Project a label position onto the line segment where it will be inserted\n */\nfunction projectLabelOntoSegment(vertices: Point[], labelPos: Point, insertPos: number, fromNode: Point, toNode: Point): Point {\n\t// Build the full routing path including start/end nodes\n\tconst fullPath = [fromNode, ...vertices, toNode];\n\t\n\t// The segment where we're inserting is between fullPath[insertPos] and fullPath[insertPos + 1]\n\tconst segmentStart = fullPath[insertPos];\n\tconst segmentEnd = fullPath[insertPos + 1];\n\t\n\t// Project the label position onto this line segment\n\treturn projectPointOntoSegment(labelPos, segmentStart, segmentEnd);\n}\n\n/**\n * Project a point onto a line segment (closest point on the segment)\n */\nfunction projectPointOntoSegment(point: Point, segmentStart: Point, segmentEnd: Point): Point {\n\tconst A = point.x - segmentStart.x;\n\tconst B = point.y - segmentStart.y;\n\tconst C = segmentEnd.x - segmentStart.x;\n\tconst D = segmentEnd.y - segmentStart.y;\n\t\n\tconst dot = A * C + B * D;\n\tconst lenSq = C * C + D * D;\n\t\n\tif (lenSq === 0) {\n\t\t// Segment is actually a point, return that point\n\t\treturn { x: segmentStart.x, y: segmentStart.y };\n\t}\n\t\n\tlet param = dot / lenSq;\n\t\n\t// Clamp to segment (don't extend beyond endpoints)\n\tparam = Math.max(0, Math.min(1, param));\n\t\n\treturn {\n\t\tx: segmentStart.x + param * C,\n\t\ty: segmentStart.y + param * D\n\t};\n}","import { Point, BBox, calculateDistance } from './constants';\nimport { intersectPolylineBox, 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\ninterface GraphData {\n\tid: string;\n\tname: string;\n\tnodesMap: Map;\n\tedges: Edge[];\n\tedgeVertices: Map;\n\tgroupsMap: Map;\n\tmetadata: any;\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, data: GraphData): 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\tconst tmp = (vertices as EdgeVertex[]);\n\n\tif (vertices.length == 0 && !edge.userDeletedVertices) {\n\t\t// Only create auto vertices if user hasn't explicitly deleted them\n\t\t// for edges with same \"from\" and \"to\", we must spread the labels so they don't overlap\n\t\t// lookup the other \"same\" edges\n\t\tconst sameEdges = data.edges.filter(e => e.from == edge.from && e.to == edge.to)\n\t\tlet spreadPos = 0\n\t\tif (sameEdges.length > 1) {\n\t\t\tconst idx = sameEdges.indexOf(edge) // my index in the list of same edges\n\t\t\tspreadPos = idx - (sameEdges.length - 1) / 2\n\n\t\t\tlet spreadX = 0, spreadY = 0;\n\t\t\tif (Math.abs(n1.x - n2.x) > Math.abs(n1.y - n2.y)) {\n\t\t\t\tspreadY = spreadPos * 70\n\t\t\t} else {\n\t\t\t\tspreadX = spreadPos * 200\n\t\t\t}\n\t\t\tconst v = edge.initVertex({\n\t\t\t\tx: (n1.x + n2.x) / 2 + spreadX,\n\t\t\t\ty: (n1.y + n2.y) / 2 + spreadY\n\t\t\t})\n\t\t\tv.label = true\n\t\t\tv.auto = true\n\t\t\tvertices.push(v)\n\t\t} else {\n\t\t\t// If there are no user-defined vertices and not a multi-edge scenario,\n\t\t\t// we don't create any auto-vertices here. AutoLayout will provide them.\n\t\t\t// The path will be a straight line between n1 and n2 (after intersection points are calculated).\n\t\t\t// ELK/autoLayout is responsible for providing bend points for non-straight lines.\n\t\t}\n\t}\n\n\tvertices.unshift(n1)\n\tvertices.push(n2)\n\n\t// Calculate intersection points with node boundaries\n\t// Find first non-label vertex for start intersection\n\tlet firstRoutingVertex = vertices[vertices.length - 1]; // Default to end node\n\tfor (let i = 1; i < vertices.length - 1; i++) {\n\t\tif (!(vertices[i] as any).label) {\n\t\t\tfirstRoutingVertex = vertices[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t// Find last non-label vertex for end intersection \n\tlet lastRoutingVertex = vertices[0]; // Default to start node\n\tfor (let i = vertices.length - 2; i > 0; i--) {\n\t\tif (!(vertices[i] as any).label) {\n\t\t\tlastRoutingVertex = vertices[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t// For connections without routing vertices, ensure we have proper direction\n\t// The defaults are already correct: firstRoutingVertex = n2, lastRoutingVertex = n1\n\t\n\t\n\t// Calculate proper node boundary intersection\n\tconst calculateNodeIntersection = (node: any, targetPoint: Point): Point => {\n\t\tconst nodeShape = node.style?.shape?.toLowerCase() || 'box';\n\t\tconst dx = targetPoint.x - node.x;\n\t\tconst dy = targetPoint.y - node.y;\n\t\tconst nodeCenter = { x: node.x, y: node.y };\n\t\t\n\t\t// If target is at center, default to right edge\n\t\tif (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01) {\n\t\t\treturn { x: node.x + node.width / 2, y: node.y };\n\t\t}\n\t\t\n\t\tif (nodeShape === 'cylinder') {\n\t\t\t// Cylinder shape intersection (same as shapes.ts)\n\t\t\tconst w = node.width;\n\t\t\tconst rx = w / 2;\n\t\t\tconst ry = rx / (5.5 + w / 70);\n\t\t\tconst halfHeight = node.height / 2;\n\t\t\t\n\t\t\t// First calculate rectangular bounds intersection\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Check intersection with rectangular bounds\n\t\t\tlet t = Infinity;\n\t\t\tif (Math.abs(cos) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(rx / cos));\n\t\t\t}\n\t\t\tif (Math.abs(sin) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfHeight / sin));\n\t\t\t}\n\t\t\t\n\t\t\tconst rectX = node.x + cos * t;\n\t\t\tconst rectY = node.y + sin * t;\n\t\t\t\n\t\t\t// Check if we need elliptical intersection for top/bottom curves\n\t\t\tconst topCurveY = node.y - halfHeight + ry;\n\t\t\tconst bottomCurveY = node.y + halfHeight - ry;\n\t\t\t\n\t\t\tif (rectY < topCurveY || rectY > bottomCurveY) {\n\t\t\t\t// Use ellipse intersection for curved parts\n\t\t\t\tconst ellipseY = rectY < topCurveY ? node.y - halfHeight + ry : node.y + halfHeight - ry;\n\t\t\t\t// Solve for ellipse intersection\n\t\t\t\tconst a = 1 / (rx * rx);\n\t\t\t\tconst b = -2 * node.x / (rx * rx);\n\t\t\t\tconst c = (node.x * node.x) / (rx * rx) + ((ellipseY - node.y) * (ellipseY - node.y)) / (ry * ry) - 1;\n\t\t\t\t\n\t\t\t\tconst discriminant = b * b - 4 * a * c;\n\t\t\t\tif (discriminant >= 0) {\n\t\t\t\t\tconst sqrt_d = Math.sqrt(discriminant);\n\t\t\t\t\tconst x1 = (-b + sqrt_d) / (2 * a);\n\t\t\t\t\tconst x2 = (-b - sqrt_d) / (2 * a);\n\t\t\t\t\t\n\t\t\t\t\t// Choose the intersection in the direction of the target\n\t\t\t\t\tconst intersectX = dx > 0 ? Math.max(x1, x2) : Math.min(x1, x2);\n\t\t\t\t\treturn { x: intersectX, y: ellipseY };\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\treturn { x: rectX, y: rectY };\n\t\t\t\n\t\t} else if (nodeShape === 'circle') {\n\t\t\tconst radius = node.width / 2;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\treturn {\n\t\t\t\tx: node.x + Math.cos(angle) * radius,\n\t\t\t\ty: node.y + Math.sin(angle) * radius\n\t\t\t};\n\t\t\t\n\t\t} else if (nodeShape === 'ellipse') {\n\t\t\tconst rx = node.width * 0.55;\n\t\t\tconst ry = node.width * 0.45;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Parametric ellipse intersection\n\t\t\tconst t = Math.sqrt((rx * rx * sin * sin) + (ry * ry * cos * cos));\n\t\t\treturn {\n\t\t\t\tx: node.x + (rx * cos * ry) / t,\n\t\t\t\ty: node.y + (ry * sin * rx) / t\n\t\t\t};\n\t\t\t\n\t\t} else {\n\t\t\t// Default rectangular intersection\n\t\t\tconst halfWidth = node.width / 2;\n\t\t\tconst halfHeight = node.height / 2;\n\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\tconst cos = Math.cos(angle);\n\t\t\tconst sin = Math.sin(angle);\n\t\t\t\n\t\t\t// Calculate which edge we hit first\n\t\t\tlet t = Infinity;\n\t\t\tif (Math.abs(cos) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfWidth / cos));\n\t\t\t}\n\t\t\tif (Math.abs(sin) > 0.01) {\n\t\t\t\tt = Math.min(t, Math.abs(halfHeight / sin));\n\t\t\t}\n\t\t\t\n\t\t\treturn {\n\t\t\t\tx: node.x + cos * t,\n\t\t\t\ty: node.y + sin * t\n\t\t\t};\n\t\t}\n\t};\n\t\n\t// Calculate intersections, but use the direction from center to NEXT vertex in sequence\n\t// This ensures the line exits the node in the direction it needs to go\n\t\n\t// For start intersection: use direction from node center to first routing vertex\n\tlet startIntersection = calculateNodeIntersection(n1, firstRoutingVertex);\n\t\n\t// For end intersection: use direction from node center to last routing vertex \n\tlet endIntersection = calculateNodeIntersection(n2, lastRoutingVertex);\n\t\n\t// Start intersection: NO offset needed - intersection already gives perfect boundary point\n\t// End intersection: NO offset needed - the arrow marker now has refX=\"0\" so the tip is at the endpoint\n\t\n\tvertices[0] = startIntersection;\n\tvertices[vertices.length - 1] = endIntersection;\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\tfallback: Point,\n): EdgeLabelPlacement {\n\tlet point = {x: fallback.x, y: fallback.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/**\n * Create edge segments and generate SVG path\n */\nexport function createEdgeSegments(vertices: Point[], bbox: BBox, n1: Point, n2: Point): { segments: Segment[], path: string } {\n\tconst segments: Segment[] = []\n\tfor (let i = 1; i < vertices.length; i++) {\n\t\tsegments.push({p: vertices[i - 1], q: vertices[i]})\n\t}\n\t\n\t// Debug the final segment that will have the arrow\n\tif (segments.length > 0) {\n\t\tconst lastSegment = segments[segments.length - 1];\n\t}\n\t// splice edge over label box\n\tintersectPolylineBox(segments, bbox)\n\n\t// Generate path based on routing style - SIMPLIFIED\n\tlet path: string\n\t\n\t// Always draw a polyline through the segments.\n\t// If segments.length is 1 (meaning direct connection or only label vertex), it will be a straight line.\n\t// If autoLayout provided bend points, those will be in `segments`.\n\tif (segments.length > 0) {\n\t\tpath = `M${segments[0].p.x},${segments[0].p.y}`\n\t\tfor (let i = 0; i < segments.length; i++) {\n\t\t\tconst s = segments[i]\n\t\t\t// For polylines, we just draw line segments to each vertex point.\n\t\t\t// The ELK 'POLYLINE' routing should give us the necessary bend points.\n\t\t\tpath += ` L${s.q.x},${s.q.y}`\n\t\t}\n\t} else {\n\t\t// Fallback for edges with no segments (should ideally not happen if n1 and n2 are defined)\n\t\t// Draw a straight line between n1 and n2 directly if no vertices/segments exist.\n\t\t// Note: Intersection points are calculated before this, so n1/n2 are already adjusted.\n\t\tpath = `M${n1.x},${n1.y} L${n2.x},${n2.y}`\n\t}\n\t\n\treturn { segments, path };\n}","import {GraphData, Node, Group, LayoutDirection} from \"./graph\";\n\nexport interface LayoutOptions {\n\tdirection?: LayoutDirection;\n\tnodeSpacing?: number;\n\tlayerSpacing?: number;\n\tcompactLayout?: boolean;\n}\n\n// Simplified spacing configuration\ninterface SpacingConfig {\n\tnodeSpacing: number;\n\tlayerSpacing: number;\n\tcomponentSpacing: number;\n\tpadding: number;\n\tgroupMultiplier: number;\n}\n\n// Spacing configuration - balanced for readability\nconst DEFAULT_SPACING: SpacingConfig = {\n\tnodeSpacing: 80, // Comfortable vertical spacing between nodes in same layer\n\tlayerSpacing: 60, // Layer spacing (between nodes in flow direction)\n\tcomponentSpacing: 80, // Separation between disconnected components\n\tpadding: 40, // Padding around the entire layout (for group labels)\n\tgroupMultiplier: 0.65, // Moderate compaction within groups\n};\n\n// Helper function to get effective spacing for a context\nfunction getEffectiveSpacing(\n\tuserOptions: LayoutOptions = {},\n\tisGroup: boolean = false\n): SpacingConfig {\n\t// Apply user overrides to base config\n\tconst effectiveConfig: SpacingConfig = {\n\t\tnodeSpacing: userOptions.nodeSpacing ?? DEFAULT_SPACING.nodeSpacing,\n\t\tlayerSpacing: userOptions.layerSpacing ?? DEFAULT_SPACING.layerSpacing,\n\t\tcomponentSpacing: DEFAULT_SPACING.componentSpacing,\n\t\tpadding: DEFAULT_SPACING.padding,\n\t\tgroupMultiplier: DEFAULT_SPACING.groupMultiplier,\n\t};\n\t\n\t// Apply group multiplier if in group context\n\tif (isGroup) {\n\t\teffectiveConfig.nodeSpacing = Math.max(\n\t\t\teffectiveConfig.nodeSpacing * effectiveConfig.groupMultiplier,\n\t\t\t30 // Minimum 30px spacing within groups\n\t\t);\n\t\teffectiveConfig.layerSpacing = Math.max(\n\t\t\teffectiveConfig.layerSpacing * effectiveConfig.groupMultiplier,\n\t\t\t35 // Minimum 35px layer spacing within groups\n\t\t);\n\t\teffectiveConfig.componentSpacing = Math.max(\n\t\t\teffectiveConfig.componentSpacing * effectiveConfig.groupMultiplier,\n\t\t\t25 // Minimum 25px component spacing within groups\n\t\t);\n\t\teffectiveConfig.padding = Math.max(\n\t\t\teffectiveConfig.padding * effectiveConfig.groupMultiplier,\n\t\t\t15 // Minimum 15px padding within groups\n\t\t);\n\t}\n\t\n\treturn effectiveConfig;\n}\n\n// Simplified ELK layout options builder\nfunction getELKOptions(\n\tspacing: SpacingConfig,\n\tuserOptions: LayoutOptions\n): Record {\n\tconst {\n\t\tdirection = 'DOWN',\n\t\tcompactLayout = false\n\t} = userOptions;\n\t\n\tconst baseOptions: Record = {\n\t\t'elk.algorithm': 'layered', // Back to layered for better orthogonal routing\n\t\t'elk.direction': direction,\n\t\t'elk.spacing.nodeNode': spacing.nodeSpacing.toString(),\n\t\t'elk.spacing.componentComponent': spacing.componentSpacing.toString(),\n\t\t'elk.padding': `[top=${spacing.padding},left=${spacing.padding},bottom=${spacing.padding},right=${spacing.padding}]`,\n\t\t\n\t\t// Layer spacing for compact layout\n\t\t'elk.layered.spacing.nodeNodeBetweenLayers': spacing.layerSpacing.toString(),\n\t\t'elk.layered.spacing.edgeNodeBetweenLayers': '10', // Minimal spacing around nodes\n\t\t'elk.layered.spacing.edgeEdgeBetweenLayers': '10', // Minimal space between edges\n\t\t\n\t\t// ORTHOGONAL edge routing for cleaner layout\n\t\t'elk.edgeRouting': 'POLYLINE',\n\t\t'elk.layered.unnecessaryBendpoints': 'false',\n\t\t\n\t\t// Minimal edge routing - straight lines where possible\n\t\t'elk.layered.edgeRouting.orthogonal.mode': 'DIRECTION_BASED',\n\t\t'elk.layered.edgeRouting.orthogonal.spacing': '5', // Minimal edge spacing\n\t\t'elk.layered.edgeRouting.orthogonal.nodeOverlapRatio': '0.1',\n\t\t\n\t\t// Compaction options\n\t\t'elk.layered.compaction.connectedComponents': 'true',\n\t\t'elk.layered.compaction.postCompaction.strategy': 'LEFT_RIGHT',\n\t\t\n\t\t// Separate components to reduce complexity\n\t\t'elk.separateConnectedComponents': 'true',\n\t\t\n\t\t// Node placement strategy for consistent vertical spacing\n\t\t'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',\n\t\t'elk.layered.nodePlacement.favorStraightEdges': 'true',\n\t\t\n\t\t// Crossing minimization - respect model order for consistent layout\n\t\t'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',\n\t\t'elk.layered.crossingMinimization.semiInteractive': 'true',\n\t\t\n\t\t// Flatten hierarchy for better edge routing\n\t\t'elk.hierarchyHandling': 'SEPARATE_CHILDREN',\n\t\t'elk.layered.considerModelOrder.strategy': 'NONE', // Ignore model ordering constraints\n\t\t\n\t\t// Edge label handling - minimal space, labels positioned above edges\n\t\t'elk.edgeLabels.placement': 'CENTER',\n\t\t'elk.edgeLabels.inline': 'true',\n\t\t'elk.spacing.edgeLabel': '5', // Minimal spacing for labels\n\t\t'elk.edgeLabels.avoidOverlap': 'false', // Disable collision avoidance\n\t\t'elk.edgeLabels.considerModelOrder': 'false',\n\t\t'elk.layered.edgeLabels.sideSelection': 'ALWAYS_UP', // Labels above edges\n\t};\n\t\n\t// Additional compact layout options if requested\n\tif (compactLayout) {\n\t\tbaseOptions['elk.spacing.nodeNode'] = Math.max(spacing.nodeSpacing * 0.7, 30).toString();\n\t\tbaseOptions['elk.layered.spacing.nodeNodeBetweenLayers'] = Math.max(spacing.layerSpacing * 0.7, 30).toString();\n\t}\n\t\n\treturn baseOptions;\n}\n\nexport async function autoLayout(graph: GraphData, options: LayoutOptions = {}): Promise<{\n\tnodes: Array<{id: string, x: number, y: number}>,\n\tedges: Array<{id: string, vertices: Array<{x: number, y: number}>, label?: {x: number, y: number}}>\n}> {\n\t// Dynamically import ELK only when auto-layout is used\n\tconst ELK = await import('elkjs/lib/elk.bundled.js').then(module => module.default);\n\tconst elk = new ELK();\n\t// Get systematic spacing configuration\n\tconst rootSpacing = getEffectiveSpacing(options, false);\n\t\n\t// Build ELK graph structure\n\tconst elkGraph = {\n\t\tid: \"root\",\n\t\tlayoutOptions: getELKOptions(rootSpacing, options),\n\t\tchildren: [] as any[],\n\t\tedges: [] as any[]\n\t};\n\n\t// Build actual ELK nodes first. Groups below take ownership of their direct\n\t// members so ELK reserves non-overlapping space for every boundary.\n\tconst nodeMap = new Map();\n\tconst elkNodes = new Map();\n\tgraph.nodesMap.forEach(node => {\n\t\tif (!node.id) return; // Skip nodes without IDs\n\t\t\n\t\tnodeMap.set(node.id, node);\n\t\t\n\t\t// Ensure minimum dimensions and validate node size data\n\t\t// Use larger height to account for shapes like Robot that extend above the center\n\t\tconst nodeWidth = Math.max(node.width || 200, 150); // Min width 150px\n\t\tconst nodeHeight = Math.max(node.height || 100, 250); // Min height 250px to account for robot shape\n\t\t\n\t\t// Add padding to node dimensions for ELK to account for arrow size\n\t\t// This makes ELK route edges to a slightly larger boundary so arrow tips stay outside\n\t\tconst arrowPadding = 25; // Padding for arrow clearance\n\t\t\n\t\telkNodes.set(node.id, {\n\t\t\tid: node.id,\n\t\t\t// Provide current position as hint to ELK\n\t\t\tx: node.x,\n\t\t\ty: node.y,\n\t\t\twidth: nodeWidth + (arrowPadding * 2),\n\t\t\theight: nodeHeight + (arrowPadding * 2),\n\t\t\tlayoutOptions: {\n\t\t\t\t// Allow ELK to move nodes but consider current positions\n\t\t\t\t'elk.position': '',\n\t\t\t\t// Force ELK to use our exact dimensions\n\t\t\t\t'elk.nodeSize.constraints': '[FIXED_SIZE]'\n\t\t\t}\n\t\t});\n\t});\n\n\tconst nodeParentGroup = new Map();\n\tconst groupParent = new Map();\n\tconst childGroupIDs = new Set();\n\n\tgraph.groupsMap.forEach(group => {\n\t\tgroup.nodes.forEach(member => {\n\t\t\tif (isGroup(member)) {\n\t\t\t\tif (!groupParent.has(member.id)) {\n\t\t\t\t\tgroupParent.set(member.id, group.id);\n\t\t\t\t}\n\t\t\t\tchildGroupIDs.add(member.id);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!nodeParentGroup.has(member.id)) {\n\t\t\t\tnodeParentGroup.set(member.id, group.id);\n\t\t\t}\n\t\t});\n\t});\n\n\tconst elkGroups = new Map();\n\tconst buildELKGroup = (group: Group): any => {\n\t\tconst existing = elkGroups.get(group.id);\n\t\tif (existing) return existing;\n\n\t\tconst children = group.nodes.flatMap(member => {\n\t\t\tif (isGroup(member)) {\n\t\t\t\treturn groupParent.get(member.id) === group.id ? [buildELKGroup(member)] : [];\n\t\t\t}\n\t\t\tconst node = elkNodes.get(member.id);\n\t\t\treturn node && nodeParentGroup.get(member.id) === group.id ? [node] : [];\n\t\t});\n\t\tconst elkGroup = {\n\t\t\tid: group.id,\n\t\t\tchildren,\n\t\t\tedges: [] as any[],\n\t\t\tlayoutOptions: getELKOptions(getEffectiveSpacing(options, true), options),\n\t\t};\n\t\telkGroups.set(group.id, elkGroup);\n\t\treturn elkGroup;\n\t};\n\n\tgraph.groupsMap.forEach(group => {\n\t\tif (!childGroupIDs.has(group.id)) {\n\t\t\tconst elkGroup = buildELKGroup(group);\n\t\t\tif (elkGroup.children.length > 0) {\n\t\t\t\telkGraph.children.push(elkGroup);\n\t\t\t}\n\t\t}\n\t});\n\telkNodes.forEach((node, id) => {\n\t\tif (!nodeParentGroup.has(id)) {\n\t\t\telkGraph.children.push(node);\n\t\t}\n\t});\n\n\tconst groupAncestors = (groupID?: string) => {\n\t\tconst ancestors: string[] = [];\n\t\tlet current = groupID;\n\t\twhile (current) {\n\t\t\tancestors.push(current);\n\t\t\tcurrent = groupParent.get(current);\n\t\t}\n\t\treturn ancestors;\n\t};\n\tconst lowestCommonGroup = (sourceID: string, destinationID: string) => {\n\t\tconst sourceAncestors = groupAncestors(nodeParentGroup.get(sourceID));\n\t\tconst destinationAncestors = new Set(groupAncestors(nodeParentGroup.get(destinationID)));\n\t\treturn sourceAncestors.find(groupID => destinationAncestors.has(groupID));\n\t};\n\n\tgraph.edges.forEach(edge => {\n\t\t// Skip edges without proper IDs\n\t\tif (!edge.id || !edge.from?.id || !edge.to?.id) return;\n\t\t\n\t\t// Verify source and target nodes exist in our node map\n\t\tif (!nodeMap.has(edge.from.id) || !nodeMap.has(edge.to.id)) {\n\t\t\tconsole.warn(`Skipping edge ${edge.id}: source ${edge.from.id} or target ${edge.to.id} not found in nodes`);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// Calculate more accurate label dimensions for ELK\n\t\tconst labelWidth = edge.label && edge.label.trim() ? \n\t\t\tMath.min(edge.label.length * 7, 200) : 0; // More realistic width estimate\n\t\t\n\t\t\n\t\tconst elkEdge = {\n\t\t\tid: edge.id,\n\t\t\tsources: [edge.from.id],\n\t\t\ttargets: [edge.to.id],\n\t\t\t// Include label information with much smaller dimensions\n\t\t\tlabels: edge.label && edge.label.trim() ? [{\n\t\t\t\tid: `${edge.id}-label`,\n\t\t\t\ttext: edge.label,\n\t\t\t\t// Much more conservative label size estimates\n\t\t\t\twidth: labelWidth,\n\t\t\t\theight: 20, // More realistic label height\n\t\t\t\tlayoutOptions: {\n\t\t\t\t\t'elk.edgeLabels.placement': 'CENTER',\n\t\t\t\t\t'elk.edgeLabels.inline': 'true'\n\t\t\t\t\t// Remove the FIXED_SIZE constraint that might be forcing detours\n\t\t\t\t}\n\t\t\t}] : []\n\t\t};\n\n\t\tconst groupID = lowestCommonGroup(edge.from.id, edge.to.id);\n\t\tconst edgeContainer = groupID ? elkGroups.get(groupID) : elkGraph;\n\t\tedgeContainer.edges.push(elkEdge);\n\t});\n\n\t// Enhanced validation - ensure ELK gets complete data\n\tif (!elkGraph.id || !elkGraph.children) {\n\t\tthrow new Error('Invalid ELK graph structure');\n\t}\n\t\n\n\ttry {\n\t\tconst layoutedGraph = await elk.layout(elkGraph);\n\t\t\n\t\t// Extract results\n\t\tconst nodes: Array<{id: string, x: number, y: number}> = [];\n\t\tconst edges: Array<{id: string, vertices: Array<{x: number, y: number}>, label?: {x: number, y: number}}> = [];\n\n\n\t\t// Extract nodes from layout result\n\t\tconst extractNodes = (container: any, offsetX = 0, offsetY = 0) => {\n\t\t\tcontainer.children?.forEach((child: any) => {\n\t\t\t\tif (child.children) {\n\t\t\t\t\t// This is a group, recurse\n\t\t\t\t\textractNodes(child, offsetX + (child.x || 0), offsetY + (child.y || 0));\n\t\t\t\t} else {\n\t\t\t\t\t// This is a node\n\t\t\t\t\t// Adjust for the padding we added - ELK positioned based on padded size\n\t\t\t\t\t// So we need to shift by the padding amount to get the real center\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\tid: child.id,\n\t\t\t\t\t\tx: offsetX + (child.x || 0) + (child.width || 0) / 2,\n\t\t\t\t\t\ty: offsetY + (child.y || 0) + (child.height || 0) / 2\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\t// Process edges from ELK layout result to get routing information\n\t\tconst processEdgesFromELK = (container: any, offsetX = 0, offsetY = 0) => {\n\t\t\tcontainer.edges?.forEach((elkEdge: any) => {\n\t\t\t\tconst vertices: Array<{x: number, y: number}> = [];\n\t\t\t\tlet label: {x: number, y: number} | undefined;\n\n\t\t\t\t// Process edge sections to get bend points\n\t\t\t\tif (elkEdge.sections && elkEdge.sections.length > 0) {\n\t\t\t\t\telkEdge.sections.forEach((section: any) => {\n\t\t\t\t\t\t// Add start point if it exists\n\t\t\t\t\t\tif (section.startPoint) {\n\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\tx: offsetX + section.startPoint.x, \n\t\t\t\t\t\t\t\ty: offsetY + section.startPoint.y\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Add bend points (this is where ELK puts the routing vertices!)\n\t\t\t\t\t\tif (section.bendPoints && section.bendPoints.length > 0) {\n\t\t\t\t\t\t\tsection.bendPoints.forEach((bp: any) => {\n\t\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\t\tx: offsetX + bp.x, \n\t\t\t\t\t\t\t\t\ty: offsetY + bp.y\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Add end point if it exists\n\t\t\t\t\t\tif (section.endPoint) {\n\t\t\t\t\t\t\tvertices.push({\n\t\t\t\t\t\t\t\tx: offsetX + section.endPoint.x, \n\t\t\t\t\t\t\t\ty: offsetY + section.endPoint.y\n\t\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\n\t\t\t\t// Extract ELK's calculated label positions (respect collision avoidance!)\n\t\t\t\tconst originalEdge = graph.edges.find(e => e.id === elkEdge.id);\n\t\t\t\tif (originalEdge?.label && originalEdge.label.trim()) {\n\t\t\t\t\tif (elkEdge.labels && elkEdge.labels.length > 0) {\n\t\t\t\t\t\tconst elkLabel = elkEdge.labels[0]; // Get first label\n\t\t\t\t\t\tif (elkLabel.x !== undefined && elkLabel.y !== undefined) {\n\t\t\t\t\t\t\tlabel = {\n\t\t\t\t\t\t\t\tx: offsetX + elkLabel.x + (elkLabel.width || 0) / 2, // Center of label\n\t\t\t\t\t\t\t\ty: offsetY + elkLabel.y + (elkLabel.height || 0) / 2\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (vertices.length >= 2) {\n\t\t\t\t\t\t// Fallback: use middle of edge if ELK didn't provide label position\n\t\t\t\t\t\tconst midIndex = Math.floor(vertices.length / 2);\n\t\t\t\t\t\tif (vertices.length % 2 === 0) {\n\t\t\t\t\t\t\tconst v1 = vertices[midIndex - 1];\n\t\t\t\t\t\t\tconst v2 = vertices[midIndex];\n\t\t\t\t\t\t\tlabel = { x: (v1.x + v2.x) / 2, y: (v1.y + v2.y) / 2 };\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlabel = vertices[midIndex];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\n\t\t\t\tedges.push({\n\t\t\t\t\tid: elkEdge.id,\n\t\t\t\t\tvertices,\n\t\t\t\t\tlabel\n\t\t\t\t});\n\t\t\t});\n\t\t\t\n\t\t\t// Also process edges in child containers (groups)\n\t\t\tcontainer.children?.forEach((child: any) => {\n\t\t\t\tif (child.edges && child.edges.length > 0) {\n\t\t\t\t\tprocessEdgesFromELK(child, offsetX + (child.x || 0), offsetY + (child.y || 0));\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\n\t\textractNodes(layoutedGraph);\n\t\tprocessEdgesFromELK(layoutedGraph);\n\n\t\t// Normalize coordinates to start near (0,0) to prevent huge canvas sizes\n\t\t// while preserving relative positioning between elements\n\t\tif (nodes.length > 0) {\n\t\t\t// Find the minimum coordinates across all elements\n\t\t\tconst minX = Math.min(...nodes.map(n => n.x));\n\t\t\tconst minY = Math.min(...nodes.map(n => n.y));\n\t\t\t\n\t\t\t// Add some padding so content doesn't start at exact (0,0)\n\t\t\tconst padding = 50;\n\t\t\tconst offsetX = -minX + padding;\n\t\t\tconst offsetY = -minY + padding;\n\t\t\t\n\t\t\t// Normalize all node positions\n\t\t\tnodes.forEach(node => {\n\t\t\t\tnode.x += offsetX;\n\t\t\t\tnode.y += offsetY;\n\t\t\t});\n\t\t\t\n\t\t\t// Normalize all edge positions\n\t\t\tedges.forEach(edge => {\n\t\t\t\tedge.vertices.forEach(vertex => {\n\t\t\t\t\tvertex.x += offsetX;\n\t\t\t\t\tvertex.y += offsetY;\n\t\t\t\t});\n\t\t\t\tif (edge.label) {\n\t\t\t\t\tedge.label.x += offsetX;\n\t\t\t\t\tedge.label.y += offsetY;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\treturn { nodes, edges };\n\n\t} catch (error) {\n\t\tconsole.warn('ELK layout failed, using fallback layout. Error:', error);\n\t\treturn createFallbackLayout(graph);\n\t}\n}\n\n// Simplified fallback layout\nfunction createFallbackLayout(graph: GraphData): {\n\tnodes: Array<{id: string, x: number, y: number}>,\n\tedges: Array<{id: string, vertices: Array<{x: number, y: number}>}>\n} {\n\tconst nodes: Array<{id: string, x: number, y: number}> = [];\n\tconst edges: Array<{id: string, vertices: Array<{x: number, y: number}>}> = [];\n\n\t// Simple grid layout for nodes\n\tlet x = 0, y = 0;\n\tconst spacing = 300;\n\tconst maxCols = Math.ceil(Math.sqrt(graph.nodesMap.size));\n\n\tlet col = 0;\n\tgraph.nodesMap.forEach(node => {\n\t\tnodes.push({\n\t\t\tid: node.id,\n\t\t\tx: x,\n\t\t\ty: y\n\t\t});\n\n\t\tcol++;\n\t\tif (col >= maxCols) {\n\t\t\tcol = 0;\n\t\t\tx = 0;\n\t\t\ty += spacing;\n\t\t} else {\n\t\t\tx += spacing;\n\t\t}\n\t});\n\n\t// Simple straight line edges\n\tgraph.edges.forEach(edge => {\n\t\tedges.push({\n\t\t\tid: edge.id,\n\t\t\tvertices: []\n\t\t});\n\t});\n\n\treturn { nodes, edges };\n}\n\nfunction isGroup(member: Node | Group): member is Group {\n\treturn \"nodes\" in member;\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","interface LiveReloadOptions {\n\tminDelay: number;\n\tmaxDelay: number;\n\thandshakeTimeout: number;\n}\n\ninterface LiveReloadMessage {\n\tcommand: string;\n\tprotocols?: string[];\n\tver?: string;\n\tpath?: string;\n}\n\nclass Timer {\n\tprivate readonly callback: () => void;\n\tprivate readonly handler: () => void;\n\tprivate running: boolean = false;\n\tprivate timeoutId: ReturnType | null = null;\n\n\tconstructor(callback: () => void) {\n\t\tthis.callback = callback;\n\t\tthis.handler = () => {\n\t\t\tthis.running = false;\n\t\t\tthis.timeoutId = null;\n\t\t\tthis.callback();\n\t\t};\n\t}\n\n\tstart(timeout: number): void {\n\t\tif (this.running) {\n\t\t\tthis.stop();\n\t\t}\n\t\tthis.timeoutId = setTimeout(this.handler, timeout);\n\t\tthis.running = true;\n\t}\n\n\tstop(): void {\n\t\tif (this.running && this.timeoutId !== null) {\n\t\t\tclearTimeout(this.timeoutId);\n\t\t\tthis.running = false;\n\t\t\tthis.timeoutId = null;\n\t\t}\n\t}\n\n\tisRunning(): boolean {\n\t\treturn this.running;\n\t}\n}\n\n/**\n * RefreshConnector implements the livereload protocol to listen for file changes via WebSocket\n */\nexport class RefreshConnector {\n\tprivate static readonly DEFAULT_OPTIONS: LiveReloadOptions = {\n\t\tminDelay: 1000,\n\t\tmaxDelay: 60000,\n\t\thandshakeTimeout: 5000\n\t};\n\n\tprivate static readonly LIVERELOAD_PROTOCOLS = [\n\t\t'http://livereload.com/protocols/official-9',\n\t\t'http://livereload.com/protocols/2.x-remote-control'\n\t];\n\n\tprivate readonly uri: string;\n\tprivate readonly options: LiveReloadOptions;\n\tprivate readonly fileChangeHandler: (path: string) => void;\n\t\n\tprivate socket: WebSocket | null = null;\n\tprivate nextDelay: number;\n\tprivate connectionDesired: boolean = false;\n\tprivate disconnectionReason: string = '';\n\t\n\tprivate readonly handshakeTimeout: Timer;\n\tprivate readonly reconnectTimer: Timer;\n\n\tconstructor(fileChangeHandler: (file: string) => void, options?: Partial) {\n\t\tthis.fileChangeHandler = fileChangeHandler;\n\t\tthis.options = { ...RefreshConnector.DEFAULT_OPTIONS, ...options };\n\t\tthis.uri = 'ws://localhost:35729/livereload';\n\t\tthis.nextDelay = this.options.minDelay;\n\n\t\tthis.handshakeTimeout = new Timer(() => this.handleHandshakeTimeout());\n\t\tthis.reconnectTimer = new Timer(() => this.attemptReconnection());\n\t}\n\n\tconnect(): void {\n\t\tthis.connectionDesired = true;\n\n\t\tif (this.isSocketConnected()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.prepareForConnection();\n\t\tthis.createWebSocket();\n\t}\n\n\tdisconnect(): void {\n\t\tthis.connectionDesired = false;\n\t\tthis.reconnectTimer.stop();\n\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.disconnectionReason = 'manual';\n\t\t\tthis.socket!.close();\n\t\t}\n\t}\n\n\tprivate isSocketConnected(): boolean {\n\t\treturn this.socket !== null && this.socket.readyState === WebSocket.OPEN;\n\t}\n\n\tprivate prepareForConnection(): void {\n\t\tthis.reconnectTimer.stop();\n\t\tthis.disconnectionReason = 'cannot-connect';\n\t}\n\n\tprivate createWebSocket(): void {\n\t\tthis.socket = new WebSocket(this.uri);\n\t\tthis.socket.onopen = () => this.handleOpen();\n\t\tthis.socket.onclose = () => this.handleClose();\n\t\tthis.socket.onmessage = (event) => this.handleMessage(event);\n\t\tthis.socket.onerror = () => this.handleError();\n\t}\n\n\tprivate handleOpen(): void {\n\t\tthis.disconnectionReason = 'handshake-failed';\n\t\tthis.startHandshake();\n\t}\n\n\tprivate handleClose(): void {\n\t\tconsole.log(`WebSocket disconnected: ${this.disconnectionReason}. Retry in ${this.nextDelay}ms`);\n\t\tthis.scheduleReconnection();\n\t}\n\n\tprivate handleMessage(event: MessageEvent): void {\n\t\ttry {\n\t\t\tconst message: LiveReloadMessage = JSON.parse(event.data);\n\t\t\tthis.processMessage(message);\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to parse WebSocket message:', error);\n\t\t}\n\t}\n\n\tprivate handleError(): void {\n\t\t// Error handling is done in onclose\n\t}\n\n\tprivate processMessage(message: LiveReloadMessage): void {\n\t\tswitch (message.command) {\n\t\t\tcase 'hello':\n\t\t\t\tthis.handleHelloMessage();\n\t\t\t\tbreak;\n\t\t\tcase 'reload':\n\t\t\t\tthis.handleReloadMessage(message);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tconsole.log('Unknown WebSocket message received:', message);\n\t\t}\n\t}\n\n\tprivate handleHelloMessage(): void {\n\t\tthis.handshakeTimeout.stop();\n\t\tthis.nextDelay = this.options.minDelay;\n\t}\n\n\tprivate handleReloadMessage(message: LiveReloadMessage): void {\n\t\t// The livereload server closes connection after sending reload\n\t\t// We must reconnect\n\t\tthis.reconnectTimer.stop();\n\t\tthis.connect();\n\n\t\tif (message.path) {\n\t\t\tthis.fileChangeHandler(message.path);\n\t\t}\n\t}\n\n\tprivate startHandshake(): void {\n\t\tconst helloMessage: LiveReloadMessage = {\n\t\t\tcommand: 'hello',\n\t\t\tprotocols: RefreshConnector.LIVERELOAD_PROTOCOLS,\n\t\t\tver: '3.3.1'\n\t\t};\n\t\t\n\t\tthis.sendCommand(helloMessage);\n\t\tthis.handshakeTimeout.start(this.options.handshakeTimeout);\n\t}\n\n\tprivate handleHandshakeTimeout(): void {\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.disconnectionReason = 'handshake-timeout';\n\t\t\tthis.socket!.close();\n\t\t}\n\t}\n\n\tprivate attemptReconnection(): void {\n\t\tif (this.connectionDesired) {\n\t\t\tthis.connect();\n\t\t}\n\t}\n\n\tprivate scheduleReconnection(): void {\n\t\tif (!this.connectionDesired) {\n\t\t\treturn; // Don't reconnect after manual disconnection\n\t\t}\n\n\t\tif (!this.reconnectTimer.isRunning()) {\n\t\t\tthis.reconnectTimer.start(this.nextDelay);\n\t\t\tthis.nextDelay = Math.min(this.options.maxDelay, this.nextDelay * 2);\n\t\t}\n\t}\n\n\tprivate sendCommand(command: LiveReloadMessage): void {\n\t\tif (this.isSocketConnected()) {\n\t\t\tthis.socket!.send(JSON.stringify(command));\n\t\t}\n\t}\n}","import { createRoot } from 'react-dom/client';\nimport React, { Suspense, lazy, useEffect, useState } from 'react';\nimport { refreshGraph } from \"./Root\";\nimport './style.css';\nimport '@fortawesome/fontawesome-free/css/all.css';\nimport { RefreshConnector } from \"./websocket\";\n\nconst Root = lazy(() => import('./Root').then(module => ({ default: module.Root })));\n\ninterface ModelData {\n\tmodel: any;\n\tlayout: any;\n}\n\ninterface AppState {\n\tdata: ModelData | null;\n\terror: string | null;\n\tloading: boolean;\n}\n\nconst App: React.FC = () => {\n\tconst [state, setState] = useState({\n\t\tdata: null,\n\t\terror: null,\n\t\tloading: true\n\t});\n\n\tconst loadData = async () => {\n\t\tsetState(prev => ({ ...prev, loading: true, error: null }));\n\t\t\n\t\ttry {\n\t\t\tconst [modelResponse, layoutResponse] = await Promise.all([\n\t\t\t\tfetch('data/model.json'),\n\t\t\t\tfetch('data/layout.json')\n\t\t\t]);\n\n\t\t\tif (!modelResponse.ok) {\n\t\t\t\tthrow new Error(`Failed to fetch model: ${modelResponse.statusText}`);\n\t\t\t}\n\t\t\t\n\t\t\tif (!layoutResponse.ok) {\n\t\t\t\tthrow new Error(`Failed to fetch layout: ${layoutResponse.statusText}`);\n\t\t\t}\n\n\t\t\tconst [model, layout] = await Promise.all([\n\t\t\t\tmodelResponse.json(),\n\t\t\t\tlayoutResponse.json()\n\t\t\t]);\n\n\t\t\tsetState({\n\t\t\t\tdata: { model, layout },\n\t\t\t\terror: null,\n\t\t\t\tloading: false\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error('Failed to load data:', error);\n\t\t\tsetState({\n\t\t\t\tdata: null,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error occurred',\n\t\t\t\tloading: false\n\t\t\t});\n\t\t}\n\t};\n\n\tconst handleFileChange = (path: string) => {\n\t\tif (path.endsWith('.svg')) {\n\t\t\treturn; // Ignore SVG changes to avoid infinite loops\n\t\t}\n\t\t\n\t\tconsole.log('File changed:', path);\n\t\trefreshGraph();\n\t\tloadData();\n\t};\n\n\tuseEffect(() => {\n\t\tconst params = new URLSearchParams(document.location.search);\n\t\t// Headless rendering has no watcher and must not receive reloads from an\n\t\t// unrelated interactive editor using the shared LiveReload port.\n\t\tconst auto = params.get('auto');\n\t\tconst save = params.get('save');\n\t\tconst automated = auto === '1' || auto === 'true' || save === '1' || save === 'true';\n\t\tconst refreshConnector = automated ? null : new RefreshConnector(handleFileChange);\n\t\trefreshConnector?.connect();\n\n\t\t// Initial data load\n\t\tloadData();\n\n\t\treturn () => {\n\t\t\trefreshConnector?.disconnect();\n\t\t};\n\t}, []);\n\n\tif (state.loading) {\n\t\treturn ;\n\t}\n\n\tif (state.error) {\n\t\treturn ;\n\t}\n\n\tif (!state.data) {\n\t\treturn ;\n\t}\n\n\treturn (\n\t\t}>\n\t\t\t\n\t\t\n\t);\n};\n\nconst LoadingScreen: React.FC = () => (\n\t
\n\t\t
Loading...
\n\t
\n);\n\nconst ErrorScreen: React.FC<{ error: string; onRetry: () => void }> = ({ error, onRetry }) => (\n\t
\n\t\t

Error loading application

\n\t\t

{error}

\n\t\t\n\t
\n);\n\n// Initialize the application\nconst container = document.getElementById('root');\nif (!container) {\n\tthrow new Error('Root container not found');\n}\n\nconst root = createRoot(container);\nroot.render();","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\";\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"],"names":["layoutDirections","TopBottom","BottomTop","LeftRight","RightLeft","listViews","model","viewsList","Object","keys","views","filter","section","endsWith","forEach","s","v","push","key","title","graphs","camelToWords","camel","split","replace","charAt","toUpperCase","slice","Toolbar","currentID","onViewChange","graph","onAutoLayout","onSave","onToggleHelp","saving","layouting","dragMode","setDragMode","jsx_runtime","jsxs","className","children","jsx","ViewSelector","ToolbarActions","length","onChange","e","target","value","disabled","hidden","map","view","style","marginLeft","fontWeight","display","alignItems","DragModeButton","UndoRedoButtons","AlignmentButtons","LayoutControls","GridControls","ZoomControls","SaveButton","HelpButton","onClick","modKey","platform","sy","Fragment","undo","redo","alignSelectionH","alignSelectionV","transform","distributeSelectionH","distributeSelectionV","gridVisible","setGridVisible","react","useState","isGridVisible","snapToGrid","setSnapToGrid","isSnapToGrid","useEffect","updateGridState","window","addEventListener","removeEventListener","toggleGrid","toggleSnapToGrid","snapAllToGrid","ZoomDisplay","zoom","setZoomState","updateZoom","currentZoom","Math","round","graph_view_graph","IX","interval","setInterval","clearInterval","a_","max","min","fitToView","hasChanges","setHasChanges","checkChanges","changed","Help","lazy","Promise","resolve","then","__webpack_require__","bind","module","default","Graph","setAutomationStatus","status","error","root","document","documentElement","dataset","mdlAutomationStatus","mdlAutomationError","reportInteractiveError","action","console","alert","Root","layout","chunk_62JRHF6Z","Kd","BV","qh","path","element","ModelPane","layouts","refreshGraph","URLSearchParams","location","search","get","searchParams","setSearchParams","ok","decodeURI","helpVisible","setHelpVisible","readyGraphID","setReadyGraphID","automationKey","useRef","automationRun","viewKey","elements","Map","relations","collectRels","el","Array","isArray","relationships","rel","set","id","people","softwareSystems","containers","el1","parent","components","el2","deploymentNodes","containerInstances","item","containerId","recAddNodes","infrastructureNodes","some","getView","jg","rankDirection","automaticLayout","layoutDirection","undefined","metadata","name","description","version","groupingIDs","ref","softwareSystemId","find","p","enterprise","styles","cssClassName","tag","toLowerCase","varPrefix","background","colorToVarMap","color","stroke","elementViewKey","containerViews","candidate","lookupContainerViewKey","sub","tags","technology","addNode","encodedViewKey","encodeURIComponent","href","exportHref","url","nodeLink","properties","nodesMap","has","sourceId","warn","destinationId","routing","addEdge","vertices","level","i","sort","a","b","groupMembers","addGroup","init","parseView","useGraph","handleAutoLayout","setLayouting","useCallback","async","options","direction","opts","autoLayout","useAutoLayout","handleSave","setSaving","response","fetch","method","body","exportSVG","detail","text","trim","Error","setSaved","useSave","ViewRedirect","handleToggleHelp","handleGraphReady","handleInteractiveAutoLayout","catch","handleInteractiveSave","params","fromEntries","entries","auto","save","current","toString","run","compact","layoutOpts","includes","compactLayout","message","String","errorMessage","toggleHelp","saveLayout","handleKeyDown","shortcut","shortcuts","Yp","preventDefault","aX","t9","Jk","DE","Vy","Hd","_t","resetView","Op","hZ","OE","Gg","moveSelected","getGridSize","J8","b3","iD","uK","l8","rB","mt","useKeyboardShortcuts","handleViewChange","handleSelect","m","log","obj","JSON","parse","stringify","Suspense","fallback","data","onSelect","onReady","svgTextWrap","width","attrs","svg","createElementNS","appendChild","measure","node","setAttribute","attr","createTextNode","height","getBBox","removeChild","clean","textMeasure","maxW","ret","words","lines","currentLine","word","join","brokenParts","maxWidth","parts","currentPart","testPart","breakLongWord","newLine","size","reduce","concat","create","type","k","classList","add","use","this","setAttributeNS","d","t","textContent","textArea","fontSize","bold","x","y","anchor","txt","line","span","dy","append","rect","r","rx","ry","icon","expand","expanded","g","setPosition","insideBox","centeredBox","uncenterBox","intersectRectFull","p1","p2","box","w","h","q","q1","q2","denominator","numerator1","numerator2","result","onLine1","onLine2","segmentIntersection","intersectRect","intersectEllipse","ellCenter","nodeCenter","point","si","c","radicand_sqrt","sqrt","pos","project","atob","len","dot","cabDistance","abs","cylinderRadiusY","shapeLabelOffsetY","shape","D3Element","_el","constructor","insert","insertBefore","querySelector","bbox","rounded","shapeSvg","intersect","_ellipse","mobiledeviceportrait","shapes","roundedbox","component","dx","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","SVG_STYLES","applyStyle","setProperty","calculateDistance","textBlock","gapAfter","field","wrapped","lineHeight","defaultEdgeStyle","thickness","opacity","dashed","defaultNodeStyle","GraphData","edges","edgeVertices","groupsMap","_undo","_gridVisible","_snapToGrid","_gridSize","_skipAutoFit","exportLayout","lo","importLayout","label","link","nodeStyle","minimumHeight","contentLayout","subtitle","nodeWidth","textWidth","HORIZONTAL_PADDING","blocks","textHeight","block","VERTICAL_PADDING","layoutNodeContent","requiredHeight","nodes","from","values","fromNode","toNode","edge","to","initVertex","edgeID","input","charCodeAt","imul","fnv1a36","stableVertexID","userDeletedVertices","nodesOrGroups","group","Boolean","setNodeSelected","selected","remove","updateEdgesSel","moveNode","disableSnap","skipUndo","snapped","redrawEdges","redrawGroups","moveEdgeVertex","redrawEdge","insertEdgeVertex","isLabel","deleteEdgeVertex","index","indexOf","delete","alignTopLeft","contentBounds","calculateContentBounds","offsetX","offsetY","vertex","resetPanTransform","getZoom","zoomGroup","graphData","bb","parentElement","clientWidth","clientHeight","updatePanningOptimized","clearViewState","shouldSkipAutoFit","updatePanning","buildEdge","buildGroup","originalSvg","exportSvg","cloneNode","querySelectorAll","getAttribute","removeAttribute","exportElastic","exportWidth","padding","exportHeight","exportZoomGroup","convertStylesToCustomProperties","script","createElement","firstChild","outerHTML","fill","minX","Infinity","minY","maxX","maxY","left","right","top","bottom","centerX","centerY","approxLabelSize","full","lst","rerender","coordinates","startsWith","normalizedPoint","assign","edgeId","an","ae","labelVertex","insertPos","labelPos","fullPath","minDistance","bestSegmentIndex","distance","distanceToSegment","findOptimalLabelPosition","projectedPos","segmentStart","segmentEnd","A","B","C","D","lenSq","param","projectPointOntoSegment","projectLabelOntoSegment","selectedNodes","selectedVertices","allElements","spacing","newX","newY","setEdgeSelected","viewportWidth","viewportHeight","zoomX","zoomY","optimalZoom","finalZoom","translateX","translateY","saveViewState","saveLayoutState","restoreLayoutState","state","updateGridDisplay","dispatchEvent","CustomEvent","snappedX","snappedY","existingGrid","existingGridRect","defs","pattern","clickListener","selectListener","dragging","buildGraph","onNodeSelect","innerHTML","__data","_buildGraph","elasticEl","addCursorInteraction","setZoom","zoomG","nodesG","edgesG","groupsG","gdata","content","shapeType","nodeBorder","setBorderStyle","border","tg","buildNodeContent","Number","buildNode","labelBounds","n1","n2","position","sameEdges","spreadPos","spreadX","spreadY","unshift","firstRoutingVertex","lastRoutingVertex","calculateNodeIntersection","targetPoint","nodeShape","halfHeight","angle","atan2","cos","sin","rectX","rectY","topCurveY","bottomCurveY","ellipseY","discriminant","sqrt_d","x1","x2","radius","halfWidth","startIntersection","endIntersection","calculateEdgeVertices","labelPlacement","segment","labelIndex","findIndex","movable","adjacentSegments","longest","targetLength","sum","traversed","segmentPosition","horizontalDistance","verticalDistance","orientation","calculateLabelPlacement","bg","placement","anchors","fraction","existing","occupied","expandBox","other","flatMap","side","edgeLabelCandidate","best","score","bounds","edgeText","edgeRect","buildEdgeLabel","segments","reverse","s2","intersectPolylineBox","createEdgeSegments","cx","overlap","total","first","second","p0","topExtension","bottomExtension","hexHeight","pad","groupRect","groupText","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","Element","closest","stopPropagation","md","convertEvent","changedTouches","onMouseMoveHandler","getSelection","setSelection","setTransform","drawingDx","drawingDy","setDragging","update","onMouseMove","ex","ey","onMouseUpHandler","navigation","end","boxSelection","shiftKey","onMouseUp","onMouseDownHandler","nodeFromEvent","effectiveMode","isSelected","translateMatch","match","parseFloat","getCurrentTransformLocal","startDrawingX","startDrawingY","pt","currentPt","currentDrawingX","currentDrawingY","createElastic","onMouseDown","event","handler","addDnd","existingCleanup","__cursorInteractionCleanup","getData","gd","beforeUnloadHandler","returnValue","setDotSelected","dotEl","mouseMoveHandler","altKey","removePrjDot","keyUpHandler","Zj","_s","clickHandler","wheelHandler","delta","sign","deltaY","newZoom","setZoomCentered","keyDownHandler","bl","Ur","hU","i1","mD","F","Gn","customInteractionCleanup","handles","handle","b1","b2","scaleMatch","oldZoom","container","newTranslateX","newTranslateY","nodeText","cursor","viewStateCache","graphId","restoreViewState","xx","yy","getEffectiveSpacing","userOptions","isGroup","effectiveConfig","nodeSpacing","layerSpacing","componentSpacing","groupMultiplier","getELKOptions","baseOptions","elk","elkGraph","layoutOptions","nodeMap","elkNodes","nodeHeight","arrowPadding","nodeParentGroup","groupParent","childGroupIDs","Set","member","elkGroups","buildELKGroup","elkGroup","groupAncestors","groupID","ancestors","labelWidth","elkEdge","sources","targets","labels","sourceID","destinationID","sourceAncestors","destinationAncestors","lowestCommonGroup","layoutedGraph","extractNodes","child","processEdgesFromELK","sections","startPoint","bendPoints","bp","endPoint","originalEdge","elkLabel","midIndex","floor","v1","v2","maxCols","ceil","col","createFallbackLayout","styleTagTransform","styleTagTransform_default","setAttributes","setAttributesWithoutAttributes_default","insertBySelector_default","domAPI","styleDomAPI_default","insertStyleElement","insertStyleElement_default","injectStylesIntoStyleTag_default","locals","Timer","callback","running","timeoutId","start","stop","isRunning","RefreshConnector","static","minDelay","maxDelay","handshakeTimeout","uri","fileChangeHandler","socket","nextDelay","connectionDesired","disconnectionReason","reconnectTimer","DEFAULT_OPTIONS","handleHandshakeTimeout","attemptReconnection","connect","isSocketConnected","prepareForConnection","createWebSocket","disconnect","close","readyState","WebSocket","OPEN","onopen","handleOpen","onclose","handleClose","onmessage","handleMessage","onerror","handleError","startHandshake","scheduleReconnection","processMessage","command","handleHelloMessage","handleReloadMessage","helloMessage","protocols","LIVERELOAD_PROTOCOLS","ver","sendCommand","send","src_Root","App","setState","loading","loadData","prev","modelResponse","layoutResponse","all","statusText","json","handleFileChange","S","refreshConnector","LoadingScreen","ErrorScreen","onRetry","justifyContent","fontFamily","whiteSpace","flexDirection","backgroundColor","borderRadius","getElementById","client","createRoot","render","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__","react_jsx_runtime__WEBPACK_IMPORTED_MODULE_2__","colSpan","matches","SA","ke","checkKey","aShortcut","bShortcut","aModifiers","bModifiers","aCount","isMac","navigator","userAgentData","userAgent","KeyboardEvent","metaKey","test","ctrlKey","___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","names","mappings","sourcesContent","sourceRoot","__WEBPACK_DEFAULT_EXPORT__"],"sourceRoot":""} \ No newline at end of file diff --git a/cmd/mdl/webapp/src/index.tsx b/cmd/mdl/webapp/src/index.tsx index 3a9bb957..85909560 100644 --- a/cmd/mdl/webapp/src/index.tsx +++ b/cmd/mdl/webapp/src/index.tsx @@ -73,16 +73,20 @@ const App: React.FC = () => { }; useEffect(() => { - // Setup refresh connector - const refreshConnector = new RefreshConnector(handleFileChange); - refreshConnector.connect(); + const params = new URLSearchParams(document.location.search); + // Headless rendering has no watcher and must not receive reloads from an + // unrelated interactive editor using the shared LiveReload port. + const auto = params.get('auto'); + const save = params.get('save'); + const automated = auto === '1' || auto === 'true' || save === '1' || save === 'true'; + const refreshConnector = automated ? null : new RefreshConnector(handleFileChange); + refreshConnector?.connect(); // Initial data load loadData(); - // Cleanup function return () => { - // RefreshConnector cleanup would go here if it had a disconnect method + refreshConnector?.disconnect(); }; }, []);