From 6c0cb15ddc461228d07893713f23a01b4498090b Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sun, 27 Sep 2026 16:49:24 +1000 Subject: [PATCH 1/6] =?UTF-8?q?canvas:=20declutter=20the=20toolbar=20?= =?UTF-8?q?=E2=80=94=20grouped,=20labelled,=20two-row=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top bar crammed brand, search, an unbounded subject-area legend, counts, diagnostics, and ten identical Unicode-glyph buttons into one flex row. Panel-openers looked the same as view actions, nothing was legible without hovering, and the legend shoved everything as it grew. Reorganised into information zones: - Two rows: row 1 is identity + search + live status (counts + validity pill clustered on the right); row 2 is the actions. - Actions grouped into bordered segments with quiet tags — Panels (Ontology/Layers/Changes/Decisions, icon+label since they are the least guessable), View (types/collapse/layout/fit, icon-only + tooltip), and Data (refresh, Export/Import). The grouping itself teaches which controls open a panel vs change the view vs move data. - The subject-area colour key moves to a thin reference strip below the toolbar, so it can never push the controls and wraps safely for many areas. - Real inline SVG icons (new canvas/src/icons.tsx, no new dependency) replace the Unicode glyphs, which rendered inconsistently across OSes. Wraps gracefully at narrow VS Code webview widths where the old single row overflowed. Verified: typecheck + build clean, panel toggles and layout menu work, no console errors, legible at 640px. --- canvas/src/TopBar.tsx | 264 ++++++++++++------ canvas/src/icons.tsx | 136 +++++++++ canvas/src/styles.css | 71 +++-- ...m-DfdI4kly.js => ModelDiagram-C9iVTvG9.js} | 2 +- .../mdl_server/static/assets/main-CZFuiW_s.js | 2 - .../mdl_server/static/assets/main-Dra0qONU.js | 2 + .../{sme-Ji3JtE2B.js => sme-BuG6X873.js} | 4 +- .../static/assets/styles-BG76mhX4.css | 1 + .../static/assets/styles-BJW39REh.css | 1 - ...{styles-CIm9mWtD.js => styles-Cn8AFsR1.js} | 0 .../server/src/mdl_server/static/index.html | 6 +- .../server/src/mdl_server/static/sme.html | 6 +- 12 files changed, 380 insertions(+), 115 deletions(-) create mode 100644 canvas/src/icons.tsx rename packages/server/src/mdl_server/static/assets/{ModelDiagram-DfdI4kly.js => ModelDiagram-C9iVTvG9.js} (97%) delete mode 100644 packages/server/src/mdl_server/static/assets/main-CZFuiW_s.js create mode 100644 packages/server/src/mdl_server/static/assets/main-Dra0qONU.js rename packages/server/src/mdl_server/static/assets/{sme-Ji3JtE2B.js => sme-BuG6X873.js} (99%) create mode 100644 packages/server/src/mdl_server/static/assets/styles-BG76mhX4.css delete mode 100644 packages/server/src/mdl_server/static/assets/styles-BJW39REh.css rename packages/server/src/mdl_server/static/assets/{styles-CIm9mWtD.js => styles-Cn8AFsR1.js} (100%) diff --git a/canvas/src/TopBar.tsx b/canvas/src/TopBar.tsx index 05d6e96..3f03001 100644 --- a/canvas/src/TopBar.tsx +++ b/canvas/src/TopBar.tsx @@ -1,5 +1,23 @@ import { useEffect, useRef, useState } from "react"; import type { Exec } from "./exec"; +import { + IconCaret, + IconChanges, + IconCheck, + IconCollapse, + IconDecisions, + IconEntity, + IconError, + IconFit, + IconLayers, + IconLayout, + IconOntology, + IconPlus, + IconRefresh, + IconRelationship, + IconTypes, + IconWarn, +} from "./icons"; import type { LayoutMode } from "./layout"; import type { PanelTab } from "./SidePanel"; import { ImportExportMenu } from "./sme/ImportExportMenu"; @@ -59,109 +77,175 @@ export function TopBar({ const errors = diagnostics?.items.filter((d) => d.severity === "error").length ?? 0; const warnings = diagnostics?.items.filter((d) => d.severity === "warning").length ?? 0; - const tabBtn = (tab: PanelTab, label: string, title: string, badge?: boolean) => ( + // A panel toggle carries an icon + label (the four least-guessable, most-used tools). + const panelBtn = ( + tab: PanelTab, + icon: React.ReactNode, + label: string, + title: string, + badge?: boolean, + ) => ( ); return (
-
- {"◮"} - Modelith - {doc.project.name} - {readOnly && read-only} -
+ {/* Row 1 — identity, search, live status */} +
+
+ {"◮"} + Modelith + {doc.project.name} + {readOnly && read-only} +
- onQuery(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") onSubmitQuery(); - }} - id="mdl-search" - /> + onQuery(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") onSubmitQuery(); + }} + id="mdl-search" + /> -
- {doc.subject_areas.map((sa) => ( - - - {sa.name} +
+ + + {doc.counts.entities} - ))} -
- -
- {doc.counts.entities} ⬛ - {doc.counts.relationships} ⤳ - {diagnostics && ( - `${d.code} ${d.message}`).join("\n") || "model valid"} - > - {errors ? `${errors} ✗` : warnings ? `${warnings} ⚠` : "✓ valid"} + + + {doc.counts.relationships} - )} + {diagnostics && ( + `${d.code} ${d.message}`).join("\n") || "model valid" + } + > + {errors ? ( + <> + + {errors} + + ) : warnings ? ( + <> + + {warnings} + + ) : ( + <> + + valid + + )} + + )} +
-
+ {/* Row 2 — actions, grouped by what they do */} +
{!readOnly && ( )} - {tabBtn("ontology", "⬡", "Ontology browser")} - {tabBtn("layers", "≣", "Four-layer stack & coverage")} - {tabBtn("changes", "±", dirty ? "Uncommitted changes!" : "Changes", dirty)} - {tabBtn("decisions", "⚖", "Decision ledger")} - - - - - - - - {/* Interchange: export the model / import SQL DDL, Mermaid, JSON Schema. - Direct-write here (the engineer canvas has no review screen), so the - import applies to the working tree and the shell refreshes. */} - + +
+ Panels + {panelBtn("ontology", , "Ontology", "Ontology browser")} + {panelBtn("layers", , "Layers", "Four-layer stack & coverage")} + {panelBtn( + "changes", + , + "Changes", + dirty ? "Uncommitted changes" : "Changes", + dirty, + )} + {panelBtn("decisions", , "Decisions", "Decision ledger")} +
+ +
+ View + + + + +
+ +
+ Data + + {/* Interchange: export the model / import SQL DDL, Mermaid, JSON Schema. + Direct-write here (the engineer canvas has no review screen), so the + import applies to the working tree and the shell refreshes. The menu + renders its own Export / Import buttons in the toolbar's grp-btn style. */} + +
+ + {/* Reference strip — subject-area colour key (data, not a control) */} + {doc.subject_areas.length > 0 && ( +
+ Subject areas + {doc.subject_areas.map((sa) => ( + + + {sa.name} + + ))} +
+ )}
); } @@ -189,15 +273,21 @@ function LayoutPicker({ onRelayout }: { onRelayout: (mode?: LayoutMode) => void return (
- {open && (
diff --git a/canvas/src/icons.tsx b/canvas/src/icons.tsx new file mode 100644 index 0000000..55c7717 --- /dev/null +++ b/canvas/src/icons.tsx @@ -0,0 +1,136 @@ +// Inline SVG icons for the canvas toolbar. No icon-font dependency: these ship in the +// bundle as tiny stroke paths, render crisply on every OS (the old Unicode glyphs did +// not), and inherit `currentColor` + size from their button. 16px default, 1.6 stroke. + +type IconProps = { size?: number }; + +function Svg({ size = 16, children }: IconProps & { children: React.ReactNode }) { + return ( + + ); +} + +// --- create --------------------------------------------------------------------- +export const IconPlus = (p: IconProps) => ( + + + +); + +// --- panels --------------------------------------------------------------------- +export const IconOntology = (p: IconProps) => ( + + + +); +export const IconLayers = (p: IconProps) => ( + + + + + +); +export const IconChanges = (p: IconProps) => ( + + + + + + +); +export const IconDecisions = (p: IconProps) => ( + + + + + +); + +// --- view ----------------------------------------------------------------------- +export const IconTypes = (p: IconProps) => ( + + + +); +export const IconCollapse = (p: IconProps) => ( + + + + +); +export const IconLayout = (p: IconProps) => ( + + + + + + +); +export const IconFit = (p: IconProps) => ( + + + +); +export const IconCaret = (p: IconProps) => ( + + + +); + +// --- data ----------------------------------------------------------------------- +export const IconRefresh = (p: IconProps) => ( + + + + +); +export const IconInterchange = (p: IconProps) => ( + + + +); + +// --- status --------------------------------------------------------------------- +export const IconEntity = (p: IconProps) => ( + + + + +); +export const IconRelationship = (p: IconProps) => ( + + + + +); +export const IconCheck = (p: IconProps) => ( + + + + +); +export const IconWarn = (p: IconProps) => ( + + + + +); +export const IconError = (p: IconProps) => ( + + + + +); diff --git a/canvas/src/styles.css b/canvas/src/styles.css index 04090bc..fd449f0 100644 --- a/canvas/src/styles.css +++ b/canvas/src/styles.css @@ -29,40 +29,78 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); } /* ---------- top bar ---------- */ .topbar { - display: flex; align-items: center; gap: 16px; - padding: 10px 16px; background: var(--bg-raised); + display: flex; flex-direction: column; + padding: 8px 16px 0; background: var(--bg-raised); border-bottom: 1px solid var(--border); z-index: 10; } +/* Row 1 (identity + search + status) and Row 2 (grouped actions). Splitting the two + stops the brand, search, status, and 10 controls from fighting for one flex row. */ +.topbar-row { display: flex; align-items: center; gap: 14px; } +.topbar-row.context { padding-bottom: 8px; } +.topbar-row.actions { gap: 10px; padding-bottom: 8px; flex-wrap: wrap; } .brand { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; } .logo { color: var(--accent); font-size: 18px; } .brand-name { font-weight: 700; letter-spacing: 0.02em; } .project-name { color: var(--text-dim); font-size: 13px; } .search { - flex: 0 1 340px; padding: 7px 12px; border-radius: 8px; + flex: 0 1 300px; margin-left: auto; padding: 7px 12px; border-radius: 8px; border: 1px solid var(--border); background: var(--bg-card); color: var(--text); font-size: 13px; outline: none; } .search:focus { border-color: var(--accent); } -.legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--text-dim); } -.legend-item { display: inline-flex; align-items: center; gap: 5px; } -.swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; } -.stats { margin-left: auto; display: flex; gap: 12px; font-size: 12px; color: var(--text-dim); } -.diag-chip { padding: 2px 8px; border-radius: 99px; font-weight: 600; } +.stats { display: flex; align-items: center; gap: 12px; font-size: 12px; color: var(--text-dim); } +.stat-item { display: inline-flex; align-items: center; gap: 5px; } +.stat-item svg { color: var(--text-dim); } +.diag-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 99px; font-weight: 600; } .diag-chip.ok { background: rgba(52, 211, 153, 0.15); color: var(--ok); } .diag-chip.warn { background: rgba(251, 191, 36, 0.15); color: var(--warn); } .diag-chip.err { background: rgba(248, 113, 113, 0.15); color: var(--err); } -.actions { display: flex; gap: 6px; } + +/* Grouped action segments — a bordered container with a quiet tag, so a panel-opener + never looks like a view action. Each group's buttons sit flush inside it. */ +.btn-group { + display: inline-flex; align-items: center; gap: 2px; + background: var(--bg-card); border: 1px solid var(--border); border-radius: 9px; padding: 2px; +} +.grp-tag { + font-size: 10px; color: var(--text-dim); opacity: 0.75; + padding: 0 6px 0 4px; letter-spacing: 0.03em; text-transform: none; white-space: nowrap; +} +.grp-btn { + display: inline-flex; align-items: center; gap: 5px; + padding: 5px 9px; border-radius: 6px; border: none; background: transparent; + color: var(--text-dim); cursor: pointer; font-size: 12px; white-space: nowrap; +} +.grp-btn.icon-only { padding: 5px 7px; } +.grp-btn:hover { color: var(--text); background: var(--bg-hover, rgba(255,255,255,0.05)); } +.grp-btn.active { color: var(--accent); } +.grp-btn.active svg { color: var(--accent); } +.grp-badge { width: 6px; height: 6px; border-radius: 50%; background: var(--warn); display: inline-block; margin-left: 1px; } + +/* The primary "New entity" button stands apart from the groups. */ .tool-btn { - padding: 6px 10px; border-radius: 8px; border: 1px solid var(--border); - background: var(--bg-card); color: var(--text-dim); cursor: pointer; font-size: 13px; + display: inline-flex; align-items: center; gap: 5px; + padding: 6px 11px; border-radius: 8px; border: 1px solid var(--border); + background: var(--bg-card); color: var(--text-dim); cursor: pointer; font-size: 12px; } .tool-btn:hover { color: var(--text); border-color: var(--border-strong); } + +/* Reference strip — the subject-area colour key, moved OUT of the action row so it can + never shove the toolbar; it wraps safely for a model with many areas. */ +.sa-legend { + display: flex; align-items: center; gap: 12px; flex-wrap: wrap; + padding: 6px 0 8px; font-size: 11px; color: var(--text-dim); + border-top: 1px solid var(--border); +} +.sa-legend-label { color: var(--text-dim); opacity: 0.7; } +.legend-item { display: inline-flex; align-items: center; gap: 5px; } +.swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; } .tool-btn.active { color: var(--accent); border-color: var(--accent); } /* layout style picker (split button: icon + caret + menu) */ -.layout-picker { position: relative; display: inline-flex; gap: 2px; } -.layout-picker .caret { padding: 6px 6px; font-size: 10px; } +.layout-picker { position: relative; display: inline-flex; gap: 0; } +.layout-picker .caret { padding: 5px 4px; } .layout-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 20; background: var(--bg-raised); border: 1px solid var(--border-strong); @@ -297,9 +335,10 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); } .physical-name-input { font-size: 12px; color: var(--text-dim); } .physical-col { display: block; font-size: 10px; color: var(--text-dim); font-family: var(--mono, monospace); } .physical-col-input { display: block; font-size: 10px; color: var(--text-dim); } -.tool-btn.primary { color: var(--accent); border-color: rgba(224,166,58,0.4); font-weight: 600; } -.tool-btn.badged::after { content: "●"; color: var(--warn); margin-left: 4px; font-size: 8px; vertical-align: super; } -.divider { width: 1px; background: var(--border); margin: 0 4px; align-self: stretch; } +.tool-btn.primary { + color: var(--accent); border-color: rgba(224,166,58,0.4); font-weight: 600; + background: rgba(224,166,58,0.1); +} /* modals */ .modal-backdrop { diff --git a/packages/server/src/mdl_server/static/assets/ModelDiagram-DfdI4kly.js b/packages/server/src/mdl_server/static/assets/ModelDiagram-C9iVTvG9.js similarity index 97% rename from packages/server/src/mdl_server/static/assets/ModelDiagram-DfdI4kly.js rename to packages/server/src/mdl_server/static/assets/ModelDiagram-C9iVTvG9.js index d9ade3e..5575237 100644 --- a/packages/server/src/mdl_server/static/assets/ModelDiagram-DfdI4kly.js +++ b/packages/server/src/mdl_server/static/assets/ModelDiagram-C9iVTvG9.js @@ -1 +1 @@ -import{r,j as s}from"./client-DOmNpTln.js";import{f as T,t as _,u as O,v as R,E as D,B as P,C as z,w as A}from"./styles-CIm9mWtD.js";const V={entity:D},$={relationship:R},l={breaking:"#f87171",additive:"#34d399",cosmetic:"#fcd34d"},N=["#5eead4","#fcd34d","#a78bfa","#fb923c","#38bdf8","#f472b6","#a3e635"],d="#64748b";function Y({subjectArea:u,severityByUlid:a,focusUlid:m,onSelect:c}){const[t,g]=r.useState(null),[f,w]=r.useState(null),[h,E]=r.useState(!0);r.useEffect(()=>{g(null),T(u).then(g).catch(n=>w(String(n)))},[u]);const{nodes:k,edges:M}=r.useMemo(()=>{if(!t)return{nodes:[],edges:[]};const n=new Map;t.subject_areas.forEach(e=>{let o=2166136261;for(let i=0;i[e.id,e])),b=a&&a.size>0,C=t.entities.map(e=>{var j;const o=(a==null?void 0:a.get(e.id))??(e.conceptual?a==null?void 0:a.get(e.conceptual.id):void 0),i=b?o?l[o]??d:d:(j=e.conceptual)!=null&&j.subject_area?n.get(e.conceptual.subject_area.id)??d:d;return{id:e.id,type:"entity",position:{x:0,y:0},data:{entity:e,color:i,dimmed:!!(b&&!o),highlighted:e.id===m,showTypes:h}}}),x=t.relationships.map(e=>({id:e.id,source:e.from.entity,target:e.to.entity,type:"relationship",data:{relationship:e,dimmed:!1}}));return{nodes:_(C,x,p),edges:x}},[t,a,m,h]);return f?s.jsx("p",{className:"sme-splash error",children:f}):t?t.entities.length?s.jsxs("div",{className:"erd-wrap",children:[s.jsxs("div",{className:"erd-bar",children:[s.jsxs("span",{className:"erd-count",children:[t.entities.length," entities · ",t.relationships.length," relationships",t.scope&&" · scoped"]}),s.jsxs("label",{className:"erd-toggle",children:[s.jsx("input",{type:"checkbox",checked:h,onChange:n=>E(n.target.checked)}),"show types"]}),a&&a.size>0&&s.jsxs("span",{className:"erd-legend",children:[s.jsx("span",{className:"sw",style:{background:l.breaking}})," breaking",s.jsx("span",{className:"sw",style:{background:l.additive}})," added",s.jsx("span",{className:"sw",style:{background:l.cosmetic}})," changed",s.jsx("span",{className:"sw",style:{background:d}})," unchanged"]})]}),s.jsx("div",{className:"erd-canvas",children:s.jsxs(O,{nodes:k,edges:M,nodeTypes:V,edgeTypes:$,fitView:!0,minZoom:.1,nodesConnectable:!1,onNodeClick:(n,p)=>c==null?void 0:c(p.id),onPaneClick:()=>c==null?void 0:c(null),onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},children:[s.jsx(P,{gap:16,color:"#1f2937"}),s.jsx(z,{showInteractive:!1}),s.jsx(A,{pannable:!0,zoomable:!0})]})})]}):s.jsx("p",{className:"sme-placeholder",children:"Nothing to draw — this subject area has no entities in it yet."}):s.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"})}export{Y as ModelDiagram}; +import{r,j as s}from"./client-DOmNpTln.js";import{f as T,t as _,u as O,v as R,E as D,B as P,C as z,w as A}from"./styles-Cn8AFsR1.js";const V={entity:D},$={relationship:R},l={breaking:"#f87171",additive:"#34d399",cosmetic:"#fcd34d"},N=["#5eead4","#fcd34d","#a78bfa","#fb923c","#38bdf8","#f472b6","#a3e635"],d="#64748b";function Y({subjectArea:u,severityByUlid:a,focusUlid:m,onSelect:c}){const[t,g]=r.useState(null),[f,w]=r.useState(null),[h,E]=r.useState(!0);r.useEffect(()=>{g(null),T(u).then(g).catch(n=>w(String(n)))},[u]);const{nodes:k,edges:M}=r.useMemo(()=>{if(!t)return{nodes:[],edges:[]};const n=new Map;t.subject_areas.forEach(e=>{let o=2166136261;for(let i=0;i[e.id,e])),b=a&&a.size>0,C=t.entities.map(e=>{var j;const o=(a==null?void 0:a.get(e.id))??(e.conceptual?a==null?void 0:a.get(e.conceptual.id):void 0),i=b?o?l[o]??d:d:(j=e.conceptual)!=null&&j.subject_area?n.get(e.conceptual.subject_area.id)??d:d;return{id:e.id,type:"entity",position:{x:0,y:0},data:{entity:e,color:i,dimmed:!!(b&&!o),highlighted:e.id===m,showTypes:h}}}),x=t.relationships.map(e=>({id:e.id,source:e.from.entity,target:e.to.entity,type:"relationship",data:{relationship:e,dimmed:!1}}));return{nodes:_(C,x,p),edges:x}},[t,a,m,h]);return f?s.jsx("p",{className:"sme-splash error",children:f}):t?t.entities.length?s.jsxs("div",{className:"erd-wrap",children:[s.jsxs("div",{className:"erd-bar",children:[s.jsxs("span",{className:"erd-count",children:[t.entities.length," entities · ",t.relationships.length," relationships",t.scope&&" · scoped"]}),s.jsxs("label",{className:"erd-toggle",children:[s.jsx("input",{type:"checkbox",checked:h,onChange:n=>E(n.target.checked)}),"show types"]}),a&&a.size>0&&s.jsxs("span",{className:"erd-legend",children:[s.jsx("span",{className:"sw",style:{background:l.breaking}})," breaking",s.jsx("span",{className:"sw",style:{background:l.additive}})," added",s.jsx("span",{className:"sw",style:{background:l.cosmetic}})," changed",s.jsx("span",{className:"sw",style:{background:d}})," unchanged"]})]}),s.jsx("div",{className:"erd-canvas",children:s.jsxs(O,{nodes:k,edges:M,nodeTypes:V,edgeTypes:$,fitView:!0,minZoom:.1,nodesConnectable:!1,onNodeClick:(n,p)=>c==null?void 0:c(p.id),onPaneClick:()=>c==null?void 0:c(null),onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},children:[s.jsx(P,{gap:16,color:"#1f2937"}),s.jsx(z,{showInteractive:!1}),s.jsx(A,{pannable:!0,zoomable:!0})]})})]}):s.jsx("p",{className:"sme-placeholder",children:"Nothing to draw — this subject area has no entities in it yet."}):s.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"})}export{Y as ModelDiagram}; diff --git a/packages/server/src/mdl_server/static/assets/main-CZFuiW_s.js b/packages/server/src/mdl_server/static/assets/main-CZFuiW_s.js deleted file mode 100644 index b1e4d95..0000000 --- a/packages/server/src/mdl_server/static/assets/main-CZFuiW_s.js +++ /dev/null @@ -1,2 +0,0 @@ -import{j as e,r as a,c as Y,R as Z}from"./client-DOmNpTln.js";import{I as O,R as ee,P as U,f as _,a as V,g as z,s as te,A as ne,b as se,M as ae,S as re}from"./styles-CIm9mWtD.js";function oe(n){return{canEdit:!n,mode:"direct",canArbitrate:!n,canCommit:!n}}function le({doc:n,diagnostics:l,query:x,onQuery:u,onSubmitQuery:m,showTypes:d,onToggleTypes:h,collapseDetail:j,onToggleCollapse:y,onFitView:T,onRelayout:R,onRefresh:P,saColors:I,readOnly:g,dirty:L,panelTab:A,onPanelTab:B,onNewEntity:M,exec:b,onImported:E,onImportBatch:F,openImport:v}){const C=(l==null?void 0:l.items.filter(o=>o.severity==="error").length)??0,w=(l==null?void 0:l.items.filter(o=>o.severity==="warning").length)??0,i=(o,N,K,f)=>e.jsx("button",{className:"tool-btn"+(A===o?" active":"")+(f?" badged":""),onClick:()=>B(o),title:K,children:N});return e.jsxs("header",{className:"topbar",children:[e.jsxs("div",{className:"brand",children:[e.jsx("span",{className:"logo",children:"◮"}),e.jsx("span",{className:"brand-name",children:"Modelith"}),e.jsx("span",{className:"project-name",children:n.project.name}),g&&e.jsx("span",{className:"chip",children:"read-only"})]}),e.jsx("input",{className:"search",type:"search",placeholder:"Search entities & attributes… ( / , Enter jumps )",value:x,onChange:o=>u(o.target.value),onKeyDown:o=>{o.key==="Enter"&&m()},id:"mdl-search"}),e.jsx("div",{className:"legend",children:n.subject_areas.map(o=>e.jsxs("span",{className:"legend-item",children:[e.jsx("span",{className:"swatch",style:{background:I.get(o.id)}}),o.name]},o.id))}),e.jsxs("div",{className:"stats",children:[e.jsxs("span",{title:"entities",children:[n.counts.entities," ⬛"]}),e.jsxs("span",{title:"relationships",children:[n.counts.relationships," ⤳"]}),l&&e.jsx("span",{className:"diag-chip"+(C?" err":w?" warn":" ok"),title:l.items.map(o=>`${o.code} ${o.message}`).join(` -`)||"model valid",children:C?`${C} ✗`:w?`${w} ⚠`:"✓ valid"})]}),e.jsxs("div",{className:"actions",children:[!g&&e.jsx("button",{className:"tool-btn primary",onClick:M,title:"New entity (n)",children:"+ Entity"}),i("ontology","⬡","Ontology browser"),i("layers","≣","Four-layer stack & coverage"),i("changes","±",L?"Uncommitted changes!":"Changes",L),i("decisions","⚖","Decision ledger"),e.jsx("span",{className:"divider"}),e.jsx("button",{className:"tool-btn"+(d?" active":""),onClick:h,title:"Toggle data types",children:"{T}"}),e.jsx("button",{className:"tool-btn"+(j?" active":""),onClick:y,title:"Collapse to keys (PK + FK only) — readable for large models",children:"⇕"}),e.jsx("button",{className:"tool-btn",onClick:P,title:"Refresh from disk (picks up terminal / external edits)",children:"⟳"}),e.jsx(ce,{onRelayout:R}),e.jsx("button",{className:"tool-btn",onClick:T,title:"Fit view",children:"⛶"}),e.jsx("span",{className:"divider"}),e.jsx(O,{exec:b,canEdit:!g,onImported:E,submitLabel:"Import",buttonClass:"tool-btn",applyBatch:F,openImport:v})]})]})}function ce({onRelayout:n}){const[l,x]=a.useState(!1),u=a.useRef(null);a.useEffect(()=>{if(!l)return;const d=h=>{u.current&&!u.current.contains(h.target)&&x(!1)};return document.addEventListener("mousedown",d),()=>document.removeEventListener("mousedown",d)},[l]);const m=d=>{n(d),x(!1)};return e.jsxs("div",{className:"layout-picker",ref:u,children:[e.jsx("button",{className:"tool-btn",onClick:()=>n("auto"),title:"Auto-layout",children:"⌗"}),e.jsx("button",{className:"tool-btn caret"+(l?" active":""),onClick:()=>x(d=>!d),title:"Layout style",children:"▾"}),l&&e.jsxs("div",{className:"layout-menu",role:"menu",children:[e.jsx("button",{onClick:()=>m("auto"),children:"Auto (by size)"}),e.jsx("button",{onClick:()=>m("hierarchical"),children:"Hierarchical"}),e.jsx("button",{onClick:()=>m("grid"),children:"Grid"})]})]})}function ie(){const[n,l]=a.useState(null),[x,u]=a.useState(null),[m,d]=a.useState(null),[h,j]=a.useState(null),[y,T]=a.useState(""),[R,P]=a.useState(!0),[I,g]=a.useState(!1),[L,A]=a.useState(()=>new Set),[B,M]=a.useState(null),[b,E]=a.useState(null),[F,v]=a.useState(!1),[C,w]=a.useState(0),i=a.useRef(null),o=a.useRef(""),N=a.useRef(!1),K=a.useMemo(()=>{const t=new Map;return n==null||n.subject_areas.forEach((r,s)=>t.set(r.id,U[s%U.length])),t},[n]),f=(n==null?void 0:n.read_only)??!0,G=a.useMemo(()=>oe(f),[f]),D=a.useMemo(()=>new URLSearchParams(window.location.search),[]),k=D.get("minimal")==="1";a.useEffect(()=>{k&&g(!0)},[k]);const H=a.useCallback(t=>{A(r=>{const s=new Set(r);return s.has(t)?s.delete(t):s.add(t),s})},[]),X=D.get("import")==="1",$=a.useRef(!1),p=a.useCallback(()=>{_().then(l).catch(t=>d(String(t))),V().then(u).catch(()=>u(null)),z().then(t=>v(!!(t.git&&!t.clean))).catch(()=>v(!1)),w(t=>t+1)},[]);a.useEffect(p,[p]),a.useEffect(()=>{const t=setInterval(()=>{_().then(r=>{l(s=>s&&r.fingerprint===s.fingerprint?s:(N.current||(o.current=r.fingerprint),k||(V().then(u).catch(()=>u(null)),z().then(c=>v(!!(c.git&&!c.clean))).catch(()=>v(!1))),w(c=>c+1),r))}).catch(()=>{})},3e3);return()=>clearInterval(t)},[k]),a.useEffect(()=>{n!=null&&n.fingerprint&&(o.current=n.fingerprint)},[n]);const q=a.useCallback(async(t,r)=>{if(n){N.current=!0;try{const s=await te(t,r,o.current||n.fingerprint);return s!=null&&s.fingerprint&&(o.current=s.fingerprint),p(),s}catch(s){throw s instanceof ne&&s.status===409?(j("Model changed on disk — view refreshed, please retry."),p()):j(s instanceof Error?s.message:String(s)),s}finally{N.current=!1}}},[n,p]);a.useEffect(()=>{if(!h)return;const t=setTimeout(()=>j(null),5e3);return()=>clearTimeout(t)},[h]),a.useEffect(()=>{if(!n||$.current)return;const t=D.get("focus");if(!t)return;const r=n.entities.find(s=>{var c;return s.name===t||((c=s.conceptual)==null?void 0:c.name)===t});r&&($.current=!0,requestAnimationFrame(()=>{var s;return(s=i.current)==null?void 0:s.focusEntity(r.id)}))},[n,D]);const J=a.useCallback(()=>{var s;if(!n)return;const t=y.trim().toLowerCase();if(!t)return;const r=n.entities.find(c=>{var S;return(c.name.toLowerCase()+" "+(((S=c.conceptual)==null?void 0:S.name.toLowerCase())??"")+" "+c.attributes.map(W=>W.name.toLowerCase()).join(" ")).includes(t)});r&&((s=i.current)==null||s.focusEntity(r.id))},[n,y]);return a.useEffect(()=>{const t=r=>{var c,Q,S;const s=(c=document.activeElement)==null?void 0:c.tagName;s==="INPUT"||s==="TEXTAREA"||s==="SELECT"||(r.key==="/"&&(r.preventDefault(),(Q=document.getElementById("mdl-search"))==null||Q.focus()),r.key==="n"&&!f&&((S=i.current)==null||S.newEntity()),r.key==="Escape"&&(M(null),T(""),E(null)))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[f]),m?e.jsxs("div",{className:"splash error",children:[e.jsxs("h1",{children:["◮"," Modelith"]}),e.jsx("p",{children:"Could not load the model:"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl serve"})," pointed at a model directory?"]})]}):n?e.jsxs("div",{className:"app",children:[!k&&e.jsx(le,{doc:n,diagnostics:x,query:y,onQuery:T,onSubmitQuery:J,showTypes:R,onToggleTypes:()=>P(t=>!t),collapseDetail:I,onToggleCollapse:()=>g(t=>!t),onFitView:()=>{var t;return(t=i.current)==null?void 0:t.fitView()},onRelayout:t=>{var r;return(r=i.current)==null?void 0:r.relayout(t)},onRefresh:p,saColors:K,readOnly:f,dirty:F,panelTab:b,onPanelTab:t=>E(r=>r===t?null:t),onNewEntity:()=>{var t;return(t=i.current)==null?void 0:t.newEntity()},exec:q,onImported:p,onImportBatch:se,openImport:X}),e.jsxs("div",{className:"canvas-wrap",children:[e.jsx(ae,{doc:n,caps:G,exec:q,selectedId:b?null:B,onSelect:M,query:y,showTypes:R,collapseDetail:I,expandedEntities:L,onToggleExpand:H,onError:j,handleRef:i}),b&&e.jsx(re,{tab:b,onClose:()=>E(null),readOnly:f,refreshKey:C,onModelChanged:p,onFocusEntity:t=>{var r;E(null),(r=i.current)==null||r.focusEntity(t)}}),h&&e.jsx("div",{className:"toast",children:h})]})]}):e.jsxs("div",{className:"splash",children:["◮"," loading model…"]})}function ue(){return e.jsx(ee,{children:e.jsx(ie,{})})}Y.createRoot(document.getElementById("root")).render(e.jsx(Z.StrictMode,{children:e.jsx(ue,{})})); diff --git a/packages/server/src/mdl_server/static/assets/main-Dra0qONU.js b/packages/server/src/mdl_server/static/assets/main-Dra0qONU.js new file mode 100644 index 0000000..736c674 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/main-Dra0qONU.js @@ -0,0 +1,2 @@ +import{j as e,r as a,c as Y,R as Z}from"./client-DOmNpTln.js";import{I as ee,R as se,P as q,f as U,a as W,g as $,s as te,A as ne,b as ae,M as re,S as ce}from"./styles-Cn8AFsR1.js";function le(s){return{canEdit:!s,mode:"direct",canArbitrate:!s,canCommit:!s}}function i({size:s=16,children:l}){return e.jsx("svg",{width:s,height:s,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.7,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false",children:l})}const ie=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M12 5v14M5 12h14"})}),oe=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M12 3l7.5 4.5v9L12 21l-7.5-4.5v-9L12 3z"})}),de=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M12 3l9 5-9 5-9-5 9-5z"}),e.jsx("path",{d:"M3 12l9 5 9-5"}),e.jsx("path",{d:"M3 16l9 5 9-5"})]}),he=s=>e.jsxs(i,{...s,children:[e.jsx("circle",{cx:"6",cy:"6",r:"2.4"}),e.jsx("circle",{cx:"6",cy:"18",r:"2.4"}),e.jsx("circle",{cx:"18",cy:"18",r:"2.4"}),e.jsx("path",{d:"M6 8.4v7.2M8.4 6H14a3 3 0 0 1 3 3v6.6"})]}),ue=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M12 4v16M6 8h12"}),e.jsx("path",{d:"M6 8l-2.5 5a2.5 2.5 0 0 0 5 0L6 8zM18 8l-2.5 5a2.5 2.5 0 0 0 5 0L18 8z"}),e.jsx("path",{d:"M9 20h6"})]}),xe=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M8 7l-4 5 4 5M16 7l4 5-4 5"})}),pe=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M8 8l4-4 4 4M8 16l4 4 4-4"}),e.jsx("path",{d:"M4 12h16"})]}),je=s=>e.jsxs(i,{...s,children:[e.jsx("rect",{x:"3",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"14",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"3",y:"15",width:"7",height:"5",rx:"1"}),e.jsx("rect",{x:"14",y:"15",width:"7",height:"5",rx:"1"})]}),me=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M4 8V5a1 1 0 0 1 1-1h3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3"})}),fe=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M6 9l6 6 6-6"})}),ge=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M20 11a8 8 0 1 0-.5 4"}),e.jsx("path",{d:"M20 4v5h-5"})]}),ye=s=>e.jsxs(i,{...s,children:[e.jsx("rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}),e.jsx("path",{d:"M4 9h16"})]}),be=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M4 7h9a4 4 0 0 1 4 4v3"}),e.jsx("path",{d:"M14 11l3 3 3-3"})]}),ve=s=>e.jsxs(i,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M8.5 12.5l2.5 2.5 4.5-5"})]}),we=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M12 4l9 16H3l9-16z"}),e.jsx("path",{d:"M12 10v4M12 17.5v.5"})]}),Ee=s=>e.jsxs(i,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M15 9l-6 6M9 9l6 6"})]});function Me({doc:s,diagnostics:l,query:f,onQuery:h,onSubmitQuery:x,showTypes:u,onToggleTypes:p,collapseDetail:g,onToggleCollapse:y,onFitView:S,onRelayout:L,onRefresh:F,saColors:R,readOnly:b,dirty:T,panelTab:P,onPanelTab:A,onNewEntity:z,exec:v,onImported:w,onImportBatch:B,openImport:E}){const N=(l==null?void 0:l.items.filter(c=>c.severity==="error").length)??0,M=(l==null?void 0:l.items.filter(c=>c.severity==="warning").length)??0,d=(c,C,K,j,V)=>e.jsxs("button",{className:"grp-btn"+(P===c?" active":""),onClick:()=>A(c),title:j,children:[C,e.jsx("span",{children:K}),V&&e.jsx("span",{className:"grp-badge","aria-label":"uncommitted changes"})]});return e.jsxs("header",{className:"topbar",children:[e.jsxs("div",{className:"topbar-row context",children:[e.jsxs("div",{className:"brand",children:[e.jsx("span",{className:"logo",children:"◮"}),e.jsx("span",{className:"brand-name",children:"Modelith"}),e.jsx("span",{className:"project-name",children:s.project.name}),b&&e.jsx("span",{className:"chip",children:"read-only"})]}),e.jsx("input",{className:"search",type:"search",placeholder:"Search entities & attributes… ( / , Enter jumps )",value:f,onChange:c=>h(c.target.value),onKeyDown:c=>{c.key==="Enter"&&x()},id:"mdl-search"}),e.jsxs("div",{className:"stats",children:[e.jsxs("span",{className:"stat-item",title:"entities",children:[e.jsx(ye,{size:14}),s.counts.entities]}),e.jsxs("span",{className:"stat-item",title:"relationships",children:[e.jsx(be,{size:14}),s.counts.relationships]}),l&&e.jsx("span",{className:"diag-chip"+(N?" err":M?" warn":" ok"),title:l.items.map(c=>`${c.code} ${c.message}`).join(` +`)||"model valid",children:N?e.jsxs(e.Fragment,{children:[e.jsx(Ee,{size:14}),N]}):M?e.jsxs(e.Fragment,{children:[e.jsx(we,{size:14}),M]}):e.jsxs(e.Fragment,{children:[e.jsx(ve,{size:14}),"valid"]})})]})]}),e.jsxs("div",{className:"topbar-row actions",children:[!b&&e.jsxs("button",{className:"tool-btn primary",onClick:z,title:"New entity (n)",children:[e.jsx(ie,{size:15}),e.jsx("span",{children:"Entity"})]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"Panels",children:[e.jsx("span",{className:"grp-tag",children:"Panels"}),d("ontology",e.jsx(oe,{size:15}),"Ontology","Ontology browser"),d("layers",e.jsx(de,{size:15}),"Layers","Four-layer stack & coverage"),d("changes",e.jsx(he,{size:15}),"Changes",T?"Uncommitted changes":"Changes",T),d("decisions",e.jsx(ue,{size:15}),"Decisions","Decision ledger")]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"View",children:[e.jsx("span",{className:"grp-tag",children:"View"}),e.jsx("button",{className:"grp-btn icon-only"+(u?" active":""),onClick:p,title:"Toggle data types","aria-label":"Toggle data types",children:e.jsx(xe,{size:15})}),e.jsx("button",{className:"grp-btn icon-only"+(g?" active":""),onClick:y,title:"Collapse to keys (PK + FK only) — readable for large models","aria-label":"Collapse to keys",children:e.jsx(pe,{size:15})}),e.jsx(Ne,{onRelayout:L}),e.jsx("button",{className:"grp-btn icon-only",onClick:S,title:"Fit view","aria-label":"Fit view",children:e.jsx(me,{size:15})})]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"Data",children:[e.jsx("span",{className:"grp-tag",children:"Data"}),e.jsx("button",{className:"grp-btn icon-only",onClick:F,title:"Refresh from disk (picks up terminal / external edits)","aria-label":"Refresh from disk",children:e.jsx(ge,{size:15})}),e.jsx(ee,{exec:v,canEdit:!b,onImported:w,submitLabel:"Import",buttonClass:"grp-btn",applyBatch:B,openImport:E})]})]}),s.subject_areas.length>0&&e.jsxs("div",{className:"sa-legend",children:[e.jsx("span",{className:"sa-legend-label",children:"Subject areas"}),s.subject_areas.map(c=>e.jsxs("span",{className:"legend-item",children:[e.jsx("span",{className:"swatch",style:{background:R.get(c.id)}}),c.name]},c.id))]})]})}function Ne({onRelayout:s}){const[l,f]=a.useState(!1),h=a.useRef(null);a.useEffect(()=>{if(!l)return;const u=p=>{h.current&&!h.current.contains(p.target)&&f(!1)};return document.addEventListener("mousedown",u),()=>document.removeEventListener("mousedown",u)},[l]);const x=u=>{s(u),f(!1)};return e.jsxs("div",{className:"layout-picker",ref:h,children:[e.jsx("button",{className:"grp-btn icon-only",onClick:()=>s("auto"),title:"Auto-layout","aria-label":"Auto-layout",children:e.jsx(je,{size:15})}),e.jsx("button",{className:"grp-btn icon-only caret"+(l?" active":""),onClick:()=>f(u=>!u),title:"Layout style","aria-label":"Layout style",children:e.jsx(fe,{size:13})}),l&&e.jsxs("div",{className:"layout-menu",role:"menu",children:[e.jsx("button",{onClick:()=>x("auto"),children:"Auto (by size)"}),e.jsx("button",{onClick:()=>x("hierarchical"),children:"Hierarchical"}),e.jsx("button",{onClick:()=>x("grid"),children:"Grid"})]})]})}function Ce(){const[s,l]=a.useState(null),[f,h]=a.useState(null),[x,u]=a.useState(null),[p,g]=a.useState(null),[y,S]=a.useState(""),[L,F]=a.useState(!0),[R,b]=a.useState(!1),[T,P]=a.useState(()=>new Set),[A,z]=a.useState(null),[v,w]=a.useState(null),[B,E]=a.useState(!1),[N,M]=a.useState(0),d=a.useRef(null),c=a.useRef(""),C=a.useRef(!1),K=a.useMemo(()=>{const t=new Map;return s==null||s.subject_areas.forEach((r,n)=>t.set(r.id,q[n%q.length])),t},[s]),j=(s==null?void 0:s.read_only)??!0,V=a.useMemo(()=>le(j),[j]),D=a.useMemo(()=>new URLSearchParams(window.location.search),[]),k=D.get("minimal")==="1";a.useEffect(()=>{k&&b(!0)},[k]);const G=a.useCallback(t=>{P(r=>{const n=new Set(r);return n.has(t)?n.delete(t):n.add(t),n})},[]),O=D.get("import")==="1",Q=a.useRef(!1),m=a.useCallback(()=>{U().then(l).catch(t=>u(String(t))),W().then(h).catch(()=>h(null)),$().then(t=>E(!!(t.git&&!t.clean))).catch(()=>E(!1)),M(t=>t+1)},[]);a.useEffect(m,[m]),a.useEffect(()=>{const t=setInterval(()=>{U().then(r=>{l(n=>n&&r.fingerprint===n.fingerprint?n:(C.current||(c.current=r.fingerprint),k||(W().then(h).catch(()=>h(null)),$().then(o=>E(!!(o.git&&!o.clean))).catch(()=>E(!1))),M(o=>o+1),r))}).catch(()=>{})},3e3);return()=>clearInterval(t)},[k]),a.useEffect(()=>{s!=null&&s.fingerprint&&(c.current=s.fingerprint)},[s]);const _=a.useCallback(async(t,r)=>{if(s){C.current=!0;try{const n=await te(t,r,c.current||s.fingerprint);return n!=null&&n.fingerprint&&(c.current=n.fingerprint),m(),n}catch(n){throw n instanceof ne&&n.status===409?(g("Model changed on disk — view refreshed, please retry."),m()):g(n instanceof Error?n.message:String(n)),n}finally{C.current=!1}}},[s,m]);a.useEffect(()=>{if(!p)return;const t=setTimeout(()=>g(null),5e3);return()=>clearTimeout(t)},[p]),a.useEffect(()=>{if(!s||Q.current)return;const t=D.get("focus");if(!t)return;const r=s.entities.find(n=>{var o;return n.name===t||((o=n.conceptual)==null?void 0:o.name)===t});r&&(Q.current=!0,requestAnimationFrame(()=>{var n;return(n=d.current)==null?void 0:n.focusEntity(r.id)}))},[s,D]);const X=a.useCallback(()=>{var n;if(!s)return;const t=y.trim().toLowerCase();if(!t)return;const r=s.entities.find(o=>{var I;return(o.name.toLowerCase()+" "+(((I=o.conceptual)==null?void 0:I.name.toLowerCase())??"")+" "+o.attributes.map(J=>J.name.toLowerCase()).join(" ")).includes(t)});r&&((n=d.current)==null||n.focusEntity(r.id))},[s,y]);return a.useEffect(()=>{const t=r=>{var o,H,I;const n=(o=document.activeElement)==null?void 0:o.tagName;n==="INPUT"||n==="TEXTAREA"||n==="SELECT"||(r.key==="/"&&(r.preventDefault(),(H=document.getElementById("mdl-search"))==null||H.focus()),r.key==="n"&&!j&&((I=d.current)==null||I.newEntity()),r.key==="Escape"&&(z(null),S(""),w(null)))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[j]),x?e.jsxs("div",{className:"splash error",children:[e.jsxs("h1",{children:["◮"," Modelith"]}),e.jsx("p",{children:"Could not load the model:"}),e.jsx("pre",{children:x}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl serve"})," pointed at a model directory?"]})]}):s?e.jsxs("div",{className:"app",children:[!k&&e.jsx(Me,{doc:s,diagnostics:f,query:y,onQuery:S,onSubmitQuery:X,showTypes:L,onToggleTypes:()=>F(t=>!t),collapseDetail:R,onToggleCollapse:()=>b(t=>!t),onFitView:()=>{var t;return(t=d.current)==null?void 0:t.fitView()},onRelayout:t=>{var r;return(r=d.current)==null?void 0:r.relayout(t)},onRefresh:m,saColors:K,readOnly:j,dirty:B,panelTab:v,onPanelTab:t=>w(r=>r===t?null:t),onNewEntity:()=>{var t;return(t=d.current)==null?void 0:t.newEntity()},exec:_,onImported:m,onImportBatch:ae,openImport:O}),e.jsxs("div",{className:"canvas-wrap",children:[e.jsx(re,{doc:s,caps:V,exec:_,selectedId:v?null:A,onSelect:z,query:y,showTypes:L,collapseDetail:R,expandedEntities:T,onToggleExpand:G,onError:g,handleRef:d}),v&&e.jsx(ce,{tab:v,onClose:()=>w(null),readOnly:j,refreshKey:N,onModelChanged:m,onFocusEntity:t=>{var r;w(null),(r=d.current)==null||r.focusEntity(t)}}),p&&e.jsx("div",{className:"toast",children:p})]})]}):e.jsxs("div",{className:"splash",children:["◮"," loading model…"]})}function ke(){return e.jsx(se,{children:e.jsx(Ce,{})})}Y.createRoot(document.getElementById("root")).render(e.jsx(Z.StrictMode,{children:e.jsx(ke,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-Ji3JtE2B.js b/packages/server/src/mdl_server/static/assets/sme-BuG6X873.js similarity index 99% rename from packages/server/src/mdl_server/static/assets/sme-Ji3JtE2B.js rename to packages/server/src/mdl_server/static/assets/sme-BuG6X873.js index 6bddc66..699046b 100644 --- a/packages/server/src/mdl_server/static/assets/sme-Ji3JtE2B.js +++ b/packages/server/src/mdl_server/static/assets/sme-BuG6X873.js @@ -1,2 +1,2 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-DfdI4kly.js","assets/client-DOmNpTln.js","assets/styles-CIm9mWtD.js","assets/styles-BJW39REh.css"])))=>i.map(i=>d[i]); -import{r as n,j as e,c as Ae,R as Ee}from"./client-DOmNpTln.js";import{p as pe,c as je,d as xe,e as $e,h as be,I as Re,R as Me,M as De,O as Pe,L as Ie,i as Oe,j as Te,A as Be,k as Le,l as Fe,m as ze,o as qe,s as We,f as le,n as Ge,q as He,r as Ve}from"./styles-CIm9mWtD.js";/* empty css */const Ke="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function ce(s,t){let a="";for(let l=0;l>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,t=new Uint8Array(10);crypto.getRandomValues(t);let a=0n;for(const l of t)a=a<<8n|BigInt(l);return ce(s,10)+ce(a,16)}const Ue={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Ye(s,t){const a=Ue[s];if(!a)return t;const l={...t};for(const i of a)l[i]||(l[i]=ge());return l}function fe(s,t){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${t.id??t.attribute_id}`;const a=t.id??t.entity_id??"",l=t.attribute_id??"";return l?`${s}:${a}:${l}`:`${s}:${a}`}function Je({subjectArea:s,describe:t}){const[a,l]=n.useState([]),[i,x]=n.useState(null),[c,r]=n.useState(!1),m=n.useRef(null),N=n.useCallback(async(h,g)=>{g=Ye(h,g),h==="set_alignment"&&g.status===void 0&&(g={...g,status:"proposed"});const _=fe(h,g),{label:C,before:T,after:S}=t(h,g);return l(y=>{const A=y.filter(k=>k.key!==_);if(h.startsWith("delete_")){const k=g.id??g.entity_id;if(k&&y.some(o=>o.op.startsWith("create_")&&o.payload.id===k))return A.filter(o=>o.payload.id!==k)}return[...A,{op:h,payload:g,label:C,before:T,after:S,key:_}]}),{ok:!0,fingerprint:"",created_id:g.id??null,diagnostics:[]}},[t]);n.useEffect(()=>{if(!a.length){x(null);return}const h=setTimeout(()=>{var _;(_=m.current)==null||_.abort();const g=new AbortController;m.current=g,r(!0),pe(a.map(C=>({op:C.op,payload:C.payload})),s,g.signal).then(x).catch(C=>{C.name!=="AbortError"&&x(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(h)},[a,s]);const b=n.useCallback(h=>{l(g=>g.filter(_=>_.key!==h))},[]),f=n.useCallback(()=>l([]),[]);return n.useMemo(()=>({pending:a,previewDoc:(i==null?void 0:i.model)??null,previewDiff:(i==null?void 0:i.diff)??null,diagnostics:(i==null?void 0:i.diagnostics)??[],failure:i&&i.failed_index!==null?{index:i.failed_index,error:i.error??"invalid change"}:null,busy:c,exec:N,drop:b,clear:f}),[a,i,c,N,b,f])}function Qe(s){const t=new Map;for(const l of s)for(const i of["id","conceptual_id","from_fk_id"]){const x=l.payload[i];l.op.startsWith("create_")&&typeof x=="string"&&t.set(x,l.key)}if(!t.size)return new Set;const a=new Set;for(const l of s)if(!l.op.startsWith("create_"))for(const i of Object.values(l.payload))typeof i=="string"&&t.has(i)&&(a.add(l.key),a.add(t.get(i)));return a}function Xe({user:s}){const[t,a]=n.useState(null);return n.useEffect(()=>{je(s).then(a).catch(()=>{})},[s]),!(t!=null&&t.ok)||!t.git?null:t.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):t.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):t.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"your proposal branch"}),t.behind?e.jsxs("span",{className:"dim",children:["· ",t.base_branch," is ",t.behind," ahead"]}):null]})}function Ze({exec:s,canEdit:t,stagedMembers:a}){const[l,i]=n.useState([]),[x,c]=n.useState(""),[r,m]=n.useState(null),[N,b]=n.useState(""),[f,h]=n.useState(new Set),[g,_]=n.useState("ancestors"),[C,T]=n.useState(1),[S,y]=n.useState(null),[A,k]=n.useState(new Set),[R,o]=n.useState(null);n.useEffect(()=>{xe().then(d=>{i(d.subject_areas),c(w=>{var M;return w||((M=d.subject_areas[0])==null?void 0:M.id)||""})}).catch(d=>o(String(d)))},[]),n.useEffect(()=>{x&&(y(null),h(new Set),$e(x).then(m).catch(d=>o(String(d))))},[x]);const E=n.useMemo(()=>a[x]??(r==null?void 0:r.members)??[],[a,x,r]),$=n.useMemo(()=>{const d=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])d.set(w.id,w);return d},[r]),D=N.trim().toLowerCase(),B=d=>!D||d.name.toLowerCase().includes(D),u=n.useMemo(()=>new Set(E),[E]),P=n.useMemo(()=>E.map(d=>$.get(d)).filter(d=>!!d&&B(d)),[E,$,D]),F=n.useMemo(()=>[...$.values()].filter(d=>!u.has(d.id)&&B(d)),[$,u,D]),I=n.useCallback(d=>{if(!r)return;const w=d.filter(q=>!(r.members??[]).includes(q)).length,M=(r.members??[]).filter(q=>!d.includes(q)).length;s("set_subject_area_members",{id:r.id,members:d,__label:`${r.name}: ${d.length} objects (+${w} −${M})`})},[r,s]),z=(d,w)=>{const M=w?[...E,...d.filter(q=>!u.has(q))]:E.filter(q=>!d.includes(q));I(M),h(new Set)},W=(d,w,M)=>{const q=new Set(d);q.has(w)?q.delete(w):q.add(w),M(q)},U=()=>{if(!r)return;const d=f.size?[...f]:E;if(!d.length){o("add an object first, or select one to expand from");return}o(null),be(r.id,{seeds:d,direction:g,levels:C}).then(w=>{y(w.hops),k(new Set(w.added))}).catch(w=>o(String(w)))},K=()=>{z([...A],!0),y(null)};if(R&&!r)return e.jsx("p",{className:"sme-splash error",children:R});if(!l.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const j=((r==null?void 0:r.inconsistent)??[]).filter(d=>!u.has(d));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:l.map(d=>e.jsxs("button",{className:"sme-sa"+(d.id===x?" active":""),onClick:()=>c(d.id),title:d.definition??void 0,children:[d.name,e.jsx("span",{className:"sme-sa-count",children:(a[d.id]??d.members).length})]},d.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:d=>b(d.target.value)})]}),j.length>0&&t&&e.jsxs("div",{className:"sa-nudge",children:[j.length," object",j.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>z(j,!0),children:["Add ",j.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${F.length})`,rows:F,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>!u.has(d)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!F.length,onClick:()=>z(F.map(d=>d.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>u.has(d)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!P.length,onClick:()=>z(P.map(d=>d.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${P.length})`,rows:P,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:g,onChange:d=>_(d.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:d=>T(Math.max(1,Math.min(10,Number(d.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!t,children:"Preview"})]}),R&&e.jsx("p",{className:"sme-error-line",children:R}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(d=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(d.id),onChange:()=>W(A,d.id,k)}),e.jsx("span",{className:"sa-hop-name",children:d.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",d.via_name," · ",d.direction.replace(/s$/,""),", level ",d.level]})]},d.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>y(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:K,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function Q({title:s,rows:t,picked:a,onToggle:l,homedHere:i}){const x=n.useMemo(()=>new Set(i),[i]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[t.map(c=>e.jsxs("li",{className:"sa-row"+(a.has(c.id)?" picked":""),onClick:()=>l(c.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(c.id)}),e.jsx("span",{className:"sa-row-name",children:c.name}),x.has(c.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},c.id)),!t.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function es({objects:s,onCreate:t,onClose:a}){const[l,i]=n.useState(""),[x,c]=n.useState(""),[r]=n.useState(s),[m,N]=n.useState([]),[b,f]=n.useState(new Set),[h,g]=n.useState(""),[_,C]=n.useState("ancestors"),[T,S]=n.useState(1),[y,A]=n.useState(null),[k,R]=n.useState(new Set),[o,E]=n.useState(""),[$,D]=n.useState(null);n.useEffect(()=>{xe().then(j=>{j.subject_areas[0]&&E(j.subject_areas[0].id)}).catch(()=>{})},[]);const B=n.useMemo(()=>new Set(m),[m]),u=h.trim().toLowerCase(),P=j=>!u||j.name.toLowerCase().includes(u),F=n.useMemo(()=>m.map(j=>r.find(d=>d.id===j)).filter(j=>!!j&&P(j)),[m,r,u]),I=n.useMemo(()=>r.filter(j=>!B.has(j.id)&&P(j)),[r,B,u]),z=(j,d)=>{N(w=>d?[...w,...j.filter(M=>!w.includes(M))]:w.filter(M=>!j.includes(M))),f(new Set)},W=(j,d,w)=>{const M=new Set(j);M.has(d)?M.delete(d):M.add(d),w(M)},U=()=>{const j=b.size?[...b]:m;if(!j.length){D("pick an object to expand from first");return}o&&(D(null),be(o,{seeds:j,direction:_,levels:T}).then(d=>{const w=d.hops.filter(M=>!B.has(M.id));A(w),R(new Set(w.map(M=>M.id)))}).catch(d=>D(String(d))))},K=()=>{l.trim()&&t(l.trim(),x.trim(),m)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:j=>j.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:l,placeholder:"e.g. UseCase-ApraStressTesting",onChange:j=>i(j.target.value),onKeyDown:j=>j.key==="Enter"&&m.length===0&&K()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:x,placeholder:"what this view is for",onChange:j=>c(j.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:h,onChange:j=>g(j.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${I.length})`,rows:I,picked:b,onToggle:j=>W(b,j,f),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>!B.has(j)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!I.length,onClick:()=>z(I.map(j=>j.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>B.has(j)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!F.length,onClick:()=>z(F.map(j=>j.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${F.length})`,rows:F,picked:b,onToggle:j=>W(b,j,f),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:_,onChange:j=>C(j.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:T,onChange:j=>S(Math.max(1,Math.min(10,Number(j.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!o,title:o?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),y&&e.jsxs("div",{className:"sa-hops",children:[y.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),y.map(j=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:k.has(j.id),onChange:()=>W(k,j.id,R)}),e.jsx("span",{className:"sa-hop-name",children:j.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",j.via_name," · ",j.direction.replace(/s$/,""),", level ",j.level]})]},j.id)),y.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!k.size,onClick:()=>{z([...k],!0),A(null)},children:["Add ",k.size]})]})]})]}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!l.trim(),onClick:K,children:["Create",m.length>0&&` with ${m.length} object${m.length===1?"":"s"}`]})]})]})})}const ss=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function ns({doc:s,subjectArea:t,onSubjectArea:a,exec:l,canEdit:i,direct:x=!1,onSelectEntity:c,onImported:r,query:m="",busy:N}){var R;const[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(!1),[T,S]=n.useState(null),y=n.useRef(null),A=n.useMemo(()=>({canEdit:i,mode:x?"direct":"staged",allow:x?void 0:ss,canArbitrate:x,canCommit:x}),[i,x]),k=n.useCallback(o=>{f(o),o&&(c==null||c(o))},[c]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),i&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(t===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((R=s.counts)==null?void 0:R.entities_total)??s.entities.length})]}),s.subject_areas.map(o=>e.jsxs("button",{className:"sme-sa"+(t===o.id?" active":""),onClick:()=>a(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:o.member_count??0})]},o.id))]}),_&&e.jsx(es,{objects:s.entities.flatMap(o=>o.conceptual?[{id:o.conceptual.id,kind:"conceptual_entity",name:o.conceptual.name,definition:o.conceptual.definition??null,synonyms:[],subject_area:o.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(o,E,$)=>{const D=ge();l("create_subject_area",{name:o,...E?{definition:E}:{},id:D}),$.length&&l("set_subject_area_members",{id:D,members:$}),C(!1),a(D)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),i&&e.jsx("button",{className:"erd-action",onClick:()=>{var o;return(o=y.current)==null?void 0:o.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(h==="ontology"?" on":""),onClick:()=>g(o=>o==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(h==="layers"?" on":""),onClick:()=>g(o=>o==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Re,{exec:l,canEdit:i,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!i&&e.jsx("span",{className:"sme-chip",children:"read-only"}),T&&e.jsx("span",{className:"erd-error",children:T})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Me,{children:e.jsx(De,{doc:s,caps:A,exec:l,selectedId:b,onSelect:k,query:m,onError:S,handleRef:y})}),h&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:h==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>g(null),children:"✕"})]}),h==="ontology"?e.jsx(Pe,{}):e.jsx(Ie,{refreshKey:0,onFocusEntity:o=>{var E;g(null),(E=y.current)==null||E.focusEntity(o)}})]})]})]})]})}function as({user:s,onView:t}){const[a,l]=n.useState(null),[i,x]=n.useState(null);if(n.useEffect(()=>{Oe(s).then(l).catch(r=>x(String(r)))},[s]),i)return e.jsx("p",{className:"sme-splash error",children:i});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const c=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[c," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(rs,{p:r,gh:a.gh,onView:()=>t(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function ts(s,t){var a,l;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?t&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:t&&((l=s.pr)==null?void 0:l.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function rs({p:s,gh:t,onView:a}){const l=ts(s,t);return e.jsxs("div",{className:"pr-card"+(l.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+l.kind,children:l.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",t&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?t&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function is({changes:s,routeAdvice:t,identity:a,user:l,onUser:i,onDrop:x,onClose:c,onProposed:r}){const m=!!(a&&a.source!=="anonymous"),[N,b]=n.useState(l||(localStorage.getItem("mdl.sme.user")??"")),[f,h]=n.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[g,_]=n.useState(""),[C,T]=n.useState(!1),S=ve(C?g:f),[y,A]=n.useState(""),[k,R]=n.useState(!1),[o,E]=n.useState(null),[$,D]=n.useState(null),B=()=>{!m&&!N.trim()||(m||(localStorage.setItem("mdl.sme.user",N.trim()),i==null||i(N.trim())),R(!0),D(null),Te({user:m?"":N.trim(),slug:S,title:f,body:y.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then(E).catch(u=>D(u instanceof Be?u.message:String(u))).finally(()=>R(!1)))};return o!=null&&o.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(o),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:o.message}),o.pr_url&&e.jsx("p",{children:e.jsx("a",{href:o.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),o.compare_url&&e.jsx("p",{children:e.jsx("a",{href:o.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:o.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(o),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:c,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:c,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,P)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>x(P),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},P))}),m?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>b(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:f,onChange:u=>h(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?g:S,onChange:u=>{T(!0),_(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:y,onChange:u=>A(u.target.value)})]}),e.jsx(ls,{cl:t,user:m?a.name:N,changeSlug:S}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:c,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:k||!m&&!N.trim()||s.length===0,onClick:B,children:k?"Submitting…":"Submit for review"})]})]})})}function ls({cl:s,user:t,changeSlug:a}){if(!(s!=null&&s.primary))return null;const l=s.reviewers_actual??s.reviewers,i=ve(t||"you"),x=s.routes.length>1;return e.jsxs(e.Fragment,{children:[x&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",l.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",i,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:l.join(", ")})]})]})]})}function ve(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const cs="modulepreload",os=function(s){return"/"+s},oe={},ds=function(t,a,l){let i=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),r=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));i=Promise.allSettled(a.map(m=>{if(m=os(m),m in oe)return;oe[m]=!0;const N=m.endsWith(".css"),b=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${b}`))return;const f=document.createElement("link");if(f.rel=N?"stylesheet":cs,N||(f.as="script"),f.crossOrigin="",f.href=m,r&&f.setAttribute("nonce",r),document.head.appendChild(f),N)return new Promise((h,g)=>{f.addEventListener("load",h),f.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${m}`)))})}))}function x(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return i.then(c=>{for(const r of c||[])r.status==="rejected"&&x(r.reason);return t().catch(x)})};function hs({diff:s,selectable:t,selected:a,onToggle:l}){var r;const[i,x]=n.useState(((r=s.objects[0])==null?void 0:r.ulid)??null),c=n.useMemo(()=>s.objects.find(m=>m.ulid===i)??s.objects[0]??null,[s,i]);return s.objects.length?e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:s.objects.map(m=>e.jsx(us,{obj:m,active:m.ulid===((c==null?void 0:c.ulid)??""),checked:(a==null?void 0:a.has(m.ulid))??!0,selectable:t,onSelect:()=>x(m.ulid),onToggle:()=>l==null?void 0:l(m.ulid)},m.ulid))}),e.jsx("div",{className:"rv-detail",children:c&&e.jsx(ps,{obj:c})})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function ms(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function us({obj:s,active:t,checked:a,selectable:l,onSelect:i,onToggle:x}){return e.jsxs("div",{className:"rv-obj"+(t?" active":""),onClick:i,children:[e.jsxs("div",{className:"rv-obj-head",children:[l&&e.jsx("input",{type:"checkbox",checked:a,onClick:c=>c.stopPropagation(),onChange:x,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",ms(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,r)=>e.jsx("li",{children:c.label},c.field+r)),s.children.flatMap(c=>c.fields.map((r,m)=>e.jsxs("li",{children:[r.label,": ",c.name_before??c.name_after]},c.ulid+m)))]})]})}function ps({obj:s}){const t=s.children.flatMap(a=>a.fields.map(l=>({field:l,on:a.name_before??a.name_after??""})));return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((a,l)=>e.jsx(de,{f:a},a.field+l)),t.map(({field:a,on:l},i)=>e.jsx(de,{f:a,on:l},"c"+i)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function de({f:s,on:t}){var l,i,x;const a=t?`${s.label}: ${t}`:s.label;if((l=s.breaks)!=null&&l.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[t??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(c=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",c.name]}),e.jsxs("span",{className:"p",children:[c.target," · ",c.materialization]})]},c.name))})]})]});if(s.kind==="definition_changed"){const c=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:me(c,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:me(r,c,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const c=new Set(((i=s.before)==null?void 0:i.map(String))??[]),r=new Set(((x=s.after)==null?void 0:x.map(String))??[]),m=[...r].filter(b=>!c.has(b)),N=[...c].filter(b=>!r.has(b));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[m.map(b=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",b]},"a"+b)),N.map(b=>e.jsxs("span",{className:"rv-chip-del",children:["− ",b]},"r"+b))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:he(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:he(s.after)})]})]})}function he(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function me(s,t,a){const l=s.split(/(\s+)/),i=t.split(/(\s+)/),x=h=>h.toLowerCase().replace(/[.,;:]/g,""),c=l.length,r=i.length,m=Array.from({length:c+1},()=>new Array(r+1).fill(0));for(let h=c-1;h>=0;h--)for(let g=r-1;g>=0;g--)m[h][g]=x(l[h])===x(i[g])?m[h+1][g+1]+1:Math.max(m[h+1][g],m[h][g+1]);const N=new Set;let b=0,f=0;for(;b=m[b][f+1]?b++:f++;return l.map((h,g)=>!h.trim()||N.has(g)?e.jsx("span",{children:h},g):a==="del"?e.jsx("del",{children:h},g):e.jsx("ins",{children:h},g))}const js=n.lazy(()=>ds(()=>import("./ModelDiagram-DfdI4kly.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function xs({stagedDiff:s,stagedRoute:t,user:a,onBack:l,onSubmit:i,excluded:x,onToggleObject:c,selectable:r}){const[m,N]=n.useState(null),[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(null),[T,S]=n.useState(null),[y,A]=n.useState("changes"),k=n.useCallback(()=>{je(a).then(o=>{C(o);const E=o.base_branch??"main";s?N(s):Le("HEAD").then($=>$.ok?N($):S($.error??"could not read the model")).catch($=>S(String($))),t?f(t):Fe("HEAD").then(f).catch(()=>{}),ze(E).then(g).catch(()=>{})}).catch(o=>S(String(o)))},[a,s,t]);if(n.useEffect(k,[k]),T)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:T})})]});if(!m)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const R=new Set(m.objects.map(o=>o.ulid).filter(o=>!x.has(o)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:m,tab:y,onTab:A}),y==="changes"?e.jsx(hs,{diff:m,selectable:r,selected:R,onToggle:c}):e.jsx(n.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(js,{severityByUlid:bs(m)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(gs,{cl:b}),e.jsx(fs,{conf:h,ctx:_}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:l,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!m.objects.length||(_?!_.can_propose:!1),title:_&&!_.can_propose?"the working tree has uncommitted changes":"",onClick:()=>i(b),children:"Submit for review →"})]})]})]})}function bs(s){const t=new Map;for(const a of s.objects)t.set(a.ulid,a.severity);return t}function ue({onBack:s,diff:t,tab:a,onTab:l}){const i=t==null?void 0:t.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),i&&e.jsxs("div",{className:"rv-sub",children:[i.objects," object",i.objects===1?"":"s"," ·"," ",(i.breaking??0)+(i.additive??0)+(i.cosmetic??0)," changes",(t==null?void 0:t.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",i.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:t==null?void 0:t.base.label})]})]}),l&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>l("changes"),children:["Changes ",(t==null?void 0:t.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>l("diagram"),children:"Diagram"})]})]})}function gs({cl:s}){if(!(s!=null&&s.primary))return null;const t=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:t.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function fs({conf:s,ctx:t}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(t==null?void 0:t.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function vs({term:s,canEdit:t,pendingCount:a,onEdit:l}){var i,x;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((i=s.subject_area)==null?void 0:i.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),t&&e.jsx("button",{className:"sme-primary",onClick:l,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(c=>e.jsx("span",{className:"sme-chip",children:c},c))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((x=s.ontology)==null?void 0:x.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(c=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:c.logical_entity}),c.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),c.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:c.physical.map(r=>r.name).join(", ")})]},c.logical_id))})]})]})}function Ns({term:s,pending:t,onStage:a,onDone:l,catalogOwned:i=[],catalog:x=null}){var E,$,D,B;const c=u=>i.includes(u),r=u=>t.find(P=>P.label===u),m=(u,P)=>{const F=r(u);return F?F.after:P},[N,b]=n.useState(m("Definition",s.definition??"")),[f,h]=n.useState(s.synonyms),[g,_]=n.useState(""),[C,T]=n.useState(((E=s.stewardship)==null?void 0:E.owner)??""),[S,y]=n.useState((($=s.stewardship)==null?void 0:$.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},k=(u,P)=>{var F,I;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:P||null},label:"Stewardship",before:`owner: ${((F=s.stewardship)==null?void 0:F.owner)??"—"} · steward: ${((I=s.stewardship)==null?void 0:I.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${P||"—"}`})},R=u=>{h(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},o=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:l,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(c("definition")||c("synonyms")||c("stewardship"))&&x&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:x.name}),". Edit them there — this glossary mirrors them read-only.",x.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:x.url,target:"_blank",rel:"noreferrer",children:["Open in ",x.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),c("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>b(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),c("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[f.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>R(f.filter(P=>P!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:g,placeholder:"add + Enter",onChange:u=>_(u.target.value),onKeyDown:u=>{u.key==="Enter"&&g.trim()&&(R([...f,g.trim()]),_(""))}})]})]}),!o&&(c("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((D=s.stewardship)==null?void 0:D.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((B=s.stewardship)==null?void 0:B.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>T(u.target.value),onBlur:()=>k(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>y(u.target.value),onBlur:()=>k(C,S)})]})]})),e.jsx(ys,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:t.length>0?`${t.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const ws=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function ys({term:s,onStage:t}){var f;const[a,l]=n.useState(!1),[i,x]=n.useState(s.name),[c,r]=n.useState([]),[m,N]=n.useState("skos:closeMatch"),b=h=>{x(h),qe(h).then(g=>r(g.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:i,onChange:h=>b(h.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:c.map(h=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var g;t({op:"set_alignment",payload:{id:s.id,aligns_to:h.prefixed,alignment:m,status:"proposed"},label:"Standard mapping",before:((g=s.ontology)==null?void 0:g.aligns_to)??"(none)",after:`${h.prefixed} (${m}, proposed)`}),l(!1)},children:[e.jsx("strong",{children:h.label})," ",e.jsx("code",{children:h.prefixed}),h.definition&&e.jsx("span",{className:"sme-align-def",children:h.definition})]},h.iri))}),e.jsx("select",{value:m,onChange:h=>N(h.target.value),children:ws.map(h=>e.jsx("option",{children:h},h))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(f=s.ontology)!=null&&f.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>l(!0),children:"Propose a mapping…"})]})]})}function _s(){const[s,t]=n.useState(null),[a,l]=n.useState(!0),[i,x]=n.useState(null),[c,r]=n.useState(""),[m,N]=n.useState(null),[b,f]=n.useState(""),[h,g]=n.useState(""),[_,C]=n.useState(null),[T,S]=n.useState(!1),[y,A]=n.useState([]),[k,R]=n.useState(!1),[o,E]=n.useState(window.location.hash==="#proposals"?"proposals":"browse"),[$,D]=n.useState(new Set),[B,u]=n.useState(()=>localStorage.getItem("mdl.sme.user")??""),[P,F]=n.useState(null),[I,z]=n.useState(null),[W,U]=n.useState(!1),[K,j]=n.useState(null),d=n.useCallback((p,v)=>{var ae;const O=J=>{var te;const V=I==null?void 0:I.entities.find(re=>{var ie;return re.id===J||((ie=re.conceptual)==null?void 0:ie.id)===J});return((te=V==null?void 0:V.conceptual)==null?void 0:te.name)??(V==null?void 0:V.name)??"object"},L=O(v.id??v.entity_id);switch(p){case"create_entity":return{label:`New entity: ${v.name}`,before:"—",after:String(v.name??"")};case"create_relationship":return{label:`New relationship: ${O(v.from_entity)} → ${O(v.to_entity)}`,before:"—",after:String(v.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${L}`,before:L,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[v.cardinality,v.optionality,v.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${L}`,before:"",after:String(v.definition??"")};case"add_attribute":return{label:`Attribute added to ${L}`,before:"—",after:String(v.name??"")};case"delete_attribute":return{label:`Attribute removed from ${L}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${L}`,before:"",after:JSON.stringify(v)};case"rename_entity":return{label:`Renamed ${L}`,before:L,after:String(v.name??"")};case"set_subject_area":return{label:`Subject area: ${L}`,before:"",after:String(v.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${L}`,before:"",after:String(v.steward??"")};case"set_subject_area_members":{const J=v.__label,V=((ae=v.members)==null?void 0:ae.length)??0;return{label:J??"Subject area members",before:"",after:`${V} object${V===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${L}`,before:"",after:String(v.aligns_to??"")};default:return{label:`${p.replace(/_/g," ")}: ${L}`,before:"",after:""}}},[I]),w=Je({subjectArea:b,describe:d}),M=n.useCallback(async(p,v)=>{const O=(I==null?void 0:I.fingerprint)??"",L=await We(p,v,O);return await le(b||void 0).then(z),L},[I,b]),q=n.useMemo(()=>{const p={};for(const v of w.pending)v.op==="set_subject_area_members"&&(p[v.payload.id]=v.payload.members??[]);return p},[w.pending]),G=n.useMemo(()=>[...y,...w.pending],[y,w.pending]),[Ne,X]=n.useState(null),[we,Z]=n.useState(null);n.useEffect(()=>{if(!G.length){X(null),Z(null);return}const p=new AbortController,v=G.map(O=>({op:O.op,payload:O.payload}));return pe(v,b||void 0,p.signal).then(O=>X(O.diff??null)).catch(O=>{O.name!=="AbortError"&&X(null)}),Ge(v).then(Z).catch(()=>Z(null)),()=>p.abort()},[G,b]);const ye=n.useMemo(()=>Qe(G),[G]).size{E(p),window.location.hash=p==="browse"?"":`#${p}`},[]);n.useEffect(()=>{const p=()=>{const v=window.location.hash;E(v==="#proposals"?"proposals":v==="#review"?"review":v==="#model"?"model":v==="#areas"?"areas":"browse")};return p(),window.addEventListener("hashchange",p),()=>window.removeEventListener("hashchange",p)},[]);const ee=n.useCallback(()=>{He({subject_area:b,q:h}).then(t).catch(p=>N(String(p)))},[b,h]);n.useEffect(ee,[ee]),n.useEffect(()=>{le(b||void 0).then(p=>{l(p.read_only),r(p.project.name),z(p),U(!!p.direct),F(p.identity??null),p.identity&&p.identity.source!=="anonymous"&&p.identity.name&&u(p.identity.name)}).catch(()=>{}),Ve().then(x).catch(()=>{})},[b]);const se=n.useMemo(()=>(s==null?void 0:s.terms.find(p=>p.id===_))??null,[s,_]),_e=n.useCallback(p=>{const v=p.key??fe(p.op,p.payload);A(O=>[...O.filter(L=>L.key!==v),{...p,key:v}])},[]),Se=p=>A(v=>v.filter((O,L)=>L!==p)),ke=()=>{A([]),R(!1),S(!1),ee()};if(m)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const Y=!a,ne=(i==null?void 0:i.catalog_owned_fields)??[],Ce=ne.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:c}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Ce&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${i==null?void 0:i.catalog_name}`,children:["mirrors ",i==null?void 0:i.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:h,onChange:p=>g(p.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(o==="browse"?" active":""),onClick:()=>H("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(o==="model"?" active":""),onClick:()=>H("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(o==="areas"?" active":""),onClick:()=>H("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(o==="proposals"?" active":""),onClick:()=>H("proposals"),children:"My proposals"})]}),G.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>H("review"),children:[G.length," change",G.length>1?"s":""," · Review →"]})]}),o==="browse"&&e.jsx(Xe,{user:B}),o==="review"?e.jsx(xs,{stagedDiff:Ne,stagedRoute:we,user:B,selectable:ye,excluded:$,onToggleObject:p=>D(v=>{const O=new Set(v);return O.has(p)?O.delete(p):O.add(p),O}),onBack:()=>H("browse"),onSubmit:p=>{j(p),R(!0)}}):o==="model"?w.previewDoc??I?e.jsx(ns,{doc:w.previewDoc??I,subjectArea:b,onSubjectArea:f,exec:W?M:w.exec,canEdit:Y,direct:W,onImported:()=>H("review"),query:h,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):o==="areas"?e.jsx(Ze,{exec:w.exec,canEdit:Y,stagedMembers:q}):o==="proposals"?e.jsx(as,{user:B,onView:()=>H("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(b===""?" active":""),onClick:()=>f(""),children:"All subject areas"}),s.subject_areas.map(p=>e.jsx("button",{className:"sme-sa"+(b===p.id?" active":""),onClick:()=>f(p.id),title:p.definition??void 0,children:p.name},p.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(p=>{var v;return e.jsxs("li",{className:"sme-list-item"+(p.id===_?" active":""),onClick:()=>{C(p.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:p.name}),p.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((v=p.ontology)==null?void 0:v.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:p.definition??"no definition yet"})]},p.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:se?T&&Y?e.jsx(Ns,{term:se,pending:y,onStage:_e,onDone:()=>S(!1),catalogOwned:ne,catalog:i?{name:i.catalog_name,url:i.catalog_url}:null}):e.jsx(vs,{term:se,canEdit:Y,pendingCount:y.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),k&&e.jsx(is,{changes:G,routeAdvice:K,identity:P,user:B,onUser:p=>{u(p),localStorage.setItem("mdl.sme.user",p)},onDrop:Se,onClose:()=>R(!1),onProposed:()=>{ke(),w.clear(),D(new Set),H("proposals")}})]})}Ae.createRoot(document.getElementById("root")).render(e.jsx(Ee.StrictMode,{children:e.jsx(_s,{})})); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-C9iVTvG9.js","assets/client-DOmNpTln.js","assets/styles-Cn8AFsR1.js","assets/styles-BG76mhX4.css"])))=>i.map(i=>d[i]); +import{r as n,j as e,c as Ae,R as Ee}from"./client-DOmNpTln.js";import{p as pe,c as je,d as xe,e as $e,h as be,I as Re,R as Me,M as De,O as Pe,L as Ie,i as Oe,j as Te,A as Be,k as Le,l as Fe,m as ze,o as qe,s as We,f as le,n as Ge,q as He,r as Ve}from"./styles-Cn8AFsR1.js";/* empty css */const Ke="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function ce(s,t){let a="";for(let l=0;l>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,t=new Uint8Array(10);crypto.getRandomValues(t);let a=0n;for(const l of t)a=a<<8n|BigInt(l);return ce(s,10)+ce(a,16)}const Ue={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Ye(s,t){const a=Ue[s];if(!a)return t;const l={...t};for(const i of a)l[i]||(l[i]=ge());return l}function fe(s,t){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${t.id??t.attribute_id}`;const a=t.id??t.entity_id??"",l=t.attribute_id??"";return l?`${s}:${a}:${l}`:`${s}:${a}`}function Je({subjectArea:s,describe:t}){const[a,l]=n.useState([]),[i,x]=n.useState(null),[c,r]=n.useState(!1),m=n.useRef(null),N=n.useCallback(async(h,g)=>{g=Ye(h,g),h==="set_alignment"&&g.status===void 0&&(g={...g,status:"proposed"});const _=fe(h,g),{label:C,before:T,after:S}=t(h,g);return l(y=>{const A=y.filter(k=>k.key!==_);if(h.startsWith("delete_")){const k=g.id??g.entity_id;if(k&&y.some(o=>o.op.startsWith("create_")&&o.payload.id===k))return A.filter(o=>o.payload.id!==k)}return[...A,{op:h,payload:g,label:C,before:T,after:S,key:_}]}),{ok:!0,fingerprint:"",created_id:g.id??null,diagnostics:[]}},[t]);n.useEffect(()=>{if(!a.length){x(null);return}const h=setTimeout(()=>{var _;(_=m.current)==null||_.abort();const g=new AbortController;m.current=g,r(!0),pe(a.map(C=>({op:C.op,payload:C.payload})),s,g.signal).then(x).catch(C=>{C.name!=="AbortError"&&x(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(h)},[a,s]);const b=n.useCallback(h=>{l(g=>g.filter(_=>_.key!==h))},[]),f=n.useCallback(()=>l([]),[]);return n.useMemo(()=>({pending:a,previewDoc:(i==null?void 0:i.model)??null,previewDiff:(i==null?void 0:i.diff)??null,diagnostics:(i==null?void 0:i.diagnostics)??[],failure:i&&i.failed_index!==null?{index:i.failed_index,error:i.error??"invalid change"}:null,busy:c,exec:N,drop:b,clear:f}),[a,i,c,N,b,f])}function Qe(s){const t=new Map;for(const l of s)for(const i of["id","conceptual_id","from_fk_id"]){const x=l.payload[i];l.op.startsWith("create_")&&typeof x=="string"&&t.set(x,l.key)}if(!t.size)return new Set;const a=new Set;for(const l of s)if(!l.op.startsWith("create_"))for(const i of Object.values(l.payload))typeof i=="string"&&t.has(i)&&(a.add(l.key),a.add(t.get(i)));return a}function Xe({user:s}){const[t,a]=n.useState(null);return n.useEffect(()=>{je(s).then(a).catch(()=>{})},[s]),!(t!=null&&t.ok)||!t.git?null:t.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):t.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):t.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"your proposal branch"}),t.behind?e.jsxs("span",{className:"dim",children:["· ",t.base_branch," is ",t.behind," ahead"]}):null]})}function Ze({exec:s,canEdit:t,stagedMembers:a}){const[l,i]=n.useState([]),[x,c]=n.useState(""),[r,m]=n.useState(null),[N,b]=n.useState(""),[f,h]=n.useState(new Set),[g,_]=n.useState("ancestors"),[C,T]=n.useState(1),[S,y]=n.useState(null),[A,k]=n.useState(new Set),[R,o]=n.useState(null);n.useEffect(()=>{xe().then(d=>{i(d.subject_areas),c(w=>{var M;return w||((M=d.subject_areas[0])==null?void 0:M.id)||""})}).catch(d=>o(String(d)))},[]),n.useEffect(()=>{x&&(y(null),h(new Set),$e(x).then(m).catch(d=>o(String(d))))},[x]);const E=n.useMemo(()=>a[x]??(r==null?void 0:r.members)??[],[a,x,r]),$=n.useMemo(()=>{const d=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])d.set(w.id,w);return d},[r]),D=N.trim().toLowerCase(),B=d=>!D||d.name.toLowerCase().includes(D),u=n.useMemo(()=>new Set(E),[E]),P=n.useMemo(()=>E.map(d=>$.get(d)).filter(d=>!!d&&B(d)),[E,$,D]),F=n.useMemo(()=>[...$.values()].filter(d=>!u.has(d.id)&&B(d)),[$,u,D]),I=n.useCallback(d=>{if(!r)return;const w=d.filter(q=>!(r.members??[]).includes(q)).length,M=(r.members??[]).filter(q=>!d.includes(q)).length;s("set_subject_area_members",{id:r.id,members:d,__label:`${r.name}: ${d.length} objects (+${w} −${M})`})},[r,s]),z=(d,w)=>{const M=w?[...E,...d.filter(q=>!u.has(q))]:E.filter(q=>!d.includes(q));I(M),h(new Set)},W=(d,w,M)=>{const q=new Set(d);q.has(w)?q.delete(w):q.add(w),M(q)},U=()=>{if(!r)return;const d=f.size?[...f]:E;if(!d.length){o("add an object first, or select one to expand from");return}o(null),be(r.id,{seeds:d,direction:g,levels:C}).then(w=>{y(w.hops),k(new Set(w.added))}).catch(w=>o(String(w)))},K=()=>{z([...A],!0),y(null)};if(R&&!r)return e.jsx("p",{className:"sme-splash error",children:R});if(!l.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const j=((r==null?void 0:r.inconsistent)??[]).filter(d=>!u.has(d));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:l.map(d=>e.jsxs("button",{className:"sme-sa"+(d.id===x?" active":""),onClick:()=>c(d.id),title:d.definition??void 0,children:[d.name,e.jsx("span",{className:"sme-sa-count",children:(a[d.id]??d.members).length})]},d.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:d=>b(d.target.value)})]}),j.length>0&&t&&e.jsxs("div",{className:"sa-nudge",children:[j.length," object",j.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>z(j,!0),children:["Add ",j.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${F.length})`,rows:F,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>!u.has(d)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!F.length,onClick:()=>z(F.map(d=>d.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>u.has(d)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!P.length,onClick:()=>z(P.map(d=>d.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${P.length})`,rows:P,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:g,onChange:d=>_(d.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:d=>T(Math.max(1,Math.min(10,Number(d.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!t,children:"Preview"})]}),R&&e.jsx("p",{className:"sme-error-line",children:R}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(d=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(d.id),onChange:()=>W(A,d.id,k)}),e.jsx("span",{className:"sa-hop-name",children:d.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",d.via_name," · ",d.direction.replace(/s$/,""),", level ",d.level]})]},d.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>y(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:K,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function Q({title:s,rows:t,picked:a,onToggle:l,homedHere:i}){const x=n.useMemo(()=>new Set(i),[i]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[t.map(c=>e.jsxs("li",{className:"sa-row"+(a.has(c.id)?" picked":""),onClick:()=>l(c.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(c.id)}),e.jsx("span",{className:"sa-row-name",children:c.name}),x.has(c.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},c.id)),!t.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function es({objects:s,onCreate:t,onClose:a}){const[l,i]=n.useState(""),[x,c]=n.useState(""),[r]=n.useState(s),[m,N]=n.useState([]),[b,f]=n.useState(new Set),[h,g]=n.useState(""),[_,C]=n.useState("ancestors"),[T,S]=n.useState(1),[y,A]=n.useState(null),[k,R]=n.useState(new Set),[o,E]=n.useState(""),[$,D]=n.useState(null);n.useEffect(()=>{xe().then(j=>{j.subject_areas[0]&&E(j.subject_areas[0].id)}).catch(()=>{})},[]);const B=n.useMemo(()=>new Set(m),[m]),u=h.trim().toLowerCase(),P=j=>!u||j.name.toLowerCase().includes(u),F=n.useMemo(()=>m.map(j=>r.find(d=>d.id===j)).filter(j=>!!j&&P(j)),[m,r,u]),I=n.useMemo(()=>r.filter(j=>!B.has(j.id)&&P(j)),[r,B,u]),z=(j,d)=>{N(w=>d?[...w,...j.filter(M=>!w.includes(M))]:w.filter(M=>!j.includes(M))),f(new Set)},W=(j,d,w)=>{const M=new Set(j);M.has(d)?M.delete(d):M.add(d),w(M)},U=()=>{const j=b.size?[...b]:m;if(!j.length){D("pick an object to expand from first");return}o&&(D(null),be(o,{seeds:j,direction:_,levels:T}).then(d=>{const w=d.hops.filter(M=>!B.has(M.id));A(w),R(new Set(w.map(M=>M.id)))}).catch(d=>D(String(d))))},K=()=>{l.trim()&&t(l.trim(),x.trim(),m)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:j=>j.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:l,placeholder:"e.g. UseCase-ApraStressTesting",onChange:j=>i(j.target.value),onKeyDown:j=>j.key==="Enter"&&m.length===0&&K()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:x,placeholder:"what this view is for",onChange:j=>c(j.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:h,onChange:j=>g(j.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${I.length})`,rows:I,picked:b,onToggle:j=>W(b,j,f),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>!B.has(j)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!I.length,onClick:()=>z(I.map(j=>j.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>B.has(j)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!F.length,onClick:()=>z(F.map(j=>j.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${F.length})`,rows:F,picked:b,onToggle:j=>W(b,j,f),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:_,onChange:j=>C(j.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:T,onChange:j=>S(Math.max(1,Math.min(10,Number(j.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!o,title:o?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),y&&e.jsxs("div",{className:"sa-hops",children:[y.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),y.map(j=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:k.has(j.id),onChange:()=>W(k,j.id,R)}),e.jsx("span",{className:"sa-hop-name",children:j.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",j.via_name," · ",j.direction.replace(/s$/,""),", level ",j.level]})]},j.id)),y.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!k.size,onClick:()=>{z([...k],!0),A(null)},children:["Add ",k.size]})]})]})]}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!l.trim(),onClick:K,children:["Create",m.length>0&&` with ${m.length} object${m.length===1?"":"s"}`]})]})]})})}const ss=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function ns({doc:s,subjectArea:t,onSubjectArea:a,exec:l,canEdit:i,direct:x=!1,onSelectEntity:c,onImported:r,query:m="",busy:N}){var R;const[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(!1),[T,S]=n.useState(null),y=n.useRef(null),A=n.useMemo(()=>({canEdit:i,mode:x?"direct":"staged",allow:x?void 0:ss,canArbitrate:x,canCommit:x}),[i,x]),k=n.useCallback(o=>{f(o),o&&(c==null||c(o))},[c]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),i&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(t===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((R=s.counts)==null?void 0:R.entities_total)??s.entities.length})]}),s.subject_areas.map(o=>e.jsxs("button",{className:"sme-sa"+(t===o.id?" active":""),onClick:()=>a(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:o.member_count??0})]},o.id))]}),_&&e.jsx(es,{objects:s.entities.flatMap(o=>o.conceptual?[{id:o.conceptual.id,kind:"conceptual_entity",name:o.conceptual.name,definition:o.conceptual.definition??null,synonyms:[],subject_area:o.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(o,E,$)=>{const D=ge();l("create_subject_area",{name:o,...E?{definition:E}:{},id:D}),$.length&&l("set_subject_area_members",{id:D,members:$}),C(!1),a(D)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),i&&e.jsx("button",{className:"erd-action",onClick:()=>{var o;return(o=y.current)==null?void 0:o.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(h==="ontology"?" on":""),onClick:()=>g(o=>o==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(h==="layers"?" on":""),onClick:()=>g(o=>o==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Re,{exec:l,canEdit:i,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!i&&e.jsx("span",{className:"sme-chip",children:"read-only"}),T&&e.jsx("span",{className:"erd-error",children:T})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Me,{children:e.jsx(De,{doc:s,caps:A,exec:l,selectedId:b,onSelect:k,query:m,onError:S,handleRef:y})}),h&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:h==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>g(null),children:"✕"})]}),h==="ontology"?e.jsx(Pe,{}):e.jsx(Ie,{refreshKey:0,onFocusEntity:o=>{var E;g(null),(E=y.current)==null||E.focusEntity(o)}})]})]})]})]})}function as({user:s,onView:t}){const[a,l]=n.useState(null),[i,x]=n.useState(null);if(n.useEffect(()=>{Oe(s).then(l).catch(r=>x(String(r)))},[s]),i)return e.jsx("p",{className:"sme-splash error",children:i});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const c=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[c," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(rs,{p:r,gh:a.gh,onView:()=>t(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function ts(s,t){var a,l;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?t&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:t&&((l=s.pr)==null?void 0:l.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function rs({p:s,gh:t,onView:a}){const l=ts(s,t);return e.jsxs("div",{className:"pr-card"+(l.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+l.kind,children:l.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",t&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?t&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function is({changes:s,routeAdvice:t,identity:a,user:l,onUser:i,onDrop:x,onClose:c,onProposed:r}){const m=!!(a&&a.source!=="anonymous"),[N,b]=n.useState(l||(localStorage.getItem("mdl.sme.user")??"")),[f,h]=n.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[g,_]=n.useState(""),[C,T]=n.useState(!1),S=ve(C?g:f),[y,A]=n.useState(""),[k,R]=n.useState(!1),[o,E]=n.useState(null),[$,D]=n.useState(null),B=()=>{!m&&!N.trim()||(m||(localStorage.setItem("mdl.sme.user",N.trim()),i==null||i(N.trim())),R(!0),D(null),Te({user:m?"":N.trim(),slug:S,title:f,body:y.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then(E).catch(u=>D(u instanceof Be?u.message:String(u))).finally(()=>R(!1)))};return o!=null&&o.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(o),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:o.message}),o.pr_url&&e.jsx("p",{children:e.jsx("a",{href:o.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),o.compare_url&&e.jsx("p",{children:e.jsx("a",{href:o.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:o.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(o),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:c,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:c,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,P)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>x(P),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},P))}),m?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>b(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:f,onChange:u=>h(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?g:S,onChange:u=>{T(!0),_(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:y,onChange:u=>A(u.target.value)})]}),e.jsx(ls,{cl:t,user:m?a.name:N,changeSlug:S}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:c,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:k||!m&&!N.trim()||s.length===0,onClick:B,children:k?"Submitting…":"Submit for review"})]})]})})}function ls({cl:s,user:t,changeSlug:a}){if(!(s!=null&&s.primary))return null;const l=s.reviewers_actual??s.reviewers,i=ve(t||"you"),x=s.routes.length>1;return e.jsxs(e.Fragment,{children:[x&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",l.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",i,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:l.join(", ")})]})]})]})}function ve(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const cs="modulepreload",os=function(s){return"/"+s},oe={},ds=function(t,a,l){let i=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),r=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));i=Promise.allSettled(a.map(m=>{if(m=os(m),m in oe)return;oe[m]=!0;const N=m.endsWith(".css"),b=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${b}`))return;const f=document.createElement("link");if(f.rel=N?"stylesheet":cs,N||(f.as="script"),f.crossOrigin="",f.href=m,r&&f.setAttribute("nonce",r),document.head.appendChild(f),N)return new Promise((h,g)=>{f.addEventListener("load",h),f.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${m}`)))})}))}function x(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return i.then(c=>{for(const r of c||[])r.status==="rejected"&&x(r.reason);return t().catch(x)})};function hs({diff:s,selectable:t,selected:a,onToggle:l}){var r;const[i,x]=n.useState(((r=s.objects[0])==null?void 0:r.ulid)??null),c=n.useMemo(()=>s.objects.find(m=>m.ulid===i)??s.objects[0]??null,[s,i]);return s.objects.length?e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:s.objects.map(m=>e.jsx(us,{obj:m,active:m.ulid===((c==null?void 0:c.ulid)??""),checked:(a==null?void 0:a.has(m.ulid))??!0,selectable:t,onSelect:()=>x(m.ulid),onToggle:()=>l==null?void 0:l(m.ulid)},m.ulid))}),e.jsx("div",{className:"rv-detail",children:c&&e.jsx(ps,{obj:c})})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function ms(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function us({obj:s,active:t,checked:a,selectable:l,onSelect:i,onToggle:x}){return e.jsxs("div",{className:"rv-obj"+(t?" active":""),onClick:i,children:[e.jsxs("div",{className:"rv-obj-head",children:[l&&e.jsx("input",{type:"checkbox",checked:a,onClick:c=>c.stopPropagation(),onChange:x,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",ms(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,r)=>e.jsx("li",{children:c.label},c.field+r)),s.children.flatMap(c=>c.fields.map((r,m)=>e.jsxs("li",{children:[r.label,": ",c.name_before??c.name_after]},c.ulid+m)))]})]})}function ps({obj:s}){const t=s.children.flatMap(a=>a.fields.map(l=>({field:l,on:a.name_before??a.name_after??""})));return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((a,l)=>e.jsx(de,{f:a},a.field+l)),t.map(({field:a,on:l},i)=>e.jsx(de,{f:a,on:l},"c"+i)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function de({f:s,on:t}){var l,i,x;const a=t?`${s.label}: ${t}`:s.label;if((l=s.breaks)!=null&&l.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[t??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(c=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",c.name]}),e.jsxs("span",{className:"p",children:[c.target," · ",c.materialization]})]},c.name))})]})]});if(s.kind==="definition_changed"){const c=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:me(c,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:me(r,c,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const c=new Set(((i=s.before)==null?void 0:i.map(String))??[]),r=new Set(((x=s.after)==null?void 0:x.map(String))??[]),m=[...r].filter(b=>!c.has(b)),N=[...c].filter(b=>!r.has(b));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[m.map(b=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",b]},"a"+b)),N.map(b=>e.jsxs("span",{className:"rv-chip-del",children:["− ",b]},"r"+b))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:he(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:he(s.after)})]})]})}function he(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function me(s,t,a){const l=s.split(/(\s+)/),i=t.split(/(\s+)/),x=h=>h.toLowerCase().replace(/[.,;:]/g,""),c=l.length,r=i.length,m=Array.from({length:c+1},()=>new Array(r+1).fill(0));for(let h=c-1;h>=0;h--)for(let g=r-1;g>=0;g--)m[h][g]=x(l[h])===x(i[g])?m[h+1][g+1]+1:Math.max(m[h+1][g],m[h][g+1]);const N=new Set;let b=0,f=0;for(;b=m[b][f+1]?b++:f++;return l.map((h,g)=>!h.trim()||N.has(g)?e.jsx("span",{children:h},g):a==="del"?e.jsx("del",{children:h},g):e.jsx("ins",{children:h},g))}const js=n.lazy(()=>ds(()=>import("./ModelDiagram-C9iVTvG9.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function xs({stagedDiff:s,stagedRoute:t,user:a,onBack:l,onSubmit:i,excluded:x,onToggleObject:c,selectable:r}){const[m,N]=n.useState(null),[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(null),[T,S]=n.useState(null),[y,A]=n.useState("changes"),k=n.useCallback(()=>{je(a).then(o=>{C(o);const E=o.base_branch??"main";s?N(s):Le("HEAD").then($=>$.ok?N($):S($.error??"could not read the model")).catch($=>S(String($))),t?f(t):Fe("HEAD").then(f).catch(()=>{}),ze(E).then(g).catch(()=>{})}).catch(o=>S(String(o)))},[a,s,t]);if(n.useEffect(k,[k]),T)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:T})})]});if(!m)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const R=new Set(m.objects.map(o=>o.ulid).filter(o=>!x.has(o)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:m,tab:y,onTab:A}),y==="changes"?e.jsx(hs,{diff:m,selectable:r,selected:R,onToggle:c}):e.jsx(n.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(js,{severityByUlid:bs(m)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(gs,{cl:b}),e.jsx(fs,{conf:h,ctx:_}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:l,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!m.objects.length||(_?!_.can_propose:!1),title:_&&!_.can_propose?"the working tree has uncommitted changes":"",onClick:()=>i(b),children:"Submit for review →"})]})]})]})}function bs(s){const t=new Map;for(const a of s.objects)t.set(a.ulid,a.severity);return t}function ue({onBack:s,diff:t,tab:a,onTab:l}){const i=t==null?void 0:t.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),i&&e.jsxs("div",{className:"rv-sub",children:[i.objects," object",i.objects===1?"":"s"," ·"," ",(i.breaking??0)+(i.additive??0)+(i.cosmetic??0)," changes",(t==null?void 0:t.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",i.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:t==null?void 0:t.base.label})]})]}),l&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>l("changes"),children:["Changes ",(t==null?void 0:t.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>l("diagram"),children:"Diagram"})]})]})}function gs({cl:s}){if(!(s!=null&&s.primary))return null;const t=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:t.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function fs({conf:s,ctx:t}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(t==null?void 0:t.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function vs({term:s,canEdit:t,pendingCount:a,onEdit:l}){var i,x;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((i=s.subject_area)==null?void 0:i.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),t&&e.jsx("button",{className:"sme-primary",onClick:l,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(c=>e.jsx("span",{className:"sme-chip",children:c},c))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((x=s.ontology)==null?void 0:x.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(c=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:c.logical_entity}),c.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),c.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:c.physical.map(r=>r.name).join(", ")})]},c.logical_id))})]})]})}function Ns({term:s,pending:t,onStage:a,onDone:l,catalogOwned:i=[],catalog:x=null}){var E,$,D,B;const c=u=>i.includes(u),r=u=>t.find(P=>P.label===u),m=(u,P)=>{const F=r(u);return F?F.after:P},[N,b]=n.useState(m("Definition",s.definition??"")),[f,h]=n.useState(s.synonyms),[g,_]=n.useState(""),[C,T]=n.useState(((E=s.stewardship)==null?void 0:E.owner)??""),[S,y]=n.useState((($=s.stewardship)==null?void 0:$.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},k=(u,P)=>{var F,I;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:P||null},label:"Stewardship",before:`owner: ${((F=s.stewardship)==null?void 0:F.owner)??"—"} · steward: ${((I=s.stewardship)==null?void 0:I.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${P||"—"}`})},R=u=>{h(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},o=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:l,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(c("definition")||c("synonyms")||c("stewardship"))&&x&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:x.name}),". Edit them there — this glossary mirrors them read-only.",x.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:x.url,target:"_blank",rel:"noreferrer",children:["Open in ",x.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),c("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>b(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),c("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[f.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>R(f.filter(P=>P!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:g,placeholder:"add + Enter",onChange:u=>_(u.target.value),onKeyDown:u=>{u.key==="Enter"&&g.trim()&&(R([...f,g.trim()]),_(""))}})]})]}),!o&&(c("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((D=s.stewardship)==null?void 0:D.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((B=s.stewardship)==null?void 0:B.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>T(u.target.value),onBlur:()=>k(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>y(u.target.value),onBlur:()=>k(C,S)})]})]})),e.jsx(ys,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:t.length>0?`${t.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const ws=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function ys({term:s,onStage:t}){var f;const[a,l]=n.useState(!1),[i,x]=n.useState(s.name),[c,r]=n.useState([]),[m,N]=n.useState("skos:closeMatch"),b=h=>{x(h),qe(h).then(g=>r(g.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:i,onChange:h=>b(h.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:c.map(h=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var g;t({op:"set_alignment",payload:{id:s.id,aligns_to:h.prefixed,alignment:m,status:"proposed"},label:"Standard mapping",before:((g=s.ontology)==null?void 0:g.aligns_to)??"(none)",after:`${h.prefixed} (${m}, proposed)`}),l(!1)},children:[e.jsx("strong",{children:h.label})," ",e.jsx("code",{children:h.prefixed}),h.definition&&e.jsx("span",{className:"sme-align-def",children:h.definition})]},h.iri))}),e.jsx("select",{value:m,onChange:h=>N(h.target.value),children:ws.map(h=>e.jsx("option",{children:h},h))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(f=s.ontology)!=null&&f.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>l(!0),children:"Propose a mapping…"})]})]})}function _s(){const[s,t]=n.useState(null),[a,l]=n.useState(!0),[i,x]=n.useState(null),[c,r]=n.useState(""),[m,N]=n.useState(null),[b,f]=n.useState(""),[h,g]=n.useState(""),[_,C]=n.useState(null),[T,S]=n.useState(!1),[y,A]=n.useState([]),[k,R]=n.useState(!1),[o,E]=n.useState(window.location.hash==="#proposals"?"proposals":"browse"),[$,D]=n.useState(new Set),[B,u]=n.useState(()=>localStorage.getItem("mdl.sme.user")??""),[P,F]=n.useState(null),[I,z]=n.useState(null),[W,U]=n.useState(!1),[K,j]=n.useState(null),d=n.useCallback((p,v)=>{var ae;const O=J=>{var te;const V=I==null?void 0:I.entities.find(re=>{var ie;return re.id===J||((ie=re.conceptual)==null?void 0:ie.id)===J});return((te=V==null?void 0:V.conceptual)==null?void 0:te.name)??(V==null?void 0:V.name)??"object"},L=O(v.id??v.entity_id);switch(p){case"create_entity":return{label:`New entity: ${v.name}`,before:"—",after:String(v.name??"")};case"create_relationship":return{label:`New relationship: ${O(v.from_entity)} → ${O(v.to_entity)}`,before:"—",after:String(v.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${L}`,before:L,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[v.cardinality,v.optionality,v.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${L}`,before:"",after:String(v.definition??"")};case"add_attribute":return{label:`Attribute added to ${L}`,before:"—",after:String(v.name??"")};case"delete_attribute":return{label:`Attribute removed from ${L}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${L}`,before:"",after:JSON.stringify(v)};case"rename_entity":return{label:`Renamed ${L}`,before:L,after:String(v.name??"")};case"set_subject_area":return{label:`Subject area: ${L}`,before:"",after:String(v.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${L}`,before:"",after:String(v.steward??"")};case"set_subject_area_members":{const J=v.__label,V=((ae=v.members)==null?void 0:ae.length)??0;return{label:J??"Subject area members",before:"",after:`${V} object${V===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${L}`,before:"",after:String(v.aligns_to??"")};default:return{label:`${p.replace(/_/g," ")}: ${L}`,before:"",after:""}}},[I]),w=Je({subjectArea:b,describe:d}),M=n.useCallback(async(p,v)=>{const O=(I==null?void 0:I.fingerprint)??"",L=await We(p,v,O);return await le(b||void 0).then(z),L},[I,b]),q=n.useMemo(()=>{const p={};for(const v of w.pending)v.op==="set_subject_area_members"&&(p[v.payload.id]=v.payload.members??[]);return p},[w.pending]),G=n.useMemo(()=>[...y,...w.pending],[y,w.pending]),[Ne,X]=n.useState(null),[we,Z]=n.useState(null);n.useEffect(()=>{if(!G.length){X(null),Z(null);return}const p=new AbortController,v=G.map(O=>({op:O.op,payload:O.payload}));return pe(v,b||void 0,p.signal).then(O=>X(O.diff??null)).catch(O=>{O.name!=="AbortError"&&X(null)}),Ge(v).then(Z).catch(()=>Z(null)),()=>p.abort()},[G,b]);const ye=n.useMemo(()=>Qe(G),[G]).size{E(p),window.location.hash=p==="browse"?"":`#${p}`},[]);n.useEffect(()=>{const p=()=>{const v=window.location.hash;E(v==="#proposals"?"proposals":v==="#review"?"review":v==="#model"?"model":v==="#areas"?"areas":"browse")};return p(),window.addEventListener("hashchange",p),()=>window.removeEventListener("hashchange",p)},[]);const ee=n.useCallback(()=>{He({subject_area:b,q:h}).then(t).catch(p=>N(String(p)))},[b,h]);n.useEffect(ee,[ee]),n.useEffect(()=>{le(b||void 0).then(p=>{l(p.read_only),r(p.project.name),z(p),U(!!p.direct),F(p.identity??null),p.identity&&p.identity.source!=="anonymous"&&p.identity.name&&u(p.identity.name)}).catch(()=>{}),Ve().then(x).catch(()=>{})},[b]);const se=n.useMemo(()=>(s==null?void 0:s.terms.find(p=>p.id===_))??null,[s,_]),_e=n.useCallback(p=>{const v=p.key??fe(p.op,p.payload);A(O=>[...O.filter(L=>L.key!==v),{...p,key:v}])},[]),Se=p=>A(v=>v.filter((O,L)=>L!==p)),ke=()=>{A([]),R(!1),S(!1),ee()};if(m)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const Y=!a,ne=(i==null?void 0:i.catalog_owned_fields)??[],Ce=ne.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:c}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Ce&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${i==null?void 0:i.catalog_name}`,children:["mirrors ",i==null?void 0:i.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:h,onChange:p=>g(p.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(o==="browse"?" active":""),onClick:()=>H("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(o==="model"?" active":""),onClick:()=>H("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(o==="areas"?" active":""),onClick:()=>H("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(o==="proposals"?" active":""),onClick:()=>H("proposals"),children:"My proposals"})]}),G.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>H("review"),children:[G.length," change",G.length>1?"s":""," · Review →"]})]}),o==="browse"&&e.jsx(Xe,{user:B}),o==="review"?e.jsx(xs,{stagedDiff:Ne,stagedRoute:we,user:B,selectable:ye,excluded:$,onToggleObject:p=>D(v=>{const O=new Set(v);return O.has(p)?O.delete(p):O.add(p),O}),onBack:()=>H("browse"),onSubmit:p=>{j(p),R(!0)}}):o==="model"?w.previewDoc??I?e.jsx(ns,{doc:w.previewDoc??I,subjectArea:b,onSubjectArea:f,exec:W?M:w.exec,canEdit:Y,direct:W,onImported:()=>H("review"),query:h,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):o==="areas"?e.jsx(Ze,{exec:w.exec,canEdit:Y,stagedMembers:q}):o==="proposals"?e.jsx(as,{user:B,onView:()=>H("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(b===""?" active":""),onClick:()=>f(""),children:"All subject areas"}),s.subject_areas.map(p=>e.jsx("button",{className:"sme-sa"+(b===p.id?" active":""),onClick:()=>f(p.id),title:p.definition??void 0,children:p.name},p.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(p=>{var v;return e.jsxs("li",{className:"sme-list-item"+(p.id===_?" active":""),onClick:()=>{C(p.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:p.name}),p.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((v=p.ontology)==null?void 0:v.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:p.definition??"no definition yet"})]},p.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:se?T&&Y?e.jsx(Ns,{term:se,pending:y,onStage:_e,onDone:()=>S(!1),catalogOwned:ne,catalog:i?{name:i.catalog_name,url:i.catalog_url}:null}):e.jsx(vs,{term:se,canEdit:Y,pendingCount:y.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),k&&e.jsx(is,{changes:G,routeAdvice:K,identity:P,user:B,onUser:p=>{u(p),localStorage.setItem("mdl.sme.user",p)},onDrop:Se,onClose:()=>R(!1),onProposed:()=>{ke(),w.clear(),D(new Set),H("proposals")}})]})}Ae.createRoot(document.getElementById("root")).render(e.jsx(Ee.StrictMode,{children:e.jsx(_s,{})})); diff --git a/packages/server/src/mdl_server/static/assets/styles-BG76mhX4.css b/packages/server/src/mdl_server/static/assets/styles-BG76mhX4.css new file mode 100644 index 0000000..a60feb7 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/styles-BG76mhX4.css @@ -0,0 +1 @@ +.react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:-webkit-grab;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;-webkit-animation:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:-webkit-grab;cursor:grab}.react-flow__node.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:-webkit-grab;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@-webkit-keyframes dashdraw{0%{stroke-dashoffset:10}}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.ie-wrap{position:relative;display:inline-flex;gap:6px}.erd-ico{font-size:13px;line-height:1;opacity:.85;margin-right:1px}.ie-wrap button:hover .erd-ico,.ie-wrap button.on .erd-ico,.ie-wrap button.active .erd-ico{opacity:1}.erd-caret{opacity:.55;margin-left:1px}.ie-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:220px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:6px;box-shadow:0 12px 32px #00000073;display:flex;flex-direction:column;gap:2px}.ie-item{display:block;padding:8px 10px;border-radius:6px;color:var(--text);text-decoration:none;font-size:13px;cursor:pointer}.ie-item:hover{background:#5eead424;color:var(--accent)}.ie-dialect{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 10px 8px;margin-bottom:4px;border-bottom:1px solid var(--border);font-size:12px;color:var(--text-dim)}.ie-dialect select{background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:3px 6px;font-size:12px}.ie-import{width:380px;padding:12px;gap:8px}.ie-tabs{display:flex;gap:4px}.ie-tab{flex:1;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;background:none;border:1px solid var(--border);color:var(--text-dim)}.ie-tab.active{border-color:var(--accent);color:var(--accent);background:#5eead41a}.ie-hint{font-size:11px;color:var(--text-dim);margin:0}.ie-textarea{width:100%;font-family:var(--mono);font-size:12px;resize:vertical;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px;box-sizing:border-box}.ie-textarea:focus{border-color:var(--accent);outline:none}.ie-file{font-size:11px;color:var(--text-dim)}.ie-warn{font-size:11px;color:var(--pk);margin:0}.ie-error{font-size:12px;color:var(--err);margin:0}.ie-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}:root{--bg: #14110f;--bg-raised: #1c1815;--bg-card: #221d19;--border: #33291f;--border-strong: #4a3c2c;--text: #ece6dd;--text-dim: #a89a86;--accent: #e0a63a;--accent-soft: rgba(224, 166, 58, .15);--accent-ink: #241a08;--pk: #f2c14e;--err: #f0857a;--warn: #e6a94a;--ok: #6cbf73;--font: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;--mono: "SF Mono", ui-monospace, "Cascadia Code", Menlo, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font);background:var(--bg);color:var(--text)}.app{display:flex;flex-direction:column;height:100%}.canvas-wrap{flex:1;position:relative;min-height:0}.react-flow{background:var(--bg)}.topbar{display:flex;flex-direction:column;padding:8px 16px 0;background:var(--bg-raised);border-bottom:1px solid var(--border);z-index:10}.topbar-row{display:flex;align-items:center;gap:14px}.topbar-row.context{padding-bottom:8px}.topbar-row.actions{gap:10px;padding-bottom:8px;flex-wrap:wrap}.brand{display:flex;align-items:baseline;gap:8px;white-space:nowrap}.logo{color:var(--accent);font-size:18px}.brand-name{font-weight:700;letter-spacing:.02em}.project-name{color:var(--text-dim);font-size:13px}.search{flex:0 1 300px;margin-left:auto;padding:7px 12px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-size:13px;outline:none}.search:focus{border-color:var(--accent)}.stats{display:flex;align-items:center;gap:12px;font-size:12px;color:var(--text-dim)}.stat-item{display:inline-flex;align-items:center;gap:5px}.stat-item svg{color:var(--text-dim)}.diag-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:99px;font-weight:600}.diag-chip.ok{background:#34d39926;color:var(--ok)}.diag-chip.warn{background:#fbbf2426;color:var(--warn)}.diag-chip.err{background:#f8717126;color:var(--err)}.btn-group{display:inline-flex;align-items:center;gap:2px;background:var(--bg-card);border:1px solid var(--border);border-radius:9px;padding:2px}.grp-tag{font-size:10px;color:var(--text-dim);opacity:.75;padding:0 6px 0 4px;letter-spacing:.03em;text-transform:none;white-space:nowrap}.grp-btn{display:inline-flex;align-items:center;gap:5px;padding:5px 9px;border-radius:6px;border:none;background:transparent;color:var(--text-dim);cursor:pointer;font-size:12px;white-space:nowrap}.grp-btn.icon-only{padding:5px 7px}.grp-btn:hover{color:var(--text);background:var(--bg-hover, rgba(255,255,255,.05))}.grp-btn.active{color:var(--accent)}.grp-btn.active svg{color:var(--accent)}.grp-badge{width:6px;height:6px;border-radius:50%;background:var(--warn);display:inline-block;margin-left:1px}.tool-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 11px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer;font-size:12px}.tool-btn:hover{color:var(--text);border-color:var(--border-strong)}.sa-legend{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:6px 0 8px;font-size:11px;color:var(--text-dim);border-top:1px solid var(--border)}.sa-legend-label{color:var(--text-dim);opacity:.7}.legend-item{display:inline-flex;align-items:center;gap:5px}.swatch{width:10px;height:10px;border-radius:3px;display:inline-block}.tool-btn.active{color:var(--accent);border-color:var(--accent)}.layout-picker{position:relative;display:inline-flex;gap:0}.layout-picker .caret{padding:5px 4px}.layout-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:20;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:8px;padding:4px;min-width:150px;box-shadow:0 8px 24px #0006}.layout-menu button{display:block;width:100%;text-align:left;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--text-dim);cursor:pointer;font-size:13px}.layout-menu button:hover{background:var(--bg-card);color:var(--text)}.entity-node{width:264px;background:var(--bg-card);border:1px solid var(--border);border-top:3px solid var(--accent);border-radius:10px;overflow:hidden;box-shadow:0 4px 18px #00000059;font-size:12px;transition:opacity .15s,box-shadow .15s,border-color .15s}.entity-node.selected{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent),0 8px 24px #00000080}.entity-node.highlighted{box-shadow:0 0 0 2px var(--pk),0 8px 24px #00000080}.entity-node.dimmed{opacity:.18}.entity-header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:#ffffff08;border-bottom:1px solid var(--border)}.entity-name{font-weight:700;font-size:13px;letter-spacing:.01em}.entity-badges{display:flex;gap:4px}.badge{font-size:10px;padding:1px 6px;border-radius:99px;background:var(--accent-soft);color:var(--accent);text-transform:uppercase;letter-spacing:.05em}.badge.ontology{background:#93c5fd1f;color:#93c5fd}.attr-section{padding:4px 0}.attr-section.keys{border-bottom:1px solid var(--border);background:#fcd34d0a}.attr-row{display:flex;align-items:center;gap:6px;padding:3px 12px;height:26px;line-height:20px;position:relative}.attr-row.empty{color:var(--text-dim);font-style:italic}.attr-row.more{color:var(--text-dim);font-style:italic;cursor:pointer;justify-content:center;height:22px;font-size:11px}.attr-row.more:hover{color:var(--accent);background:var(--accent-soft)}.attr-row.rel-hit{background:var(--accent-soft);box-shadow:inset 2px 0 0 var(--accent)}.attr-row.rel-hit .attr-name{color:var(--accent);font-weight:600}.attr-row.rel-dim{opacity:.35}.attr-row.endpoint .attr-name{font-style:normal}.attr-port{width:8px!important;height:8px!important;opacity:0;top:50%!important}.attr-port.react-flow__handle-left{left:-5px!important}.attr-port.react-flow__handle-right{right:-5px!important}.entity-node:hover .attr-port{opacity:.5}.attr-row:hover .attr-port{opacity:1!important;background:var(--accent)!important}.attr-icon{width:14px;text-align:center;font-size:10px;flex:none}.attr-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attr-name.pk{color:var(--pk);font-weight:600}.attr-type{color:var(--text-dim);font-family:var(--mono);font-size:10px;flex:none}.attr-notnull{color:var(--text-dim);font-size:8px;flex:none}.port{width:11px!important;height:11px!important;background:var(--border-strong)!important;border:2px solid var(--bg)!important;opacity:.55;transition:transform .12s ease,background .12s ease,opacity .12s ease}.entity-node:hover .port{opacity:1;background:var(--accent)!important;transform:scale(1.25);box-shadow:0 0 0 4px var(--accent-soft);cursor:crosshair}.port:hover{transform:scale(1.6)!important;cursor:crosshair}.entity-node:hover{border-color:var(--accent);box-shadow:0 0 0 1px #e0a63a40}.react-flow__connectionline .react-flow__edge-path,.react-flow__connection-path{stroke:var(--accent)!important;stroke-width:2!important}.rel-path{stroke:var(--border-strong);stroke-width:1.6}.rel-edge{cursor:pointer}.rel-glyph{stroke:var(--border-strong);stroke-width:1.6;fill:none}.rel-glyph.ring{fill:var(--bg)}.rel-edge:hover .rel-path,.rel-edge.selected .rel-path,.rel-edge:hover .rel-glyph,.rel-edge.selected .rel-glyph{stroke:var(--accent)}.rel-edge.dimmed{opacity:.12}.rel-label{fill:var(--text);font-size:11px;text-anchor:middle;paint-order:stroke;stroke:var(--bg);stroke-width:4px}.detail-panel{position:absolute;top:12px;right:12px;bottom:12px;width:340px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:18px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080}.detail-header{display:flex;justify-content:space-between;align-items:flex-start}.detail-header h2{margin:0 0 6px;font-size:18px}.icon-btn{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:14px}.icon-btn:hover{color:var(--text)}.definition{color:var(--text-dim);font-size:13px;line-height:1.5}.detail-panel section{margin-top:18px}.detail-panel h3{margin:0 0 8px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}.kv{display:flex;gap:8px;margin:4px 0;font-size:12px;align-items:baseline}.kv .k{color:var(--text-dim);min-width:72px}.kv .v{word-break:break-all}.chip{display:inline-block;font-size:11px;padding:2px 8px;margin:2px 4px 2px 0;border-radius:99px;background:#94a3b81f;color:var(--text-dim)}.chip.pattern{background:var(--accent-soft);color:var(--accent)}.chip.layer-industry{background:#93c5fd26;color:#93c5fd}.chip.layer-core{background:var(--accent-soft);color:var(--accent)}.chip.layer-domain{background:#f0abfc26;color:#f0abfc}.chip.layer-specialised{background:#fcd34d26;color:var(--pk)}.cat-badge{font-size:10px;font-weight:700;margin-left:6px;padding:2px 8px;border-radius:5px;cursor:help}.cat-badge.supertype{background:#a78bfa2e;color:#a78bfa}.cat-badge.subtype{background:var(--accent-soft);color:var(--accent)}.udp-row td{padding:3px 6px;border-bottom:1px solid var(--border);font-size:12px}.udp-key{color:var(--text-dim);font-family:var(--mono);font-size:11px}.udp-val{color:var(--text)}.enum-badge{font-size:8px;font-weight:700;margin-left:6px;padding:1px 5px;border-radius:4px;background:#a78bfa2e;color:#a78bfa;vertical-align:middle;cursor:help}.key-row{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:12px}.key-badge{font-size:9px;font-weight:700;padding:2px 6px;border-radius:5px;background:#94a3b826;color:var(--text-dim);flex:none}.key-badge.pk{background:#fcd34d2e;color:var(--pk)}.key-badge.unique,.key-badge.alternate{background:var(--accent-soft);color:var(--accent)}.key-name{font-weight:600}.key-cols{color:var(--text-dim);font-family:var(--mono);font-size:11px}.attr-table{width:100%;border-collapse:collapse;font-size:12px;table-layout:auto}.attr-table td{padding:4px 6px;border-bottom:1px solid var(--border)}.attr-table td:first-child{width:100%}.attr-table td:first-child .edit-input{min-width:120px}.attr-table td.pk{color:var(--pk);font-weight:600}.attr-table td.type{color:var(--text-dim);font-family:var(--mono);font-size:11px}.attr-table td.null{color:var(--text-dim);font-size:10px;text-align:right}.rel-link{display:flex;gap:8px;align-items:center;width:100%;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:7px 10px;margin:4px 0;cursor:pointer;font-size:12px;text-align:left}.rel-link:hover{border-color:var(--accent)}.rel-card{color:var(--text-dim);font-family:var(--mono);font-size:11px}.ulid{font-family:var(--mono);font-size:11px;color:var(--text-dim);cursor:copy;word-break:break-all}.ulid:hover{color:var(--accent)}.splash{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-dim)}.splash.error pre{background:var(--bg-card);padding:12px 16px;border-radius:8px;max-width:640px;overflow:auto;color:var(--err);font-size:12px}.react-flow__minimap{background:var(--bg-raised)!important;border-radius:10px}.react-flow__controls{border-radius:10px;overflow:hidden;box-shadow:none!important}.react-flow__controls-button{background:var(--bg-raised)!important;border-bottom:1px solid var(--border)!important;fill:var(--text-dim)!important}.react-flow__controls-button:hover{fill:var(--text)!important}.edit-input{width:100%;padding:6px 9px;border-radius:7px;font-size:12px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-family:var(--font);outline:none}.edit-input:focus{border-color:var(--accent)}.entity-title-input{font-size:17px;font-weight:700;background:transparent;border-color:transparent;padding-left:0}.entity-title-input:hover,.entity-title-input:focus{border-color:var(--border);background:var(--bg-card);padding-left:9px}.definition-input{color:var(--text-dim);line-height:1.5;resize:vertical}.chip-select,.cell-select{font-size:11px;padding:2px 6px;margin:2px 6px 2px 0;border-radius:99px;background:#94a3b81f;color:var(--text-dim);border:1px solid transparent;outline:none}.chip-select:hover,.cell-select:hover{border-color:var(--border-strong);color:var(--text)}.cell-select{border-radius:6px;background:transparent;max-width:110px}.mini-btn{font-size:11px;padding:3px 9px;border-radius:7px;cursor:pointer;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim)}.mini-btn:hover{color:var(--accent);border-color:var(--accent)}.mini-btn.danger:hover{color:var(--err);border-color:var(--err)}.h3-actions{float:right;display:inline-flex;gap:6px}.empty-hint{color:var(--text-dim);font-size:12px;font-style:italic}.add-attr{display:flex;gap:6px;margin-top:8px}.rel-row{display:flex;gap:6px;align-items:flex-start}.rel-row .rel-main{flex:1}.verb-phrases{display:flex;align-items:center;gap:4px;margin-top:2px;padding-left:4px}.verb-phrases .verb-input{font-size:11px;font-style:italic;color:var(--text-dim);min-width:40px}.verb-phrases .verb-sep{color:var(--text-dim);font-size:11px}.physical-name-row{display:flex;align-items:center;gap:6px;margin-top:2px}.physical-name-row .physical-label{font-size:9px;text-transform:uppercase;color:var(--text-dim);letter-spacing:.04em}.physical-name-input{font-size:12px;color:var(--text-dim)}.physical-col{display:block;font-size:10px;color:var(--text-dim);font-family:var(--mono, monospace)}.physical-col-input{display:block;font-size:10px;color:var(--text-dim)}.tool-btn.primary{color:var(--accent);border-color:#e0a63a66;font-weight:600;background:#e0a63a1a}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#05080db3;z-index:50;display:flex;align-items:center;justify-content:center}.modal{width:440px;max-height:80vh;overflow-y:auto;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:14px;padding:20px;box-shadow:0 24px 80px #0009}.modal.wide{width:600px}.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}.modal-header h2{margin:0;font-size:16px}.modal-search{margin-bottom:10px}.modal-footer{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:14px}.modal-footer label{font-size:11px;color:var(--text-dim);display:flex;flex-direction:column;gap:3px}.modal-footer select,.form-row select{background:var(--bg-card);color:var(--text);border:1px solid var(--border);border-radius:7px;padding:5px 8px;font-size:12px}.primary-btn{padding:8px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent);color:var(--accent-ink);font-weight:700;font-size:13px}.primary-btn:disabled{opacity:.4;cursor:default}.danger-btn{padding:8px 16px;border-radius:8px;border:1px solid var(--err);cursor:pointer;background:#f8717124;color:var(--err);font-weight:700;font-size:13px}.form-row{display:flex;flex-direction:column;gap:4px;margin-bottom:14px;font-size:12px;color:var(--text-dim)}.form-row>label{font-weight:600;letter-spacing:.02em;text-transform:uppercase;font-size:10px}.form-error{color:var(--err);font-size:11px}.form-preview{color:var(--text-dim);font-size:11px;word-break:break-all;opacity:.85}.form-preview:before{content:""}.form-hint{color:var(--text-dim);font-size:11px;font-style:italic;opacity:.75;word-break:break-word}.term-results{display:flex;flex-direction:column;gap:8px;max-height:44vh;overflow-y:auto}.term-card{text-align:left;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px;cursor:pointer;color:var(--text)}.term-card:hover{border-color:var(--border-strong)}.term-card.picked{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.term-head{display:flex;justify-content:space-between;align-items:baseline}.term-label{font-weight:600;font-size:13px}.term-source{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em}.term-iri{font-size:10px;color:var(--accent);display:block;margin:2px 0;word-break:break-all}.term-def{font-size:11px;color:var(--text-dim);margin:4px 0 0;line-height:1.45}.kind-badge{font-size:8px;padding:1px 5px;margin-right:6px;border-radius:999px;background:#f0abfc26;color:#f0abfc;letter-spacing:.04em}.src-picker{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}.src-chip{display:inline-flex;align-items:center;gap:5px;font-size:11px;padding:3px 9px;border-radius:999px;background:var(--bg);border:1px solid var(--border);color:var(--text-dim);cursor:pointer}.src-chip:hover{border-color:var(--border-strong);color:var(--text)}.src-chip.active{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}.src-layer{font-size:8px;padding:0 5px;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.side-panel{position:absolute;top:12px;right:12px;bottom:12px;width:380px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:18px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080}.side-panel h2{margin:0;font-size:16px}.side-panel h4{margin:10px 0 6px;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}.panel-body{margin-top:12px}.panel-body.ok{color:var(--ok)}.meta-line{font-size:10px;color:var(--text-dim);margin:6px 0 10px}.coverage-banner{font-size:12px;padding:8px 12px;border-radius:9px;margin-bottom:12px;background:#34d3991a;color:var(--ok)}.coverage-banner.warn{background:#fbbf241a;color:var(--warn)}.layer-band{border:1px dashed var(--border);border-radius:10px;padding:8px 12px 10px;margin-bottom:10px}.layer-band.external{border-style:solid;background:#93c5fd0a}.stack-term{display:flex;justify-content:space-between;align-items:baseline;gap:8px;width:100%;background:var(--bg-card);border:1px solid var(--border);color:var(--text);border-radius:7px;padding:5px 10px;margin:4px 0;font-size:12px;cursor:pointer;text-align:left}div.stack-term{cursor:default}.stack-term:hover{border-color:var(--border-strong)}.stack-term.uncovered{border-color:var(--err)}.align-arrow{font-size:10px;color:var(--accent);white-space:nowrap}.align-arrow.unresolved{color:var(--warn)}.align-arrow.missing{color:var(--err);font-style:italic}.align-arrow.proposed{color:var(--warn);font-style:italic}.ref-list{display:flex;flex-direction:column;gap:6px;margin-top:4px}.ref-card{border:1px solid var(--border);border-radius:8px;padding:6px 8px;background:var(--bg)}.ref-card.proposed{border-color:var(--warn)}.ref-head{display:flex;align-items:baseline;gap:8px;justify-content:space-between}.ref-uri{font-family:var(--mono);font-size:12px;word-break:break-all}.ref-status{font-size:9px;text-transform:uppercase;letter-spacing:.04em}.ref-status.accepted{color:var(--accent)}.ref-status.proposed{color:var(--warn)}.ref-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:5px}.ref-pill{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--accent-soft);color:var(--accent)}.ref-via{font-size:10px;color:var(--text-dim)}.ref-actions{display:flex;gap:6px;margin-top:6px}.dirty-list{list-style:none;padding:0;margin:0 0 10px;font-size:12px}.dirty-list li{padding:2px 0}.dirty-state{color:var(--warn);margin-right:6px;font-family:var(--mono)}.diff-view{background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px;font-size:10.5px;line-height:1.5;overflow:auto;max-height:42vh;font-family:var(--mono);white-space:pre;margin-bottom:10px}.diff-add{color:var(--ok)}.diff-del{color:var(--err)}.diff-hunk{color:#93c5fd}.decision{border:1px solid var(--border);border-radius:9px;padding:8px 12px;margin-bottom:8px}.decision.accepted{opacity:.65}.decision.rejected{opacity:.45}.decision-head{display:flex;gap:6px;align-items:center}.decision-mark{font-weight:700}.decision-subject{font-size:12px;margin:6px 0}.decision-actions{display:flex;gap:6px}.toast{position:absolute;bottom:20px;left:50%;transform:translate(-50%);background:var(--bg-raised);border:1px solid var(--warn);color:var(--text);border-radius:10px;padding:10px 18px;font-size:12px;z-index:60;box-shadow:0 10px 30px #00000080} diff --git a/packages/server/src/mdl_server/static/assets/styles-BJW39REh.css b/packages/server/src/mdl_server/static/assets/styles-BJW39REh.css deleted file mode 100644 index f1c1d8c..0000000 --- a/packages/server/src/mdl_server/static/assets/styles-BJW39REh.css +++ /dev/null @@ -1 +0,0 @@ -.react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:-webkit-grab;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;-webkit-animation:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:-webkit-grab;cursor:grab}.react-flow__node.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:-webkit-grab;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@-webkit-keyframes dashdraw{0%{stroke-dashoffset:10}}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.ie-wrap{position:relative;display:inline-flex;gap:6px}.erd-ico{font-size:13px;line-height:1;opacity:.85;margin-right:1px}.ie-wrap button:hover .erd-ico,.ie-wrap button.on .erd-ico,.ie-wrap button.active .erd-ico{opacity:1}.erd-caret{opacity:.55;margin-left:1px}.ie-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:30;min-width:220px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:6px;box-shadow:0 12px 32px #00000073;display:flex;flex-direction:column;gap:2px}.ie-item{display:block;padding:8px 10px;border-radius:6px;color:var(--text);text-decoration:none;font-size:13px;cursor:pointer}.ie-item:hover{background:#5eead424;color:var(--accent)}.ie-dialect{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 10px 8px;margin-bottom:4px;border-bottom:1px solid var(--border);font-size:12px;color:var(--text-dim)}.ie-dialect select{background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:3px 6px;font-size:12px}.ie-import{width:380px;padding:12px;gap:8px}.ie-tabs{display:flex;gap:4px}.ie-tab{flex:1;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;background:none;border:1px solid var(--border);color:var(--text-dim)}.ie-tab.active{border-color:var(--accent);color:var(--accent);background:#5eead41a}.ie-hint{font-size:11px;color:var(--text-dim);margin:0}.ie-textarea{width:100%;font-family:var(--mono);font-size:12px;resize:vertical;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:8px;box-sizing:border-box}.ie-textarea:focus{border-color:var(--accent);outline:none}.ie-file{font-size:11px;color:var(--text-dim)}.ie-warn{font-size:11px;color:var(--pk);margin:0}.ie-error{font-size:12px;color:var(--err);margin:0}.ie-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}:root{--bg: #14110f;--bg-raised: #1c1815;--bg-card: #221d19;--border: #33291f;--border-strong: #4a3c2c;--text: #ece6dd;--text-dim: #a89a86;--accent: #e0a63a;--accent-soft: rgba(224, 166, 58, .15);--accent-ink: #241a08;--pk: #f2c14e;--err: #f0857a;--warn: #e6a94a;--ok: #6cbf73;--font: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;--mono: "SF Mono", ui-monospace, "Cascadia Code", Menlo, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font);background:var(--bg);color:var(--text)}.app{display:flex;flex-direction:column;height:100%}.canvas-wrap{flex:1;position:relative;min-height:0}.react-flow{background:var(--bg)}.topbar{display:flex;align-items:center;gap:16px;padding:10px 16px;background:var(--bg-raised);border-bottom:1px solid var(--border);z-index:10}.brand{display:flex;align-items:baseline;gap:8px;white-space:nowrap}.logo{color:var(--accent);font-size:18px}.brand-name{font-weight:700;letter-spacing:.02em}.project-name{color:var(--text-dim);font-size:13px}.search{flex:0 1 340px;padding:7px 12px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-size:13px;outline:none}.search:focus{border-color:var(--accent)}.legend{display:flex;gap:12px;flex-wrap:wrap;font-size:12px;color:var(--text-dim)}.legend-item{display:inline-flex;align-items:center;gap:5px}.swatch{width:10px;height:10px;border-radius:3px;display:inline-block}.stats{margin-left:auto;display:flex;gap:12px;font-size:12px;color:var(--text-dim)}.diag-chip{padding:2px 8px;border-radius:99px;font-weight:600}.diag-chip.ok{background:#34d39926;color:var(--ok)}.diag-chip.warn{background:#fbbf2426;color:var(--warn)}.diag-chip.err{background:#f8717126;color:var(--err)}.actions{display:flex;gap:6px}.tool-btn{padding:6px 10px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer;font-size:13px}.tool-btn:hover{color:var(--text);border-color:var(--border-strong)}.tool-btn.active{color:var(--accent);border-color:var(--accent)}.layout-picker{position:relative;display:inline-flex;gap:2px}.layout-picker .caret{padding:6px;font-size:10px}.layout-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:20;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:8px;padding:4px;min-width:150px;box-shadow:0 8px 24px #0006}.layout-menu button{display:block;width:100%;text-align:left;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--text-dim);cursor:pointer;font-size:13px}.layout-menu button:hover{background:var(--bg-card);color:var(--text)}.entity-node{width:264px;background:var(--bg-card);border:1px solid var(--border);border-top:3px solid var(--accent);border-radius:10px;overflow:hidden;box-shadow:0 4px 18px #00000059;font-size:12px;transition:opacity .15s,box-shadow .15s,border-color .15s}.entity-node.selected{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent),0 8px 24px #00000080}.entity-node.highlighted{box-shadow:0 0 0 2px var(--pk),0 8px 24px #00000080}.entity-node.dimmed{opacity:.18}.entity-header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:#ffffff08;border-bottom:1px solid var(--border)}.entity-name{font-weight:700;font-size:13px;letter-spacing:.01em}.entity-badges{display:flex;gap:4px}.badge{font-size:10px;padding:1px 6px;border-radius:99px;background:var(--accent-soft);color:var(--accent);text-transform:uppercase;letter-spacing:.05em}.badge.ontology{background:#93c5fd1f;color:#93c5fd}.attr-section{padding:4px 0}.attr-section.keys{border-bottom:1px solid var(--border);background:#fcd34d0a}.attr-row{display:flex;align-items:center;gap:6px;padding:3px 12px;height:26px;line-height:20px;position:relative}.attr-row.empty{color:var(--text-dim);font-style:italic}.attr-row.more{color:var(--text-dim);font-style:italic;cursor:pointer;justify-content:center;height:22px;font-size:11px}.attr-row.more:hover{color:var(--accent);background:var(--accent-soft)}.attr-row.rel-hit{background:var(--accent-soft);box-shadow:inset 2px 0 0 var(--accent)}.attr-row.rel-hit .attr-name{color:var(--accent);font-weight:600}.attr-row.rel-dim{opacity:.35}.attr-row.endpoint .attr-name{font-style:normal}.attr-port{width:8px!important;height:8px!important;opacity:0;top:50%!important}.attr-port.react-flow__handle-left{left:-5px!important}.attr-port.react-flow__handle-right{right:-5px!important}.entity-node:hover .attr-port{opacity:.5}.attr-row:hover .attr-port{opacity:1!important;background:var(--accent)!important}.attr-icon{width:14px;text-align:center;font-size:10px;flex:none}.attr-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attr-name.pk{color:var(--pk);font-weight:600}.attr-type{color:var(--text-dim);font-family:var(--mono);font-size:10px;flex:none}.attr-notnull{color:var(--text-dim);font-size:8px;flex:none}.port{width:11px!important;height:11px!important;background:var(--border-strong)!important;border:2px solid var(--bg)!important;opacity:.55;transition:transform .12s ease,background .12s ease,opacity .12s ease}.entity-node:hover .port{opacity:1;background:var(--accent)!important;transform:scale(1.25);box-shadow:0 0 0 4px var(--accent-soft);cursor:crosshair}.port:hover{transform:scale(1.6)!important;cursor:crosshair}.entity-node:hover{border-color:var(--accent);box-shadow:0 0 0 1px #e0a63a40}.react-flow__connectionline .react-flow__edge-path,.react-flow__connection-path{stroke:var(--accent)!important;stroke-width:2!important}.rel-path{stroke:var(--border-strong);stroke-width:1.6}.rel-edge{cursor:pointer}.rel-glyph{stroke:var(--border-strong);stroke-width:1.6;fill:none}.rel-glyph.ring{fill:var(--bg)}.rel-edge:hover .rel-path,.rel-edge.selected .rel-path,.rel-edge:hover .rel-glyph,.rel-edge.selected .rel-glyph{stroke:var(--accent)}.rel-edge.dimmed{opacity:.12}.rel-label{fill:var(--text);font-size:11px;text-anchor:middle;paint-order:stroke;stroke:var(--bg);stroke-width:4px}.detail-panel{position:absolute;top:12px;right:12px;bottom:12px;width:340px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:18px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080}.detail-header{display:flex;justify-content:space-between;align-items:flex-start}.detail-header h2{margin:0 0 6px;font-size:18px}.icon-btn{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:14px}.icon-btn:hover{color:var(--text)}.definition{color:var(--text-dim);font-size:13px;line-height:1.5}.detail-panel section{margin-top:18px}.detail-panel h3{margin:0 0 8px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}.kv{display:flex;gap:8px;margin:4px 0;font-size:12px;align-items:baseline}.kv .k{color:var(--text-dim);min-width:72px}.kv .v{word-break:break-all}.chip{display:inline-block;font-size:11px;padding:2px 8px;margin:2px 4px 2px 0;border-radius:99px;background:#94a3b81f;color:var(--text-dim)}.chip.pattern{background:var(--accent-soft);color:var(--accent)}.chip.layer-industry{background:#93c5fd26;color:#93c5fd}.chip.layer-core{background:var(--accent-soft);color:var(--accent)}.chip.layer-domain{background:#f0abfc26;color:#f0abfc}.chip.layer-specialised{background:#fcd34d26;color:var(--pk)}.cat-badge{font-size:10px;font-weight:700;margin-left:6px;padding:2px 8px;border-radius:5px;cursor:help}.cat-badge.supertype{background:#a78bfa2e;color:#a78bfa}.cat-badge.subtype{background:var(--accent-soft);color:var(--accent)}.udp-row td{padding:3px 6px;border-bottom:1px solid var(--border);font-size:12px}.udp-key{color:var(--text-dim);font-family:var(--mono);font-size:11px}.udp-val{color:var(--text)}.enum-badge{font-size:8px;font-weight:700;margin-left:6px;padding:1px 5px;border-radius:4px;background:#a78bfa2e;color:#a78bfa;vertical-align:middle;cursor:help}.key-row{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:12px}.key-badge{font-size:9px;font-weight:700;padding:2px 6px;border-radius:5px;background:#94a3b826;color:var(--text-dim);flex:none}.key-badge.pk{background:#fcd34d2e;color:var(--pk)}.key-badge.unique,.key-badge.alternate{background:var(--accent-soft);color:var(--accent)}.key-name{font-weight:600}.key-cols{color:var(--text-dim);font-family:var(--mono);font-size:11px}.attr-table{width:100%;border-collapse:collapse;font-size:12px;table-layout:auto}.attr-table td{padding:4px 6px;border-bottom:1px solid var(--border)}.attr-table td:first-child{width:100%}.attr-table td:first-child .edit-input{min-width:120px}.attr-table td.pk{color:var(--pk);font-weight:600}.attr-table td.type{color:var(--text-dim);font-family:var(--mono);font-size:11px}.attr-table td.null{color:var(--text-dim);font-size:10px;text-align:right}.rel-link{display:flex;gap:8px;align-items:center;width:100%;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:7px 10px;margin:4px 0;cursor:pointer;font-size:12px;text-align:left}.rel-link:hover{border-color:var(--accent)}.rel-card{color:var(--text-dim);font-family:var(--mono);font-size:11px}.ulid{font-family:var(--mono);font-size:11px;color:var(--text-dim);cursor:copy;word-break:break-all}.ulid:hover{color:var(--accent)}.splash{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-dim)}.splash.error pre{background:var(--bg-card);padding:12px 16px;border-radius:8px;max-width:640px;overflow:auto;color:var(--err);font-size:12px}.react-flow__minimap{background:var(--bg-raised)!important;border-radius:10px}.react-flow__controls{border-radius:10px;overflow:hidden;box-shadow:none!important}.react-flow__controls-button{background:var(--bg-raised)!important;border-bottom:1px solid var(--border)!important;fill:var(--text-dim)!important}.react-flow__controls-button:hover{fill:var(--text)!important}.edit-input{width:100%;padding:6px 9px;border-radius:7px;font-size:12px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-family:var(--font);outline:none}.edit-input:focus{border-color:var(--accent)}.entity-title-input{font-size:17px;font-weight:700;background:transparent;border-color:transparent;padding-left:0}.entity-title-input:hover,.entity-title-input:focus{border-color:var(--border);background:var(--bg-card);padding-left:9px}.definition-input{color:var(--text-dim);line-height:1.5;resize:vertical}.chip-select,.cell-select{font-size:11px;padding:2px 6px;margin:2px 6px 2px 0;border-radius:99px;background:#94a3b81f;color:var(--text-dim);border:1px solid transparent;outline:none}.chip-select:hover,.cell-select:hover{border-color:var(--border-strong);color:var(--text)}.cell-select{border-radius:6px;background:transparent;max-width:110px}.mini-btn{font-size:11px;padding:3px 9px;border-radius:7px;cursor:pointer;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim)}.mini-btn:hover{color:var(--accent);border-color:var(--accent)}.mini-btn.danger:hover{color:var(--err);border-color:var(--err)}.h3-actions{float:right;display:inline-flex;gap:6px}.empty-hint{color:var(--text-dim);font-size:12px;font-style:italic}.add-attr{display:flex;gap:6px;margin-top:8px}.rel-row{display:flex;gap:6px;align-items:flex-start}.rel-row .rel-main{flex:1}.verb-phrases{display:flex;align-items:center;gap:4px;margin-top:2px;padding-left:4px}.verb-phrases .verb-input{font-size:11px;font-style:italic;color:var(--text-dim);min-width:40px}.verb-phrases .verb-sep{color:var(--text-dim);font-size:11px}.physical-name-row{display:flex;align-items:center;gap:6px;margin-top:2px}.physical-name-row .physical-label{font-size:9px;text-transform:uppercase;color:var(--text-dim);letter-spacing:.04em}.physical-name-input{font-size:12px;color:var(--text-dim)}.physical-col{display:block;font-size:10px;color:var(--text-dim);font-family:var(--mono, monospace)}.physical-col-input{display:block;font-size:10px;color:var(--text-dim)}.tool-btn.primary{color:var(--accent);border-color:#e0a63a66;font-weight:600}.tool-btn.badged:after{content:"●";color:var(--warn);margin-left:4px;font-size:8px;vertical-align:super}.divider{width:1px;background:var(--border);margin:0 4px;align-self:stretch}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#05080db3;z-index:50;display:flex;align-items:center;justify-content:center}.modal{width:440px;max-height:80vh;overflow-y:auto;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:14px;padding:20px;box-shadow:0 24px 80px #0009}.modal.wide{width:600px}.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}.modal-header h2{margin:0;font-size:16px}.modal-search{margin-bottom:10px}.modal-footer{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:14px}.modal-footer label{font-size:11px;color:var(--text-dim);display:flex;flex-direction:column;gap:3px}.modal-footer select,.form-row select{background:var(--bg-card);color:var(--text);border:1px solid var(--border);border-radius:7px;padding:5px 8px;font-size:12px}.primary-btn{padding:8px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent);color:var(--accent-ink);font-weight:700;font-size:13px}.primary-btn:disabled{opacity:.4;cursor:default}.danger-btn{padding:8px 16px;border-radius:8px;border:1px solid var(--err);cursor:pointer;background:#f8717124;color:var(--err);font-weight:700;font-size:13px}.form-row{display:flex;flex-direction:column;gap:4px;margin-bottom:14px;font-size:12px;color:var(--text-dim)}.form-row>label{font-weight:600;letter-spacing:.02em;text-transform:uppercase;font-size:10px}.form-error{color:var(--err);font-size:11px}.form-preview{color:var(--text-dim);font-size:11px;word-break:break-all;opacity:.85}.form-preview:before{content:""}.form-hint{color:var(--text-dim);font-size:11px;font-style:italic;opacity:.75;word-break:break-word}.term-results{display:flex;flex-direction:column;gap:8px;max-height:44vh;overflow-y:auto}.term-card{text-align:left;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px;cursor:pointer;color:var(--text)}.term-card:hover{border-color:var(--border-strong)}.term-card.picked{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.term-head{display:flex;justify-content:space-between;align-items:baseline}.term-label{font-weight:600;font-size:13px}.term-source{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em}.term-iri{font-size:10px;color:var(--accent);display:block;margin:2px 0;word-break:break-all}.term-def{font-size:11px;color:var(--text-dim);margin:4px 0 0;line-height:1.45}.kind-badge{font-size:8px;padding:1px 5px;margin-right:6px;border-radius:999px;background:#f0abfc26;color:#f0abfc;letter-spacing:.04em}.src-picker{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}.src-chip{display:inline-flex;align-items:center;gap:5px;font-size:11px;padding:3px 9px;border-radius:999px;background:var(--bg);border:1px solid var(--border);color:var(--text-dim);cursor:pointer}.src-chip:hover{border-color:var(--border-strong);color:var(--text)}.src-chip.active{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}.src-layer{font-size:8px;padding:0 5px;border-radius:999px;text-transform:uppercase;letter-spacing:.04em}.side-panel{position:absolute;top:12px;right:12px;bottom:12px;width:380px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:18px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080}.side-panel h2{margin:0;font-size:16px}.side-panel h4{margin:10px 0 6px;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim)}.panel-body{margin-top:12px}.panel-body.ok{color:var(--ok)}.meta-line{font-size:10px;color:var(--text-dim);margin:6px 0 10px}.coverage-banner{font-size:12px;padding:8px 12px;border-radius:9px;margin-bottom:12px;background:#34d3991a;color:var(--ok)}.coverage-banner.warn{background:#fbbf241a;color:var(--warn)}.layer-band{border:1px dashed var(--border);border-radius:10px;padding:8px 12px 10px;margin-bottom:10px}.layer-band.external{border-style:solid;background:#93c5fd0a}.stack-term{display:flex;justify-content:space-between;align-items:baseline;gap:8px;width:100%;background:var(--bg-card);border:1px solid var(--border);color:var(--text);border-radius:7px;padding:5px 10px;margin:4px 0;font-size:12px;cursor:pointer;text-align:left}div.stack-term{cursor:default}.stack-term:hover{border-color:var(--border-strong)}.stack-term.uncovered{border-color:var(--err)}.align-arrow{font-size:10px;color:var(--accent);white-space:nowrap}.align-arrow.unresolved{color:var(--warn)}.align-arrow.missing{color:var(--err);font-style:italic}.align-arrow.proposed{color:var(--warn);font-style:italic}.ref-list{display:flex;flex-direction:column;gap:6px;margin-top:4px}.ref-card{border:1px solid var(--border);border-radius:8px;padding:6px 8px;background:var(--bg)}.ref-card.proposed{border-color:var(--warn)}.ref-head{display:flex;align-items:baseline;gap:8px;justify-content:space-between}.ref-uri{font-family:var(--mono);font-size:12px;word-break:break-all}.ref-status{font-size:9px;text-transform:uppercase;letter-spacing:.04em}.ref-status.accepted{color:var(--accent)}.ref-status.proposed{color:var(--warn)}.ref-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:5px}.ref-pill{font-size:10px;padding:1px 6px;border-radius:999px;background:var(--accent-soft);color:var(--accent)}.ref-via{font-size:10px;color:var(--text-dim)}.ref-actions{display:flex;gap:6px;margin-top:6px}.dirty-list{list-style:none;padding:0;margin:0 0 10px;font-size:12px}.dirty-list li{padding:2px 0}.dirty-state{color:var(--warn);margin-right:6px;font-family:var(--mono)}.diff-view{background:var(--bg);border:1px solid var(--border);border-radius:9px;padding:10px;font-size:10.5px;line-height:1.5;overflow:auto;max-height:42vh;font-family:var(--mono);white-space:pre;margin-bottom:10px}.diff-add{color:var(--ok)}.diff-del{color:var(--err)}.diff-hunk{color:#93c5fd}.decision{border:1px solid var(--border);border-radius:9px;padding:8px 12px;margin-bottom:8px}.decision.accepted{opacity:.65}.decision.rejected{opacity:.45}.decision-head{display:flex;gap:6px;align-items:center}.decision-mark{font-weight:700}.decision-subject{font-size:12px;margin:6px 0}.decision-actions{display:flex;gap:6px}.toast{position:absolute;bottom:20px;left:50%;transform:translate(-50%);background:var(--bg-raised);border:1px solid var(--warn);color:var(--text);border-radius:10px;padding:10px 18px;font-size:12px;z-index:60;box-shadow:0 10px 30px #00000080} diff --git a/packages/server/src/mdl_server/static/assets/styles-CIm9mWtD.js b/packages/server/src/mdl_server/static/assets/styles-Cn8AFsR1.js similarity index 100% rename from packages/server/src/mdl_server/static/assets/styles-CIm9mWtD.js rename to packages/server/src/mdl_server/static/assets/styles-Cn8AFsR1.js diff --git a/packages/server/src/mdl_server/static/index.html b/packages/server/src/mdl_server/static/index.html index 2cf9518..3bae9e8 100644 --- a/packages/server/src/mdl_server/static/index.html +++ b/packages/server/src/mdl_server/static/index.html @@ -4,10 +4,10 @@ Modelith - + - - + +
diff --git a/packages/server/src/mdl_server/static/sme.html b/packages/server/src/mdl_server/static/sme.html index 27e4cfe..80bd876 100644 --- a/packages/server/src/mdl_server/static/sme.html +++ b/packages/server/src/mdl_server/static/sme.html @@ -4,10 +4,10 @@ Modelith Glossary - + - - + + From 20892a3792079eb89b08579291ab080506ea8b51 Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sun, 27 Sep 2026 17:09:53 +1000 Subject: [PATCH 2/6] spec: semantic model compare & merge view (Tier 1) The Tier 1 GTM capability: one semantic diff/merge view reused across every model-change flow (preview, propose, drift, reverse-accept, import, conflicts) in both the Studio Canvas and VS Code. Free local review is the bottom-up funnel; the blocking CI gate stays the paid wedge. The toolbar declutter (prior commit) is the first step of making the Canvas the polished home for this review experience; the compare/merge view builds on the same surface. --- specs/modelith-semantic-compare-merge-spec.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 specs/modelith-semantic-compare-merge-spec.md diff --git a/specs/modelith-semantic-compare-merge-spec.md b/specs/modelith-semantic-compare-merge-spec.md new file mode 100644 index 0000000..2372086 --- /dev/null +++ b/specs/modelith-semantic-compare-merge-spec.md @@ -0,0 +1,204 @@ +# Semantic model compare & merge — one review view across every change flow + +## Context + +Modelith presents model changes in many flows — staged edits (preview), SME proposals, git-ref diffs, +drift, reverse-inference acceptance, imports, merge conflicts — but the review experience is fragmented +and the richest view is trapped in one corner of the Studio Canvas (the SME propose flow). A mature +commercial modeling tool (Hackolade Studio) ships a 3-pane semantic compare & merge (left / merged / +right) with per-change accept/reject for additions, modifications, moves and deletions, matching-method +and display filters, and conflict markers. That is the bar for a data-modeling tool. + +This is also the paid wedge made visible. The GTM thesis is that the commercial product is +drift-enforcement-in-CI, whose hard SLO is "classify a change as breaking/additive/cosmetic and name the +exact dbt models a breaking change will take down, at PR time." A rich, shared diff/merge view is how +that classification lands in the product. It ships FREE for local review (the bottom-up funnel); the +*blocking* CI gate, shared policy, and report stay the paid server boundary. The incumbent gates all of +this behind a desktop license key; Modelith gives compare/merge away and charges for the team gate. + +**The foundation already exists** (validated by codebase exploration): +- One canonical semantic-diff payload — `ModelDiffDoc`: ULID-keyed, nested object→fields→children, + severity-tagged (breaking/additive/cosmetic/unmanaged), per-field before/after. Built by + `ModelDiff.to_doc()` (`packages/core/src/mdl_core/diff.py:283`), serialized by `render_json` + (`packages/core/src/mdl_core/diff_render.py:29`), mirrored at `canvas/src/types.ts:351`. Server + enriches it with `path` (source file per object) and `breaks` (dbt models a breaking change would + break) via `_attach_paths`/`_attach_breaks` (`packages/server/src/mdl_server/git_api.py:417,430`). +- One shape-aware renderer — `canvas/src/sme/DiffView.tsx` (word-diff for prose, +/− chips for lists, + scalar before→after, break-impact panel), already prop-driven and API-free. +- An ER diff overlay — `canvas/src/sme/ModelDiagram.tsx` tints `EntityNode`/`RelationshipEdge` by a + `severityByUlid` map. +- A review shell — `canvas/src/sme/ReviewScreen.tsx` (changes/diagram tabs, route/reviewer panel, + conflict banner). +- VS Code already embeds the Canvas via an iframe webview parameterized by `?query` (`?import=1`), + `vscode/src/canvasPanel.ts:59,169` — so a review route renders in VS Code with no new rendering code. +- Flows already producing `ModelDiffDoc`: preview (`/api/preview` → `PreviewDoc.diff`), propose + (`/api/git/propose`), git-diff (`/api/git/diff/model|refs`). A name-keyed variant + `diff_models_by_name` (`diff.py:550`) exists for fresh-ULID comparisons (import/compare). + +**The gaps this spec closes:** +1. Selection is per-OBJECT, not per-FIELD, and there is no 3-pane merge / conflict resolution. +2. Drift (`DriftReport`), reverse-decisions (ledger), and conflicts (`ConflictDoc`) don't speak + `ModelDiffDoc` — they need adapters into it (severity vocabulary is already shared). +3. Import writes-then-git-diffs instead of a semantic "review before it lands." +4. VS Code has no semantic diff (only native text diff + a drift tree); `compareReversed` is a + text-diff outlier. +5. Accept/apply semantics differ per flow (PR / ledger verdict / reconcile / write) — the view needs a + pluggable apply action, not one hardcoded to propose. + +Intended outcome: a modeler reviews any model change — staged edit, drift, inferred relationship, erwin +import, branch comparison, merge conflict — in the SAME rich, filterable, per-change-selectable view, +in both the Canvas and VS Code. + +## Design + +### One payload, one component, pluggable sources and sinks + +``` + SOURCES (adapters → ModelDiffDoc) SHARED VIEW SINKS (pluggable apply) + preview ─┐ ┌───────────────┐ propose → PR + git-diff/refs ┼─ already ModelDiffDoc ─▶│ ReviewView │──accept──▶ ledger → verdict write + propose ─┘ │ DiffView++ │ drift → --reconcile fold + drift ───────── DriftReport ─adapter─▶│ filters │ import → write files + decisions ────── Decision[] ─adapter─▶│ per-field │ (read-only sources: no sink) + import ───────── diff_by_name ────────▶ │ ER overlay │ + conflicts ────── merge_driver ─3-way──▶ │ 3-pane merge │ + └───────────────┘ +``` + +The whole design is: everything converges on `ModelDiffDoc`, one React component renders it, and each +flow supplies (a) a fetch that returns the doc and (b) an apply action. `ModelDiffDoc` is not changed in +shape; where a 3-pane merge needs base/ours/theirs, that is carried as an optional wrapper, not a new +per-node schema. + +### The shared component: `ReviewView` + +Generalize `DiffView`/`ReviewScreen` into a reusable `ReviewView` that stays prop-driven and API-free +(so VS Code hosts it verbatim in the webview). It renders a `ModelDiffDoc` and takes: +- `diff: ModelDiffDoc` (2-way) or `merge: { base, ours, theirs, conflicts }` (3-way). +- `selection`: per-FIELD selection — keys are `(ulid, field)` not just object ULIDs, so a user can + accept the definition change but reject the domain change on one attribute (the competitor's + per-addition/modification/move toggles). Widen the existing staging set (`SmeApp.excluded`, + `canvas/src/staging/useStaging.ts`) from object to field granularity; keep the dependency-locking + (`dependentKeys`) so changes that must travel together stay linked. +- `filters`: client-side over the doc — show all / only differences / only additions / only deletions / + only modifications / only moves / only conflicts (severity + ChangeType are already on each node). + This is the competitor's left rail. +- `actions: { label, apply(selection) }[]`: the pluggable action bar. Same component drives Propose + (PR), Accept (ledger verdict), Reconcile (drift fold), Apply-import (write files). Read-only sources + pass none. +- `mode`: `"unified"` (default 2-pane master-detail, today's view) or `"three-pane"` (base | merged | + right, for merge/conflict sources) reusing `FieldDiff` in each pane. +- ER overlay tab stays (`ModelDiagram` + `severityByUlid`), now available to every source. + +Reuse, do not rewrite: `FieldDiff`/`wordDiff` (shape-aware rendering), `ModelDiagram` (ER overlay), +`ReviewScreen`'s route/reviewer/conflict panels. + +### Source adapters → `ModelDiffDoc` + +Keep `diff_models` pure; adapters live beside the server enrichers and reuse `_attach_paths`/ +`_attach_breaks` so every source gets source-file + break-impact enrichment. +- **Drift → ModelDiffDoc**: `drift_to_diff(report, model)` maps each `DriftItem` (name-keyed, + manifest-shaped, `packages/reverse/src/mdl_reverse/drift.py`) onto an `ObjectChange`/`FieldChange` + (name-keyed, like the `--against` path), preserving `payload` for the reconcile action and the shared + severity. New endpoint `GET /api/drift?target=` returns the adapted doc (drift has no server endpoint + today — it is CLI/CI-only). Apply action = `mdl drift --reconcile` fold for additive/cosmetic. +- **Decisions → ModelDiffDoc**: `decisions_to_diff(ledger, model)` turns each proposed `Decision` (an + inferred relationship, a stripped key, an SCD2 pattern; `packages/reverse/src/mdl_reverse/ledger.py`) + into a proposed `ObjectChange` with its `evidence` as detail. Apply action = `set_verdict` + (accept/reject) — reuse `POST /api/decisions/{signal_key}/verdict`. +- **Import (pre-write) → ModelDiffDoc**: wire `diff_models_by_name(current_model, imported_model)` + (`diff.py:550`, name-keyed since imported ULIDs are fresh) so erwin/DDL imports show a semantic diff + BEFORE writing. New endpoint `POST /api/import/preview` returns the doc; apply action writes the + files. This is the highest-value new flow — today imports write then git-diff. +- **Conflicts → 3-way (last)**: `merge_model_files` already computes structural base/ours/theirs + conflicts (`packages/core/src/mdl_core/merge_driver.py:31`); `conflicts_to_diff` feeds the 3-pane + mode. Lowest priority (furthest from the current shape); design the 3-pane contract for it, implement + drift/decisions/import first. + +### Routing in the Canvas + +Add a source-parameterized review route to the hash router (`canvas/src/sme/SmeApp.tsx` already +hash-routes tabs): `#review?source=drift`, `?source=diff&base=main&head=`, `?source=import`, +`?source=decisions`. A small per-source loader fetches the adapted doc and mounts `ReviewView` with the +right actions. Re-point the architect canvas's raw `
` textual git diff (`ChangesView`,
+`canvas/src/SidePanel.tsx:291`) and `DecisionsView` at the shared component so the direct-write app gets
+the rich review too.
+
+### Surfacing in VS Code (via the existing webview — no new rendering code)
+
+Primary path: commands `modelith.reviewDrift`, `modelith.reviewChanges` (working tree vs base),
+`modelith.reviewImport`, `modelith.reviewDecisions`, each calling `canvas.open(dir, "review&source=…")`
+so `ReviewView` renders in the `modelithCanvas` iframe webview exactly as `?import=1` does today. Keep
+the native Drift tree (`vscode/src/driftView.ts`) and Reverse Review tree (`vscode/src/reverseView.ts`)
+as fast glance-able entry points, and add an "Open in review" action on each that launches the webview
+for the deep-dive. Re-point `modelith.compareReversed` (`vscode/src/extension.ts:1472`) from the native
+text diff of `mdl model render` to the webview review over `diff_models_by_name`→`ModelDiffDoc`.
+
+## Open-core boundary (which parts are free vs paid)
+
+- **Free** (local, complete): the entire `ReviewView` — 2-pane and 3-pane, per-field selection, filters,
+  ER overlay — for reviewing staged edits, imports, local `mdl drift`, branch/ref diffs, and resolving a
+  local merge conflict. This is the bottom-up funnel; it must never be crippled.
+- **Paid (Team, server boundary)**: the *blocking* CI gate that runs the same classification in the
+  org's CI with a shared policy file and a posted PR report; CODEOWNERS-routed proposal review with PR
+  state; hosted SME Studio. The view is free; enforcing it across a team is the wedge.
+
+## Phasing (each phase shippable)
+
+1. **Per-field selection + filters + pluggable actions** on the existing 2-pane `ReviewView`, wired to
+   flows that ALREADY produce `ModelDiffDoc` (preview, propose, git-diff/refs). Immediate UX win, no
+   adapters. Modernizes the SME review on its own.
+2. **Import pre-write review** (import adapter + canvas route + VS Code command) — highest-value new
+   flow; erwin import becomes "review before it lands," reusing phase 1.
+3. **Drift review** (drift adapter + `/api/drift` + canvas route + VS Code command + tree "Open in
+   review").
+4. **Decisions review** (decisions adapter + route + VS Code).
+5. **3-pane merge / conflict resolution** (3-pane mode + conflicts adapter) — full compare/merge parity.
+   Last, since it is furthest from the current shape; the common 2-way review ships long before it.
+6. Re-point `compareReversed`; retire the architect `
` git diff for the shared component.
+
+## Critical files
+
+- `canvas/src/sme/DiffView.tsx`, `ReviewScreen.tsx`, `ModelDiagram.tsx` — the component to generalize.
+- `canvas/src/staging/useStaging.ts`, `sme/SmeApp.tsx` — selection state, widened to per-field.
+- `canvas/src/types.ts` — `ModelDiffDoc` and friends (shared contract; optional 3-way wrapper).
+- `canvas/src/api.ts` — new fetches (`/api/drift`, `/api/import/preview`, decisions-as-diff).
+- `packages/server/src/mdl_server/git_api.py` — reuse `_attach_paths`/`_attach_breaks`; new endpoints.
+- `packages/server/src/mdl_server/` — new adapter module(s): `drift_to_diff`, `decisions_to_diff`.
+- `packages/core/src/mdl_core/diff.py` — `diff_models_by_name` (reuse); shared adapter helpers.
+- `vscode/src/extension.ts`, `canvasPanel.ts`, `driftView.ts`, `reverseView.ts` — review commands +
+  `canvas.open(dir, "review&source=…")`; re-point `compareReversed`.
+
+## Verification
+
+- `uv run pytest && uv run ruff check packages/` — adapters produce valid `ModelDiffDoc` the canvas
+  types accept; new endpoints return the right shape; existing diff/drift tests unchanged.
+- `cd canvas && npm run build` (Node 20) — generalized `ReviewView` typechecks; per-field selection +
+  filters render.
+- `cd vscode && npm run typecheck && npm run regression` (Node 20) — review commands registered
+  (Gate 3), all gates green.
+- Manual per phase: open the review from each wired flow in the Canvas AND VS Code (webview); toggle
+  per-field selections + filters; exercise each source's apply action; import shows a semantic diff
+  before writing.
+- Clean-install Docker check (`./sandbox/clean-install/run.sh`) still green (server/import path).
+
+## Non-goals / explicitly rejected
+
+- A monolithic desktop-IDE workspace (Object Browser + Properties pane + central pane): fights the
+  git-native, CLI-first, every-surface-a-client architecture. Modelith's equivalent is Canvas +
+  Inspector + VS Code.
+- An in-app Git panel (branch/commit/PR management inside the tool): anti-strategic. Modelith IS
+  git-native; re-centralizing git state contradicts the core differentiator.
+- Changing `ModelDiffDoc`'s per-node shape. Merge state (base/ours/theirs) is an optional wrapper, not a
+  schema change, so the one payload and one renderer stay singular.
+
+## Risk posture
+
+- Additive and phased: phase 1 modernizes the existing review with no adapters and no new payload; later
+  phases add one source at a time behind the same component. Each phase ships alone.
+- One payload, one renderer: adapters converge on `ModelDiffDoc`, so there is exactly one review UI to
+  maintain; the two intentional deviations (drift name-keying, compareReversed text) are reconciled by
+  adopting the existing `diff_models_by_name`, not by inventing a shape.
+- VS Code reuse via the existing iframe webview + `?query` means the extension gains the full semantic
+  review with essentially no new rendering code — the lowest-risk cross-frontend path.
+- The 3-pane merge/conflict work is isolated to the last phase, so the hard part never blocks the wins.

From 3e0c2c1aa470e8a4f55314778615883f99a2356f Mon Sep 17 00:00:00 2001
From: Debasish Bose 
Date: Sun, 27 Sep 2026 17:26:33 +1000
Subject: [PATCH 3/6] =?UTF-8?q?vscode:=20Model=20tree=20=E2=80=94=20entiti?=
 =?UTF-8?q?es=20=E2=86=92=20attributes,=20click=20opens=20the=20YAML?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Adds a logical Model Explorer to the VS Code side panel: every entity
expands to its attributes (PK marked with a key icon), and clicking an
entity or attribute opens logical/entities/.yaml with the cursor on
that object. This is the 'what is my model' view.

The 'Entities' shown in the old Warehouse Config tree actually come from
classifying dbt MODELS via reverse-config explain, which confused a user
into thinking it was their logical model. That tree is renamed to
'Warehouse Mapping' (how your dbt models map to entities), so the two are
no longer conflated: Model = the logical model, Warehouse Mapping = the
warehouse classification.

Backed by a new CLI command:
- mdl model tree --format json — emits every entity with its attributes
  (name, domain, role, nullable, is_pk) and the source file per entity, so
  the extension stays thin and the shape is testable in pytest. Built on
  mdl_core.query.model_tree(model, path_for=...), reusing ModelRepo's file
  map for the click target.

Tests: core query (pk flag, source file exists, subject-area scope), CLI
(json shape, files resolve on disk, text format), and the tree-view
rendering harness (entity→attribute drill-down, key icon, both clicks open
the owning YAML, resting states). 932 pytest pass; all 6 extension gates
green (44 commands). CLI reused, extension 0.3.17.
---
 packages/cli/src/mdl_cli/main.py          |  25 +++
 packages/cli/tests/test_model_tree_cli.py |  57 +++++++
 packages/core/src/mdl_core/query.py       |  63 ++++++++
 packages/core/tests/test_model_tree.py    |  58 +++++++
 uv.lock                                   |   2 +-
 vscode/package.json                       |  30 +++-
 vscode/scripts/treeview.test.mjs          |  68 +++++++-
 vscode/src/extension.ts                   |  25 ++-
 vscode/src/modelView.ts                   | 189 ++++++++++++++++++++++
 9 files changed, 508 insertions(+), 9 deletions(-)
 create mode 100644 packages/cli/tests/test_model_tree_cli.py
 create mode 100644 packages/core/tests/test_model_tree.py
 create mode 100644 vscode/src/modelView.ts

diff --git a/packages/cli/src/mdl_cli/main.py b/packages/cli/src/mdl_cli/main.py
index 54e62b7..5869e43 100644
--- a/packages/cli/src/mdl_cli/main.py
+++ b/packages/cli/src/mdl_cli/main.py
@@ -3158,6 +3158,31 @@ def model_detail(
     typer.echo(json.dumps(entities_detail(_load(model_dir).model, limit=limit), default=str))
 
 
+@model_app.command("tree")
+def model_tree_cmd(
+    model_dir: Path = typer.Option(Path("."), "--model-dir", "-m"),
+    subject_area: str = typer.Option(None, "--subject-area", help="Scope by area name or ULID"),
+    fmt: str = typer.Option("json", "--format", help="json (the VS Code Model tree consumes it)"),
+) -> None:
+    """A navigable tree of the logical model — every entity with its attributes and the
+    source file each lives in — for an editor's Model Explorer (entity → attributes, click
+    to open the YAML). Uncapped, unlike `model detail`. `--format json` is what the VS Code
+    Model tree consumes."""
+    from mdl_core.query import model_tree
+
+    repo = _load(model_dir)
+    tree = model_tree(repo.model, path_for=repo.path_for_ulid, subject_area=subject_area)
+    if fmt == "json":
+        typer.echo(json.dumps(tree, default=str))
+        return
+    # a terse text fallback for the terminal
+    for e in tree["entities"]:
+        typer.secho(e["name"], fg=typer.colors.CYAN)
+        for a in e["attributes"]:
+            key = " (pk)" if a["is_pk"] else ""
+            typer.echo(f"    {a['name']}: {a['domain']}{key}")
+
+
 @app.command()
 def mcp(
     repo: Path = typer.Option(
diff --git a/packages/cli/tests/test_model_tree_cli.py b/packages/cli/tests/test_model_tree_cli.py
new file mode 100644
index 0000000..c23b206
--- /dev/null
+++ b/packages/cli/tests/test_model_tree_cli.py
@@ -0,0 +1,57 @@
+"""CLI: `mdl model tree` — the JSON the VS Code Model Explorer consumes (entity →
+attributes, with a source file per entity for click-to-open)."""
+
+from __future__ import annotations
+
+import json
+
+from typer.testing import CliRunner
+
+from mdl_cli.main import app
+
+runner = CliRunner()
+
+
+def _demo(tmp_path):
+    d = tmp_path / "proj"
+    assert runner.invoke(app, ["init", "--demo", str(d)]).exit_code == 0
+    return d / "model"
+
+
+def test_model_tree_json_shape(tmp_path):
+    model = _demo(tmp_path)
+    r = runner.invoke(app, ["model", "tree", "-m", str(model), "--format", "json"])
+    assert r.exit_code == 0, r.output
+    tree = json.loads(r.stdout)
+    assert "entities" in tree and tree["entities"]
+    e = tree["entities"][0]
+    assert {"name", "attributes", "file"} <= set(e)
+    assert isinstance(e["attributes"], list)
+
+
+def test_model_tree_files_resolve_on_disk(tmp_path):
+    # every entity's `file` must be a real path relative to the model dir — this is the
+    # exact path the extension opens when a user clicks an entity in the tree.
+    model = _demo(tmp_path)
+    tree = json.loads(runner.invoke(app, ["model", "tree", "-m", str(model)]).stdout)
+    for e in tree["entities"]:
+        assert e["file"], f"{e['name']} has no source file"
+        assert (model / e["file"]).exists(), f"{e['file']} does not exist"
+
+
+def test_model_tree_attributes_have_pk_flag(tmp_path):
+    model = _demo(tmp_path)
+    tree = json.loads(runner.invoke(app, ["model", "tree", "-m", str(model)]).stdout)
+    # at least one entity should have a pk attribute (the demo has keyed entities)
+    all_attrs = [a for e in tree["entities"] for a in e["attributes"]]
+    assert any(a["is_pk"] for a in all_attrs)
+    # every attribute carries the fields the tree renders
+    for a in all_attrs:
+        assert {"name", "domain", "role", "nullable", "is_pk"} <= set(a)
+
+
+def test_model_tree_text_format(tmp_path):
+    model = _demo(tmp_path)
+    r = runner.invoke(app, ["model", "tree", "-m", str(model), "--format", "text"])
+    assert r.exit_code == 0, r.output
+    assert "(pk)" in r.output  # the text fallback marks primary keys
diff --git a/packages/core/src/mdl_core/query.py b/packages/core/src/mdl_core/query.py
index 6461c9b..c49202f 100644
--- a/packages/core/src/mdl_core/query.py
+++ b/packages/core/src/mdl_core/query.py
@@ -274,3 +274,66 @@ def _resolve_subject_area_id(model: Model, ref: str) -> str | None:
         if sa.name.lower() == lref:
             return sa.id
     return None
+
+
+def model_tree(
+    model: Model,
+    *,
+    path_for=None,
+    subject_area: str | None = None,
+) -> dict:
+    """A navigable tree of the LOGICAL model for an editor's Model Explorer: every entity
+    with its attributes and each object's source file. Unlike `list_entities` (counts
+    only) and `entities_detail` (chat-sized, capped), this is the full, uncapped structure
+    a tree view drills into, and it carries the source file per entity so a click can open
+    the YAML. `path_for(ulid) -> rel path | None` is threaded in by the CLI (the file map
+    lives on ModelRepo, not on the pure Model); when absent, `file` is null.
+
+    Each entity: name, physical_name, subject_area, definition, pattern, file, and
+    attributes (name, physical_name, domain, role, nullable, is_pk). Attributes keep their
+    authored order. `is_pk` is true when the attribute is a member of a pk KeyGroup or,
+    failing a declared pk, carries the legacy `role: business_key`.
+    """
+    # A requested-but-unknown area resolves to None; scope to NOTHING rather than
+    # silently ignoring the filter (which would show the whole model).
+    want_scope = subject_area is not None
+    sa_id = _resolve_subject_area_id(model, subject_area) if want_scope else None
+    sa_name = {sa.id: sa.name for sa in model.subject_areas.values()}
+    ce_sa = {ce.id: ce.subject_area for ce in model.conceptual_entities.values()}
+
+    entities: list[dict] = []
+    for le in sorted(model.logical_entities.values(), key=lambda e: e.name):
+        area_id = ce_sa.get(le.realises) if le.realises else None
+        if want_scope and area_id != sa_id:
+            continue
+        # pk membership: declared pk KeyGroup members, else legacy business_key attrs
+        pk_ids: set[str] = set()
+        declared_pk = False
+        for kg in model.key_groups.values():
+            if kg.entity == le.id and kg.type == "pk":
+                declared_pk = True
+                pk_ids.update(kg.members)
+        attributes = [
+            {
+                "name": a.name,
+                "physical_name": a.physical_name,
+                "domain": a.domain,
+                "role": a.role,
+                "nullable": a.nullable,
+                "is_pk": (a.id in pk_ids) if declared_pk else (a.role == "business_key"),
+            }
+            for a in le.attributes
+        ]
+        entities.append(
+            {
+                "name": le.name,
+                "physical_name": le.physical_name,
+                "subject_area": sa_name.get(area_id) if area_id else None,
+                "definition": le.definition,
+                "pattern": le.pattern,
+                "file": path_for(le.id) if path_for else None,
+                "attributes": attributes,
+            }
+        )
+
+    return {"project": model.config.name, "entities": entities}
diff --git a/packages/core/tests/test_model_tree.py b/packages/core/tests/test_model_tree.py
new file mode 100644
index 0000000..6c351a8
--- /dev/null
+++ b/packages/core/tests/test_model_tree.py
@@ -0,0 +1,58 @@
+"""The model_tree query — the navigable entity→attribute structure the VS Code Model
+Explorer drills into, with the source file per entity for click-to-open."""
+
+from __future__ import annotations
+
+from mdl_core.query import model_tree
+from mdl_core.repo import ModelRepo
+
+
+def _repo(model_dir):
+    return ModelRepo.load(model_dir)
+
+
+def test_model_tree_entities_and_attributes(model_dir):
+    repo = _repo(model_dir)
+    tree = model_tree(repo.model, path_for=repo.path_for_ulid)
+    by_name = {e["name"]: e for e in tree["entities"]}
+    assert {"counterparty", "trade"} <= set(by_name)
+
+    cp = by_name["counterparty"]
+    attr_names = [a["name"] for a in cp["attributes"]]
+    # attributes keep their authored order
+    assert attr_names == ["counterparty_id", "legal_name"]
+
+
+def test_model_tree_marks_primary_key(model_dir):
+    tree = model_tree(_repo(model_dir).model, path_for=_repo(model_dir).path_for_ulid)
+    cp = next(e for e in tree["entities"] if e["name"] == "counterparty")
+    pk = next(a for a in cp["attributes"] if a["name"] == "counterparty_id")
+    other = next(a for a in cp["attributes"] if a["name"] == "legal_name")
+    # counterparty_id is a business_key → is_pk; legal_name is not
+    assert pk["is_pk"] is True
+    assert other["is_pk"] is False
+    assert other["role"] == "attribute"
+
+
+def test_model_tree_carries_source_file_that_exists(model_dir):
+    # the `file` per entity is what a click opens; it must be a real relative path on disk
+    repo = _repo(model_dir)
+    tree = model_tree(repo.model, path_for=repo.path_for_ulid)
+    cp = next(e for e in tree["entities"] if e["name"] == "counterparty")
+    assert cp["file"] == "logical/entities/counterparty.yaml"
+    assert (model_dir / cp["file"]).exists()
+
+
+def test_model_tree_without_path_for_has_null_file(model_dir):
+    # pure Model (no ModelRepo file map) → file is null, never crashes
+    tree = model_tree(_repo(model_dir).model)
+    assert all(e["file"] is None for e in tree["entities"])
+
+
+def test_model_tree_scopes_to_subject_area(model_dir):
+    repo = _repo(model_dir)
+    # both fixture entities are homed in Trading; an unknown area scopes to nothing
+    scoped = model_tree(repo.model, path_for=repo.path_for_ulid, subject_area="Trading")
+    assert scoped["entities"]
+    empty = model_tree(repo.model, subject_area="no-such-area")
+    assert empty["entities"] == []
diff --git a/uv.lock b/uv.lock
index 20370f9..a65abfa 100644
--- a/uv.lock
+++ b/uv.lock
@@ -939,7 +939,7 @@ requires-dist = [
 
 [[package]]
 name = "modelith-dbt"
-version = "0.6.10"
+version = "0.6.11"
 source = { editable = "." }
 dependencies = [
     { name = "dbt-artifacts-parser" },
diff --git a/vscode/package.json b/vscode/package.json
index 71321b3..7a45d46 100644
--- a/vscode/package.json
+++ b/vscode/package.json
@@ -2,7 +2,7 @@
   "name": "modelith-vscode",
   "displayName": "Modelith",
   "description": "Ontology-anchored, git-native data modeling for dbt — validate, generate, drift, and the Modelith canvas inside VS Code.",
-  "version": "0.3.16",
+  "version": "0.3.17",
   "publisher": "BosonResearch",
   "license": "Apache-2.0",
   "icon": "icon.png",
@@ -90,11 +90,17 @@
     },
     "views": {
       "modelith": [
+        {
+          "id": "modelithModel",
+          "name": "Model",
+          "icon": "$(symbol-class)",
+          "contextualTitle": "Modelith Model"
+        },
         {
           "id": "modelithConfig",
-          "name": "Warehouse Config",
+          "name": "Warehouse Mapping",
           "icon": "$(settings-gear)",
-          "contextualTitle": "Modelith Warehouse Config"
+          "contextualTitle": "Modelith Warehouse Mapping"
         },
         {
           "id": "modelithReverse",
@@ -235,12 +241,21 @@
       },
       {
         "command": "modelith.configRefresh",
-        "title": "Modelith: Refresh Warehouse Config",
+        "title": "Modelith: Refresh Warehouse Mapping",
         "icon": "$(refresh)"
       },
       {
         "command": "modelith.configFocusView",
-        "title": "Modelith: Show Warehouse Config"
+        "title": "Modelith: Show Warehouse Mapping"
+      },
+      {
+        "command": "modelith.refreshModel",
+        "title": "Modelith: Refresh Model tree",
+        "icon": "$(refresh)"
+      },
+      {
+        "command": "modelith.openModelObject",
+        "title": "Modelith: Open model object"
       },
       {
         "command": "modelith.configOpenProject",
@@ -528,6 +543,11 @@
         }
       ],
       "view/title": [
+        {
+          "command": "modelith.refreshModel",
+          "when": "view == modelithModel",
+          "group": "navigation@1"
+        },
         {
           "command": "modelith.configSuggest",
           "when": "view == modelithConfig",
diff --git a/vscode/scripts/treeview.test.mjs b/vscode/scripts/treeview.test.mjs
index 8ff9ddf..153eaa8 100644
--- a/vscode/scripts/treeview.test.mjs
+++ b/vscode/scripts/treeview.test.mjs
@@ -20,11 +20,12 @@ const VSCODE_STUB = `
 export const TreeItemCollapsibleState = { None: 0, Collapsed: 1, Expanded: 2 };
 export class ThemeIcon { constructor(id, color) { this.id = id; this.color = color; } }
 export class ThemeColor { constructor(id) { this.id = id; } }
-export class MarkdownString { constructor(v) { this.value = v; } }
+export class MarkdownString { constructor(v) { this.value = v ?? ""; } appendMarkdown(v) { this.value += v; return this; } }
 export class TreeItem {
   constructor(label, collapsibleState) { this.label = label; this.collapsibleState = collapsibleState; }
 }
 export class EventEmitter { constructor(){ this.event = () => ({ dispose(){} }); } fire(){} dispose(){} }
+export const Uri = { file: (p) => ({ fsPath: p, path: p, scheme: "file" }) };
 export const window = { createOutputChannel: () => ({ appendLine(){}, append(){}, show(){} }) };
 export const workspace = { getConfiguration: () => ({ get: () => undefined }) };
 // ./mdl exports the provider imports — never called here (we drive getChildren directly).
@@ -226,6 +227,71 @@ console.log("\nWarehouse Config — unclassified custom prefixes surface for ass
   ok(!top.some((n) => n.kind === "prefixGroup" && n.prefix !== "pres_art_"), "only real unclassified prefixes group");
 }
 
+// --- Model frame: entity → attribute drill-down + click-to-open the YAML ------------
+const { ModelTreeProvider } = await load("modelView.ts");
+console.log("\nModel tree — entities expand to attributes; click opens the YAML");
+{
+  const p = new ModelTreeProvider({ appendLine() {}, append() {}, show() {} });
+  // inject the model dir + entities (bypass the CLI); getChildren renders from them
+  p.modelDir = "/tmp/proj/model";
+  p.entities = [
+    {
+      name: "counterparty",
+      physical_name: "COUNTERPARTY",
+      subject_area: "Trading",
+      definition: "A trading partner.",
+      pattern: null,
+      file: "logical/entities/counterparty.yaml",
+      attributes: [
+        { name: "counterparty_id", physical_name: null, domain: "id_bigint", role: "business_key", nullable: false, is_pk: true },
+        { name: "legal_name", physical_name: null, domain: "string", role: "attribute", nullable: true, is_pk: false },
+      ],
+    },
+    { name: "trade", physical_name: null, subject_area: "Trading", definition: null, pattern: null, file: "logical/entities/trade.yaml", attributes: [] },
+  ];
+
+  // top level = one row per entity, each collapsible when it has attributes
+  const top = p.getChildren();
+  const topItems = top.map((n) => p.getTreeItem(n));
+  ok(labels(topItems).join(",") === "counterparty,trade", `entities render as rows: ${labels(topItems)}`);
+  const cpItem = topItems[0];
+  ok(cpItem.collapsibleState === 1, "an entity with attributes is collapsible (drill-down)");
+  ok(String(cpItem.description).includes("2 attr"), `entity row shows an attribute count: ${cpItem.description}`);
+  ok(topItems[1].collapsibleState === 0, "an entity with no attributes is a leaf");
+
+  // clicking an entity opens its YAML (the openModelObject command with file + name)
+  ok(cpItem.command?.command === "modelith.openModelObject", "entity click opens the model object");
+  ok(String(cpItem.command.arguments[0].fsPath ?? cpItem.command.arguments[0].path).includes("counterparty.yaml"),
+    "entity click targets its own logical YAML");
+
+  // expanding the entity yields its attributes, PK first-classed
+  const cpNode = top[0];
+  const attrRows = p.getChildren(cpNode);
+  const attrItems = attrRows.map((n) => p.getTreeItem(n));
+  ok(labels(attrItems).join(",") === "counterparty_id,legal_name", `attributes render in order: ${labels(attrItems)}`);
+  ok(attrItems[0].iconPath?.id === "key", "the primary-key attribute gets the key icon");
+  ok(attrItems[1].iconPath?.id === "symbol-field", "a non-key attribute gets the field icon");
+  ok(String(attrItems[0].description).includes("pk"), `pk attribute is marked: ${attrItems[0].description}`);
+  // clicking an attribute opens the OWNING entity's YAML
+  ok(attrItems[0].command?.command === "modelith.openModelObject", "attribute click opens the model object");
+  ok(String(attrItems[0].command.arguments[0].fsPath ?? attrItems[0].command.arguments[0].path).includes("counterparty.yaml"),
+    "attribute click opens the owning entity's YAML");
+}
+
+console.log("\nModel tree — resting states");
+{
+  const p = new ModelTreeProvider({ appendLine() {}, append() {}, show() {} });
+  // no model dir -> explains itself
+  const rows = p.getChildren().map((n) => p.getTreeItem(n));
+  ok(String(rows[0].label).includes("No model"), "no-model state explains itself, not blank");
+
+  const p2 = new ModelTreeProvider({ appendLine() {}, append() {}, show() {} });
+  p2.modelDir = "/tmp/proj/model";
+  p2.entities = [];
+  const rows2 = p2.getChildren().map((n) => p2.getTreeItem(n));
+  ok(String(rows2[0].label).includes("No entities"), "empty model points at init/import, not blank");
+}
+
 rmSync(dir, { recursive: true, force: true });
 console.log();
 if (failed) { console.error(`✗ ${failed} assertion(s) failed`); process.exit(1); }
diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts
index 74b4c8d..127ae06 100644
--- a/vscode/src/extension.ts
+++ b/vscode/src/extension.ts
@@ -6,6 +6,7 @@ import type { LanguageClient } from "vscode-languageclient/node";
 import { CanvasManager } from "./canvasPanel";
 import { registerChatParticipant } from "./chatParticipant";
 import { ConfigTreeProvider } from "./configView";
+import { ModelTreeProvider, openModelObject } from "./modelView";
 import { DocsProvider } from "./docsView";
 import { DriftCodeActionProvider, DriftManager } from "./driftDiagnostics";
 import { DriftTreeProvider } from "./driftView";
@@ -306,12 +307,32 @@ export async function activate(ctx: vscode.ExtensionContext): Promise {
     stateWatcher.onDidDelete(onStateChange),
   );
 
-  // Warehouse Config: a live view of how the reverse: config classifies every dbt model,
-  // grouped by role, with Suggest/Import in its title bar. The home of the config workflow.
+  // Warehouse Mapping: a live view of how the reverse: config classifies every dbt model
+  // INTO entities, grouped by role, with Suggest/Import in its title bar. Distinct from the
+  // Model tree below (which is the logical model itself); this one answers "how does my
+  // warehouse map to entities?".
   const configTree = new ConfigTreeProvider(out);
   ctx.subscriptions.push(vscode.window.registerTreeDataProvider("modelithConfig", configTree));
   void configTree.refresh();
 
+  // Model: the logical model as an entity → attribute tree; clicking opens the YAML.
+  const modelTree = new ModelTreeProvider(out);
+  ctx.subscriptions.push(vscode.window.registerTreeDataProvider("modelithModel", modelTree));
+  ctx.subscriptions.push(
+    vscode.commands.registerCommand(
+      "modelith.openModelObject",
+      (uri: vscode.Uri, reveal: string) => openModelObject(uri, reveal),
+    ),
+    vscode.commands.registerCommand("modelith.refreshModel", () => modelTree.refresh()),
+    // keep the Model tree current when a logical entity YAML is saved (editor or CLI edit)
+    vscode.workspace.onDidSaveTextDocument((doc) => {
+      if (doc.uri.fsPath.includes(`${path.sep}logical${path.sep}`) && doc.uri.fsPath.endsWith(".yaml")) {
+        void modelTree.refresh();
+      }
+    }),
+  );
+  void modelTree.refresh();
+
   // Active-model indicator: which model dir the panels read. In a multi-model workspace
   // (e.g. an original model/ plus a fresh model_reversed/) auto-discovery picks the
   // shallowest, which may not be the one the user is reviewing. This status-bar item
diff --git a/vscode/src/modelView.ts b/vscode/src/modelView.ts
new file mode 100644
index 0000000..4976e50
--- /dev/null
+++ b/vscode/src/modelView.ts
@@ -0,0 +1,189 @@
+import * as path from "path";
+import * as vscode from "vscode";
+import { findMdl, findModelDir, runMdl } from "./mdl";
+
+/** The Model tree: the LOGICAL model as an entity → attribute drill-down. Each entity
+ * expands to its attributes (PK marked), and clicking an entity or attribute opens the
+ * entity's `logical/entities/.yaml` at the right line. This is the "what is my
+ * model" view — distinct from the Warehouse Mapping tree, which shows how dbt models
+ * classify into entities. Fed by `mdl model tree --format json`, so it always reflects
+ * the committed model, never a stale copy. */
+
+export interface TreeAttribute {
+  name: string;
+  physical_name: string | null;
+  domain: string | null;
+  role: string;
+  nullable: boolean;
+  is_pk: boolean;
+}
+export interface TreeEntity {
+  name: string;
+  physical_name: string | null;
+  subject_area: string | null;
+  definition: string | null;
+  pattern: string | null;
+  file: string | null;
+  attributes: TreeAttribute[];
+}
+
+type Node = EntityNode | AttributeNode | StatusNode;
+interface EntityNode {
+  kind: "entity";
+  entity: TreeEntity;
+}
+interface AttributeNode {
+  kind: "attribute";
+  entity: TreeEntity;
+  attr: TreeAttribute;
+}
+/** A resting row when the model is empty / unreadable, so the pane explains itself
+ * instead of rendering blank. */
+interface StatusNode {
+  kind: "status";
+  label: string;
+  icon: vscode.ThemeIcon;
+}
+
+export class ModelTreeProvider implements vscode.TreeDataProvider {
+  private readonly emitter = new vscode.EventEmitter();
+  readonly onDidChangeTreeData = this.emitter.event;
+  private entities: TreeEntity[] = [];
+  private modelDir: string | undefined;
+
+  constructor(private readonly out: vscode.OutputChannel) {}
+
+  /** Re-read the model from `mdl model tree --format json`. */
+  async refresh(): Promise {
+    const dir = await findModelDir();
+    this.modelDir = dir;
+    if (!dir) {
+      this.entities = [];
+      this.emitter.fire(undefined);
+      return;
+    }
+    try {
+      const bin = await findMdl(dir);
+      const r = await runMdl(bin, ["model", "tree", "-m", ".", "--format", "json"], dir);
+      this.entities =
+        r.code === 0 && r.stdout.trim()
+          ? ((JSON.parse(r.stdout) as { entities?: TreeEntity[] }).entities ?? [])
+          : [];
+    } catch (e) {
+      this.out.appendLine(`[model] could not read the model tree: ${e}`);
+      this.entities = [];
+    }
+    this.emitter.fire(undefined);
+  }
+
+  getTreeItem(node: Node): vscode.TreeItem {
+    if (node.kind === "status") {
+      const ti = new vscode.TreeItem(node.label, vscode.TreeItemCollapsibleState.None);
+      ti.iconPath = node.icon;
+      return ti;
+    }
+    if (node.kind === "entity") {
+      const e = node.entity;
+      const ti = new vscode.TreeItem(
+        e.name,
+        e.attributes.length
+          ? vscode.TreeItemCollapsibleState.Collapsed
+          : vscode.TreeItemCollapsibleState.None,
+      );
+      ti.iconPath = new vscode.ThemeIcon("symbol-class");
+      const bits: string[] = [`${e.attributes.length} attr`];
+      if (e.subject_area) bits.push(e.subject_area);
+      if (e.pattern) bits.push(e.pattern);
+      ti.description = bits.join(" · ");
+      const md = new vscode.MarkdownString();
+      md.appendMarkdown(`**${e.name}**${e.physical_name ? ` · \`${e.physical_name}\`` : ""}\n\n`);
+      if (e.definition) md.appendMarkdown(`${e.definition}\n\n`);
+      if (e.subject_area) md.appendMarkdown(`Subject area: ${e.subject_area}\n\n`);
+      ti.tooltip = md;
+      ti.contextValue = "modelEntity";
+      // click opens the entity's logical YAML
+      ti.command = this.openCommand(e, e.name);
+      return ti;
+    }
+    // attribute row
+    const a = node.attr;
+    const ti = new vscode.TreeItem(a.name, vscode.TreeItemCollapsibleState.None);
+    ti.iconPath = new vscode.ThemeIcon(a.is_pk ? "key" : "symbol-field");
+    const bits: string[] = [];
+    if (a.domain) bits.push(a.domain);
+    if (a.is_pk) bits.push("pk");
+    if (!a.nullable) bits.push("not null");
+    ti.description = bits.join(" · ");
+    if (a.physical_name) {
+      const md = new vscode.MarkdownString();
+      md.appendMarkdown(`**${a.name}** · \`${a.physical_name}\`\n\n${a.domain ?? ""}`);
+      ti.tooltip = md;
+    }
+    ti.contextValue = "modelAttribute";
+    // click opens the owning entity's YAML at the attribute line
+    ti.command = this.openCommand(node.entity, a.name);
+    return ti;
+  }
+
+  /** A command that opens the entity's source YAML, revealing the given search term
+   * (the entity or attribute name) so the cursor lands near it. */
+  private openCommand(entity: TreeEntity, reveal: string): vscode.Command | undefined {
+    if (!entity.file || !this.modelDir) return undefined;
+    const uri = vscode.Uri.file(path.join(this.modelDir, entity.file));
+    return {
+      command: "modelith.openModelObject",
+      title: "Open in the model YAML",
+      arguments: [uri, reveal],
+    };
+  }
+
+  getChildren(node?: Node): Node[] {
+    if (!node) {
+      if (!this.modelDir) {
+        return [
+          {
+            kind: "status",
+            label: "No model in this workspace",
+            icon: new vscode.ThemeIcon("info"),
+          },
+        ];
+      }
+      if (!this.entities.length) {
+        return [
+          {
+            kind: "status",
+            label: "No entities yet — run mdl init or import a model",
+            icon: new vscode.ThemeIcon("info"),
+          },
+        ];
+      }
+      return this.entities.map((entity) => ({ kind: "entity" as const, entity }));
+    }
+    if (node.kind === "entity") {
+      return node.entity.attributes.map((attr) => ({
+        kind: "attribute" as const,
+        entity: node.entity,
+        attr,
+      }));
+    }
+    return [];
+  }
+}
+
+/** Open a model object's YAML and move the cursor to the first line mentioning `reveal`
+ * (the entity or attribute name), so a click lands where the user expected. */
+export async function openModelObject(uri: vscode.Uri, reveal: string): Promise {
+  const doc = await vscode.workspace.openTextDocument(uri);
+  const editor = await vscode.window.showTextDocument(doc);
+  const text = doc.getText();
+  // match `name: ` first (the authored field), else a bare mention
+  const idx =
+    text.indexOf(`: ${reveal}\n`) >= 0
+      ? text.indexOf(`: ${reveal}\n`)
+      : text.indexOf(reveal);
+  if (idx >= 0) {
+    const pos = doc.positionAt(idx);
+    editor.selection = new vscode.Selection(pos, pos);
+    editor.revealRange(new vscode.Range(pos, pos), vscode.TextEditorRevealType.InCenter);
+  }
+}

From baef9ce5d546dbe6a1b4f5bc63b8710b9d4e9709 Mon Sep 17 00:00:00 2001
From: Debasish Bose 
Date: Sun, 27 Sep 2026 17:33:32 +1000
Subject: [PATCH 4/6] =?UTF-8?q?canvas:=20compare/merge=20Phase=201=20?=
 =?UTF-8?q?=E2=80=94=20display=20filters=20+=20per-field=20selection?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The review DiffView gains the first slice of the semantic compare/merge
view (Tier 1 spec): display filters and per-field change selection, so a
reviewer can isolate additions/renames/etc. and accept or reject changes
one field at a time — not just whole objects.

- Filters (All / Additions / Modifications / Renames / Deletions /
  Conflicts) are VIEW-ONLY: they change what is visible, never what is
  selected, so a hidden change can never be silently dropped from a
  proposal. Each chip shows its count; empty categories are hidden.
- Per-field selection keys on (ulid, field). An object is included when
  ≥1 of its fields is selected; the header checkbox shows a tri-state
  (all/some/none) and toggles every field at once. The original per-object
  path (onToggle) is kept intact, so ReviewScreen works unchanged; the new
  per-field mode activates only when a host passes onToggleField.
- Pure semantics live in reviewModel.ts (filter + selection), so they are
  unit-testable apart from the component.

Testing (per the 'system + interaction tests matter now' directive):
- Vitest + Testing Library added to canvas/ (first JS test runner here):
  reviewModel unit tests (8) + DiffView interaction tests (5) that render
  the real component and click filters + per-field checkboxes, asserting
  filtering never changes selection and per-field exclusion drops the right
  objects.
- pytest server-side system test (test_review_diff_contract): the
  /api/preview ModelDiffDoc carries the (ulid, field) identity per-field
  selection depends on, and is stable across re-previews — the wire
  contract guard between server and the canvas selection.

935 pytest pass, 13 canvas tests pass, ruff clean, canvas typechecks.
---
 canvas/package-lock.json                      | 1436 ++++++++++++++++-
 canvas/package.json                           |   10 +-
 canvas/src/sme/DiffView.test.tsx              |  104 ++
 canvas/src/sme/DiffView.tsx                   |  175 +-
 canvas/src/sme/reviewModel.test.ts            |   91 ++
 canvas/src/sme/reviewModel.ts                 |  134 ++
 canvas/src/sme/sme.css                        |   24 +
 canvas/src/test/diffFixture.ts                |   68 +
 canvas/src/test/setup.ts                      |    1 +
 canvas/vitest.config.ts                       |   14 +
 .../{mocks-DUwiNnjR.js => mocks-AfcrRcE6.js}  |    0
 .../mdl_server/static/assets/sme-BuG6X873.js  |    2 -
 .../mdl_server/static/assets/sme-C10_zE1B.css |    1 +
 .../mdl_server/static/assets/sme-Csp0f4gO.js  |    2 +
 .../mdl_server/static/assets/sme-DYGKbOTM.css |    1 -
 .../server/src/mdl_server/static/mocks.html   |    4 +-
 .../server/src/mdl_server/static/sme.html     |    4 +-
 .../server/tests/test_review_diff_contract.py |   72 +
 18 files changed, 2097 insertions(+), 46 deletions(-)
 create mode 100644 canvas/src/sme/DiffView.test.tsx
 create mode 100644 canvas/src/sme/reviewModel.test.ts
 create mode 100644 canvas/src/sme/reviewModel.ts
 create mode 100644 canvas/src/test/diffFixture.ts
 create mode 100644 canvas/src/test/setup.ts
 create mode 100644 canvas/vitest.config.ts
 rename packages/server/src/mdl_server/static/assets/{mocks-DUwiNnjR.js => mocks-AfcrRcE6.js} (100%)
 delete mode 100644 packages/server/src/mdl_server/static/assets/sme-BuG6X873.js
 create mode 100644 packages/server/src/mdl_server/static/assets/sme-C10_zE1B.css
 create mode 100644 packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js
 delete mode 100644 packages/server/src/mdl_server/static/assets/sme-DYGKbOTM.css
 create mode 100644 packages/server/tests/test_review_diff_contract.py

diff --git a/canvas/package-lock.json b/canvas/package-lock.json
index 4016b28..24abe14 100644
--- a/canvas/package-lock.json
+++ b/canvas/package-lock.json
@@ -14,13 +14,45 @@
         "reactflow": "^11.11.4"
       },
       "devDependencies": {
+        "@testing-library/jest-dom": "^6.9.1",
+        "@testing-library/react": "^16.3.3",
         "@types/react": "^18.3.12",
         "@types/react-dom": "^18.3.1",
         "@vitejs/plugin-react": "^4.3.4",
+        "jsdom": "^25.0.1",
         "typescript": "^5.6.3",
-        "vite": "^5.4.11"
+        "vite": "^5.4.11",
+        "vitest": "^2.1.9"
       }
     },
+    "node_modules/@adobe/css-tools": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz",
+      "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@asamuzakjp/css-color": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+      "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/css-calc": "^2.1.3",
+        "@csstools/css-color-parser": "^3.0.9",
+        "@csstools/css-parser-algorithms": "^3.0.4",
+        "@csstools/css-tokenizer": "^3.0.3",
+        "lru-cache": "^10.4.3"
+      }
+    },
+    "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/@babel/code-frame": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -255,6 +287,16 @@
         "@babel/core": "^7.0.0-0"
       }
     },
+    "node_modules/@babel/runtime": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
+      "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/template": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
@@ -303,6 +345,121 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@csstools/color-helpers": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+      "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT-0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@csstools/css-calc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+      "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-color-parser": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+      "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@csstools/color-helpers": "^5.1.0",
+        "@csstools/css-calc": "^2.1.4"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-parser-algorithms": "^3.0.5",
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-parser-algorithms": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+      "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@csstools/css-tokenizer": "^3.0.4"
+      }
+    },
+    "node_modules/@csstools/css-tokenizer": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+      "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/csstools"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/csstools"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/@dagrejs/dagre": {
       "version": "1.1.8",
       "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.8.tgz",
@@ -1221,6 +1378,90 @@
         "win32"
       ]
     },
+    "node_modules/@testing-library/dom": {
+      "version": "10.4.2",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz",
+      "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/runtime": "^7.12.5",
+        "@types/aria-query": "^5.0.1",
+        "aria-query": "5.3.0",
+        "dom-accessibility-api": "^0.5.9",
+        "lz-string": "^1.5.0",
+        "picocolors": "1.1.1",
+        "pretty-format": "^27.0.2"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@testing-library/jest-dom": {
+      "version": "6.9.1",
+      "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz",
+      "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@adobe/css-tools": "^4.4.0",
+        "aria-query": "^5.0.0",
+        "css.escape": "^1.5.1",
+        "dom-accessibility-api": "^0.6.3",
+        "picocolors": "^1.1.1",
+        "redent": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=14",
+        "npm": ">=6",
+        "yarn": ">=1"
+      }
+    },
+    "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
+      "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@testing-library/react": {
+      "version": "16.3.3",
+      "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz",
+      "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@testing-library/dom": "^10.0.0",
+        "@types/react": "^18.0.0 || ^19.0.0",
+        "@types/react-dom": "^18.0.0 || ^19.0.0",
+        "react": "^18.0.0 || ^19.0.0",
+        "react-dom": "^18.0.0 || ^19.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@types/aria-query": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
+      "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/@types/babel__core": {
       "version": "7.20.5",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -1581,6 +1822,181 @@
         "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
       }
     },
+    "node_modules/@vitest/expect": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
+      "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
+      "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.12"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
+      "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
+      "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "2.1.9",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
+      "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
+      "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^3.0.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
+      "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "loupe": "^3.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/aria-query": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+      "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/baseline-browser-mapping": {
       "version": "2.11.7",
       "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.7.tgz",
@@ -1628,6 +2044,30 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/caniuse-lite": {
       "version": "1.0.30001806",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
@@ -1649,18 +2089,86 @@
       ],
       "license": "CC-BY-4.0"
     },
+    "node_modules/chai": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+      "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
+      "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
     "node_modules/classcat": {
       "version": "5.0.5",
       "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
       "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
       "license": "MIT"
     },
-    "node_modules/convert-source-map": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
-      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
       "dev": true,
-      "license": "MIT"
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/css.escape": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
+      "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cssstyle": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+      "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@asamuzakjp/css-color": "^3.2.0",
+        "rrweb-cssom": "^0.8.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/cssstyle/node_modules/rrweb-cssom": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+      "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/csstype": {
       "version": "3.2.3",
@@ -1774,6 +2282,20 @@
         "node": ">=12"
       }
     },
+    "node_modules/data-urls": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+      "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/debug": {
       "version": "4.4.3",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -1792,6 +2314,66 @@
         }
       }
     },
+    "node_modules/decimal.js": {
+      "version": "10.6.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+      "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dequal": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dom-accessibility-api": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
+      "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.5.398",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz",
@@ -1799,6 +2381,75 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.21.5",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -1848,6 +2499,43 @@
         "node": ">=6"
       }
     },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+      "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+      "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.4",
+        "mime-types": "^2.1.35"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1863,6 +2551,16 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -1873,12 +2571,218 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/html-encoding-sniffer": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+      "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-encoding": "^3.1.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/http-proxy-agent": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-potential-custom-element-name": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "license": "MIT"
     },
+    "node_modules/jsdom": {
+      "version": "25.0.1",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
+      "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssstyle": "^4.1.0",
+        "data-urls": "^5.0.0",
+        "decimal.js": "^10.4.3",
+        "form-data": "^4.0.0",
+        "html-encoding-sniffer": "^4.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.5",
+        "is-potential-custom-element-name": "^1.0.1",
+        "nwsapi": "^2.2.12",
+        "parse5": "^7.1.2",
+        "rrweb-cssom": "^0.7.1",
+        "saxes": "^6.0.0",
+        "symbol-tree": "^3.2.4",
+        "tough-cookie": "^5.0.0",
+        "w3c-xmlserializer": "^5.0.0",
+        "webidl-conversions": "^7.0.0",
+        "whatwg-encoding": "^3.1.1",
+        "whatwg-mimetype": "^4.0.0",
+        "whatwg-url": "^14.0.0",
+        "ws": "^8.18.0",
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "canvas": "^2.11.2"
+      },
+      "peerDependenciesMeta": {
+        "canvas": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/jsesc": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -1917,6 +2821,13 @@
         "loose-envify": "cli.js"
       }
     },
+    "node_modules/loupe": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+      "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/lru-cache": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -1927,6 +2838,70 @@
         "yallist": "^3.0.2"
       }
     },
+    "node_modules/lz-string": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
+      "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "lz-string": "bin/bin.js"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/min-indent": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+      "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -1963,6 +2938,43 @@
         "node": ">=18"
       }
     },
+    "node_modules/nwsapi": {
+      "version": "2.2.28",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.28.tgz",
+      "integrity": "sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -1999,6 +3011,32 @@
         "node": "^10 || ^12 || >=14"
       }
     },
+    "node_modules/pretty-format": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
+      "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "ansi-regex": "^5.0.1",
+        "ansi-styles": "^5.0.0",
+        "react-is": "^17.0.1"
+      },
+      "engines": {
+        "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/react": {
       "version": "18.3.1",
       "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -2024,6 +3062,14 @@
         "react": "^18.3.1"
       }
     },
+    "node_modules/react-is": {
+      "version": "17.0.2",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+      "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true
+    },
     "node_modules/react-refresh": {
       "version": "0.17.0",
       "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@@ -2052,6 +3098,20 @@
         "react-dom": ">=17"
       }
     },
+    "node_modules/redent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
+      "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "indent-string": "^4.0.0",
+        "strip-indent": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/rollup": {
       "version": "4.62.3",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.3.tgz",
@@ -2097,6 +3157,33 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/rrweb-cssom": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
+      "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/saxes": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+      "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "xmlchars": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=v12.22.7"
+      }
+    },
     "node_modules/scheduler": {
       "version": "0.23.2",
       "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -2116,6 +3203,13 @@
         "semver": "bin/semver.js"
       }
     },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true,
+      "license": "ISC"
+    },
     "node_modules/source-map-js": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -2126,6 +3220,130 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/std-env": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+      "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/strip-indent": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+      "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "min-indent": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/symbol-tree": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinypool": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+      "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+      "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+      "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tldts": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+      "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tldts-core": "^6.1.86"
+      },
+      "bin": {
+        "tldts": "bin/cli.js"
+      }
+    },
+    "node_modules/tldts-core": {
+      "version": "6.1.86",
+      "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+      "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tough-cookie": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+      "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tldts": "^6.1.32"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
+      "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/typescript": {
       "version": "5.9.3",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -2240,6 +3458,212 @@
         }
       }
     },
+    "node_modules/vite-node": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
+      "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.3.7",
+        "es-module-lexer": "^1.5.4",
+        "pathe": "^1.1.2",
+        "vite": "^5.0.0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/vitest": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
+      "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/expect": "2.1.9",
+        "@vitest/mocker": "2.1.9",
+        "@vitest/pretty-format": "^2.1.9",
+        "@vitest/runner": "2.1.9",
+        "@vitest/snapshot": "2.1.9",
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "debug": "^4.3.7",
+        "expect-type": "^1.1.0",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2",
+        "std-env": "^3.8.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.1",
+        "tinypool": "^1.0.1",
+        "tinyrainbow": "^1.2.0",
+        "vite": "^5.0.0",
+        "vite-node": "2.1.9",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "@vitest/browser": "2.1.9",
+        "@vitest/ui": "2.1.9",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/w3c-xmlserializer": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+      "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "xml-name-validator": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/whatwg-encoding": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+      "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+      "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+      "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "14.2.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
+      "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "^5.1.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ws": {
+      "version": "8.22.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
+      "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xml-name-validator": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+      "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
diff --git a/canvas/package.json b/canvas/package.json
index 2eb5607..0abe96d 100644
--- a/canvas/package.json
+++ b/canvas/package.json
@@ -6,7 +6,9 @@
   "scripts": {
     "dev": "vite",
     "build": "tsc --noEmit && vite build",
-    "typecheck": "tsc --noEmit"
+    "typecheck": "tsc --noEmit",
+    "test": "vitest run",
+    "test:watch": "vitest"
   },
   "dependencies": {
     "@dagrejs/dagre": "^1.1.4",
@@ -15,10 +17,14 @@
     "reactflow": "^11.11.4"
   },
   "devDependencies": {
+    "@testing-library/jest-dom": "^6.9.1",
+    "@testing-library/react": "^16.3.3",
     "@types/react": "^18.3.12",
     "@types/react-dom": "^18.3.1",
     "@vitejs/plugin-react": "^4.3.4",
+    "jsdom": "^25.0.1",
     "typescript": "^5.6.3",
-    "vite": "^5.4.11"
+    "vite": "^5.4.11",
+    "vitest": "^2.1.9"
   }
 }
diff --git a/canvas/src/sme/DiffView.test.tsx b/canvas/src/sme/DiffView.test.tsx
new file mode 100644
index 0000000..575d23d
--- /dev/null
+++ b/canvas/src/sme/DiffView.test.tsx
@@ -0,0 +1,104 @@
+import { fireEvent, render, screen, within } from "@testing-library/react";
+import { useState } from "react";
+import { describe, expect, it } from "vitest";
+import { makeDiff } from "../test/diffFixture";
+import { DiffView } from "./DiffView";
+import { allFieldKeys, fieldKey, includedUlids, toggleField } from "./reviewModel";
+
+/** The list-pane row for an entity (its name also appears in the detail 

, so scope + * to the .rv-list to disambiguate). */ +function listRow(name: string): HTMLElement { + const list = document.querySelector(".rv-list") as HTMLElement; + return within(list).getByText(name).closest(".rv-obj") as HTMLElement; +} + +/** A host harness mirroring how ReviewScreen would drive per-field selection: it owns the + * field-keyed Set and exposes the derived included ULIDs so a test can assert them. */ +function PerFieldHost() { + const diff = makeDiff(); + const [selected, setSelected] = useState>(() => allFieldKeys(diff)); + const included = includedUlids(diff, selected); + return ( +
+
{included.sort().join(",")}
+ setSelected((s) => toggleField(fieldKey(ulid, field), s))} + /> +
+ ); +} + +describe("DiffView — filters (view only)", () => { + it("shows all changes by default and filters visibility without dropping selection", () => { + render(); + const list = () => document.querySelector(".rv-list") as HTMLElement; + // all four objects present initially + expect(within(list()).getByText("counterparty")).toBeInTheDocument(); + expect(within(list()).getByText("instrument")).toBeInTheDocument(); + expect(within(list()).getByText("legacy")).toBeInTheDocument(); + + // click the Additions filter -> only the added entity is visible in the list + fireEvent.click(screen.getByRole("tab", { name: /Additions/ })); + expect(within(list()).getByText("instrument")).toBeInTheDocument(); + expect(within(list()).queryByText("counterparty")).not.toBeInTheDocument(); + expect(within(list()).queryByText("legacy")).not.toBeInTheDocument(); + + // filtering did NOT change selection: everything is still included + expect(screen.getByTestId("included")).toHaveTextContent("E_ADD,E_DEL,E_MOD,E_REN"); + + // back to All -> everything visible again + fireEvent.click(screen.getByRole("tab", { name: /^All/ })); + expect(within(list()).getByText("counterparty")).toBeInTheDocument(); + }); + + it("only offers filters that have matches", () => { + render(); + // the fixture has one of each, so every filter with count>0 shows + for (const label of ["All", "Additions", "Modifications", "Renames", "Deletions", "Conflicts"]) { + expect(screen.getByRole("tab", { name: new RegExp(label) })).toBeInTheDocument(); + } + }); +}); + +describe("DiffView — per-field selection", () => { + it("unchecking every field of an object excludes it from the action", () => { + render(); + // focus the modified entity (it has two field changes) + fireEvent.click(listRow("counterparty")); + // its two per-field checkboxes live in the detail pane + const defBox = screen.getByLabelText("include change: definition"); + const domBox = screen.getByLabelText("include change: domain"); + expect(defBox).toBeChecked(); + expect(domBox).toBeChecked(); + + // uncheck both -> E_MOD drops out, others stay + fireEvent.click(defBox); + fireEvent.click(domBox); + expect(screen.getByTestId("included")).toHaveTextContent("E_ADD,E_DEL,E_REN"); + expect(screen.getByTestId("included")).not.toHaveTextContent("E_MOD"); + }); + + it("the object header checkbox reflects a partial (indeterminate) selection", () => { + render(); + fireEvent.click(listRow("counterparty")); + // uncheck ONE field -> the object stays included, header is indeterminate + fireEvent.click(screen.getByLabelText("include change: definition")); + expect(screen.getByTestId("included")).toHaveTextContent("E_MOD"); + const row = listRow("counterparty"); + const header = within(row as HTMLElement).getByRole("checkbox") as HTMLInputElement; + expect(header.indeterminate).toBe(true); + }); + + it("the header checkbox toggles all of an object's fields at once", () => { + render(); + fireEvent.click(listRow("counterparty")); + const row = listRow("counterparty"); + const header = within(row as HTMLElement).getByRole("checkbox"); + // header on -> off excludes the whole object + fireEvent.click(header); + expect(screen.getByTestId("included")).not.toHaveTextContent("E_MOD"); + }); +}); diff --git a/canvas/src/sme/DiffView.tsx b/canvas/src/sme/DiffView.tsx index eaeff23..658624c 100644 --- a/canvas/src/sme/DiffView.tsx +++ b/canvas/src/sme/DiffView.tsx @@ -1,49 +1,111 @@ import { useMemo, useState } from "react"; import type { FieldChangeDoc, ModelDiffDoc, ObjectChangeDoc } from "../types"; +import { + type ObjectSelectionState, + type ReviewFilter, + REVIEW_FILTERS, + fieldKey, + filterCounts, + filterObjects, + objectSelectionState, +} from "./reviewModel"; -/** The changed-object list and detail pane (plan §M1/M2). +/** The changed-object list and detail pane (plan §M1/M2), Phase 1: display FILTERS + + * per-FIELD selection. * * One list of only what changed, grouped by object, each change a plain-language - * sentence produced server-side (mdl_core.diff builds the label and detail, so - * the CLI, the PR body and this screen never drift). */ + * sentence produced server-side (mdl_core.diff builds the label and detail, so the CLI, + * the PR body and this screen never drift). Filters are a VIEW concern — they change what + * is visible, never what is selected (a hidden change is never silently dropped). + * + * Selection has two modes, chosen by which callback the host passes: + * - per-FIELD (`onToggleField` / field-keyed `selected`): the new Phase 1 model — each + * change is independently checkable and the object header shows a tri-state. + * - per-OBJECT (`onToggle` / ulid-keyed `selected`): the original propose flow, kept so + * existing callers work unchanged. */ export function DiffView({ diff, selectable, selected, onToggle, + onToggleField, }: { diff: ModelDiffDoc; - /** per-object checkboxes for selective proposal; off when a create_* is staged */ + /** show selection checkboxes; off when a create_* is staged */ selectable?: boolean; + /** the selected set — object ULIDs (per-object mode) or field keys (per-field mode) */ selected?: Set; + /** per-OBJECT toggle (original mode). When set, checkboxes are whole-object. */ onToggle?: (ulid: string) => void; + /** per-FIELD toggle (Phase 1). When set, checkboxes are per-field with an object + * tri-state header; takes precedence over onToggle. */ + onToggleField?: (ulid: string, field: string) => void; }) { + const [filter, setFilter] = useState("all"); + const visible = useMemo(() => filterObjects(diff, filter), [diff, filter]); + const counts = useMemo(() => filterCounts(diff), [diff]); const [focus, setFocus] = useState(diff.objects[0]?.ulid ?? null); const obj = useMemo( - () => diff.objects.find((o) => o.ulid === focus) ?? diff.objects[0] ?? null, - [diff, focus], + () => visible.find((o) => o.ulid === focus) ?? visible[0] ?? diff.objects[0] ?? null, + [visible, diff, focus], ); + const perField = !!onToggleField; if (!diff.objects.length) { return

No model changes against {diff.base.label}.

; } return ( -
-
- {diff.objects.map((o) => ( - setFocus(o.ulid)} - onToggle={() => onToggle?.(o.ulid)} - /> - ))} +
+ {selectable !== undefined && ( +
+ {REVIEW_FILTERS.filter((f) => f.id === "all" || counts[f.id] > 0).map((f) => ( + + ))} +
+ )} +
+
+ {visible.length === 0 ? ( +

No changes match this filter.

+ ) : ( + visible.map((o) => ( + setFocus(o.ulid)} + onToggleObject={() => onToggle?.(o.ulid)} + onToggleField={onToggleField} + /> + )) + )} +
+
+ {obj && ( + + )} +
-
{obj && }
); } @@ -57,27 +119,50 @@ function badge(o: ObjectChangeDoc): string { function ObjectRow({ obj, active, - checked, selectable, + perField, + objState, + checkedObject, onSelect, - onToggle, + onToggleObject, + onToggleField, }: { obj: ObjectChangeDoc; active: boolean; - checked: boolean; selectable?: boolean; + perField: boolean; + /** the object's tri-state in per-field mode */ + objState?: ObjectSelectionState; + /** whole-object checked, in per-object mode */ + checkedObject: boolean; onSelect: () => void; - onToggle: () => void; + onToggleObject: () => void; + onToggleField?: (ulid: string, field: string) => void; }) { + // In per-field mode the header checkbox toggles ALL of the object's fields at once, and + // shows indeterminate when only some are selected. + const setIndeterminate = (el: HTMLInputElement | null) => { + if (el && perField) el.indeterminate = objState === "some"; + }; + const headerChecked = perField ? objState !== "none" : checkedObject; + const toggleHeader = () => { + if (!perField) return onToggleObject(); + // toggle every field key of this object via the field callback + for (const f of obj.fields) onToggleField?.(obj.ulid, f.field); + for (const c of obj.children) for (const f of c.fields) onToggleField?.(obj.ulid, `${c.ulid}.${f.field}`); + if (!obj.fields.length && !obj.children.length) onToggleField?.(obj.ulid, "*"); + }; + return (
{selectable && ( e.stopPropagation()} - onChange={onToggle} + onChange={toggleHeader} aria-label={`include ${obj.name_before ?? obj.name_after}`} /> )} @@ -104,10 +189,38 @@ function ObjectRow({ ); } -function Detail({ obj }: { obj: ObjectChangeDoc }) { +function Detail({ + obj, + selectable, + perField, + selected, + onToggleField, +}: { + obj: ObjectChangeDoc; + selectable?: boolean; + perField?: boolean; + selected?: Set; + onToggleField?: (ulid: string, field: string) => void; +}) { const children = obj.children.flatMap((c) => - c.fields.map((f) => ({ field: f, on: c.name_before ?? c.name_after ?? "" })), + c.fields.map((f) => ({ field: f, on: c.name_before ?? c.name_after ?? "", key: `${c.ulid}.${f.field}` })), ); + // one selectable row per field, only when the host wired per-field selection + const pick = (field: string, node: React.ReactNode) => { + if (!selectable || !perField) return node; + const key = fieldKey(obj.ulid, field); + return ( +
+ onToggleField?.(obj.ulid, field)} + aria-label={`include change: ${field}`} + /> +
{node}
+
+ ); + }; return ( <>

@@ -119,10 +232,10 @@ function Detail({ obj }: { obj: ObjectChangeDoc }) { {obj.path && · {obj.path}}

{obj.fields.map((f, i) => ( - +
{pick(f.field, )}
))} - {children.map(({ field, on }, i) => ( - + {children.map(({ field, on, key }, i) => ( +
{pick(key, )}
))} {obj.severity === "breaking" && (
diff --git a/canvas/src/sme/reviewModel.test.ts b/canvas/src/sme/reviewModel.test.ts new file mode 100644 index 0000000..18c262e --- /dev/null +++ b/canvas/src/sme/reviewModel.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from "vitest"; +import { makeDiff } from "../test/diffFixture"; +import { + allFieldKeys, + fieldKey, + filterCounts, + filterObjects, + includedUlids, + objectFieldKeys, + objectIncluded, + objectSelectionState, + toggleField, + toggleObject, +} from "./reviewModel"; + +describe("filters (view-only)", () => { + const diff = makeDiff(); + + it("counts each change category", () => { + const c = filterCounts(diff); + expect(c.all).toBe(4); + expect(c.additions).toBe(1); // E_ADD + expect(c.deletions).toBe(1); // E_DEL + expect(c.moves).toBe(1); // E_REN + expect(c.modifications).toBe(1); // E_MOD (renamed E_REN excluded) + expect(c.conflicts).toBe(1); // E_DEL is breaking + }); + + it("filters the visible list without touching anything else", () => { + expect(filterObjects(diff, "additions").map((o) => o.ulid)).toEqual(["E_ADD"]); + expect(filterObjects(diff, "conflicts").map((o) => o.ulid)).toEqual(["E_DEL"]); + expect(filterObjects(diff, "all")).toHaveLength(4); + }); +}); + +describe("per-field selection", () => { + const diff = makeDiff(); + + it("gives a modified object one key per field", () => { + const mod = diff.objects.find((o) => o.ulid === "E_MOD")!; + expect(objectFieldKeys(mod)).toEqual([ + fieldKey("E_MOD", "definition"), + fieldKey("E_MOD", "domain"), + ]); + }); + + it("gives an added object with no field rows a single '*' key", () => { + const add = diff.objects.find((o) => o.ulid === "E_ADD")!; + expect(objectFieldKeys(add)).toEqual([fieldKey("E_ADD", "*")]); + }); + + it("derives object state from its selected fields", () => { + const mod = diff.objects.find((o) => o.ulid === "E_MOD")!; + const [k1, k2] = objectFieldKeys(mod); + expect(objectSelectionState(mod, new Set([k1, k2]))).toBe("all"); + expect(objectSelectionState(mod, new Set([k1]))).toBe("some"); + expect(objectSelectionState(mod, new Set())).toBe("none"); + // included when ≥1 field selected + expect(objectIncluded(mod, new Set([k1]))).toBe(true); + expect(objectIncluded(mod, new Set())).toBe(false); + }); + + it("toggleField flips one field immutably", () => { + const k = fieldKey("E_MOD", "domain"); + const start = new Set(); + const on = toggleField(k, start); + expect(on.has(k)).toBe(true); + expect(start.has(k)).toBe(false); // input untouched + expect(toggleField(k, on).has(k)).toBe(false); + }); + + it("toggleObject turns all fields off when all on, else all on", () => { + const mod = diff.objects.find((o) => o.ulid === "E_MOD")!; + const keys = objectFieldKeys(mod); + const allOn = new Set(keys); + expect([...toggleObject(mod, allOn)]).toHaveLength(0); // all -> none + const someOn = new Set([keys[0]]); + expect(objectSelectionState(mod, toggleObject(mod, someOn))).toBe("all"); // some -> all + }); + + it("includedUlids reflects field-level exclusion", () => { + const all = allFieldKeys(diff); + expect(includedUlids(diff, all).sort()).toEqual(["E_ADD", "E_DEL", "E_MOD", "E_REN"]); + // deselect both fields of E_MOD -> it drops out, others stay + let sel = all; + for (const k of objectFieldKeys(diff.objects.find((o) => o.ulid === "E_MOD")!)) { + sel = toggleField(k, sel); + } + expect(includedUlids(diff, sel).sort()).toEqual(["E_ADD", "E_DEL", "E_REN"]); + }); +}); diff --git a/canvas/src/sme/reviewModel.ts b/canvas/src/sme/reviewModel.ts new file mode 100644 index 0000000..5c7fc4a --- /dev/null +++ b/canvas/src/sme/reviewModel.ts @@ -0,0 +1,134 @@ +// Pure helpers behind the review view: change filtering and per-field selection. +// Kept out of the component so the semantics are unit-testable on their own. + +import type { ChangeType, ModelDiffDoc, ObjectChangeDoc } from "../types"; + +// The display filters (competitor parity): a VIEW concern only — filtering changes +// what is visible, never what is selected for the action (so a hidden change can +// never be silently dropped from a proposal). +export type ReviewFilter = "all" | "additions" | "modifications" | "moves" | "deletions" | "conflicts"; + +export const REVIEW_FILTERS: { id: ReviewFilter; label: string }[] = [ + { id: "all", label: "All" }, + { id: "additions", label: "Additions" }, + { id: "modifications", label: "Modifications" }, + { id: "moves", label: "Renames" }, + { id: "deletions", label: "Deletions" }, + { id: "conflicts", label: "Conflicts" }, +]; + +/** Does an object match the active filter? `conflicts` maps to breaking severity (the + * only conflict signal a 2-way diff carries); `moves` maps to a rename. */ +export function objectMatchesFilter(o: ObjectChangeDoc, f: ReviewFilter): boolean { + switch (f) { + case "all": + return true; + case "additions": + return o.change === "added"; + case "deletions": + return o.change === "removed"; + case "moves": + return o.renamed; + case "modifications": + return o.change === "modified" && !o.renamed; + case "conflicts": + return o.severity === "breaking"; + } +} + +/** The objects visible under a filter. Selection is untouched — this is view-only. */ +export function filterObjects(diff: ModelDiffDoc, f: ReviewFilter): ObjectChangeDoc[] { + return f === "all" ? diff.objects : diff.objects.filter((o) => objectMatchesFilter(o, f)); +} + +/** Count per filter, for filter chips that show how many changes each reveals. */ +export function filterCounts(diff: ModelDiffDoc): Record { + const out = {} as Record; + for (const { id } of REVIEW_FILTERS) { + out[id] = id === "all" ? diff.objects.length : diff.objects.filter((o) => objectMatchesFilter(o, id)).length; + } + return out; +} + +// --- per-field selection ----------------------------------------------------------- +// Selection is keyed by (object ulid, field). An object is INCLUDED when at least one +// of its fields is selected; unchecking every field excludes the object. The object +// header shows a tri-state derived from its fields. + +/** The stable key for one field change of one object. A whole-object change (added / + * removed, which carry object-level identity rather than a field) uses the "*" field. */ +export function fieldKey(ulid: string, field: string): string { + return `${ulid}\u0000${field}`; +} + +/** Every selectable field key of an object, including its children's fields. An + * added/removed object with no field rows still gets one "*" key so it is selectable. */ +export function objectFieldKeys(o: ObjectChangeDoc): string[] { + const keys = o.fields.map((f) => fieldKey(o.ulid, f.field)); + for (const c of o.children) { + for (const f of c.fields) keys.push(fieldKey(o.ulid, `${c.ulid}.${f.field}`)); + } + return keys.length ? keys : [fieldKey(o.ulid, "*")]; +} + +export type ObjectSelectionState = "all" | "some" | "none"; + +/** The tri-state of an object, derived from which of its field keys are selected. */ +export function objectSelectionState(o: ObjectChangeDoc, selected: Set): ObjectSelectionState { + const keys = objectFieldKeys(o); + const on = keys.filter((k) => selected.has(k)).length; + if (on === 0) return "none"; + if (on === keys.length) return "all"; + return "some"; +} + +/** An object is included in the action when ≥1 of its fields is selected. */ +export function objectIncluded(o: ObjectChangeDoc, selected: Set): boolean { + return objectSelectionState(o, selected) !== "none"; +} + +/** The ULIDs the action should apply to, given a field-level selection. */ +export function includedUlids(diff: ModelDiffDoc, selected: Set): string[] { + return diff.objects.filter((o) => objectIncluded(o, selected)).map((o) => o.ulid); +} + +/** All field keys across the whole diff — the initial "everything selected" set. */ +export function allFieldKeys(diff: ModelDiffDoc): Set { + const s = new Set(); + for (const o of diff.objects) for (const k of objectFieldKeys(o)) s.add(k); + return s; +} + +/** Toggle every field of an object at once (the header checkbox): if all are on, turn + * them off; otherwise turn them all on. Returns a NEW set (never mutates the input). */ +export function toggleObject(o: ObjectChangeDoc, selected: Set): Set { + const next = new Set(selected); + const keys = objectFieldKeys(o); + const allOn = keys.every((k) => next.has(k)); + for (const k of keys) { + if (allOn) next.delete(k); + else next.add(k); + } + return next; +} + +/** Toggle one field key. Returns a NEW set. */ +export function toggleField(key: string, selected: Set): Set { + const next = new Set(selected); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; +} + +// A pluggable action the review view offers (Propose / Accept / Reconcile / Apply +// import). The view stays source-agnostic; the host wires the label + what apply does. +export interface ReviewAction { + id: string; + label: string; + primary?: boolean; + /** disabled reason, or undefined when enabled */ + disabledReason?: string; + apply: (selectedUlids: string[]) => void; +} + +export type { ChangeType }; diff --git a/canvas/src/sme/sme.css b/canvas/src/sme/sme.css index 8fb12c9..4cfa000 100644 --- a/canvas/src/sme/sme.css +++ b/canvas/src/sme/sme.css @@ -202,10 +202,34 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); } } .rv-tab.active { background: var(--bg-card); color: var(--text); border-color: var(--border-strong); } +.rv-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; } .rv-body { flex: 1; display: grid; grid-template-columns: 380px 1fr; min-height: 0; } .rv-list { border-right: 1px solid var(--border); overflow-y: auto; padding: 10px; } .rv-detail { overflow-y: auto; padding: 22px 28px; } +/* display filters — a view concern; change what is visible, not what is selected */ +.rv-filters { + display: flex; gap: 4px; flex-wrap: wrap; padding: 8px 10px; + border-bottom: 1px solid var(--border); +} +.rv-filter { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 10px; border-radius: 99px; border: 1px solid var(--border); + background: transparent; color: var(--text-dim); cursor: pointer; font-size: 12px; +} +.rv-filter:hover { color: var(--text); border-color: var(--border-strong); } +.rv-filter.active { color: var(--accent); border-color: var(--accent); } +.rv-filter-count { + font-size: 11px; color: var(--text-dim); background: var(--bg-card); + border-radius: 99px; padding: 0 6px; min-width: 16px; text-align: center; +} +.rv-filter.active .rv-filter-count { color: var(--accent); } + +/* per-field selection rows in the detail pane */ +.rv-field-pick { display: flex; gap: 10px; align-items: flex-start; } +.rv-field-pick > input[type="checkbox"] { margin-top: 16px; accent-color: var(--accent); cursor: pointer; } +.rv-field-body { flex: 1; min-width: 0; } + /* one changed object */ .rv-obj { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 8px; diff --git a/canvas/src/test/diffFixture.ts b/canvas/src/test/diffFixture.ts new file mode 100644 index 0000000..9440402 --- /dev/null +++ b/canvas/src/test/diffFixture.ts @@ -0,0 +1,68 @@ +import type { ModelDiffDoc, ObjectChangeDoc } from "../types"; + +// A realistic ModelDiffDoc with one of each change kind, for the review-view tests. +// Shapes mirror mdl_core.diff_render.render_json exactly. + +function obj(o: Partial & Pick): ObjectChangeDoc { + return { + object_kind: "logical_entity", + object_kind_label: "Entity", + name_before: null, + name_after: null, + renamed: false, + severity: "additive", + path: null, + fields: [], + children: [], + ...o, + }; +} + +export function makeDiff(): ModelDiffDoc { + return { + ok: true, + base: { ref: "HEAD", sha: "abc", label: "on disk" }, + head: { ref: null, label: "your staged changes" }, + counts: { entities: 4 }, + max_severity: "breaking", + has_breaking: true, + config: [], + objects: [ + // a modified entity with TWO field changes (per-field selection target) + obj({ + ulid: "E_MOD", + change: "modified", + name_before: "counterparty", + name_after: "counterparty", + severity: "additive", + fields: [ + { field: "definition", kind: "definition_changed", severity: "cosmetic", label: "Definition changed", detail: "", before: "a party", after: "a trading party" }, + { field: "domain", kind: "attribute_domain_changed", severity: "additive", label: "Domain changed", detail: "", before: "string", after: "text" }, + ], + }), + // an added entity (additions filter) + obj({ ulid: "E_ADD", change: "added", name_after: "instrument", severity: "additive" }), + // a removed entity that BREAKS dbt models (deletions + conflicts filters) + obj({ + ulid: "E_DEL", + change: "removed", + name_before: "legacy", + severity: "breaking", + fields: [ + { + field: "entity", + kind: "entity_removed", + severity: "breaking", + label: "Entity removed", + detail: "", + before: "legacy", + after: null, + breaks: [{ name: "dim_legacy", target: "snowflake", materialization: "table" }], + }, + ], + }), + // a renamed entity (moves filter) + obj({ ulid: "E_REN", change: "modified", renamed: true, name_before: "old_name", name_after: "new_name", severity: "cosmetic" }), + ], + }; +} diff --git a/canvas/src/test/setup.ts b/canvas/src/test/setup.ts new file mode 100644 index 0000000..f149f27 --- /dev/null +++ b/canvas/src/test/setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom/vitest"; diff --git a/canvas/vitest.config.ts b/canvas/vitest.config.ts new file mode 100644 index 0000000..21450a5 --- /dev/null +++ b/canvas/vitest.config.ts @@ -0,0 +1,14 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vitest/config"; + +// Component + interaction tests for the canvas (jsdom). Kept separate from the Vite +// app build; `npm test` runs these headless. +export default defineConfig({ + plugins: [react()], + test: { + environment: "jsdom", + globals: true, + setupFiles: ["./src/test/setup.ts"], + include: ["src/**/*.test.{ts,tsx}"], + }, +}); diff --git a/packages/server/src/mdl_server/static/assets/mocks-DUwiNnjR.js b/packages/server/src/mdl_server/static/assets/mocks-AfcrRcE6.js similarity index 100% rename from packages/server/src/mdl_server/static/assets/mocks-DUwiNnjR.js rename to packages/server/src/mdl_server/static/assets/mocks-AfcrRcE6.js diff --git a/packages/server/src/mdl_server/static/assets/sme-BuG6X873.js b/packages/server/src/mdl_server/static/assets/sme-BuG6X873.js deleted file mode 100644 index 699046b..0000000 --- a/packages/server/src/mdl_server/static/assets/sme-BuG6X873.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-C9iVTvG9.js","assets/client-DOmNpTln.js","assets/styles-Cn8AFsR1.js","assets/styles-BG76mhX4.css"])))=>i.map(i=>d[i]); -import{r as n,j as e,c as Ae,R as Ee}from"./client-DOmNpTln.js";import{p as pe,c as je,d as xe,e as $e,h as be,I as Re,R as Me,M as De,O as Pe,L as Ie,i as Oe,j as Te,A as Be,k as Le,l as Fe,m as ze,o as qe,s as We,f as le,n as Ge,q as He,r as Ve}from"./styles-Cn8AFsR1.js";/* empty css */const Ke="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function ce(s,t){let a="";for(let l=0;l>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,t=new Uint8Array(10);crypto.getRandomValues(t);let a=0n;for(const l of t)a=a<<8n|BigInt(l);return ce(s,10)+ce(a,16)}const Ue={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Ye(s,t){const a=Ue[s];if(!a)return t;const l={...t};for(const i of a)l[i]||(l[i]=ge());return l}function fe(s,t){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${t.id??t.attribute_id}`;const a=t.id??t.entity_id??"",l=t.attribute_id??"";return l?`${s}:${a}:${l}`:`${s}:${a}`}function Je({subjectArea:s,describe:t}){const[a,l]=n.useState([]),[i,x]=n.useState(null),[c,r]=n.useState(!1),m=n.useRef(null),N=n.useCallback(async(h,g)=>{g=Ye(h,g),h==="set_alignment"&&g.status===void 0&&(g={...g,status:"proposed"});const _=fe(h,g),{label:C,before:T,after:S}=t(h,g);return l(y=>{const A=y.filter(k=>k.key!==_);if(h.startsWith("delete_")){const k=g.id??g.entity_id;if(k&&y.some(o=>o.op.startsWith("create_")&&o.payload.id===k))return A.filter(o=>o.payload.id!==k)}return[...A,{op:h,payload:g,label:C,before:T,after:S,key:_}]}),{ok:!0,fingerprint:"",created_id:g.id??null,diagnostics:[]}},[t]);n.useEffect(()=>{if(!a.length){x(null);return}const h=setTimeout(()=>{var _;(_=m.current)==null||_.abort();const g=new AbortController;m.current=g,r(!0),pe(a.map(C=>({op:C.op,payload:C.payload})),s,g.signal).then(x).catch(C=>{C.name!=="AbortError"&&x(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(h)},[a,s]);const b=n.useCallback(h=>{l(g=>g.filter(_=>_.key!==h))},[]),f=n.useCallback(()=>l([]),[]);return n.useMemo(()=>({pending:a,previewDoc:(i==null?void 0:i.model)??null,previewDiff:(i==null?void 0:i.diff)??null,diagnostics:(i==null?void 0:i.diagnostics)??[],failure:i&&i.failed_index!==null?{index:i.failed_index,error:i.error??"invalid change"}:null,busy:c,exec:N,drop:b,clear:f}),[a,i,c,N,b,f])}function Qe(s){const t=new Map;for(const l of s)for(const i of["id","conceptual_id","from_fk_id"]){const x=l.payload[i];l.op.startsWith("create_")&&typeof x=="string"&&t.set(x,l.key)}if(!t.size)return new Set;const a=new Set;for(const l of s)if(!l.op.startsWith("create_"))for(const i of Object.values(l.payload))typeof i=="string"&&t.has(i)&&(a.add(l.key),a.add(t.get(i)));return a}function Xe({user:s}){const[t,a]=n.useState(null);return n.useEffect(()=>{je(s).then(a).catch(()=>{})},[s]),!(t!=null&&t.ok)||!t.git?null:t.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):t.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):t.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:t.branch}),e.jsx("span",{children:"your proposal branch"}),t.behind?e.jsxs("span",{className:"dim",children:["· ",t.base_branch," is ",t.behind," ahead"]}):null]})}function Ze({exec:s,canEdit:t,stagedMembers:a}){const[l,i]=n.useState([]),[x,c]=n.useState(""),[r,m]=n.useState(null),[N,b]=n.useState(""),[f,h]=n.useState(new Set),[g,_]=n.useState("ancestors"),[C,T]=n.useState(1),[S,y]=n.useState(null),[A,k]=n.useState(new Set),[R,o]=n.useState(null);n.useEffect(()=>{xe().then(d=>{i(d.subject_areas),c(w=>{var M;return w||((M=d.subject_areas[0])==null?void 0:M.id)||""})}).catch(d=>o(String(d)))},[]),n.useEffect(()=>{x&&(y(null),h(new Set),$e(x).then(m).catch(d=>o(String(d))))},[x]);const E=n.useMemo(()=>a[x]??(r==null?void 0:r.members)??[],[a,x,r]),$=n.useMemo(()=>{const d=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])d.set(w.id,w);return d},[r]),D=N.trim().toLowerCase(),B=d=>!D||d.name.toLowerCase().includes(D),u=n.useMemo(()=>new Set(E),[E]),P=n.useMemo(()=>E.map(d=>$.get(d)).filter(d=>!!d&&B(d)),[E,$,D]),F=n.useMemo(()=>[...$.values()].filter(d=>!u.has(d.id)&&B(d)),[$,u,D]),I=n.useCallback(d=>{if(!r)return;const w=d.filter(q=>!(r.members??[]).includes(q)).length,M=(r.members??[]).filter(q=>!d.includes(q)).length;s("set_subject_area_members",{id:r.id,members:d,__label:`${r.name}: ${d.length} objects (+${w} −${M})`})},[r,s]),z=(d,w)=>{const M=w?[...E,...d.filter(q=>!u.has(q))]:E.filter(q=>!d.includes(q));I(M),h(new Set)},W=(d,w,M)=>{const q=new Set(d);q.has(w)?q.delete(w):q.add(w),M(q)},U=()=>{if(!r)return;const d=f.size?[...f]:E;if(!d.length){o("add an object first, or select one to expand from");return}o(null),be(r.id,{seeds:d,direction:g,levels:C}).then(w=>{y(w.hops),k(new Set(w.added))}).catch(w=>o(String(w)))},K=()=>{z([...A],!0),y(null)};if(R&&!r)return e.jsx("p",{className:"sme-splash error",children:R});if(!l.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const j=((r==null?void 0:r.inconsistent)??[]).filter(d=>!u.has(d));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:l.map(d=>e.jsxs("button",{className:"sme-sa"+(d.id===x?" active":""),onClick:()=>c(d.id),title:d.definition??void 0,children:[d.name,e.jsx("span",{className:"sme-sa-count",children:(a[d.id]??d.members).length})]},d.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:d=>b(d.target.value)})]}),j.length>0&&t&&e.jsxs("div",{className:"sa-nudge",children:[j.length," object",j.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>z(j,!0),children:["Add ",j.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${F.length})`,rows:F,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>!u.has(d)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!F.length,onClick:()=>z(F.map(d=>d.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!f.size,onClick:()=>z([...f].filter(d=>u.has(d)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!t||!P.length,onClick:()=>z(P.map(d=>d.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${P.length})`,rows:P,picked:f,onToggle:d=>W(f,d,h),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:g,onChange:d=>_(d.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:d=>T(Math.max(1,Math.min(10,Number(d.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!t,children:"Preview"})]}),R&&e.jsx("p",{className:"sme-error-line",children:R}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(d=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(d.id),onChange:()=>W(A,d.id,k)}),e.jsx("span",{className:"sa-hop-name",children:d.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",d.via_name," · ",d.direction.replace(/s$/,""),", level ",d.level]})]},d.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>y(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:K,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function Q({title:s,rows:t,picked:a,onToggle:l,homedHere:i}){const x=n.useMemo(()=>new Set(i),[i]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[t.map(c=>e.jsxs("li",{className:"sa-row"+(a.has(c.id)?" picked":""),onClick:()=>l(c.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(c.id)}),e.jsx("span",{className:"sa-row-name",children:c.name}),x.has(c.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},c.id)),!t.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function es({objects:s,onCreate:t,onClose:a}){const[l,i]=n.useState(""),[x,c]=n.useState(""),[r]=n.useState(s),[m,N]=n.useState([]),[b,f]=n.useState(new Set),[h,g]=n.useState(""),[_,C]=n.useState("ancestors"),[T,S]=n.useState(1),[y,A]=n.useState(null),[k,R]=n.useState(new Set),[o,E]=n.useState(""),[$,D]=n.useState(null);n.useEffect(()=>{xe().then(j=>{j.subject_areas[0]&&E(j.subject_areas[0].id)}).catch(()=>{})},[]);const B=n.useMemo(()=>new Set(m),[m]),u=h.trim().toLowerCase(),P=j=>!u||j.name.toLowerCase().includes(u),F=n.useMemo(()=>m.map(j=>r.find(d=>d.id===j)).filter(j=>!!j&&P(j)),[m,r,u]),I=n.useMemo(()=>r.filter(j=>!B.has(j.id)&&P(j)),[r,B,u]),z=(j,d)=>{N(w=>d?[...w,...j.filter(M=>!w.includes(M))]:w.filter(M=>!j.includes(M))),f(new Set)},W=(j,d,w)=>{const M=new Set(j);M.has(d)?M.delete(d):M.add(d),w(M)},U=()=>{const j=b.size?[...b]:m;if(!j.length){D("pick an object to expand from first");return}o&&(D(null),be(o,{seeds:j,direction:_,levels:T}).then(d=>{const w=d.hops.filter(M=>!B.has(M.id));A(w),R(new Set(w.map(M=>M.id)))}).catch(d=>D(String(d))))},K=()=>{l.trim()&&t(l.trim(),x.trim(),m)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:j=>j.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:l,placeholder:"e.g. UseCase-ApraStressTesting",onChange:j=>i(j.target.value),onKeyDown:j=>j.key==="Enter"&&m.length===0&&K()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:x,placeholder:"what this view is for",onChange:j=>c(j.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:h,onChange:j=>g(j.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Q,{title:`Available (${I.length})`,rows:I,picked:b,onToggle:j=>W(b,j,f),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>!B.has(j)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!I.length,onClick:()=>z(I.map(j=>j.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!b.size,onClick:()=>z([...b].filter(j=>B.has(j)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!F.length,onClick:()=>z(F.map(j=>j.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Q,{title:`Included (${F.length})`,rows:F,picked:b,onToggle:j=>W(b,j,f),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:_,onChange:j=>C(j.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:T,onChange:j=>S(Math.max(1,Math.min(10,Number(j.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:U,disabled:!o,title:o?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),y&&e.jsxs("div",{className:"sa-hops",children:[y.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),y.map(j=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:k.has(j.id),onChange:()=>W(k,j.id,R)}),e.jsx("span",{className:"sa-hop-name",children:j.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",j.via_name," · ",j.direction.replace(/s$/,""),", level ",j.level]})]},j.id)),y.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!k.size,onClick:()=>{z([...k],!0),A(null)},children:["Add ",k.size]})]})]})]}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!l.trim(),onClick:K,children:["Create",m.length>0&&` with ${m.length} object${m.length===1?"":"s"}`]})]})]})})}const ss=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function ns({doc:s,subjectArea:t,onSubjectArea:a,exec:l,canEdit:i,direct:x=!1,onSelectEntity:c,onImported:r,query:m="",busy:N}){var R;const[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(!1),[T,S]=n.useState(null),y=n.useRef(null),A=n.useMemo(()=>({canEdit:i,mode:x?"direct":"staged",allow:x?void 0:ss,canArbitrate:x,canCommit:x}),[i,x]),k=n.useCallback(o=>{f(o),o&&(c==null||c(o))},[c]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),i&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(t===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((R=s.counts)==null?void 0:R.entities_total)??s.entities.length})]}),s.subject_areas.map(o=>e.jsxs("button",{className:"sme-sa"+(t===o.id?" active":""),onClick:()=>a(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:o.member_count??0})]},o.id))]}),_&&e.jsx(es,{objects:s.entities.flatMap(o=>o.conceptual?[{id:o.conceptual.id,kind:"conceptual_entity",name:o.conceptual.name,definition:o.conceptual.definition??null,synonyms:[],subject_area:o.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(o,E,$)=>{const D=ge();l("create_subject_area",{name:o,...E?{definition:E}:{},id:D}),$.length&&l("set_subject_area_members",{id:D,members:$}),C(!1),a(D)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),i&&e.jsx("button",{className:"erd-action",onClick:()=>{var o;return(o=y.current)==null?void 0:o.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=y.current)==null?void 0:o.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(h==="ontology"?" on":""),onClick:()=>g(o=>o==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(h==="layers"?" on":""),onClick:()=>g(o=>o==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Re,{exec:l,canEdit:i,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!i&&e.jsx("span",{className:"sme-chip",children:"read-only"}),T&&e.jsx("span",{className:"erd-error",children:T})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Me,{children:e.jsx(De,{doc:s,caps:A,exec:l,selectedId:b,onSelect:k,query:m,onError:S,handleRef:y})}),h&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:h==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>g(null),children:"✕"})]}),h==="ontology"?e.jsx(Pe,{}):e.jsx(Ie,{refreshKey:0,onFocusEntity:o=>{var E;g(null),(E=y.current)==null||E.focusEntity(o)}})]})]})]})]})}function as({user:s,onView:t}){const[a,l]=n.useState(null),[i,x]=n.useState(null);if(n.useEffect(()=>{Oe(s).then(l).catch(r=>x(String(r)))},[s]),i)return e.jsx("p",{className:"sme-splash error",children:i});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const c=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[c," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(rs,{p:r,gh:a.gh,onView:()=>t(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function ts(s,t){var a,l;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?t&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:t&&((l=s.pr)==null?void 0:l.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function rs({p:s,gh:t,onView:a}){const l=ts(s,t);return e.jsxs("div",{className:"pr-card"+(l.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+l.kind,children:l.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",t&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?t&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function is({changes:s,routeAdvice:t,identity:a,user:l,onUser:i,onDrop:x,onClose:c,onProposed:r}){const m=!!(a&&a.source!=="anonymous"),[N,b]=n.useState(l||(localStorage.getItem("mdl.sme.user")??"")),[f,h]=n.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[g,_]=n.useState(""),[C,T]=n.useState(!1),S=ve(C?g:f),[y,A]=n.useState(""),[k,R]=n.useState(!1),[o,E]=n.useState(null),[$,D]=n.useState(null),B=()=>{!m&&!N.trim()||(m||(localStorage.setItem("mdl.sme.user",N.trim()),i==null||i(N.trim())),R(!0),D(null),Te({user:m?"":N.trim(),slug:S,title:f,body:y.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then(E).catch(u=>D(u instanceof Be?u.message:String(u))).finally(()=>R(!1)))};return o!=null&&o.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(o),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:o.message}),o.pr_url&&e.jsx("p",{children:e.jsx("a",{href:o.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),o.compare_url&&e.jsx("p",{children:e.jsx("a",{href:o.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:o.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(o),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:c,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:c,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,P)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>x(P),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},P))}),m?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>b(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:f,onChange:u=>h(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?g:S,onChange:u=>{T(!0),_(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:y,onChange:u=>A(u.target.value)})]}),e.jsx(ls,{cl:t,user:m?a.name:N,changeSlug:S}),$&&e.jsx("p",{className:"sme-error-line",children:$}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:c,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:k||!m&&!N.trim()||s.length===0,onClick:B,children:k?"Submitting…":"Submit for review"})]})]})})}function ls({cl:s,user:t,changeSlug:a}){if(!(s!=null&&s.primary))return null;const l=s.reviewers_actual??s.reviewers,i=ve(t||"you"),x=s.routes.length>1;return e.jsxs(e.Fragment,{children:[x&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",l.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",i,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:l.join(", ")})]})]})]})}function ve(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const cs="modulepreload",os=function(s){return"/"+s},oe={},ds=function(t,a,l){let i=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),r=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));i=Promise.allSettled(a.map(m=>{if(m=os(m),m in oe)return;oe[m]=!0;const N=m.endsWith(".css"),b=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${b}`))return;const f=document.createElement("link");if(f.rel=N?"stylesheet":cs,N||(f.as="script"),f.crossOrigin="",f.href=m,r&&f.setAttribute("nonce",r),document.head.appendChild(f),N)return new Promise((h,g)=>{f.addEventListener("load",h),f.addEventListener("error",()=>g(new Error(`Unable to preload CSS for ${m}`)))})}))}function x(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return i.then(c=>{for(const r of c||[])r.status==="rejected"&&x(r.reason);return t().catch(x)})};function hs({diff:s,selectable:t,selected:a,onToggle:l}){var r;const[i,x]=n.useState(((r=s.objects[0])==null?void 0:r.ulid)??null),c=n.useMemo(()=>s.objects.find(m=>m.ulid===i)??s.objects[0]??null,[s,i]);return s.objects.length?e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:s.objects.map(m=>e.jsx(us,{obj:m,active:m.ulid===((c==null?void 0:c.ulid)??""),checked:(a==null?void 0:a.has(m.ulid))??!0,selectable:t,onSelect:()=>x(m.ulid),onToggle:()=>l==null?void 0:l(m.ulid)},m.ulid))}),e.jsx("div",{className:"rv-detail",children:c&&e.jsx(ps,{obj:c})})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function ms(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function us({obj:s,active:t,checked:a,selectable:l,onSelect:i,onToggle:x}){return e.jsxs("div",{className:"rv-obj"+(t?" active":""),onClick:i,children:[e.jsxs("div",{className:"rv-obj-head",children:[l&&e.jsx("input",{type:"checkbox",checked:a,onClick:c=>c.stopPropagation(),onChange:x,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",ms(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,r)=>e.jsx("li",{children:c.label},c.field+r)),s.children.flatMap(c=>c.fields.map((r,m)=>e.jsxs("li",{children:[r.label,": ",c.name_before??c.name_after]},c.ulid+m)))]})]})}function ps({obj:s}){const t=s.children.flatMap(a=>a.fields.map(l=>({field:l,on:a.name_before??a.name_after??""})));return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((a,l)=>e.jsx(de,{f:a},a.field+l)),t.map(({field:a,on:l},i)=>e.jsx(de,{f:a,on:l},"c"+i)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function de({f:s,on:t}){var l,i,x;const a=t?`${s.label}: ${t}`:s.label;if((l=s.breaks)!=null&&l.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[t??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(c=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",c.name]}),e.jsxs("span",{className:"p",children:[c.target," · ",c.materialization]})]},c.name))})]})]});if(s.kind==="definition_changed"){const c=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:me(c,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:me(r,c,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const c=new Set(((i=s.before)==null?void 0:i.map(String))??[]),r=new Set(((x=s.after)==null?void 0:x.map(String))??[]),m=[...r].filter(b=>!c.has(b)),N=[...c].filter(b=>!r.has(b));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[m.map(b=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",b]},"a"+b)),N.map(b=>e.jsxs("span",{className:"rv-chip-del",children:["− ",b]},"r"+b))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:he(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:he(s.after)})]})]})}function he(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function me(s,t,a){const l=s.split(/(\s+)/),i=t.split(/(\s+)/),x=h=>h.toLowerCase().replace(/[.,;:]/g,""),c=l.length,r=i.length,m=Array.from({length:c+1},()=>new Array(r+1).fill(0));for(let h=c-1;h>=0;h--)for(let g=r-1;g>=0;g--)m[h][g]=x(l[h])===x(i[g])?m[h+1][g+1]+1:Math.max(m[h+1][g],m[h][g+1]);const N=new Set;let b=0,f=0;for(;b=m[b][f+1]?b++:f++;return l.map((h,g)=>!h.trim()||N.has(g)?e.jsx("span",{children:h},g):a==="del"?e.jsx("del",{children:h},g):e.jsx("ins",{children:h},g))}const js=n.lazy(()=>ds(()=>import("./ModelDiagram-C9iVTvG9.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function xs({stagedDiff:s,stagedRoute:t,user:a,onBack:l,onSubmit:i,excluded:x,onToggleObject:c,selectable:r}){const[m,N]=n.useState(null),[b,f]=n.useState(null),[h,g]=n.useState(null),[_,C]=n.useState(null),[T,S]=n.useState(null),[y,A]=n.useState("changes"),k=n.useCallback(()=>{je(a).then(o=>{C(o);const E=o.base_branch??"main";s?N(s):Le("HEAD").then($=>$.ok?N($):S($.error??"could not read the model")).catch($=>S(String($))),t?f(t):Fe("HEAD").then(f).catch(()=>{}),ze(E).then(g).catch(()=>{})}).catch(o=>S(String(o)))},[a,s,t]);if(n.useEffect(k,[k]),T)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:T})})]});if(!m)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const R=new Set(m.objects.map(o=>o.ulid).filter(o=>!x.has(o)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(ue,{onBack:l,diff:m,tab:y,onTab:A}),y==="changes"?e.jsx(hs,{diff:m,selectable:r,selected:R,onToggle:c}):e.jsx(n.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(js,{severityByUlid:bs(m)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(gs,{cl:b}),e.jsx(fs,{conf:h,ctx:_}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:l,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!m.objects.length||(_?!_.can_propose:!1),title:_&&!_.can_propose?"the working tree has uncommitted changes":"",onClick:()=>i(b),children:"Submit for review →"})]})]})]})}function bs(s){const t=new Map;for(const a of s.objects)t.set(a.ulid,a.severity);return t}function ue({onBack:s,diff:t,tab:a,onTab:l}){const i=t==null?void 0:t.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),i&&e.jsxs("div",{className:"rv-sub",children:[i.objects," object",i.objects===1?"":"s"," ·"," ",(i.breaking??0)+(i.additive??0)+(i.cosmetic??0)," changes",(t==null?void 0:t.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",i.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:t==null?void 0:t.base.label})]})]}),l&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>l("changes"),children:["Changes ",(t==null?void 0:t.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>l("diagram"),children:"Diagram"})]})]})}function gs({cl:s}){if(!(s!=null&&s.primary))return null;const t=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:t.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function fs({conf:s,ctx:t}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(t==null?void 0:t.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function vs({term:s,canEdit:t,pendingCount:a,onEdit:l}){var i,x;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((i=s.subject_area)==null?void 0:i.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),t&&e.jsx("button",{className:"sme-primary",onClick:l,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(c=>e.jsx("span",{className:"sme-chip",children:c},c))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((x=s.ontology)==null?void 0:x.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(c=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:c.logical_entity}),c.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),c.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:c.physical.map(r=>r.name).join(", ")})]},c.logical_id))})]})]})}function Ns({term:s,pending:t,onStage:a,onDone:l,catalogOwned:i=[],catalog:x=null}){var E,$,D,B;const c=u=>i.includes(u),r=u=>t.find(P=>P.label===u),m=(u,P)=>{const F=r(u);return F?F.after:P},[N,b]=n.useState(m("Definition",s.definition??"")),[f,h]=n.useState(s.synonyms),[g,_]=n.useState(""),[C,T]=n.useState(((E=s.stewardship)==null?void 0:E.owner)??""),[S,y]=n.useState((($=s.stewardship)==null?void 0:$.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},k=(u,P)=>{var F,I;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:P||null},label:"Stewardship",before:`owner: ${((F=s.stewardship)==null?void 0:F.owner)??"—"} · steward: ${((I=s.stewardship)==null?void 0:I.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${P||"—"}`})},R=u=>{h(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},o=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:l,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(c("definition")||c("synonyms")||c("stewardship"))&&x&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:x.name}),". Edit them there — this glossary mirrors them read-only.",x.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:x.url,target:"_blank",rel:"noreferrer",children:["Open in ",x.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),c("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>b(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),c("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[f.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>R(f.filter(P=>P!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:g,placeholder:"add + Enter",onChange:u=>_(u.target.value),onKeyDown:u=>{u.key==="Enter"&&g.trim()&&(R([...f,g.trim()]),_(""))}})]})]}),!o&&(c("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((D=s.stewardship)==null?void 0:D.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((B=s.stewardship)==null?void 0:B.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>T(u.target.value),onBlur:()=>k(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>y(u.target.value),onBlur:()=>k(C,S)})]})]})),e.jsx(ys,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:t.length>0?`${t.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const ws=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function ys({term:s,onStage:t}){var f;const[a,l]=n.useState(!1),[i,x]=n.useState(s.name),[c,r]=n.useState([]),[m,N]=n.useState("skos:closeMatch"),b=h=>{x(h),qe(h).then(g=>r(g.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:i,onChange:h=>b(h.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:c.map(h=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var g;t({op:"set_alignment",payload:{id:s.id,aligns_to:h.prefixed,alignment:m,status:"proposed"},label:"Standard mapping",before:((g=s.ontology)==null?void 0:g.aligns_to)??"(none)",after:`${h.prefixed} (${m}, proposed)`}),l(!1)},children:[e.jsx("strong",{children:h.label})," ",e.jsx("code",{children:h.prefixed}),h.definition&&e.jsx("span",{className:"sme-align-def",children:h.definition})]},h.iri))}),e.jsx("select",{value:m,onChange:h=>N(h.target.value),children:ws.map(h=>e.jsx("option",{children:h},h))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(f=s.ontology)!=null&&f.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>l(!0),children:"Propose a mapping…"})]})]})}function _s(){const[s,t]=n.useState(null),[a,l]=n.useState(!0),[i,x]=n.useState(null),[c,r]=n.useState(""),[m,N]=n.useState(null),[b,f]=n.useState(""),[h,g]=n.useState(""),[_,C]=n.useState(null),[T,S]=n.useState(!1),[y,A]=n.useState([]),[k,R]=n.useState(!1),[o,E]=n.useState(window.location.hash==="#proposals"?"proposals":"browse"),[$,D]=n.useState(new Set),[B,u]=n.useState(()=>localStorage.getItem("mdl.sme.user")??""),[P,F]=n.useState(null),[I,z]=n.useState(null),[W,U]=n.useState(!1),[K,j]=n.useState(null),d=n.useCallback((p,v)=>{var ae;const O=J=>{var te;const V=I==null?void 0:I.entities.find(re=>{var ie;return re.id===J||((ie=re.conceptual)==null?void 0:ie.id)===J});return((te=V==null?void 0:V.conceptual)==null?void 0:te.name)??(V==null?void 0:V.name)??"object"},L=O(v.id??v.entity_id);switch(p){case"create_entity":return{label:`New entity: ${v.name}`,before:"—",after:String(v.name??"")};case"create_relationship":return{label:`New relationship: ${O(v.from_entity)} → ${O(v.to_entity)}`,before:"—",after:String(v.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${L}`,before:L,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[v.cardinality,v.optionality,v.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${L}`,before:"",after:String(v.definition??"")};case"add_attribute":return{label:`Attribute added to ${L}`,before:"—",after:String(v.name??"")};case"delete_attribute":return{label:`Attribute removed from ${L}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${L}`,before:"",after:JSON.stringify(v)};case"rename_entity":return{label:`Renamed ${L}`,before:L,after:String(v.name??"")};case"set_subject_area":return{label:`Subject area: ${L}`,before:"",after:String(v.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${L}`,before:"",after:String(v.steward??"")};case"set_subject_area_members":{const J=v.__label,V=((ae=v.members)==null?void 0:ae.length)??0;return{label:J??"Subject area members",before:"",after:`${V} object${V===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${L}`,before:"",after:String(v.aligns_to??"")};default:return{label:`${p.replace(/_/g," ")}: ${L}`,before:"",after:""}}},[I]),w=Je({subjectArea:b,describe:d}),M=n.useCallback(async(p,v)=>{const O=(I==null?void 0:I.fingerprint)??"",L=await We(p,v,O);return await le(b||void 0).then(z),L},[I,b]),q=n.useMemo(()=>{const p={};for(const v of w.pending)v.op==="set_subject_area_members"&&(p[v.payload.id]=v.payload.members??[]);return p},[w.pending]),G=n.useMemo(()=>[...y,...w.pending],[y,w.pending]),[Ne,X]=n.useState(null),[we,Z]=n.useState(null);n.useEffect(()=>{if(!G.length){X(null),Z(null);return}const p=new AbortController,v=G.map(O=>({op:O.op,payload:O.payload}));return pe(v,b||void 0,p.signal).then(O=>X(O.diff??null)).catch(O=>{O.name!=="AbortError"&&X(null)}),Ge(v).then(Z).catch(()=>Z(null)),()=>p.abort()},[G,b]);const ye=n.useMemo(()=>Qe(G),[G]).size{E(p),window.location.hash=p==="browse"?"":`#${p}`},[]);n.useEffect(()=>{const p=()=>{const v=window.location.hash;E(v==="#proposals"?"proposals":v==="#review"?"review":v==="#model"?"model":v==="#areas"?"areas":"browse")};return p(),window.addEventListener("hashchange",p),()=>window.removeEventListener("hashchange",p)},[]);const ee=n.useCallback(()=>{He({subject_area:b,q:h}).then(t).catch(p=>N(String(p)))},[b,h]);n.useEffect(ee,[ee]),n.useEffect(()=>{le(b||void 0).then(p=>{l(p.read_only),r(p.project.name),z(p),U(!!p.direct),F(p.identity??null),p.identity&&p.identity.source!=="anonymous"&&p.identity.name&&u(p.identity.name)}).catch(()=>{}),Ve().then(x).catch(()=>{})},[b]);const se=n.useMemo(()=>(s==null?void 0:s.terms.find(p=>p.id===_))??null,[s,_]),_e=n.useCallback(p=>{const v=p.key??fe(p.op,p.payload);A(O=>[...O.filter(L=>L.key!==v),{...p,key:v}])},[]),Se=p=>A(v=>v.filter((O,L)=>L!==p)),ke=()=>{A([]),R(!1),S(!1),ee()};if(m)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const Y=!a,ne=(i==null?void 0:i.catalog_owned_fields)??[],Ce=ne.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:c}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Ce&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${i==null?void 0:i.catalog_name}`,children:["mirrors ",i==null?void 0:i.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:h,onChange:p=>g(p.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(o==="browse"?" active":""),onClick:()=>H("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(o==="model"?" active":""),onClick:()=>H("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(o==="areas"?" active":""),onClick:()=>H("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(o==="proposals"?" active":""),onClick:()=>H("proposals"),children:"My proposals"})]}),G.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>H("review"),children:[G.length," change",G.length>1?"s":""," · Review →"]})]}),o==="browse"&&e.jsx(Xe,{user:B}),o==="review"?e.jsx(xs,{stagedDiff:Ne,stagedRoute:we,user:B,selectable:ye,excluded:$,onToggleObject:p=>D(v=>{const O=new Set(v);return O.has(p)?O.delete(p):O.add(p),O}),onBack:()=>H("browse"),onSubmit:p=>{j(p),R(!0)}}):o==="model"?w.previewDoc??I?e.jsx(ns,{doc:w.previewDoc??I,subjectArea:b,onSubjectArea:f,exec:W?M:w.exec,canEdit:Y,direct:W,onImported:()=>H("review"),query:h,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):o==="areas"?e.jsx(Ze,{exec:w.exec,canEdit:Y,stagedMembers:q}):o==="proposals"?e.jsx(as,{user:B,onView:()=>H("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(b===""?" active":""),onClick:()=>f(""),children:"All subject areas"}),s.subject_areas.map(p=>e.jsx("button",{className:"sme-sa"+(b===p.id?" active":""),onClick:()=>f(p.id),title:p.definition??void 0,children:p.name},p.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(p=>{var v;return e.jsxs("li",{className:"sme-list-item"+(p.id===_?" active":""),onClick:()=>{C(p.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:p.name}),p.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((v=p.ontology)==null?void 0:v.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:p.definition??"no definition yet"})]},p.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:se?T&&Y?e.jsx(Ns,{term:se,pending:y,onStage:_e,onDone:()=>S(!1),catalogOwned:ne,catalog:i?{name:i.catalog_name,url:i.catalog_url}:null}):e.jsx(vs,{term:se,canEdit:Y,pendingCount:y.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),k&&e.jsx(is,{changes:G,routeAdvice:K,identity:P,user:B,onUser:p=>{u(p),localStorage.setItem("mdl.sme.user",p)},onDrop:Se,onClose:()=>R(!1),onProposed:()=>{ke(),w.clear(),D(new Set),H("proposals")}})]})}Ae.createRoot(document.getElementById("root")).render(e.jsx(Ee.StrictMode,{children:e.jsx(_s,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-C10_zE1B.css b/packages/server/src/mdl_server/static/assets/sme-C10_zE1B.css new file mode 100644 index 0000000..d643f9c --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/sme-C10_zE1B.css @@ -0,0 +1 @@ +:root{--bg: #0e1218;--bg-raised: #161c26;--bg-card: #1b232f;--border: #2a3442;--border-strong: #3d4a5c;--text: #e6ecf3;--text-dim: #93a1b5;--accent: #5eead4;--pk: #fcd34d;--err: #f87171;--ok: #34d399;--font: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;--mono: ui-monospace, "SF Mono", Menlo, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font);background:var(--bg);color:var(--text)}.sme{display:flex;flex-direction:column;height:100%}.sme-splash{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-dim)}.sme-splash.error pre{background:var(--bg-card);padding:12px;border-radius:8px;color:var(--err)}.sme-top{display:flex;align-items:center;gap:16px;padding:12px 20px;background:var(--bg-raised);border-bottom:1px solid var(--border)}.sme-brand{display:flex;align-items:baseline;gap:8px;font-weight:700;white-space:nowrap}.sme-logo{color:var(--accent);font-size:18px}.sme-project{color:var(--text-dim);font-weight:400;font-size:13px}.sme-search{flex:0 1 420px;padding:9px 14px;border-radius:9px;font-size:14px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);outline:none}.sme-search:focus{border-color:var(--accent)}.sme-tray{margin-left:auto;padding:9px 16px;border-radius:99px;border:none;cursor:pointer;background:var(--pk);color:#1a1400;font-weight:700;font-size:13px}.sme-chip{display:inline-block;font-size:11px;padding:2px 9px;margin:0 4px 0 0;border-radius:99px;background:#94a3b824;color:var(--text-dim)}.sme-chip.catalog{background:#fbbf2429;color:#fbbf24}.sme-catalog-banner{border:1px solid rgba(251,191,36,.35);background:#fbbf2414;border-radius:8px;padding:10px 12px;font-size:13px;line-height:1.5;color:var(--text);margin:4px 0 10px}.sme-catalog-banner a{color:#fbbf24;font-weight:600}.sme-catalog-note{color:var(--text-dim);font-size:12px;margin-top:4px}.sme-readonly-value{margin:0;padding:8px 10px;border-radius:6px;background:#94a3b80f;border:1px dashed var(--border);color:var(--text-dim);font-size:14px;line-height:1.5}.sme-body{flex:1;display:grid;grid-template-columns:220px 340px 1fr;min-height:0}.sme-nav{border-right:1px solid var(--border);padding:12px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}.sme-sa{text-align:left;background:none;border:none;color:var(--text-dim);cursor:pointer;padding:8px 10px;border-radius:8px;font-size:13px}.sme-sa:hover{background:var(--bg-card);color:var(--text)}.sme-sa.active{background:#5eead41f;color:var(--accent);font-weight:600}.sme-list{list-style:none;margin:0;padding:8px;overflow-y:auto;border-right:1px solid var(--border)}.sme-list-item{padding:10px 12px;border-radius:9px;cursor:pointer;margin-bottom:4px}.sme-list-item:hover{background:var(--bg-card)}.sme-list-item.active{background:#5eead41a;box-shadow:inset 2px 0 0 var(--accent)}.sme-term-name{font-weight:600;font-size:14px;margin-right:6px}.sme-kind{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em}.sme-badge{font-size:10px;padding:1px 7px;border-radius:99px;margin-left:4px;background:#94a3b829;color:var(--text-dim)}.sme-badge.proposed{background:#fcd34d29;color:var(--pk)}.sme-term-def{display:block;color:var(--text-dim);font-size:12px;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sme-empty{color:var(--text-dim);padding:12px;font-style:italic}.sme-detail{overflow-y:auto;padding:24px 32px}.sme-placeholder{color:var(--text-dim);margin-top:40px;text-align:center}.sme-card-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.sme-card-head h1,.sme-card-head h2{margin:0 0 6px;font-size:22px}.sme-card section,.sme-editor .sme-field{margin-top:22px}.sme-card h3,.sme-editor h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 8px}.sme-definition{font-size:15px;line-height:1.6;color:var(--text)}.sme-kv{display:flex;gap:10px;align-items:baseline;margin:4px 0;font-size:13px}.sme-kv .k{color:var(--text-dim);min-width:70px}.sme-muted{color:var(--text-dim);font-size:13px}.sme-usage{list-style:none;margin:0;padding:0}.sme-usage li{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;margin-bottom:6px;font-size:13px}.sme-usage-model{font-weight:600}.sme-usage-phys{color:var(--text-dim);font-family:var(--mono);font-size:11px}.sme-current-align{font-family:var(--mono);color:var(--accent);margin-right:8px}.sme-primary{padding:9px 18px;border-radius:9px;border:none;cursor:pointer;background:var(--accent);color:#06281f;font-weight:700;font-size:14px}.sme-primary:disabled{opacity:.45;cursor:default}.sme-secondary{padding:8px 14px;border-radius:9px;cursor:pointer;font-size:13px;background:var(--bg-card);border:1px solid var(--border);color:var(--text)}.sme-link{background:none;border:none;color:var(--accent);cursor:pointer;font-size:13px;margin-left:8px}.sme-link.danger{color:var(--err)}.sme-hint,.sme-note{color:var(--text-dim);font-size:13px;line-height:1.5}.sme-field{display:flex;flex-direction:column;gap:6px}.sme-field>span{font-size:12px;color:var(--text-dim)}.sme-field textarea,.sme-field input,.sme-field select{padding:9px 12px;border-radius:9px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-family:var(--font);font-size:14px;outline:none;resize:vertical}.sme-field textarea:focus,.sme-field input:focus{border-color:var(--accent)}.sme-field-row{display:flex;gap:16px;margin-top:22px}.sme-field-row .sme-field{flex:1}.sme-syn-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.sme-syn-row input{flex:1;min-width:120px}.sme-chip.removable{cursor:pointer;background:#5eead424;color:var(--accent)}.sme-align-results{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow-y:auto;margin:8px 0}.sme-align-card{text-align:left;background:var(--bg-card);border:1px solid var(--border);border-radius:9px;padding:10px 12px;cursor:pointer;color:var(--text)}.sme-align-card:hover{border-color:var(--accent)}.sme-align-card code{color:var(--accent);font-size:11px;margin-left:6px}.sme-align-def{display:block;color:var(--text-dim);font-size:12px;margin-top:4px}.sme-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#05080db8;z-index:50;display:flex;align-items:center;justify-content:center}.sme-modal{width:560px;max-height:84vh;overflow-y:auto;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:16px;padding:24px;box-shadow:0 24px 80px #0009}.sme-modal h2{margin:0 0 4px}.sme-modal .sme-field{margin-top:14px}.sme-diff{list-style:none;margin:8px 0 0;padding:0}.sme-diff li{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin-bottom:8px}.sme-diff-head{display:flex;justify-content:space-between;margin-bottom:6px}.sme-diff-before{color:var(--err);font-size:13px;text-decoration:line-through;opacity:.8}.sme-diff-after{color:var(--ok);font-size:13px}.sme-modal-foot{display:flex;gap:10px;justify-content:flex-end;margin-top:20px}.sme-error-line{color:var(--err);font-size:13px}.sme-signed-in{margin-top:14px;font-size:13px;color:var(--text-dim);background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px}.sme-signed-in strong{color:var(--text)}.sme-modal a{color:var(--accent)}:root{--sev-breaking: var(--err);--sev-additive: var(--ok);--sev-cosmetic: var(--text-dim);--sev-context: #4b5768}.rv-shell{flex:1;display:flex;flex-direction:column;min-height:0}.sme-tabs{display:flex;gap:4px;margin-left:12px}.sme-tab{background:none;border:none;color:var(--text-dim);cursor:pointer;padding:7px 12px;border-radius:8px;font-size:13px;font-family:var(--font)}.sme-tab:hover{color:var(--text)}.sme-tab.active{color:var(--accent);font-weight:600;background:#5eead41a}.git-banner{display:flex;align-items:center;gap:10px;padding:8px 20px;background:#94a3b80f;border-bottom:1px solid var(--border);font-size:12.5px;color:var(--text-dim)}.git-banner code{font-family:var(--mono);font-size:11.5px;color:var(--text);background:var(--bg-card);padding:2px 8px;border-radius:6px}.git-banner.warn{background:#fcd34d12;color:var(--pk)}.git-banner .dim{color:var(--text-dim)}.rv-chip-del{font-size:12px;padding:3px 10px;border-radius:99px;background:#f8717124;color:var(--err)}.pr-chip.declined{background:#94a3b824;color:var(--text-dim)}.rv-obj-head input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.rv-head{padding:14px 24px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;gap:20px}.rv-back{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:13px;padding:4px 0;font-family:var(--font)}.rv-back:hover{color:var(--accent)}.rv-title{margin:0 0 3px;font-size:19px}.rv-sub{color:var(--text-dim);font-size:13px}.rv-sub .warn{color:var(--err);font-weight:600}.rv-tabs{margin-left:auto;display:flex;gap:4px}.rv-tab{background:none;border:1px solid var(--border);color:var(--text-dim);padding:7px 16px;border-radius:8px;cursor:pointer;font-size:13px;font-family:var(--font)}.rv-tab.active{background:var(--bg-card);color:var(--text);border-color:var(--border-strong)}.rv-wrap{flex:1;display:flex;flex-direction:column;min-height:0}.rv-body{flex:1;display:grid;grid-template-columns:380px 1fr;min-height:0}.rv-list{border-right:1px solid var(--border);overflow-y:auto;padding:10px}.rv-detail{overflow-y:auto;padding:22px 28px}.rv-filters{display:flex;gap:4px;flex-wrap:wrap;padding:8px 10px;border-bottom:1px solid var(--border)}.rv-filter{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:99px;border:1px solid var(--border);background:transparent;color:var(--text-dim);cursor:pointer;font-size:12px}.rv-filter:hover{color:var(--text);border-color:var(--border-strong)}.rv-filter.active{color:var(--accent);border-color:var(--accent)}.rv-filter-count{font-size:11px;color:var(--text-dim);background:var(--bg-card);border-radius:99px;padding:0 6px;min-width:16px;text-align:center}.rv-filter.active .rv-filter-count{color:var(--accent)}.rv-field-pick{display:flex;gap:10px;align-items:flex-start}.rv-field-pick>input[type=checkbox]{margin-top:16px;accent-color:var(--accent);cursor:pointer}.rv-field-body{flex:1;min-width:0}.rv-obj{border:1px solid var(--border);border-radius:10px;margin-bottom:8px;background:var(--bg-card);cursor:pointer;overflow:hidden}.rv-obj:hover{border-color:var(--border-strong)}.rv-obj.active{border-color:var(--accent);box-shadow:inset 2px 0 0 var(--accent)}.rv-obj-head{display:flex;align-items:baseline;gap:8px;padding:11px 13px}.rv-obj-name{font-weight:600;font-size:14px}.rv-obj-kind{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em}.rv-obj-fields{list-style:none;margin:0;padding:0 13px 11px 26px}.rv-obj-fields li{font-size:12.5px;color:var(--text-dim);line-height:1.7;position:relative}.rv-obj-fields li:before{content:"└";position:absolute;left:-13px;color:var(--border-strong)}.sev{font-size:10px;padding:2px 8px;border-radius:99px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;margin-left:auto;white-space:nowrap}.sev.breaking{background:#f8717129;color:var(--sev-breaking)}.sev.additive{background:#34d39924;color:var(--sev-additive)}.sev.cosmetic{background:#94a3b829;color:var(--sev-cosmetic)}.sev.renamed{background:#fcd34d29;color:var(--pk)}.rv-detail h2{margin:0 0 4px;font-size:20px}.rv-detail-kind{color:var(--text-dim);font-size:13px;margin-bottom:22px}.rv-section{margin-top:24px}.rv-section h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 9px}.rv-ba{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--border);border:1px solid var(--border);border-radius:9px;overflow:hidden}.rv-ba-col{background:var(--bg-card);padding:12px 14px}.rv-ba-label{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-dim);margin-bottom:7px}.rv-ba-text{font-size:13.5px;line-height:1.6}.rv-ba-text del{background:#f871712e;color:#fca5a5;text-decoration:none}.rv-ba-text ins{background:#34d39929;color:#6ee7b7;text-decoration:none}.rv-hint{color:var(--text-dim);font-size:11.5px;margin-top:7px;font-style:italic}.rv-scalar{display:flex;align-items:center;gap:10px;font-size:13.5px;background:var(--bg-card);border:1px solid var(--border);border-radius:9px;padding:11px 14px}.rv-scalar .from{color:var(--err)}.rv-scalar .to{color:var(--ok)}.rv-scalar .arrow{color:var(--text-dim)}.rv-chips{display:flex;flex-wrap:wrap;gap:6px}.rv-chip-add{font-size:12px;padding:3px 10px;border-radius:99px;background:#34d39924;color:var(--ok)}.rv-break{border:1px solid rgba(248,113,113,.4);background:#f871710f;border-radius:10px;padding:14px 16px}.rv-break-head{display:flex;align-items:baseline;gap:8px;color:var(--err);font-weight:700;font-size:14px;margin-bottom:4px}.rv-break-meta{color:var(--text-dim);font-size:12px;font-family:var(--mono);margin-left:auto}.rv-break-body{font-size:13.5px;line-height:1.6;margin:10px 0 12px}.rv-usage{list-style:none;margin:0;padding:0}.rv-usage li{display:flex;align-items:center;gap:9px;padding:8px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;margin-bottom:6px;font-size:13px}.rv-usage .m{font-weight:600}.rv-usage .p{color:var(--text-dim);font-family:var(--mono);font-size:11px}.rv-note{display:flex;gap:9px;align-items:flex-start;margin-top:18px;color:var(--text-dim);font-size:13px;line-height:1.6}.rv-note .i{color:var(--accent)}.rv-foot{border-top:1px solid var(--border);background:var(--bg-raised);padding:13px 24px}.rv-route{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;font-size:13px}.rv-route-badge{font-weight:700;color:var(--pk);background:#fcd34d21;padding:3px 11px;border-radius:99px;font-size:12px}.rv-route .k{color:var(--text-dim)}.rv-src{color:var(--text-dim);font-size:11px;font-style:italic}.rv-gates{margin-top:6px;font-size:12.5px;color:var(--text-dim)}.rv-gates code{font-family:var(--mono);color:var(--text);background:var(--bg-card);padding:1px 6px;border-radius:5px;margin-right:5px;font-size:11.5px}.rv-conflict{margin-top:10px;font-size:13px;display:flex;align-items:center;gap:8px}.rv-conflict.ok{color:var(--ok)}.rv-conflict.bad{color:var(--err)}.rv-conflict .detail{color:var(--text-dim)}.rv-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:12px}.pr-wrap{padding:20px 24px;max-width:940px}.pr-head{display:flex;align-items:baseline;gap:12px;margin-bottom:16px}.pr-head h2{margin:0;font-size:19px}.pr-head .meta{color:var(--text-dim);font-size:13px;margin-left:auto}.pr-card{border:1px solid var(--border);border-radius:11px;background:var(--bg-card);padding:14px 16px;margin-bottom:10px}.pr-card.attention{border-color:#f8717173}.pr-top{display:flex;align-items:baseline;gap:12px}.pr-title{font-weight:600;font-size:14.5px}.pr-chip{font-size:11px;padding:3px 10px;border-radius:99px;margin-left:auto;white-space:nowrap;font-weight:600}.pr-chip.accepted{background:#34d39924;color:var(--ok)}.pr-chip.changes{background:#5eead421;color:var(--accent)}.pr-chip.attention{background:#f8717126;color:var(--err)}.pr-chip.awaiting,.pr-chip.local{background:#94a3b824;color:var(--text-dim)}.pr-branch{font-family:var(--mono);font-size:11px;color:var(--text-dim);margin-top:5px}.pr-summary{font-size:13px;margin-top:7px}.pr-detail{font-size:12.5px;color:var(--err);margin-top:6px;line-height:1.5}.pr-foot{display:flex;align-items:center;gap:10px;margin-top:11px}.pr-foot .pr-link{font-size:12.5px;color:var(--text-dim)}.pr-foot .pr-link a{color:var(--accent)}.pr-btns{margin-left:auto;display:flex;gap:8px}.pr-degrade{margin-top:14px;font-size:12.5px;color:var(--text-dim);line-height:1.6;border-left:2px solid var(--border-strong);padding-left:12px}.sub-advice{border:1px solid rgba(252,211,77,.35);background:#fcd34d12;border-radius:9px;padding:12px 14px;margin-top:16px;font-size:13px;line-height:1.6}.sub-advice .i{color:var(--pk)}.sub-split{background:none;border:none;color:var(--pk);cursor:pointer;font-size:13px;font-weight:600;padding:8px 0 0;font-family:var(--font)}.sub-dest{margin-top:16px;font-size:13px}.sub-dest .row{display:flex;gap:10px;margin:5px 0}.sub-dest .k{color:var(--text-dim);min-width:80px}.sub-dest code{font-family:var(--mono);font-size:12px;color:var(--accent)}.sme-model-view{flex:1;display:grid;grid-template-columns:220px 1fr;min-height:0}.sme-sa-count{float:right;font-size:10px;padding:1px 6px;border-radius:99px;background:#94a3b82e;color:var(--text-dim)}.erd-wrap{flex:1;display:flex;flex-direction:column;min-height:0}.erd-bar{display:flex;align-items:center;gap:16px;padding:8px 16px;border-bottom:1px solid var(--border);font-size:12px;color:var(--text-dim)}.erd-count{font-variant-numeric:tabular-nums}.erd-toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.erd-toggle input{accent-color:var(--accent)}.erd-legend{margin-left:auto;display:flex;align-items:center;gap:8px}.erd-legend .sw{display:inline-block;width:10px;height:10px;border-radius:3px;margin-left:8px}.erd-canvas{flex:1;min-height:320px}.erd-canvas .react-flow__attribution{display:none}.erd-busy{color:var(--pk);font-size:11px}.erd-error{color:var(--err);font-size:11.5px;margin-left:auto}.sme-model-view .erd-canvas{position:relative;min-height:0}.erd-action{background:#5eead41f;border:1px solid rgba(94,234,212,.35);color:var(--accent);border-radius:7px;padding:3px 10px;cursor:pointer;font-size:12px;font-family:var(--font);font-weight:600}.erd-action:hover{background:#5eead433}.erd-hint{color:var(--text-dim);font-size:11.5px;font-style:italic}.sa-editor{flex:1;display:grid;grid-template-columns:220px 1fr;min-height:0}.sa-main{overflow-y:auto;padding:18px 24px;display:flex;flex-direction:column;gap:14px}.sa-head h2{margin:0 0 4px;font-size:19px}.sa-filter{flex:none;width:100%;margin-top:10px}.sa-nudge{border:1px solid rgba(252,211,77,.35);background:#fcd34d12;border-radius:8px;padding:9px 12px;font-size:13px;color:var(--text)}.sa-panes{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;align-items:stretch}.sa-pane{border:1px solid var(--border);border-radius:10px;background:var(--bg-card);display:flex;flex-direction:column;min-height:260px;max-height:380px}.sa-pane h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0;padding:10px 12px;border-bottom:1px solid var(--border)}.sa-pane ul{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1}.sa-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:7px;cursor:pointer;font-size:13px}.sa-row:hover{background:#94a3b814}.sa-row.picked{background:#5eead41f}.sa-row input{accent-color:var(--accent);pointer-events:none}.sa-row-name{flex:1}.sme-badge.borrowed{background:#94a3b824;color:var(--text-dim)}.sa-controls{display:flex;flex-direction:column;gap:6px;justify-content:center}.sa-controls button{min-width:44px}.sa-expand{border-top:1px solid var(--border);padding-top:14px}.sa-expand h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 9px}.sa-expand-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sa-expand-row select{padding:7px 10px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-size:13px}.sa-expand-row label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-dim)}.sa-expand-row input[type=number]{width:56px;padding:6px 8px;border-radius:7px;border:1px solid var(--border);background:var(--bg-card);color:var(--text)}.sa-hops{margin-top:12px;display:flex;flex-direction:column;gap:4px}.sa-hop{display:flex;align-items:baseline;gap:9px;padding:7px 10px;border-radius:7px;background:var(--bg-card);border:1px solid var(--border);font-size:13px;cursor:pointer}.sa-hop input{accent-color:var(--accent)}.sa-hop-name{font-weight:600}.sa-hop-why{color:var(--text-dim);font-size:11.5px;font-family:var(--mono)}.sa-hop-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:10px}.erd-panels{display:flex;gap:6px}.erd-group{display:inline-flex;gap:6px}.erd-sep{width:1px;align-self:stretch;margin:2px;background:var(--border);flex:none}.erd-caret{opacity:.55;margin-left:1px}.erd-action.ghost{background:none;border-color:var(--border);color:var(--text-dim);font-weight:500;display:inline-flex;align-items:center;gap:5px}.erd-action.ghost:hover{color:var(--text);border-color:var(--border-strong);background:none}.erd-action.ghost.on{background:#5eead41f;border-color:#5eead459;color:var(--accent)}.sme-nav-head{display:flex;align-items:center;justify-content:space-between;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);padding:2px 10px 8px}.sme-nav-add{background:none;border:1px solid var(--border);color:var(--text-dim);width:20px;height:20px;line-height:1;border-radius:6px;cursor:pointer;font-size:14px;font-family:var(--font)}.sme-nav-add:hover{color:var(--accent);border-color:#5eead466}.sme-nav-new input{width:100%;padding:7px 10px;border-radius:8px;font-size:13px;border:1px solid var(--accent);background:var(--bg-card);color:var(--text);outline:none;font-family:var(--font)}.sme-sa{display:flex;align-items:center;gap:8px}.sme-sa>:first-child{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sme-sa .sme-sa-count{float:none;margin-left:auto;flex:none}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.sa-modal{width:min(900px,92vw);max-height:88vh}.sa-modal .sa-panes{margin-top:14px}.sa-modal .sa-pane{min-height:200px;max-height:260px}.sa-modal .sa-filter{margin-top:14px}.sa-modal .sa-expand{margin-top:16px} diff --git a/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js b/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js new file mode 100644 index 0000000..3ae4d32 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-C9iVTvG9.js","assets/client-DOmNpTln.js","assets/styles-Cn8AFsR1.js","assets/styles-BG76mhX4.css"])))=>i.map(i=>d[i]); +import{r as t,j as e,c as Me,R as Re}from"./client-DOmNpTln.js";import{p as je,c as xe,d as fe,e as De,h as be,I as Pe,R as Ie,M as Oe,O as Be,L as Le,i as ze,j as We,A as qe,k as He,l as Ve,m as Ge,o as Ke,s as Ue,f as ce,n as Te,q as Ye,r as Fe}from"./styles-Cn8AFsR1.js";/* empty css */const Je="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function oe(s,n){let a="";for(let l=0;l>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,n=new Uint8Array(10);crypto.getRandomValues(n);let a=0n;for(const l of n)a=a<<8n|BigInt(l);return oe(s,10)+oe(a,16)}const Qe={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Xe(s,n){const a=Qe[s];if(!a)return n;const l={...n};for(const i of a)l[i]||(l[i]=ge());return l}function ve(s,n){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${n.id??n.attribute_id}`;const a=n.id??n.entity_id??"",l=n.attribute_id??"";return l?`${s}:${a}:${l}`:`${s}:${a}`}function Ze({subjectArea:s,describe:n}){const[a,l]=t.useState([]),[i,j]=t.useState(null),[h,r]=t.useState(!1),m=t.useRef(null),N=t.useCallback(async(c,p)=>{p=Xe(c,p),c==="set_alignment"&&p.status===void 0&&(p={...p,status:"proposed"});const v=ve(c,p),{label:C,before:B,after:S}=n(c,p);return l(k=>{const A=k.filter(_=>_.key!==v);if(c.startsWith("delete_")){const _=p.id??p.entity_id;if(_&&k.some(o=>o.op.startsWith("create_")&&o.payload.id===_))return A.filter(o=>o.payload.id!==_)}return[...A,{op:c,payload:p,label:C,before:B,after:S,key:v}]}),{ok:!0,fingerprint:"",created_id:p.id??null,diagnostics:[]}},[n]);t.useEffect(()=>{if(!a.length){j(null);return}const c=setTimeout(()=>{var v;(v=m.current)==null||v.abort();const p=new AbortController;m.current=p,r(!0),je(a.map(C=>({op:C.op,payload:C.payload})),s,p.signal).then(j).catch(C=>{C.name!=="AbortError"&&j(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(c)},[a,s]);const x=t.useCallback(c=>{l(p=>p.filter(v=>v.key!==c))},[]),g=t.useCallback(()=>l([]),[]);return t.useMemo(()=>({pending:a,previewDoc:(i==null?void 0:i.model)??null,previewDiff:(i==null?void 0:i.diff)??null,diagnostics:(i==null?void 0:i.diagnostics)??[],failure:i&&i.failed_index!==null?{index:i.failed_index,error:i.error??"invalid change"}:null,busy:h,exec:N,drop:x,clear:g}),[a,i,h,N,x,g])}function es(s){const n=new Map;for(const l of s)for(const i of["id","conceptual_id","from_fk_id"]){const j=l.payload[i];l.op.startsWith("create_")&&typeof j=="string"&&n.set(j,l.key)}if(!n.size)return new Set;const a=new Set;for(const l of s)if(!l.op.startsWith("create_"))for(const i of Object.values(l.payload))typeof i=="string"&&n.has(i)&&(a.add(l.key),a.add(n.get(i)));return a}function ss({user:s}){const[n,a]=t.useState(null);return t.useEffect(()=>{xe(s).then(a).catch(()=>{})},[s]),!(n!=null&&n.ok)||!n.git?null:n.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):n.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):n.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"your proposal branch"}),n.behind?e.jsxs("span",{className:"dim",children:["· ",n.base_branch," is ",n.behind," ahead"]}):null]})}function ns({exec:s,canEdit:n,stagedMembers:a}){const[l,i]=t.useState([]),[j,h]=t.useState(""),[r,m]=t.useState(null),[N,x]=t.useState(""),[g,c]=t.useState(new Set),[p,v]=t.useState("ancestors"),[C,B]=t.useState(1),[S,k]=t.useState(null),[A,_]=t.useState(new Set),[M,o]=t.useState(null);t.useEffect(()=>{fe().then(d=>{i(d.subject_areas),h(w=>{var R;return w||((R=d.subject_areas[0])==null?void 0:R.id)||""})}).catch(d=>o(String(d)))},[]),t.useEffect(()=>{j&&(k(null),c(new Set),De(j).then(m).catch(d=>o(String(d))))},[j]);const $=t.useMemo(()=>a[j]??(r==null?void 0:r.members)??[],[a,j,r]),E=t.useMemo(()=>{const d=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])d.set(w.id,w);return d},[r]),D=N.trim().toLowerCase(),L=d=>!D||d.name.toLowerCase().includes(D),u=t.useMemo(()=>new Set($),[$]),P=t.useMemo(()=>$.map(d=>E.get(d)).filter(d=>!!d&&L(d)),[$,E,D]),W=t.useMemo(()=>[...E.values()].filter(d=>!u.has(d.id)&&L(d)),[E,u,D]),I=t.useCallback(d=>{if(!r)return;const w=d.filter(H=>!(r.members??[]).includes(H)).length,R=(r.members??[]).filter(H=>!d.includes(H)).length;s("set_subject_area_members",{id:r.id,members:d,__label:`${r.name}: ${d.length} objects (+${w} −${R})`})},[r,s]),q=(d,w)=>{const R=w?[...$,...d.filter(H=>!u.has(H))]:$.filter(H=>!d.includes(H));I(R),c(new Set)},V=(d,w,R)=>{const H=new Set(d);H.has(w)?H.delete(w):H.add(w),R(H)},Y=()=>{if(!r)return;const d=g.size?[...g]:$;if(!d.length){o("add an object first, or select one to expand from");return}o(null),be(r.id,{seeds:d,direction:p,levels:C}).then(w=>{k(w.hops),_(new Set(w.added))}).catch(w=>o(String(w)))},T=()=>{q([...A],!0),k(null)};if(M&&!r)return e.jsx("p",{className:"sme-splash error",children:M});if(!l.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const b=((r==null?void 0:r.inconsistent)??[]).filter(d=>!u.has(d));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:l.map(d=>e.jsxs("button",{className:"sme-sa"+(d.id===j?" active":""),onClick:()=>h(d.id),title:d.definition??void 0,children:[d.name,e.jsx("span",{className:"sme-sa-count",children:(a[d.id]??d.members).length})]},d.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:d=>x(d.target.value)})]}),b.length>0&&n&&e.jsxs("div",{className:"sa-nudge",children:[b.length," object",b.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>q(b,!0),children:["Add ",b.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(X,{title:`Available (${W.length})`,rows:W,picked:g,onToggle:d=>V(g,d,c),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(d=>!u.has(d)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!W.length,onClick:()=>q(W.map(d=>d.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(d=>u.has(d)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!P.length,onClick:()=>q(P.map(d=>d.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(X,{title:`Included (${P.length})`,rows:P,picked:g,onToggle:d=>V(g,d,c),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:p,onChange:d=>v(d.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:d=>B(Math.max(1,Math.min(10,Number(d.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:Y,disabled:!n,children:"Preview"})]}),M&&e.jsx("p",{className:"sme-error-line",children:M}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(d=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(d.id),onChange:()=>V(A,d.id,_)}),e.jsx("span",{className:"sa-hop-name",children:d.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",d.via_name," · ",d.direction.replace(/s$/,""),", level ",d.level]})]},d.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>k(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:T,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function X({title:s,rows:n,picked:a,onToggle:l,homedHere:i}){const j=t.useMemo(()=>new Set(i),[i]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[n.map(h=>e.jsxs("li",{className:"sa-row"+(a.has(h.id)?" picked":""),onClick:()=>l(h.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(h.id)}),e.jsx("span",{className:"sa-row-name",children:h.name}),j.has(h.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},h.id)),!n.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function as({objects:s,onCreate:n,onClose:a}){const[l,i]=t.useState(""),[j,h]=t.useState(""),[r]=t.useState(s),[m,N]=t.useState([]),[x,g]=t.useState(new Set),[c,p]=t.useState(""),[v,C]=t.useState("ancestors"),[B,S]=t.useState(1),[k,A]=t.useState(null),[_,M]=t.useState(new Set),[o,$]=t.useState(""),[E,D]=t.useState(null);t.useEffect(()=>{fe().then(b=>{b.subject_areas[0]&&$(b.subject_areas[0].id)}).catch(()=>{})},[]);const L=t.useMemo(()=>new Set(m),[m]),u=c.trim().toLowerCase(),P=b=>!u||b.name.toLowerCase().includes(u),W=t.useMemo(()=>m.map(b=>r.find(d=>d.id===b)).filter(b=>!!b&&P(b)),[m,r,u]),I=t.useMemo(()=>r.filter(b=>!L.has(b.id)&&P(b)),[r,L,u]),q=(b,d)=>{N(w=>d?[...w,...b.filter(R=>!w.includes(R))]:w.filter(R=>!b.includes(R))),g(new Set)},V=(b,d,w)=>{const R=new Set(b);R.has(d)?R.delete(d):R.add(d),w(R)},Y=()=>{const b=x.size?[...x]:m;if(!b.length){D("pick an object to expand from first");return}o&&(D(null),be(o,{seeds:b,direction:v,levels:B}).then(d=>{const w=d.hops.filter(R=>!L.has(R.id));A(w),M(new Set(w.map(R=>R.id)))}).catch(d=>D(String(d))))},T=()=>{l.trim()&&n(l.trim(),j.trim(),m)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:b=>b.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:l,placeholder:"e.g. UseCase-ApraStressTesting",onChange:b=>i(b.target.value),onKeyDown:b=>b.key==="Enter"&&m.length===0&&T()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:j,placeholder:"what this view is for",onChange:b=>h(b.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:c,onChange:b=>p(b.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(X,{title:`Available (${I.length})`,rows:I,picked:x,onToggle:b=>V(x,b,g),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(b=>!L.has(b)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!I.length,onClick:()=>q(I.map(b=>b.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(b=>L.has(b)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!W.length,onClick:()=>q(W.map(b=>b.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(X,{title:`Included (${W.length})`,rows:W,picked:x,onToggle:b=>V(x,b,g),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:v,onChange:b=>C(b.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:B,onChange:b=>S(Math.max(1,Math.min(10,Number(b.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:Y,disabled:!o,title:o?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),k&&e.jsxs("div",{className:"sa-hops",children:[k.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),k.map(b=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:_.has(b.id),onChange:()=>V(_,b.id,M)}),e.jsx("span",{className:"sa-hop-name",children:b.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",b.via_name," · ",b.direction.replace(/s$/,""),", level ",b.level]})]},b.id)),k.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!_.size,onClick:()=>{q([..._],!0),A(null)},children:["Add ",_.size]})]})]})]}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!l.trim(),onClick:T,children:["Create",m.length>0&&` with ${m.length} object${m.length===1?"":"s"}`]})]})]})})}const ts=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function rs({doc:s,subjectArea:n,onSubjectArea:a,exec:l,canEdit:i,direct:j=!1,onSelectEntity:h,onImported:r,query:m="",busy:N}){var M;const[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(!1),[B,S]=t.useState(null),k=t.useRef(null),A=t.useMemo(()=>({canEdit:i,mode:j?"direct":"staged",allow:j?void 0:ts,canArbitrate:j,canCommit:j}),[i,j]),_=t.useCallback(o=>{g(o),o&&(h==null||h(o))},[h]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),i&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(n===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((M=s.counts)==null?void 0:M.entities_total)??s.entities.length})]}),s.subject_areas.map(o=>e.jsxs("button",{className:"sme-sa"+(n===o.id?" active":""),onClick:()=>a(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:o.member_count??0})]},o.id))]}),v&&e.jsx(as,{objects:s.entities.flatMap(o=>o.conceptual?[{id:o.conceptual.id,kind:"conceptual_entity",name:o.conceptual.name,definition:o.conceptual.definition??null,synonyms:[],subject_area:o.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(o,$,E)=>{const D=ge();l("create_subject_area",{name:o,...$?{definition:$}:{},id:D}),E.length&&l("set_subject_area_members",{id:D,members:E}),C(!1),a(D)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),i&&e.jsx("button",{className:"erd-action",onClick:()=>{var o;return(o=k.current)==null?void 0:o.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=k.current)==null?void 0:o.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=k.current)==null?void 0:o.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(c==="ontology"?" on":""),onClick:()=>p(o=>o==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(c==="layers"?" on":""),onClick:()=>p(o=>o==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Pe,{exec:l,canEdit:i,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!i&&e.jsx("span",{className:"sme-chip",children:"read-only"}),B&&e.jsx("span",{className:"erd-error",children:B})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Ie,{children:e.jsx(Oe,{doc:s,caps:A,exec:l,selectedId:x,onSelect:_,query:m,onError:S,handleRef:k})}),c&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:c==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>p(null),children:"✕"})]}),c==="ontology"?e.jsx(Be,{}):e.jsx(Le,{refreshKey:0,onFocusEntity:o=>{var $;p(null),($=k.current)==null||$.focusEntity(o)}})]})]})]})]})}function is({user:s,onView:n}){const[a,l]=t.useState(null),[i,j]=t.useState(null);if(t.useEffect(()=>{ze(s).then(l).catch(r=>j(String(r)))},[s]),i)return e.jsx("p",{className:"sme-splash error",children:i});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const h=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[h," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(cs,{p:r,gh:a.gh,onView:()=>n(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function ls(s,n){var a,l;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?n&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:n&&((l=s.pr)==null?void 0:l.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function cs({p:s,gh:n,onView:a}){const l=ls(s,n);return e.jsxs("div",{className:"pr-card"+(l.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+l.kind,children:l.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",n&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?n&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function os({changes:s,routeAdvice:n,identity:a,user:l,onUser:i,onDrop:j,onClose:h,onProposed:r}){const m=!!(a&&a.source!=="anonymous"),[N,x]=t.useState(l||(localStorage.getItem("mdl.sme.user")??"")),[g,c]=t.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[p,v]=t.useState(""),[C,B]=t.useState(!1),S=Ne(C?p:g),[k,A]=t.useState(""),[_,M]=t.useState(!1),[o,$]=t.useState(null),[E,D]=t.useState(null),L=()=>{!m&&!N.trim()||(m||(localStorage.setItem("mdl.sme.user",N.trim()),i==null||i(N.trim())),M(!0),D(null),We({user:m?"":N.trim(),slug:S,title:g,body:k.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then($).catch(u=>D(u instanceof qe?u.message:String(u))).finally(()=>M(!1)))};return o!=null&&o.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(o),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:o.message}),o.pr_url&&e.jsx("p",{children:e.jsx("a",{href:o.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),o.compare_url&&e.jsx("p",{children:e.jsx("a",{href:o.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:o.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(o),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:h,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:h,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,P)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>j(P),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},P))}),m?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>x(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:g,onChange:u=>c(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?p:S,onChange:u=>{B(!0),v(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:k,onChange:u=>A(u.target.value)})]}),e.jsx(ds,{cl:n,user:m?a.name:N,changeSlug:S}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:h,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:_||!m&&!N.trim()||s.length===0,onClick:L,children:_?"Submitting…":"Submit for review"})]})]})})}function ds({cl:s,user:n,changeSlug:a}){if(!(s!=null&&s.primary))return null;const l=s.reviewers_actual??s.reviewers,i=Ne(n||"you"),j=s.routes.length>1;return e.jsxs(e.Fragment,{children:[j&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",l.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",i,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:l.join(", ")})]})]})]})}function Ne(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const hs="modulepreload",ms=function(s){return"/"+s},de={},us=function(n,a,l){let i=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const h=document.querySelector("meta[property=csp-nonce]"),r=(h==null?void 0:h.nonce)||(h==null?void 0:h.getAttribute("nonce"));i=Promise.allSettled(a.map(m=>{if(m=ms(m),m in de)return;de[m]=!0;const N=m.endsWith(".css"),x=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${x}`))return;const g=document.createElement("link");if(g.rel=N?"stylesheet":hs,N||(g.as="script"),g.crossOrigin="",g.href=m,r&&g.setAttribute("nonce",r),document.head.appendChild(g),N)return new Promise((c,p)=>{g.addEventListener("load",c),g.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${m}`)))})}))}function j(h){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=h,window.dispatchEvent(r),!r.defaultPrevented)throw h}return i.then(h=>{for(const r of h||[])r.status==="rejected"&&j(r.reason);return n().catch(j)})},ye=[{id:"all",label:"All"},{id:"additions",label:"Additions"},{id:"modifications",label:"Modifications"},{id:"moves",label:"Renames"},{id:"deletions",label:"Deletions"},{id:"conflicts",label:"Conflicts"}];function we(s,n){switch(n){case"all":return!0;case"additions":return s.change==="added";case"deletions":return s.change==="removed";case"moves":return s.renamed;case"modifications":return s.change==="modified"&&!s.renamed;case"conflicts":return s.severity==="breaking"}}function ps(s,n){return n==="all"?s.objects:s.objects.filter(a=>we(a,n))}function js(s){const n={};for(const{id:a}of ye)n[a]=a==="all"?s.objects.length:s.objects.filter(l=>we(l,a)).length;return n}function Q(s,n){return`${s}\0${n}`}function xs(s){const n=s.fields.map(a=>Q(s.ulid,a.field));for(const a of s.children)for(const l of a.fields)n.push(Q(s.ulid,`${a.ulid}.${l.field}`));return n.length?n:[Q(s.ulid,"*")]}function fs(s,n){const a=xs(s),l=a.filter(i=>n.has(i)).length;return l===0?"none":l===a.length?"all":"some"}function bs({diff:s,selectable:n,selected:a,onToggle:l,onToggleField:i}){var p;const[j,h]=t.useState("all"),r=t.useMemo(()=>ps(s,j),[s,j]),m=t.useMemo(()=>js(s),[s]),[N,x]=t.useState(((p=s.objects[0])==null?void 0:p.ulid)??null),g=t.useMemo(()=>r.find(v=>v.ulid===N)??r[0]??s.objects[0]??null,[r,s,N]),c=!!i;return s.objects.length?e.jsxs("div",{className:"rv-wrap",children:[n!==void 0&&e.jsx("div",{className:"rv-filters",role:"tablist","aria-label":"Filter changes",children:ye.filter(v=>v.id==="all"||m[v.id]>0).map(v=>e.jsxs("button",{role:"tab","aria-selected":j===v.id,className:"rv-filter"+(j===v.id?" active":""),onClick:()=>h(v.id),children:[v.label,e.jsx("span",{className:"rv-filter-count",children:m[v.id]})]},v.id))}),e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:r.length===0?e.jsx("p",{className:"sme-placeholder",children:"No changes match this filter."}):r.map(v=>e.jsx(vs,{obj:v,active:v.ulid===((g==null?void 0:g.ulid)??""),selectable:n,perField:c,objState:c?fs(v,a??new Set):void 0,checkedObject:(a==null?void 0:a.has(v.ulid))??!0,onSelect:()=>x(v.ulid),onToggleObject:()=>l==null?void 0:l(v.ulid),onToggleField:i},v.ulid))}),e.jsx("div",{className:"rv-detail",children:g&&e.jsx(Ns,{obj:g,selectable:n,perField:c,selected:a,onToggleField:i})})]})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function gs(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function vs({obj:s,active:n,selectable:a,perField:l,objState:i,checkedObject:j,onSelect:h,onToggleObject:r,onToggleField:m}){const N=c=>{c&&l&&(c.indeterminate=i==="some")},x=l?i!=="none":j,g=()=>{if(!l)return r();for(const c of s.fields)m==null||m(s.ulid,c.field);for(const c of s.children)for(const p of c.fields)m==null||m(s.ulid,`${c.ulid}.${p.field}`);!s.fields.length&&!s.children.length&&(m==null||m(s.ulid,"*"))};return e.jsxs("div",{className:"rv-obj"+(n?" active":""),onClick:h,children:[e.jsxs("div",{className:"rv-obj-head",children:[a&&e.jsx("input",{type:"checkbox",ref:N,checked:x,onClick:c=>c.stopPropagation(),onChange:g,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",gs(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,p)=>e.jsx("li",{children:c.label},c.field+p)),s.children.flatMap(c=>c.fields.map((p,v)=>e.jsxs("li",{children:[p.label,": ",c.name_before??c.name_after]},c.ulid+v)))]})]})}function Ns({obj:s,selectable:n,perField:a,selected:l,onToggleField:i}){const j=s.children.flatMap(r=>r.fields.map(m=>({field:m,on:r.name_before??r.name_after??"",key:`${r.ulid}.${m.field}`}))),h=(r,m)=>{if(!n||!a)return m;const N=Q(s.ulid,r);return e.jsxs("div",{className:"rv-field-pick",children:[e.jsx("input",{type:"checkbox",checked:(l==null?void 0:l.has(N))??!0,onChange:()=>i==null?void 0:i(s.ulid,r),"aria-label":`include change: ${r}`}),e.jsx("div",{className:"rv-field-body",children:m})]})};return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((r,m)=>e.jsx("div",{children:h(r.field,e.jsx(he,{f:r}))},r.field+m)),j.map(({field:r,on:m,key:N},x)=>e.jsx("div",{children:h(N,e.jsx(he,{f:r,on:m}))},"c"+x)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function he({f:s,on:n}){var l,i,j;const a=n?`${s.label}: ${n}`:s.label;if((l=s.breaks)!=null&&l.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[n??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(h=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",h.name]}),e.jsxs("span",{className:"p",children:[h.target," · ",h.materialization]})]},h.name))})]})]});if(s.kind==="definition_changed"){const h=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:ue(h,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:ue(r,h,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const h=new Set(((i=s.before)==null?void 0:i.map(String))??[]),r=new Set(((j=s.after)==null?void 0:j.map(String))??[]),m=[...r].filter(x=>!h.has(x)),N=[...h].filter(x=>!r.has(x));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[m.map(x=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",x]},"a"+x)),N.map(x=>e.jsxs("span",{className:"rv-chip-del",children:["− ",x]},"r"+x))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:me(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:me(s.after)})]})]})}function me(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function ue(s,n,a){const l=s.split(/(\s+)/),i=n.split(/(\s+)/),j=c=>c.toLowerCase().replace(/[.,;:]/g,""),h=l.length,r=i.length,m=Array.from({length:h+1},()=>new Array(r+1).fill(0));for(let c=h-1;c>=0;c--)for(let p=r-1;p>=0;p--)m[c][p]=j(l[c])===j(i[p])?m[c+1][p+1]+1:Math.max(m[c+1][p],m[c][p+1]);const N=new Set;let x=0,g=0;for(;x=m[x][g+1]?x++:g++;return l.map((c,p)=>!c.trim()||N.has(p)?e.jsx("span",{children:c},p):a==="del"?e.jsx("del",{children:c},p):e.jsx("ins",{children:c},p))}const ys=t.lazy(()=>us(()=>import("./ModelDiagram-C9iVTvG9.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function ws({stagedDiff:s,stagedRoute:n,user:a,onBack:l,onSubmit:i,excluded:j,onToggleObject:h,selectable:r}){const[m,N]=t.useState(null),[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(null),[B,S]=t.useState(null),[k,A]=t.useState("changes"),_=t.useCallback(()=>{xe(a).then(o=>{C(o);const $=o.base_branch??"main";s?N(s):He("HEAD").then(E=>E.ok?N(E):S(E.error??"could not read the model")).catch(E=>S(String(E))),n?g(n):Ve("HEAD").then(g).catch(()=>{}),Ge($).then(p).catch(()=>{})}).catch(o=>S(String(o)))},[a,s,n]);if(t.useEffect(_,[_]),B)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:l,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:B})})]});if(!m)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const M=new Set(m.objects.map(o=>o.ulid).filter(o=>!j.has(o)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:l,diff:m,tab:k,onTab:A}),k==="changes"?e.jsx(bs,{diff:m,selectable:r,selected:M,onToggle:h}):e.jsx(t.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(ys,{severityByUlid:ks(m)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(Ss,{cl:x}),e.jsx(_s,{conf:c,ctx:v}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:l,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!m.objects.length||(v?!v.can_propose:!1),title:v&&!v.can_propose?"the working tree has uncommitted changes":"",onClick:()=>i(x),children:"Submit for review →"})]})]})]})}function ks(s){const n=new Map;for(const a of s.objects)n.set(a.ulid,a.severity);return n}function pe({onBack:s,diff:n,tab:a,onTab:l}){const i=n==null?void 0:n.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),i&&e.jsxs("div",{className:"rv-sub",children:[i.objects," object",i.objects===1?"":"s"," ·"," ",(i.breaking??0)+(i.additive??0)+(i.cosmetic??0)," changes",(n==null?void 0:n.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",i.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:n==null?void 0:n.base.label})]})]}),l&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>l("changes"),children:["Changes ",(n==null?void 0:n.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>l("diagram"),children:"Diagram"})]})]})}function Ss({cl:s}){if(!(s!=null&&s.primary))return null;const n=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:n.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function _s({conf:s,ctx:n}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(n==null?void 0:n.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function Cs({term:s,canEdit:n,pendingCount:a,onEdit:l}){var i,j;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((i=s.subject_area)==null?void 0:i.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),n&&e.jsx("button",{className:"sme-primary",onClick:l,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(h=>e.jsx("span",{className:"sme-chip",children:h},h))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((j=s.ontology)==null?void 0:j.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(h=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:h.logical_entity}),h.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),h.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:h.physical.map(r=>r.name).join(", ")})]},h.logical_id))})]})]})}function As({term:s,pending:n,onStage:a,onDone:l,catalogOwned:i=[],catalog:j=null}){var $,E,D,L;const h=u=>i.includes(u),r=u=>n.find(P=>P.label===u),m=(u,P)=>{const W=r(u);return W?W.after:P},[N,x]=t.useState(m("Definition",s.definition??"")),[g,c]=t.useState(s.synonyms),[p,v]=t.useState(""),[C,B]=t.useState((($=s.stewardship)==null?void 0:$.owner)??""),[S,k]=t.useState(((E=s.stewardship)==null?void 0:E.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},_=(u,P)=>{var W,I;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:P||null},label:"Stewardship",before:`owner: ${((W=s.stewardship)==null?void 0:W.owner)??"—"} · steward: ${((I=s.stewardship)==null?void 0:I.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${P||"—"}`})},M=u=>{c(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},o=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:l,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(h("definition")||h("synonyms")||h("stewardship"))&&j&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:j.name}),". Edit them there — this glossary mirrors them read-only.",j.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:j.url,target:"_blank",rel:"noreferrer",children:["Open in ",j.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),h("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>x(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),h("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[g.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>M(g.filter(P=>P!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:p,placeholder:"add + Enter",onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&p.trim()&&(M([...g,p.trim()]),v(""))}})]})]}),!o&&(h("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((D=s.stewardship)==null?void 0:D.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((L=s.stewardship)==null?void 0:L.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>B(u.target.value),onBlur:()=>_(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>k(u.target.value),onBlur:()=>_(C,S)})]})]})),e.jsx(Es,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:n.length>0?`${n.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const $s=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function Es({term:s,onStage:n}){var g;const[a,l]=t.useState(!1),[i,j]=t.useState(s.name),[h,r]=t.useState([]),[m,N]=t.useState("skos:closeMatch"),x=c=>{j(c),Ke(c).then(p=>r(p.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:i,onChange:c=>x(c.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:h.map(c=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var p;n({op:"set_alignment",payload:{id:s.id,aligns_to:c.prefixed,alignment:m,status:"proposed"},label:"Standard mapping",before:((p=s.ontology)==null?void 0:p.aligns_to)??"(none)",after:`${c.prefixed} (${m}, proposed)`}),l(!1)},children:[e.jsx("strong",{children:c.label})," ",e.jsx("code",{children:c.prefixed}),c.definition&&e.jsx("span",{className:"sme-align-def",children:c.definition})]},c.iri))}),e.jsx("select",{value:m,onChange:c=>N(c.target.value),children:$s.map(c=>e.jsx("option",{children:c},c))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(g=s.ontology)!=null&&g.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>l(!0),children:"Propose a mapping…"})]})]})}function Ms(){const[s,n]=t.useState(null),[a,l]=t.useState(!0),[i,j]=t.useState(null),[h,r]=t.useState(""),[m,N]=t.useState(null),[x,g]=t.useState(""),[c,p]=t.useState(""),[v,C]=t.useState(null),[B,S]=t.useState(!1),[k,A]=t.useState([]),[_,M]=t.useState(!1),[o,$]=t.useState(window.location.hash==="#proposals"?"proposals":"browse"),[E,D]=t.useState(new Set),[L,u]=t.useState(()=>localStorage.getItem("mdl.sme.user")??""),[P,W]=t.useState(null),[I,q]=t.useState(null),[V,Y]=t.useState(!1),[T,b]=t.useState(null),d=t.useCallback((f,y)=>{var te;const O=J=>{var re;const U=I==null?void 0:I.entities.find(ie=>{var le;return ie.id===J||((le=ie.conceptual)==null?void 0:le.id)===J});return((re=U==null?void 0:U.conceptual)==null?void 0:re.name)??(U==null?void 0:U.name)??"object"},z=O(y.id??y.entity_id);switch(f){case"create_entity":return{label:`New entity: ${y.name}`,before:"—",after:String(y.name??"")};case"create_relationship":return{label:`New relationship: ${O(y.from_entity)} → ${O(y.to_entity)}`,before:"—",after:String(y.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${z}`,before:z,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[y.cardinality,y.optionality,y.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${z}`,before:"",after:String(y.definition??"")};case"add_attribute":return{label:`Attribute added to ${z}`,before:"—",after:String(y.name??"")};case"delete_attribute":return{label:`Attribute removed from ${z}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${z}`,before:"",after:JSON.stringify(y)};case"rename_entity":return{label:`Renamed ${z}`,before:z,after:String(y.name??"")};case"set_subject_area":return{label:`Subject area: ${z}`,before:"",after:String(y.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${z}`,before:"",after:String(y.steward??"")};case"set_subject_area_members":{const J=y.__label,U=((te=y.members)==null?void 0:te.length)??0;return{label:J??"Subject area members",before:"",after:`${U} object${U===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${z}`,before:"",after:String(y.aligns_to??"")};default:return{label:`${f.replace(/_/g," ")}: ${z}`,before:"",after:""}}},[I]),w=Ze({subjectArea:x,describe:d}),R=t.useCallback(async(f,y)=>{const O=(I==null?void 0:I.fingerprint)??"",z=await Ue(f,y,O);return await ce(x||void 0).then(q),z},[I,x]),H=t.useMemo(()=>{const f={};for(const y of w.pending)y.op==="set_subject_area_members"&&(f[y.payload.id]=y.payload.members??[]);return f},[w.pending]),G=t.useMemo(()=>[...k,...w.pending],[k,w.pending]),[ke,Z]=t.useState(null),[Se,ee]=t.useState(null);t.useEffect(()=>{if(!G.length){Z(null),ee(null);return}const f=new AbortController,y=G.map(O=>({op:O.op,payload:O.payload}));return je(y,x||void 0,f.signal).then(O=>Z(O.diff??null)).catch(O=>{O.name!=="AbortError"&&Z(null)}),Te(y).then(ee).catch(()=>ee(null)),()=>f.abort()},[G,x]);const _e=t.useMemo(()=>es(G),[G]).size{$(f),window.location.hash=f==="browse"?"":`#${f}`},[]);t.useEffect(()=>{const f=()=>{const y=window.location.hash;$(y==="#proposals"?"proposals":y==="#review"?"review":y==="#model"?"model":y==="#areas"?"areas":"browse")};return f(),window.addEventListener("hashchange",f),()=>window.removeEventListener("hashchange",f)},[]);const se=t.useCallback(()=>{Ye({subject_area:x,q:c}).then(n).catch(f=>N(String(f)))},[x,c]);t.useEffect(se,[se]),t.useEffect(()=>{ce(x||void 0).then(f=>{l(f.read_only),r(f.project.name),q(f),Y(!!f.direct),W(f.identity??null),f.identity&&f.identity.source!=="anonymous"&&f.identity.name&&u(f.identity.name)}).catch(()=>{}),Fe().then(j).catch(()=>{})},[x]);const ne=t.useMemo(()=>(s==null?void 0:s.terms.find(f=>f.id===v))??null,[s,v]),Ce=t.useCallback(f=>{const y=f.key??ve(f.op,f.payload);A(O=>[...O.filter(z=>z.key!==y),{...f,key:y}])},[]),Ae=f=>A(y=>y.filter((O,z)=>z!==f)),$e=()=>{A([]),M(!1),S(!1),se()};if(m)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const F=!a,ae=(i==null?void 0:i.catalog_owned_fields)??[],Ee=ae.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:h}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Ee&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${i==null?void 0:i.catalog_name}`,children:["mirrors ",i==null?void 0:i.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:c,onChange:f=>p(f.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(o==="browse"?" active":""),onClick:()=>K("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(o==="model"?" active":""),onClick:()=>K("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(o==="areas"?" active":""),onClick:()=>K("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(o==="proposals"?" active":""),onClick:()=>K("proposals"),children:"My proposals"})]}),G.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>K("review"),children:[G.length," change",G.length>1?"s":""," · Review →"]})]}),o==="browse"&&e.jsx(ss,{user:L}),o==="review"?e.jsx(ws,{stagedDiff:ke,stagedRoute:Se,user:L,selectable:_e,excluded:E,onToggleObject:f=>D(y=>{const O=new Set(y);return O.has(f)?O.delete(f):O.add(f),O}),onBack:()=>K("browse"),onSubmit:f=>{b(f),M(!0)}}):o==="model"?w.previewDoc??I?e.jsx(rs,{doc:w.previewDoc??I,subjectArea:x,onSubjectArea:g,exec:V?R:w.exec,canEdit:F,direct:V,onImported:()=>K("review"),query:c,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):o==="areas"?e.jsx(ns,{exec:w.exec,canEdit:F,stagedMembers:H}):o==="proposals"?e.jsx(is,{user:L,onView:()=>K("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(x===""?" active":""),onClick:()=>g(""),children:"All subject areas"}),s.subject_areas.map(f=>e.jsx("button",{className:"sme-sa"+(x===f.id?" active":""),onClick:()=>g(f.id),title:f.definition??void 0,children:f.name},f.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(f=>{var y;return e.jsxs("li",{className:"sme-list-item"+(f.id===v?" active":""),onClick:()=>{C(f.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:f.name}),f.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((y=f.ontology)==null?void 0:y.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:f.definition??"no definition yet"})]},f.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:ne?B&&F?e.jsx(As,{term:ne,pending:k,onStage:Ce,onDone:()=>S(!1),catalogOwned:ae,catalog:i?{name:i.catalog_name,url:i.catalog_url}:null}):e.jsx(Cs,{term:ne,canEdit:F,pendingCount:k.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),_&&e.jsx(os,{changes:G,routeAdvice:T,identity:P,user:L,onUser:f=>{u(f),localStorage.setItem("mdl.sme.user",f)},onDrop:Ae,onClose:()=>M(!1),onProposed:()=>{$e(),w.clear(),D(new Set),K("proposals")}})]})}Me.createRoot(document.getElementById("root")).render(e.jsx(Re.StrictMode,{children:e.jsx(Ms,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-DYGKbOTM.css b/packages/server/src/mdl_server/static/assets/sme-DYGKbOTM.css deleted file mode 100644 index f0414a2..0000000 --- a/packages/server/src/mdl_server/static/assets/sme-DYGKbOTM.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg: #0e1218;--bg-raised: #161c26;--bg-card: #1b232f;--border: #2a3442;--border-strong: #3d4a5c;--text: #e6ecf3;--text-dim: #93a1b5;--accent: #5eead4;--pk: #fcd34d;--err: #f87171;--ok: #34d399;--font: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;--mono: ui-monospace, "SF Mono", Menlo, monospace}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:var(--font);background:var(--bg);color:var(--text)}.sme{display:flex;flex-direction:column;height:100%}.sme-splash{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-dim)}.sme-splash.error pre{background:var(--bg-card);padding:12px;border-radius:8px;color:var(--err)}.sme-top{display:flex;align-items:center;gap:16px;padding:12px 20px;background:var(--bg-raised);border-bottom:1px solid var(--border)}.sme-brand{display:flex;align-items:baseline;gap:8px;font-weight:700;white-space:nowrap}.sme-logo{color:var(--accent);font-size:18px}.sme-project{color:var(--text-dim);font-weight:400;font-size:13px}.sme-search{flex:0 1 420px;padding:9px 14px;border-radius:9px;font-size:14px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);outline:none}.sme-search:focus{border-color:var(--accent)}.sme-tray{margin-left:auto;padding:9px 16px;border-radius:99px;border:none;cursor:pointer;background:var(--pk);color:#1a1400;font-weight:700;font-size:13px}.sme-chip{display:inline-block;font-size:11px;padding:2px 9px;margin:0 4px 0 0;border-radius:99px;background:#94a3b824;color:var(--text-dim)}.sme-chip.catalog{background:#fbbf2429;color:#fbbf24}.sme-catalog-banner{border:1px solid rgba(251,191,36,.35);background:#fbbf2414;border-radius:8px;padding:10px 12px;font-size:13px;line-height:1.5;color:var(--text);margin:4px 0 10px}.sme-catalog-banner a{color:#fbbf24;font-weight:600}.sme-catalog-note{color:var(--text-dim);font-size:12px;margin-top:4px}.sme-readonly-value{margin:0;padding:8px 10px;border-radius:6px;background:#94a3b80f;border:1px dashed var(--border);color:var(--text-dim);font-size:14px;line-height:1.5}.sme-body{flex:1;display:grid;grid-template-columns:220px 340px 1fr;min-height:0}.sme-nav{border-right:1px solid var(--border);padding:12px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}.sme-sa{text-align:left;background:none;border:none;color:var(--text-dim);cursor:pointer;padding:8px 10px;border-radius:8px;font-size:13px}.sme-sa:hover{background:var(--bg-card);color:var(--text)}.sme-sa.active{background:#5eead41f;color:var(--accent);font-weight:600}.sme-list{list-style:none;margin:0;padding:8px;overflow-y:auto;border-right:1px solid var(--border)}.sme-list-item{padding:10px 12px;border-radius:9px;cursor:pointer;margin-bottom:4px}.sme-list-item:hover{background:var(--bg-card)}.sme-list-item.active{background:#5eead41a;box-shadow:inset 2px 0 0 var(--accent)}.sme-term-name{font-weight:600;font-size:14px;margin-right:6px}.sme-kind{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em}.sme-badge{font-size:10px;padding:1px 7px;border-radius:99px;margin-left:4px;background:#94a3b829;color:var(--text-dim)}.sme-badge.proposed{background:#fcd34d29;color:var(--pk)}.sme-term-def{display:block;color:var(--text-dim);font-size:12px;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sme-empty{color:var(--text-dim);padding:12px;font-style:italic}.sme-detail{overflow-y:auto;padding:24px 32px}.sme-placeholder{color:var(--text-dim);margin-top:40px;text-align:center}.sme-card-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.sme-card-head h1,.sme-card-head h2{margin:0 0 6px;font-size:22px}.sme-card section,.sme-editor .sme-field{margin-top:22px}.sme-card h3,.sme-editor h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 8px}.sme-definition{font-size:15px;line-height:1.6;color:var(--text)}.sme-kv{display:flex;gap:10px;align-items:baseline;margin:4px 0;font-size:13px}.sme-kv .k{color:var(--text-dim);min-width:70px}.sme-muted{color:var(--text-dim);font-size:13px}.sme-usage{list-style:none;margin:0;padding:0}.sme-usage li{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;margin-bottom:6px;font-size:13px}.sme-usage-model{font-weight:600}.sme-usage-phys{color:var(--text-dim);font-family:var(--mono);font-size:11px}.sme-current-align{font-family:var(--mono);color:var(--accent);margin-right:8px}.sme-primary{padding:9px 18px;border-radius:9px;border:none;cursor:pointer;background:var(--accent);color:#06281f;font-weight:700;font-size:14px}.sme-primary:disabled{opacity:.45;cursor:default}.sme-secondary{padding:8px 14px;border-radius:9px;cursor:pointer;font-size:13px;background:var(--bg-card);border:1px solid var(--border);color:var(--text)}.sme-link{background:none;border:none;color:var(--accent);cursor:pointer;font-size:13px;margin-left:8px}.sme-link.danger{color:var(--err)}.sme-hint,.sme-note{color:var(--text-dim);font-size:13px;line-height:1.5}.sme-field{display:flex;flex-direction:column;gap:6px}.sme-field>span{font-size:12px;color:var(--text-dim)}.sme-field textarea,.sme-field input,.sme-field select{padding:9px 12px;border-radius:9px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-family:var(--font);font-size:14px;outline:none;resize:vertical}.sme-field textarea:focus,.sme-field input:focus{border-color:var(--accent)}.sme-field-row{display:flex;gap:16px;margin-top:22px}.sme-field-row .sme-field{flex:1}.sme-syn-row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.sme-syn-row input{flex:1;min-width:120px}.sme-chip.removable{cursor:pointer;background:#5eead424;color:var(--accent)}.sme-align-results{display:flex;flex-direction:column;gap:6px;max-height:240px;overflow-y:auto;margin:8px 0}.sme-align-card{text-align:left;background:var(--bg-card);border:1px solid var(--border);border-radius:9px;padding:10px 12px;cursor:pointer;color:var(--text)}.sme-align-card:hover{border-color:var(--accent)}.sme-align-card code{color:var(--accent);font-size:11px;margin-left:6px}.sme-align-def{display:block;color:var(--text-dim);font-size:12px;margin-top:4px}.sme-modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#05080db8;z-index:50;display:flex;align-items:center;justify-content:center}.sme-modal{width:560px;max-height:84vh;overflow-y:auto;background:var(--bg-raised);border:1px solid var(--border-strong);border-radius:16px;padding:24px;box-shadow:0 24px 80px #0009}.sme-modal h2{margin:0 0 4px}.sme-modal .sme-field{margin-top:14px}.sme-diff{list-style:none;margin:8px 0 0;padding:0}.sme-diff li{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin-bottom:8px}.sme-diff-head{display:flex;justify-content:space-between;margin-bottom:6px}.sme-diff-before{color:var(--err);font-size:13px;text-decoration:line-through;opacity:.8}.sme-diff-after{color:var(--ok);font-size:13px}.sme-modal-foot{display:flex;gap:10px;justify-content:flex-end;margin-top:20px}.sme-error-line{color:var(--err);font-size:13px}.sme-signed-in{margin-top:14px;font-size:13px;color:var(--text-dim);background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:10px 12px}.sme-signed-in strong{color:var(--text)}.sme-modal a{color:var(--accent)}:root{--sev-breaking: var(--err);--sev-additive: var(--ok);--sev-cosmetic: var(--text-dim);--sev-context: #4b5768}.rv-shell{flex:1;display:flex;flex-direction:column;min-height:0}.sme-tabs{display:flex;gap:4px;margin-left:12px}.sme-tab{background:none;border:none;color:var(--text-dim);cursor:pointer;padding:7px 12px;border-radius:8px;font-size:13px;font-family:var(--font)}.sme-tab:hover{color:var(--text)}.sme-tab.active{color:var(--accent);font-weight:600;background:#5eead41a}.git-banner{display:flex;align-items:center;gap:10px;padding:8px 20px;background:#94a3b80f;border-bottom:1px solid var(--border);font-size:12.5px;color:var(--text-dim)}.git-banner code{font-family:var(--mono);font-size:11.5px;color:var(--text);background:var(--bg-card);padding:2px 8px;border-radius:6px}.git-banner.warn{background:#fcd34d12;color:var(--pk)}.git-banner .dim{color:var(--text-dim)}.rv-chip-del{font-size:12px;padding:3px 10px;border-radius:99px;background:#f8717124;color:var(--err)}.pr-chip.declined{background:#94a3b824;color:var(--text-dim)}.rv-obj-head input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.rv-head{padding:14px 24px;border-bottom:1px solid var(--border);display:flex;align-items:flex-start;gap:20px}.rv-back{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:13px;padding:4px 0;font-family:var(--font)}.rv-back:hover{color:var(--accent)}.rv-title{margin:0 0 3px;font-size:19px}.rv-sub{color:var(--text-dim);font-size:13px}.rv-sub .warn{color:var(--err);font-weight:600}.rv-tabs{margin-left:auto;display:flex;gap:4px}.rv-tab{background:none;border:1px solid var(--border);color:var(--text-dim);padding:7px 16px;border-radius:8px;cursor:pointer;font-size:13px;font-family:var(--font)}.rv-tab.active{background:var(--bg-card);color:var(--text);border-color:var(--border-strong)}.rv-body{flex:1;display:grid;grid-template-columns:380px 1fr;min-height:0}.rv-list{border-right:1px solid var(--border);overflow-y:auto;padding:10px}.rv-detail{overflow-y:auto;padding:22px 28px}.rv-obj{border:1px solid var(--border);border-radius:10px;margin-bottom:8px;background:var(--bg-card);cursor:pointer;overflow:hidden}.rv-obj:hover{border-color:var(--border-strong)}.rv-obj.active{border-color:var(--accent);box-shadow:inset 2px 0 0 var(--accent)}.rv-obj-head{display:flex;align-items:baseline;gap:8px;padding:11px 13px}.rv-obj-name{font-weight:600;font-size:14px}.rv-obj-kind{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em}.rv-obj-fields{list-style:none;margin:0;padding:0 13px 11px 26px}.rv-obj-fields li{font-size:12.5px;color:var(--text-dim);line-height:1.7;position:relative}.rv-obj-fields li:before{content:"└";position:absolute;left:-13px;color:var(--border-strong)}.sev{font-size:10px;padding:2px 8px;border-radius:99px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;margin-left:auto;white-space:nowrap}.sev.breaking{background:#f8717129;color:var(--sev-breaking)}.sev.additive{background:#34d39924;color:var(--sev-additive)}.sev.cosmetic{background:#94a3b829;color:var(--sev-cosmetic)}.sev.renamed{background:#fcd34d29;color:var(--pk)}.rv-detail h2{margin:0 0 4px;font-size:20px}.rv-detail-kind{color:var(--text-dim);font-size:13px;margin-bottom:22px}.rv-section{margin-top:24px}.rv-section h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 9px}.rv-ba{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--border);border:1px solid var(--border);border-radius:9px;overflow:hidden}.rv-ba-col{background:var(--bg-card);padding:12px 14px}.rv-ba-label{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-dim);margin-bottom:7px}.rv-ba-text{font-size:13.5px;line-height:1.6}.rv-ba-text del{background:#f871712e;color:#fca5a5;text-decoration:none}.rv-ba-text ins{background:#34d39929;color:#6ee7b7;text-decoration:none}.rv-hint{color:var(--text-dim);font-size:11.5px;margin-top:7px;font-style:italic}.rv-scalar{display:flex;align-items:center;gap:10px;font-size:13.5px;background:var(--bg-card);border:1px solid var(--border);border-radius:9px;padding:11px 14px}.rv-scalar .from{color:var(--err)}.rv-scalar .to{color:var(--ok)}.rv-scalar .arrow{color:var(--text-dim)}.rv-chips{display:flex;flex-wrap:wrap;gap:6px}.rv-chip-add{font-size:12px;padding:3px 10px;border-radius:99px;background:#34d39924;color:var(--ok)}.rv-break{border:1px solid rgba(248,113,113,.4);background:#f871710f;border-radius:10px;padding:14px 16px}.rv-break-head{display:flex;align-items:baseline;gap:8px;color:var(--err);font-weight:700;font-size:14px;margin-bottom:4px}.rv-break-meta{color:var(--text-dim);font-size:12px;font-family:var(--mono);margin-left:auto}.rv-break-body{font-size:13.5px;line-height:1.6;margin:10px 0 12px}.rv-usage{list-style:none;margin:0;padding:0}.rv-usage li{display:flex;align-items:center;gap:9px;padding:8px 12px;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;margin-bottom:6px;font-size:13px}.rv-usage .m{font-weight:600}.rv-usage .p{color:var(--text-dim);font-family:var(--mono);font-size:11px}.rv-note{display:flex;gap:9px;align-items:flex-start;margin-top:18px;color:var(--text-dim);font-size:13px;line-height:1.6}.rv-note .i{color:var(--accent)}.rv-foot{border-top:1px solid var(--border);background:var(--bg-raised);padding:13px 24px}.rv-route{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;font-size:13px}.rv-route-badge{font-weight:700;color:var(--pk);background:#fcd34d21;padding:3px 11px;border-radius:99px;font-size:12px}.rv-route .k{color:var(--text-dim)}.rv-src{color:var(--text-dim);font-size:11px;font-style:italic}.rv-gates{margin-top:6px;font-size:12.5px;color:var(--text-dim)}.rv-gates code{font-family:var(--mono);color:var(--text);background:var(--bg-card);padding:1px 6px;border-radius:5px;margin-right:5px;font-size:11.5px}.rv-conflict{margin-top:10px;font-size:13px;display:flex;align-items:center;gap:8px}.rv-conflict.ok{color:var(--ok)}.rv-conflict.bad{color:var(--err)}.rv-conflict .detail{color:var(--text-dim)}.rv-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:12px}.pr-wrap{padding:20px 24px;max-width:940px}.pr-head{display:flex;align-items:baseline;gap:12px;margin-bottom:16px}.pr-head h2{margin:0;font-size:19px}.pr-head .meta{color:var(--text-dim);font-size:13px;margin-left:auto}.pr-card{border:1px solid var(--border);border-radius:11px;background:var(--bg-card);padding:14px 16px;margin-bottom:10px}.pr-card.attention{border-color:#f8717173}.pr-top{display:flex;align-items:baseline;gap:12px}.pr-title{font-weight:600;font-size:14.5px}.pr-chip{font-size:11px;padding:3px 10px;border-radius:99px;margin-left:auto;white-space:nowrap;font-weight:600}.pr-chip.accepted{background:#34d39924;color:var(--ok)}.pr-chip.changes{background:#5eead421;color:var(--accent)}.pr-chip.attention{background:#f8717126;color:var(--err)}.pr-chip.awaiting,.pr-chip.local{background:#94a3b824;color:var(--text-dim)}.pr-branch{font-family:var(--mono);font-size:11px;color:var(--text-dim);margin-top:5px}.pr-summary{font-size:13px;margin-top:7px}.pr-detail{font-size:12.5px;color:var(--err);margin-top:6px;line-height:1.5}.pr-foot{display:flex;align-items:center;gap:10px;margin-top:11px}.pr-foot .pr-link{font-size:12.5px;color:var(--text-dim)}.pr-foot .pr-link a{color:var(--accent)}.pr-btns{margin-left:auto;display:flex;gap:8px}.pr-degrade{margin-top:14px;font-size:12.5px;color:var(--text-dim);line-height:1.6;border-left:2px solid var(--border-strong);padding-left:12px}.sub-advice{border:1px solid rgba(252,211,77,.35);background:#fcd34d12;border-radius:9px;padding:12px 14px;margin-top:16px;font-size:13px;line-height:1.6}.sub-advice .i{color:var(--pk)}.sub-split{background:none;border:none;color:var(--pk);cursor:pointer;font-size:13px;font-weight:600;padding:8px 0 0;font-family:var(--font)}.sub-dest{margin-top:16px;font-size:13px}.sub-dest .row{display:flex;gap:10px;margin:5px 0}.sub-dest .k{color:var(--text-dim);min-width:80px}.sub-dest code{font-family:var(--mono);font-size:12px;color:var(--accent)}.sme-model-view{flex:1;display:grid;grid-template-columns:220px 1fr;min-height:0}.sme-sa-count{float:right;font-size:10px;padding:1px 6px;border-radius:99px;background:#94a3b82e;color:var(--text-dim)}.erd-wrap{flex:1;display:flex;flex-direction:column;min-height:0}.erd-bar{display:flex;align-items:center;gap:16px;padding:8px 16px;border-bottom:1px solid var(--border);font-size:12px;color:var(--text-dim)}.erd-count{font-variant-numeric:tabular-nums}.erd-toggle{display:flex;align-items:center;gap:6px;cursor:pointer}.erd-toggle input{accent-color:var(--accent)}.erd-legend{margin-left:auto;display:flex;align-items:center;gap:8px}.erd-legend .sw{display:inline-block;width:10px;height:10px;border-radius:3px;margin-left:8px}.erd-canvas{flex:1;min-height:320px}.erd-canvas .react-flow__attribution{display:none}.erd-busy{color:var(--pk);font-size:11px}.erd-error{color:var(--err);font-size:11.5px;margin-left:auto}.sme-model-view .erd-canvas{position:relative;min-height:0}.erd-action{background:#5eead41f;border:1px solid rgba(94,234,212,.35);color:var(--accent);border-radius:7px;padding:3px 10px;cursor:pointer;font-size:12px;font-family:var(--font);font-weight:600}.erd-action:hover{background:#5eead433}.erd-hint{color:var(--text-dim);font-size:11.5px;font-style:italic}.sa-editor{flex:1;display:grid;grid-template-columns:220px 1fr;min-height:0}.sa-main{overflow-y:auto;padding:18px 24px;display:flex;flex-direction:column;gap:14px}.sa-head h2{margin:0 0 4px;font-size:19px}.sa-filter{flex:none;width:100%;margin-top:10px}.sa-nudge{border:1px solid rgba(252,211,77,.35);background:#fcd34d12;border-radius:8px;padding:9px 12px;font-size:13px;color:var(--text)}.sa-panes{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;align-items:stretch}.sa-pane{border:1px solid var(--border);border-radius:10px;background:var(--bg-card);display:flex;flex-direction:column;min-height:260px;max-height:380px}.sa-pane h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0;padding:10px 12px;border-bottom:1px solid var(--border)}.sa-pane ul{list-style:none;margin:0;padding:6px;overflow-y:auto;flex:1}.sa-row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:7px;cursor:pointer;font-size:13px}.sa-row:hover{background:#94a3b814}.sa-row.picked{background:#5eead41f}.sa-row input{accent-color:var(--accent);pointer-events:none}.sa-row-name{flex:1}.sme-badge.borrowed{background:#94a3b824;color:var(--text-dim)}.sa-controls{display:flex;flex-direction:column;gap:6px;justify-content:center}.sa-controls button{min-width:44px}.sa-expand{border-top:1px solid var(--border);padding-top:14px}.sa-expand h3{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin:0 0 9px}.sa-expand-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sa-expand-row select{padding:7px 10px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);font-size:13px}.sa-expand-row label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-dim)}.sa-expand-row input[type=number]{width:56px;padding:6px 8px;border-radius:7px;border:1px solid var(--border);background:var(--bg-card);color:var(--text)}.sa-hops{margin-top:12px;display:flex;flex-direction:column;gap:4px}.sa-hop{display:flex;align-items:baseline;gap:9px;padding:7px 10px;border-radius:7px;background:var(--bg-card);border:1px solid var(--border);font-size:13px;cursor:pointer}.sa-hop input{accent-color:var(--accent)}.sa-hop-name{font-weight:600}.sa-hop-why{color:var(--text-dim);font-size:11.5px;font-family:var(--mono)}.sa-hop-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:10px}.erd-panels{display:flex;gap:6px}.erd-group{display:inline-flex;gap:6px}.erd-sep{width:1px;align-self:stretch;margin:2px;background:var(--border);flex:none}.erd-caret{opacity:.55;margin-left:1px}.erd-action.ghost{background:none;border-color:var(--border);color:var(--text-dim);font-weight:500;display:inline-flex;align-items:center;gap:5px}.erd-action.ghost:hover{color:var(--text);border-color:var(--border-strong);background:none}.erd-action.ghost.on{background:#5eead41f;border-color:#5eead459;color:var(--accent)}.sme-nav-head{display:flex;align-items:center;justify-content:space-between;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);padding:2px 10px 8px}.sme-nav-add{background:none;border:1px solid var(--border);color:var(--text-dim);width:20px;height:20px;line-height:1;border-radius:6px;cursor:pointer;font-size:14px;font-family:var(--font)}.sme-nav-add:hover{color:var(--accent);border-color:#5eead466}.sme-nav-new input{width:100%;padding:7px 10px;border-radius:8px;font-size:13px;border:1px solid var(--accent);background:var(--bg-card);color:var(--text);outline:none;font-family:var(--font)}.sme-sa{display:flex;align-items:center;gap:8px}.sme-sa>:first-child{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sme-sa .sme-sa-count{float:none;margin-left:auto;flex:none}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.sa-modal{width:min(900px,92vw);max-height:88vh}.sa-modal .sa-panes{margin-top:14px}.sa-modal .sa-pane{min-height:200px;max-height:260px}.sa-modal .sa-filter{margin-top:14px}.sa-modal .sa-expand{margin-top:16px} diff --git a/packages/server/src/mdl_server/static/mocks.html b/packages/server/src/mdl_server/static/mocks.html index 9fd9841..66810f1 100644 --- a/packages/server/src/mdl_server/static/mocks.html +++ b/packages/server/src/mdl_server/static/mocks.html @@ -4,10 +4,10 @@ Modelith — Model Git-Ops mocks - + - +
diff --git a/packages/server/src/mdl_server/static/sme.html b/packages/server/src/mdl_server/static/sme.html index 80bd876..beadf8f 100644 --- a/packages/server/src/mdl_server/static/sme.html +++ b/packages/server/src/mdl_server/static/sme.html @@ -4,11 +4,11 @@ Modelith Glossary - + - +
diff --git a/packages/server/tests/test_review_diff_contract.py b/packages/server/tests/test_review_diff_contract.py new file mode 100644 index 0000000..2f431ad --- /dev/null +++ b/packages/server/tests/test_review_diff_contract.py @@ -0,0 +1,72 @@ +"""System test: the ModelDiffDoc the review view (canvas DiffView Phase 1) consumes. + +The canvas review view filters and per-field-selects over the diff the server produces +from /api/preview. This asserts the exact wire contract that per-field selection depends +on — every changed object carries a stable ulid, and its field changes carry a `field` +key — so a frontend `(ulid, field)` selection maps to a real server change. If this shape +drifts, the canvas selection silently breaks; this test is the guard. +""" + +from __future__ import annotations + + +def _le(client, name: str) -> str: + doc = client.get("/api/model").json() + return next(e for e in doc["entities"] if e["name"] == name)["id"] + + +def test_preview_diff_has_ulid_and_field_keyed_changes(client): + """A modify preview yields a diff whose objects have ulids and whose field changes + have `field` keys — the (ulid, field) identity the review view selects over.""" + r = client.post( + "/api/preview", + json={ + "changes": [ + {"op": "rename_entity", "payload": {"id": _le(client, "trade"), "name": "deal"}} + ] + }, + ) + assert r.status_code == 200 + doc = r.json() + assert doc["ok"] + diff = doc["diff"] + assert diff["objects"], "a rename should produce at least one changed object" + + obj = diff["objects"][0] + # the identity half of a (ulid, field) selection key + assert obj["ulid"], "every changed object carries a stable ulid" + assert obj["change"] in ("added", "removed", "modified") + # the field half — each field change is independently addressable + for f in obj["fields"]: + assert "field" in f and f["field"], "each field change has a stable field key" + assert "severity" in f + assert f["severity"] in ("breaking", "additive", "cosmetic", "unmanaged") + + +def test_preview_diff_change_types_cover_the_filter_categories(client): + """The review view's filters (additions/modifications/renames/deletions) key off + `change` and `renamed`. A rename must be flagged renamed=True with change=modified, + so the 'Renames' filter isolates it — the exact field the frontend filters on.""" + r = client.post( + "/api/preview", + json={ + "changes": [ + {"op": "rename_entity", "payload": {"id": _le(client, "counterparty"), "name": "party"}} + ] + }, + ) + diff = r.json()["diff"] + renamed = [o for o in diff["objects"] if o["renamed"]] + assert renamed, "a rename must surface as a renamed object for the Renames filter" + assert all(o["change"] == "modified" for o in renamed) + + +def test_preview_diff_is_stable_for_selection(client): + """The same staged change previewed twice yields the same object ulids — so a + per-field selection the user made does not shift under them on a re-preview.""" + change = [ + {"op": "rename_entity", "payload": {"id": _le(client, "trade"), "name": "deal"}} + ] + first = client.post("/api/preview", json={"changes": change}).json()["diff"] + second = client.post("/api/preview", json={"changes": change}).json()["diff"] + assert [o["ulid"] for o in first["objects"]] == [o["ulid"] for o in second["objects"]] From 889320d881a254ecf50367f9039c3e2291c5fbbe Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sun, 27 Sep 2026 18:35:07 +1000 Subject: [PATCH 5/6] canvas: wire per-field selection through the live review screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validating Phase 1 against a real project surfaced a gap: DiffView had the per-field capability but ReviewScreen still passed the per-OBJECT callback (onToggle), so on the real SME review screen the filter toolbar rendered but the per-field checkboxes stayed dormant — no caller activated them. Wire it through: ReviewScreen now tracks excluded (ulid, field) keys and passes onToggleField; SmeApp toggles field keys in its excluded set. The selected set is derived as all field keys minus the excluded ones (default: everything included), matching the object auto-derivation the DiffView tests assert. Verified live on the ibor demo: staged a real definition edit, opened the review screen, confirmed the per-field 'DEFINITION CHANGED' checkbox renders and that unchecking it derives the object header to excluded — the exact semantics the unit + interaction tests cover. 13 canvas tests pass, typecheck clean. --- canvas/src/sme/ReviewScreen.tsx | 13 ++++++++----- canvas/src/sme/SmeApp.tsx | 8 +++++--- .../src/mdl_server/static/assets/sme-Csp0f4gO.js | 2 -- .../src/mdl_server/static/assets/sme-aoCnP9El.js | 2 ++ packages/server/src/mdl_server/static/sme.html | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) delete mode 100644 packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js create mode 100644 packages/server/src/mdl_server/static/assets/sme-aoCnP9El.js diff --git a/canvas/src/sme/ReviewScreen.tsx b/canvas/src/sme/ReviewScreen.tsx index ba69336..357c4ed 100644 --- a/canvas/src/sme/ReviewScreen.tsx +++ b/canvas/src/sme/ReviewScreen.tsx @@ -2,6 +2,7 @@ import { Suspense, lazy, useCallback, useEffect, useState } from "react"; import { fetchClassification, fetchConflicts, fetchGitContext, fetchModelDiff } from "../api"; import type { ClassificationDoc, ConflictDoc, GitContext, ModelDiffDoc } from "../types"; import { DiffView } from "./DiffView"; +import { allFieldKeys } from "./reviewModel"; const ModelDiagram = lazy(() => import("./ModelDiagram").then((m) => ({ default: m.ModelDiagram })), @@ -19,9 +20,9 @@ export function ReviewScreen({ user, onBack, onSubmit, - /** ULIDs the SME has excluded from this proposal (selective proposal) */ + /** field keys the SME has excluded from this proposal (per-field selective review) */ excluded, - onToggleObject, + onToggleField, selectable, }: { stagedDiff?: ModelDiffDoc | null; @@ -31,8 +32,9 @@ export function ReviewScreen({ user: string; onBack: () => void; onSubmit: (cl: ClassificationDoc | null) => void; + /** excluded (ulid, field) keys; a field not in the set is included */ excluded: Set; - onToggleObject: (ulid: string) => void; + onToggleField: (ulid: string, field: string) => void; selectable: boolean; }) { const [diff, setDiff] = useState(null); @@ -82,7 +84,8 @@ export function ReviewScreen({ } if (!diff) return
◮ reading your changes…
; - const selected = new Set(diff.objects.map((o) => o.ulid).filter((u) => !excluded.has(u))); + // selected = every field key EXCEPT the ones the SME unticked (default: all included) + const selected = new Set([...allFieldKeys(diff)].filter((k) => !excluded.has(k))); return (
@@ -92,7 +95,7 @@ export function ReviewScreen({ diff={diff} selectable={selectable} selected={selected} - onToggle={onToggleObject} + onToggleField={onToggleField} /> ) : ( ◮ drawing the model…
}> diff --git a/canvas/src/sme/SmeApp.tsx b/canvas/src/sme/SmeApp.tsx index eb98c46..38bec41 100644 --- a/canvas/src/sme/SmeApp.tsx +++ b/canvas/src/sme/SmeApp.tsx @@ -26,6 +26,7 @@ import { ModelWorkspace } from "./ModelWorkspace"; import { SubjectAreaEditor } from "./SubjectAreaEditor"; import { ProposalsList } from "./ProposalsList"; import { ProposeDialog } from "./ProposeDialog"; +import { fieldKey } from "./reviewModel"; import { ReviewScreen } from "./ReviewScreen"; import { TermCard } from "./TermCard"; import { TermEditor } from "./TermEditor"; @@ -360,11 +361,12 @@ export function SmeApp() { user={user} selectable={selectable} excluded={excluded} - onToggleObject={(u) => + onToggleField={(ulid, field) => setExcluded((prev) => { const next = new Set(prev); - if (next.has(u)) next.delete(u); - else next.add(u); + const key = fieldKey(ulid, field); + if (next.has(key)) next.delete(key); + else next.add(key); return next; }) } diff --git a/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js b/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js deleted file mode 100644 index 3ae4d32..0000000 --- a/packages/server/src/mdl_server/static/assets/sme-Csp0f4gO.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-C9iVTvG9.js","assets/client-DOmNpTln.js","assets/styles-Cn8AFsR1.js","assets/styles-BG76mhX4.css"])))=>i.map(i=>d[i]); -import{r as t,j as e,c as Me,R as Re}from"./client-DOmNpTln.js";import{p as je,c as xe,d as fe,e as De,h as be,I as Pe,R as Ie,M as Oe,O as Be,L as Le,i as ze,j as We,A as qe,k as He,l as Ve,m as Ge,o as Ke,s as Ue,f as ce,n as Te,q as Ye,r as Fe}from"./styles-Cn8AFsR1.js";/* empty css */const Je="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function oe(s,n){let a="";for(let l=0;l>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,n=new Uint8Array(10);crypto.getRandomValues(n);let a=0n;for(const l of n)a=a<<8n|BigInt(l);return oe(s,10)+oe(a,16)}const Qe={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Xe(s,n){const a=Qe[s];if(!a)return n;const l={...n};for(const i of a)l[i]||(l[i]=ge());return l}function ve(s,n){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${n.id??n.attribute_id}`;const a=n.id??n.entity_id??"",l=n.attribute_id??"";return l?`${s}:${a}:${l}`:`${s}:${a}`}function Ze({subjectArea:s,describe:n}){const[a,l]=t.useState([]),[i,j]=t.useState(null),[h,r]=t.useState(!1),m=t.useRef(null),N=t.useCallback(async(c,p)=>{p=Xe(c,p),c==="set_alignment"&&p.status===void 0&&(p={...p,status:"proposed"});const v=ve(c,p),{label:C,before:B,after:S}=n(c,p);return l(k=>{const A=k.filter(_=>_.key!==v);if(c.startsWith("delete_")){const _=p.id??p.entity_id;if(_&&k.some(o=>o.op.startsWith("create_")&&o.payload.id===_))return A.filter(o=>o.payload.id!==_)}return[...A,{op:c,payload:p,label:C,before:B,after:S,key:v}]}),{ok:!0,fingerprint:"",created_id:p.id??null,diagnostics:[]}},[n]);t.useEffect(()=>{if(!a.length){j(null);return}const c=setTimeout(()=>{var v;(v=m.current)==null||v.abort();const p=new AbortController;m.current=p,r(!0),je(a.map(C=>({op:C.op,payload:C.payload})),s,p.signal).then(j).catch(C=>{C.name!=="AbortError"&&j(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(c)},[a,s]);const x=t.useCallback(c=>{l(p=>p.filter(v=>v.key!==c))},[]),g=t.useCallback(()=>l([]),[]);return t.useMemo(()=>({pending:a,previewDoc:(i==null?void 0:i.model)??null,previewDiff:(i==null?void 0:i.diff)??null,diagnostics:(i==null?void 0:i.diagnostics)??[],failure:i&&i.failed_index!==null?{index:i.failed_index,error:i.error??"invalid change"}:null,busy:h,exec:N,drop:x,clear:g}),[a,i,h,N,x,g])}function es(s){const n=new Map;for(const l of s)for(const i of["id","conceptual_id","from_fk_id"]){const j=l.payload[i];l.op.startsWith("create_")&&typeof j=="string"&&n.set(j,l.key)}if(!n.size)return new Set;const a=new Set;for(const l of s)if(!l.op.startsWith("create_"))for(const i of Object.values(l.payload))typeof i=="string"&&n.has(i)&&(a.add(l.key),a.add(n.get(i)));return a}function ss({user:s}){const[n,a]=t.useState(null);return t.useEffect(()=>{xe(s).then(a).catch(()=>{})},[s]),!(n!=null&&n.ok)||!n.git?null:n.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):n.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):n.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"your proposal branch"}),n.behind?e.jsxs("span",{className:"dim",children:["· ",n.base_branch," is ",n.behind," ahead"]}):null]})}function ns({exec:s,canEdit:n,stagedMembers:a}){const[l,i]=t.useState([]),[j,h]=t.useState(""),[r,m]=t.useState(null),[N,x]=t.useState(""),[g,c]=t.useState(new Set),[p,v]=t.useState("ancestors"),[C,B]=t.useState(1),[S,k]=t.useState(null),[A,_]=t.useState(new Set),[M,o]=t.useState(null);t.useEffect(()=>{fe().then(d=>{i(d.subject_areas),h(w=>{var R;return w||((R=d.subject_areas[0])==null?void 0:R.id)||""})}).catch(d=>o(String(d)))},[]),t.useEffect(()=>{j&&(k(null),c(new Set),De(j).then(m).catch(d=>o(String(d))))},[j]);const $=t.useMemo(()=>a[j]??(r==null?void 0:r.members)??[],[a,j,r]),E=t.useMemo(()=>{const d=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])d.set(w.id,w);return d},[r]),D=N.trim().toLowerCase(),L=d=>!D||d.name.toLowerCase().includes(D),u=t.useMemo(()=>new Set($),[$]),P=t.useMemo(()=>$.map(d=>E.get(d)).filter(d=>!!d&&L(d)),[$,E,D]),W=t.useMemo(()=>[...E.values()].filter(d=>!u.has(d.id)&&L(d)),[E,u,D]),I=t.useCallback(d=>{if(!r)return;const w=d.filter(H=>!(r.members??[]).includes(H)).length,R=(r.members??[]).filter(H=>!d.includes(H)).length;s("set_subject_area_members",{id:r.id,members:d,__label:`${r.name}: ${d.length} objects (+${w} −${R})`})},[r,s]),q=(d,w)=>{const R=w?[...$,...d.filter(H=>!u.has(H))]:$.filter(H=>!d.includes(H));I(R),c(new Set)},V=(d,w,R)=>{const H=new Set(d);H.has(w)?H.delete(w):H.add(w),R(H)},Y=()=>{if(!r)return;const d=g.size?[...g]:$;if(!d.length){o("add an object first, or select one to expand from");return}o(null),be(r.id,{seeds:d,direction:p,levels:C}).then(w=>{k(w.hops),_(new Set(w.added))}).catch(w=>o(String(w)))},T=()=>{q([...A],!0),k(null)};if(M&&!r)return e.jsx("p",{className:"sme-splash error",children:M});if(!l.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const b=((r==null?void 0:r.inconsistent)??[]).filter(d=>!u.has(d));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:l.map(d=>e.jsxs("button",{className:"sme-sa"+(d.id===j?" active":""),onClick:()=>h(d.id),title:d.definition??void 0,children:[d.name,e.jsx("span",{className:"sme-sa-count",children:(a[d.id]??d.members).length})]},d.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:d=>x(d.target.value)})]}),b.length>0&&n&&e.jsxs("div",{className:"sa-nudge",children:[b.length," object",b.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>q(b,!0),children:["Add ",b.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(X,{title:`Available (${W.length})`,rows:W,picked:g,onToggle:d=>V(g,d,c),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(d=>!u.has(d)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!W.length,onClick:()=>q(W.map(d=>d.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(d=>u.has(d)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!P.length,onClick:()=>q(P.map(d=>d.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(X,{title:`Included (${P.length})`,rows:P,picked:g,onToggle:d=>V(g,d,c),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:p,onChange:d=>v(d.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:d=>B(Math.max(1,Math.min(10,Number(d.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:Y,disabled:!n,children:"Preview"})]}),M&&e.jsx("p",{className:"sme-error-line",children:M}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(d=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(d.id),onChange:()=>V(A,d.id,_)}),e.jsx("span",{className:"sa-hop-name",children:d.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",d.via_name," · ",d.direction.replace(/s$/,""),", level ",d.level]})]},d.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>k(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:T,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function X({title:s,rows:n,picked:a,onToggle:l,homedHere:i}){const j=t.useMemo(()=>new Set(i),[i]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[n.map(h=>e.jsxs("li",{className:"sa-row"+(a.has(h.id)?" picked":""),onClick:()=>l(h.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(h.id)}),e.jsx("span",{className:"sa-row-name",children:h.name}),j.has(h.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},h.id)),!n.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function as({objects:s,onCreate:n,onClose:a}){const[l,i]=t.useState(""),[j,h]=t.useState(""),[r]=t.useState(s),[m,N]=t.useState([]),[x,g]=t.useState(new Set),[c,p]=t.useState(""),[v,C]=t.useState("ancestors"),[B,S]=t.useState(1),[k,A]=t.useState(null),[_,M]=t.useState(new Set),[o,$]=t.useState(""),[E,D]=t.useState(null);t.useEffect(()=>{fe().then(b=>{b.subject_areas[0]&&$(b.subject_areas[0].id)}).catch(()=>{})},[]);const L=t.useMemo(()=>new Set(m),[m]),u=c.trim().toLowerCase(),P=b=>!u||b.name.toLowerCase().includes(u),W=t.useMemo(()=>m.map(b=>r.find(d=>d.id===b)).filter(b=>!!b&&P(b)),[m,r,u]),I=t.useMemo(()=>r.filter(b=>!L.has(b.id)&&P(b)),[r,L,u]),q=(b,d)=>{N(w=>d?[...w,...b.filter(R=>!w.includes(R))]:w.filter(R=>!b.includes(R))),g(new Set)},V=(b,d,w)=>{const R=new Set(b);R.has(d)?R.delete(d):R.add(d),w(R)},Y=()=>{const b=x.size?[...x]:m;if(!b.length){D("pick an object to expand from first");return}o&&(D(null),be(o,{seeds:b,direction:v,levels:B}).then(d=>{const w=d.hops.filter(R=>!L.has(R.id));A(w),M(new Set(w.map(R=>R.id)))}).catch(d=>D(String(d))))},T=()=>{l.trim()&&n(l.trim(),j.trim(),m)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:b=>b.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:l,placeholder:"e.g. UseCase-ApraStressTesting",onChange:b=>i(b.target.value),onKeyDown:b=>b.key==="Enter"&&m.length===0&&T()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:j,placeholder:"what this view is for",onChange:b=>h(b.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:c,onChange:b=>p(b.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(X,{title:`Available (${I.length})`,rows:I,picked:x,onToggle:b=>V(x,b,g),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(b=>!L.has(b)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!I.length,onClick:()=>q(I.map(b=>b.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(b=>L.has(b)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!W.length,onClick:()=>q(W.map(b=>b.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(X,{title:`Included (${W.length})`,rows:W,picked:x,onToggle:b=>V(x,b,g),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:v,onChange:b=>C(b.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:B,onChange:b=>S(Math.max(1,Math.min(10,Number(b.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:Y,disabled:!o,title:o?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),k&&e.jsxs("div",{className:"sa-hops",children:[k.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),k.map(b=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:_.has(b.id),onChange:()=>V(_,b.id,M)}),e.jsx("span",{className:"sa-hop-name",children:b.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",b.via_name," · ",b.direction.replace(/s$/,""),", level ",b.level]})]},b.id)),k.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!_.size,onClick:()=>{q([..._],!0),A(null)},children:["Add ",_.size]})]})]})]}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!l.trim(),onClick:T,children:["Create",m.length>0&&` with ${m.length} object${m.length===1?"":"s"}`]})]})]})})}const ts=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function rs({doc:s,subjectArea:n,onSubjectArea:a,exec:l,canEdit:i,direct:j=!1,onSelectEntity:h,onImported:r,query:m="",busy:N}){var M;const[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(!1),[B,S]=t.useState(null),k=t.useRef(null),A=t.useMemo(()=>({canEdit:i,mode:j?"direct":"staged",allow:j?void 0:ts,canArbitrate:j,canCommit:j}),[i,j]),_=t.useCallback(o=>{g(o),o&&(h==null||h(o))},[h]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),i&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(n===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((M=s.counts)==null?void 0:M.entities_total)??s.entities.length})]}),s.subject_areas.map(o=>e.jsxs("button",{className:"sme-sa"+(n===o.id?" active":""),onClick:()=>a(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:o.member_count??0})]},o.id))]}),v&&e.jsx(as,{objects:s.entities.flatMap(o=>o.conceptual?[{id:o.conceptual.id,kind:"conceptual_entity",name:o.conceptual.name,definition:o.conceptual.definition??null,synonyms:[],subject_area:o.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(o,$,E)=>{const D=ge();l("create_subject_area",{name:o,...$?{definition:$}:{},id:D}),E.length&&l("set_subject_area_members",{id:D,members:E}),C(!1),a(D)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),i&&e.jsx("button",{className:"erd-action",onClick:()=>{var o;return(o=k.current)==null?void 0:o.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=k.current)==null?void 0:o.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var o;return(o=k.current)==null?void 0:o.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(c==="ontology"?" on":""),onClick:()=>p(o=>o==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(c==="layers"?" on":""),onClick:()=>p(o=>o==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Pe,{exec:l,canEdit:i,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!i&&e.jsx("span",{className:"sme-chip",children:"read-only"}),B&&e.jsx("span",{className:"erd-error",children:B})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Ie,{children:e.jsx(Oe,{doc:s,caps:A,exec:l,selectedId:x,onSelect:_,query:m,onError:S,handleRef:k})}),c&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:c==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>p(null),children:"✕"})]}),c==="ontology"?e.jsx(Be,{}):e.jsx(Le,{refreshKey:0,onFocusEntity:o=>{var $;p(null),($=k.current)==null||$.focusEntity(o)}})]})]})]})]})}function is({user:s,onView:n}){const[a,l]=t.useState(null),[i,j]=t.useState(null);if(t.useEffect(()=>{ze(s).then(l).catch(r=>j(String(r)))},[s]),i)return e.jsx("p",{className:"sme-splash error",children:i});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const h=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[h," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(cs,{p:r,gh:a.gh,onView:()=>n(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function ls(s,n){var a,l;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?n&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:n&&((l=s.pr)==null?void 0:l.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function cs({p:s,gh:n,onView:a}){const l=ls(s,n);return e.jsxs("div",{className:"pr-card"+(l.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+l.kind,children:l.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",n&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?n&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function os({changes:s,routeAdvice:n,identity:a,user:l,onUser:i,onDrop:j,onClose:h,onProposed:r}){const m=!!(a&&a.source!=="anonymous"),[N,x]=t.useState(l||(localStorage.getItem("mdl.sme.user")??"")),[g,c]=t.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[p,v]=t.useState(""),[C,B]=t.useState(!1),S=Ne(C?p:g),[k,A]=t.useState(""),[_,M]=t.useState(!1),[o,$]=t.useState(null),[E,D]=t.useState(null),L=()=>{!m&&!N.trim()||(m||(localStorage.setItem("mdl.sme.user",N.trim()),i==null||i(N.trim())),M(!0),D(null),We({user:m?"":N.trim(),slug:S,title:g,body:k.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then($).catch(u=>D(u instanceof qe?u.message:String(u))).finally(()=>M(!1)))};return o!=null&&o.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(o),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:o.message}),o.pr_url&&e.jsx("p",{children:e.jsx("a",{href:o.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),o.compare_url&&e.jsx("p",{children:e.jsx("a",{href:o.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:o.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(o),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:h,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:h,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,P)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>j(P),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},P))}),m?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>x(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:g,onChange:u=>c(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?p:S,onChange:u=>{B(!0),v(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:k,onChange:u=>A(u.target.value)})]}),e.jsx(ds,{cl:n,user:m?a.name:N,changeSlug:S}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:h,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:_||!m&&!N.trim()||s.length===0,onClick:L,children:_?"Submitting…":"Submit for review"})]})]})})}function ds({cl:s,user:n,changeSlug:a}){if(!(s!=null&&s.primary))return null;const l=s.reviewers_actual??s.reviewers,i=Ne(n||"you"),j=s.routes.length>1;return e.jsxs(e.Fragment,{children:[j&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",l.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",i,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:l.join(", ")})]})]})]})}function Ne(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const hs="modulepreload",ms=function(s){return"/"+s},de={},us=function(n,a,l){let i=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const h=document.querySelector("meta[property=csp-nonce]"),r=(h==null?void 0:h.nonce)||(h==null?void 0:h.getAttribute("nonce"));i=Promise.allSettled(a.map(m=>{if(m=ms(m),m in de)return;de[m]=!0;const N=m.endsWith(".css"),x=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${m}"]${x}`))return;const g=document.createElement("link");if(g.rel=N?"stylesheet":hs,N||(g.as="script"),g.crossOrigin="",g.href=m,r&&g.setAttribute("nonce",r),document.head.appendChild(g),N)return new Promise((c,p)=>{g.addEventListener("load",c),g.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${m}`)))})}))}function j(h){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=h,window.dispatchEvent(r),!r.defaultPrevented)throw h}return i.then(h=>{for(const r of h||[])r.status==="rejected"&&j(r.reason);return n().catch(j)})},ye=[{id:"all",label:"All"},{id:"additions",label:"Additions"},{id:"modifications",label:"Modifications"},{id:"moves",label:"Renames"},{id:"deletions",label:"Deletions"},{id:"conflicts",label:"Conflicts"}];function we(s,n){switch(n){case"all":return!0;case"additions":return s.change==="added";case"deletions":return s.change==="removed";case"moves":return s.renamed;case"modifications":return s.change==="modified"&&!s.renamed;case"conflicts":return s.severity==="breaking"}}function ps(s,n){return n==="all"?s.objects:s.objects.filter(a=>we(a,n))}function js(s){const n={};for(const{id:a}of ye)n[a]=a==="all"?s.objects.length:s.objects.filter(l=>we(l,a)).length;return n}function Q(s,n){return`${s}\0${n}`}function xs(s){const n=s.fields.map(a=>Q(s.ulid,a.field));for(const a of s.children)for(const l of a.fields)n.push(Q(s.ulid,`${a.ulid}.${l.field}`));return n.length?n:[Q(s.ulid,"*")]}function fs(s,n){const a=xs(s),l=a.filter(i=>n.has(i)).length;return l===0?"none":l===a.length?"all":"some"}function bs({diff:s,selectable:n,selected:a,onToggle:l,onToggleField:i}){var p;const[j,h]=t.useState("all"),r=t.useMemo(()=>ps(s,j),[s,j]),m=t.useMemo(()=>js(s),[s]),[N,x]=t.useState(((p=s.objects[0])==null?void 0:p.ulid)??null),g=t.useMemo(()=>r.find(v=>v.ulid===N)??r[0]??s.objects[0]??null,[r,s,N]),c=!!i;return s.objects.length?e.jsxs("div",{className:"rv-wrap",children:[n!==void 0&&e.jsx("div",{className:"rv-filters",role:"tablist","aria-label":"Filter changes",children:ye.filter(v=>v.id==="all"||m[v.id]>0).map(v=>e.jsxs("button",{role:"tab","aria-selected":j===v.id,className:"rv-filter"+(j===v.id?" active":""),onClick:()=>h(v.id),children:[v.label,e.jsx("span",{className:"rv-filter-count",children:m[v.id]})]},v.id))}),e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:r.length===0?e.jsx("p",{className:"sme-placeholder",children:"No changes match this filter."}):r.map(v=>e.jsx(vs,{obj:v,active:v.ulid===((g==null?void 0:g.ulid)??""),selectable:n,perField:c,objState:c?fs(v,a??new Set):void 0,checkedObject:(a==null?void 0:a.has(v.ulid))??!0,onSelect:()=>x(v.ulid),onToggleObject:()=>l==null?void 0:l(v.ulid),onToggleField:i},v.ulid))}),e.jsx("div",{className:"rv-detail",children:g&&e.jsx(Ns,{obj:g,selectable:n,perField:c,selected:a,onToggleField:i})})]})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function gs(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function vs({obj:s,active:n,selectable:a,perField:l,objState:i,checkedObject:j,onSelect:h,onToggleObject:r,onToggleField:m}){const N=c=>{c&&l&&(c.indeterminate=i==="some")},x=l?i!=="none":j,g=()=>{if(!l)return r();for(const c of s.fields)m==null||m(s.ulid,c.field);for(const c of s.children)for(const p of c.fields)m==null||m(s.ulid,`${c.ulid}.${p.field}`);!s.fields.length&&!s.children.length&&(m==null||m(s.ulid,"*"))};return e.jsxs("div",{className:"rv-obj"+(n?" active":""),onClick:h,children:[e.jsxs("div",{className:"rv-obj-head",children:[a&&e.jsx("input",{type:"checkbox",ref:N,checked:x,onClick:c=>c.stopPropagation(),onChange:g,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",gs(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,p)=>e.jsx("li",{children:c.label},c.field+p)),s.children.flatMap(c=>c.fields.map((p,v)=>e.jsxs("li",{children:[p.label,": ",c.name_before??c.name_after]},c.ulid+v)))]})]})}function Ns({obj:s,selectable:n,perField:a,selected:l,onToggleField:i}){const j=s.children.flatMap(r=>r.fields.map(m=>({field:m,on:r.name_before??r.name_after??"",key:`${r.ulid}.${m.field}`}))),h=(r,m)=>{if(!n||!a)return m;const N=Q(s.ulid,r);return e.jsxs("div",{className:"rv-field-pick",children:[e.jsx("input",{type:"checkbox",checked:(l==null?void 0:l.has(N))??!0,onChange:()=>i==null?void 0:i(s.ulid,r),"aria-label":`include change: ${r}`}),e.jsx("div",{className:"rv-field-body",children:m})]})};return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((r,m)=>e.jsx("div",{children:h(r.field,e.jsx(he,{f:r}))},r.field+m)),j.map(({field:r,on:m,key:N},x)=>e.jsx("div",{children:h(N,e.jsx(he,{f:r,on:m}))},"c"+x)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function he({f:s,on:n}){var l,i,j;const a=n?`${s.label}: ${n}`:s.label;if((l=s.breaks)!=null&&l.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[n??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(h=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",h.name]}),e.jsxs("span",{className:"p",children:[h.target," · ",h.materialization]})]},h.name))})]})]});if(s.kind==="definition_changed"){const h=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:ue(h,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:ue(r,h,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const h=new Set(((i=s.before)==null?void 0:i.map(String))??[]),r=new Set(((j=s.after)==null?void 0:j.map(String))??[]),m=[...r].filter(x=>!h.has(x)),N=[...h].filter(x=>!r.has(x));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[m.map(x=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",x]},"a"+x)),N.map(x=>e.jsxs("span",{className:"rv-chip-del",children:["− ",x]},"r"+x))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:me(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:me(s.after)})]})]})}function me(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function ue(s,n,a){const l=s.split(/(\s+)/),i=n.split(/(\s+)/),j=c=>c.toLowerCase().replace(/[.,;:]/g,""),h=l.length,r=i.length,m=Array.from({length:h+1},()=>new Array(r+1).fill(0));for(let c=h-1;c>=0;c--)for(let p=r-1;p>=0;p--)m[c][p]=j(l[c])===j(i[p])?m[c+1][p+1]+1:Math.max(m[c+1][p],m[c][p+1]);const N=new Set;let x=0,g=0;for(;x=m[x][g+1]?x++:g++;return l.map((c,p)=>!c.trim()||N.has(p)?e.jsx("span",{children:c},p):a==="del"?e.jsx("del",{children:c},p):e.jsx("ins",{children:c},p))}const ys=t.lazy(()=>us(()=>import("./ModelDiagram-C9iVTvG9.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function ws({stagedDiff:s,stagedRoute:n,user:a,onBack:l,onSubmit:i,excluded:j,onToggleObject:h,selectable:r}){const[m,N]=t.useState(null),[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(null),[B,S]=t.useState(null),[k,A]=t.useState("changes"),_=t.useCallback(()=>{xe(a).then(o=>{C(o);const $=o.base_branch??"main";s?N(s):He("HEAD").then(E=>E.ok?N(E):S(E.error??"could not read the model")).catch(E=>S(String(E))),n?g(n):Ve("HEAD").then(g).catch(()=>{}),Ge($).then(p).catch(()=>{})}).catch(o=>S(String(o)))},[a,s,n]);if(t.useEffect(_,[_]),B)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:l,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:B})})]});if(!m)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const M=new Set(m.objects.map(o=>o.ulid).filter(o=>!j.has(o)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:l,diff:m,tab:k,onTab:A}),k==="changes"?e.jsx(bs,{diff:m,selectable:r,selected:M,onToggle:h}):e.jsx(t.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(ys,{severityByUlid:ks(m)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(Ss,{cl:x}),e.jsx(_s,{conf:c,ctx:v}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:l,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!m.objects.length||(v?!v.can_propose:!1),title:v&&!v.can_propose?"the working tree has uncommitted changes":"",onClick:()=>i(x),children:"Submit for review →"})]})]})]})}function ks(s){const n=new Map;for(const a of s.objects)n.set(a.ulid,a.severity);return n}function pe({onBack:s,diff:n,tab:a,onTab:l}){const i=n==null?void 0:n.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),i&&e.jsxs("div",{className:"rv-sub",children:[i.objects," object",i.objects===1?"":"s"," ·"," ",(i.breaking??0)+(i.additive??0)+(i.cosmetic??0)," changes",(n==null?void 0:n.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",i.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:n==null?void 0:n.base.label})]})]}),l&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>l("changes"),children:["Changes ",(n==null?void 0:n.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>l("diagram"),children:"Diagram"})]})]})}function Ss({cl:s}){if(!(s!=null&&s.primary))return null;const n=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:n.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function _s({conf:s,ctx:n}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(n==null?void 0:n.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function Cs({term:s,canEdit:n,pendingCount:a,onEdit:l}){var i,j;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((i=s.subject_area)==null?void 0:i.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),n&&e.jsx("button",{className:"sme-primary",onClick:l,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(h=>e.jsx("span",{className:"sme-chip",children:h},h))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((j=s.ontology)==null?void 0:j.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(h=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:h.logical_entity}),h.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),h.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:h.physical.map(r=>r.name).join(", ")})]},h.logical_id))})]})]})}function As({term:s,pending:n,onStage:a,onDone:l,catalogOwned:i=[],catalog:j=null}){var $,E,D,L;const h=u=>i.includes(u),r=u=>n.find(P=>P.label===u),m=(u,P)=>{const W=r(u);return W?W.after:P},[N,x]=t.useState(m("Definition",s.definition??"")),[g,c]=t.useState(s.synonyms),[p,v]=t.useState(""),[C,B]=t.useState((($=s.stewardship)==null?void 0:$.owner)??""),[S,k]=t.useState(((E=s.stewardship)==null?void 0:E.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},_=(u,P)=>{var W,I;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:P||null},label:"Stewardship",before:`owner: ${((W=s.stewardship)==null?void 0:W.owner)??"—"} · steward: ${((I=s.stewardship)==null?void 0:I.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${P||"—"}`})},M=u=>{c(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},o=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:l,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(h("definition")||h("synonyms")||h("stewardship"))&&j&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:j.name}),". Edit them there — this glossary mirrors them read-only.",j.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:j.url,target:"_blank",rel:"noreferrer",children:["Open in ",j.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),h("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>x(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),h("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[g.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>M(g.filter(P=>P!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:p,placeholder:"add + Enter",onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&p.trim()&&(M([...g,p.trim()]),v(""))}})]})]}),!o&&(h("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((D=s.stewardship)==null?void 0:D.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((L=s.stewardship)==null?void 0:L.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>B(u.target.value),onBlur:()=>_(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>k(u.target.value),onBlur:()=>_(C,S)})]})]})),e.jsx(Es,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:n.length>0?`${n.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const $s=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function Es({term:s,onStage:n}){var g;const[a,l]=t.useState(!1),[i,j]=t.useState(s.name),[h,r]=t.useState([]),[m,N]=t.useState("skos:closeMatch"),x=c=>{j(c),Ke(c).then(p=>r(p.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:i,onChange:c=>x(c.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:h.map(c=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var p;n({op:"set_alignment",payload:{id:s.id,aligns_to:c.prefixed,alignment:m,status:"proposed"},label:"Standard mapping",before:((p=s.ontology)==null?void 0:p.aligns_to)??"(none)",after:`${c.prefixed} (${m}, proposed)`}),l(!1)},children:[e.jsx("strong",{children:c.label})," ",e.jsx("code",{children:c.prefixed}),c.definition&&e.jsx("span",{className:"sme-align-def",children:c.definition})]},c.iri))}),e.jsx("select",{value:m,onChange:c=>N(c.target.value),children:$s.map(c=>e.jsx("option",{children:c},c))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(g=s.ontology)!=null&&g.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>l(!0),children:"Propose a mapping…"})]})]})}function Ms(){const[s,n]=t.useState(null),[a,l]=t.useState(!0),[i,j]=t.useState(null),[h,r]=t.useState(""),[m,N]=t.useState(null),[x,g]=t.useState(""),[c,p]=t.useState(""),[v,C]=t.useState(null),[B,S]=t.useState(!1),[k,A]=t.useState([]),[_,M]=t.useState(!1),[o,$]=t.useState(window.location.hash==="#proposals"?"proposals":"browse"),[E,D]=t.useState(new Set),[L,u]=t.useState(()=>localStorage.getItem("mdl.sme.user")??""),[P,W]=t.useState(null),[I,q]=t.useState(null),[V,Y]=t.useState(!1),[T,b]=t.useState(null),d=t.useCallback((f,y)=>{var te;const O=J=>{var re;const U=I==null?void 0:I.entities.find(ie=>{var le;return ie.id===J||((le=ie.conceptual)==null?void 0:le.id)===J});return((re=U==null?void 0:U.conceptual)==null?void 0:re.name)??(U==null?void 0:U.name)??"object"},z=O(y.id??y.entity_id);switch(f){case"create_entity":return{label:`New entity: ${y.name}`,before:"—",after:String(y.name??"")};case"create_relationship":return{label:`New relationship: ${O(y.from_entity)} → ${O(y.to_entity)}`,before:"—",after:String(y.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${z}`,before:z,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[y.cardinality,y.optionality,y.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${z}`,before:"",after:String(y.definition??"")};case"add_attribute":return{label:`Attribute added to ${z}`,before:"—",after:String(y.name??"")};case"delete_attribute":return{label:`Attribute removed from ${z}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${z}`,before:"",after:JSON.stringify(y)};case"rename_entity":return{label:`Renamed ${z}`,before:z,after:String(y.name??"")};case"set_subject_area":return{label:`Subject area: ${z}`,before:"",after:String(y.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${z}`,before:"",after:String(y.steward??"")};case"set_subject_area_members":{const J=y.__label,U=((te=y.members)==null?void 0:te.length)??0;return{label:J??"Subject area members",before:"",after:`${U} object${U===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${z}`,before:"",after:String(y.aligns_to??"")};default:return{label:`${f.replace(/_/g," ")}: ${z}`,before:"",after:""}}},[I]),w=Ze({subjectArea:x,describe:d}),R=t.useCallback(async(f,y)=>{const O=(I==null?void 0:I.fingerprint)??"",z=await Ue(f,y,O);return await ce(x||void 0).then(q),z},[I,x]),H=t.useMemo(()=>{const f={};for(const y of w.pending)y.op==="set_subject_area_members"&&(f[y.payload.id]=y.payload.members??[]);return f},[w.pending]),G=t.useMemo(()=>[...k,...w.pending],[k,w.pending]),[ke,Z]=t.useState(null),[Se,ee]=t.useState(null);t.useEffect(()=>{if(!G.length){Z(null),ee(null);return}const f=new AbortController,y=G.map(O=>({op:O.op,payload:O.payload}));return je(y,x||void 0,f.signal).then(O=>Z(O.diff??null)).catch(O=>{O.name!=="AbortError"&&Z(null)}),Te(y).then(ee).catch(()=>ee(null)),()=>f.abort()},[G,x]);const _e=t.useMemo(()=>es(G),[G]).size{$(f),window.location.hash=f==="browse"?"":`#${f}`},[]);t.useEffect(()=>{const f=()=>{const y=window.location.hash;$(y==="#proposals"?"proposals":y==="#review"?"review":y==="#model"?"model":y==="#areas"?"areas":"browse")};return f(),window.addEventListener("hashchange",f),()=>window.removeEventListener("hashchange",f)},[]);const se=t.useCallback(()=>{Ye({subject_area:x,q:c}).then(n).catch(f=>N(String(f)))},[x,c]);t.useEffect(se,[se]),t.useEffect(()=>{ce(x||void 0).then(f=>{l(f.read_only),r(f.project.name),q(f),Y(!!f.direct),W(f.identity??null),f.identity&&f.identity.source!=="anonymous"&&f.identity.name&&u(f.identity.name)}).catch(()=>{}),Fe().then(j).catch(()=>{})},[x]);const ne=t.useMemo(()=>(s==null?void 0:s.terms.find(f=>f.id===v))??null,[s,v]),Ce=t.useCallback(f=>{const y=f.key??ve(f.op,f.payload);A(O=>[...O.filter(z=>z.key!==y),{...f,key:y}])},[]),Ae=f=>A(y=>y.filter((O,z)=>z!==f)),$e=()=>{A([]),M(!1),S(!1),se()};if(m)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:m}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const F=!a,ae=(i==null?void 0:i.catalog_owned_fields)??[],Ee=ae.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:h}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Ee&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${i==null?void 0:i.catalog_name}`,children:["mirrors ",i==null?void 0:i.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:c,onChange:f=>p(f.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(o==="browse"?" active":""),onClick:()=>K("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(o==="model"?" active":""),onClick:()=>K("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(o==="areas"?" active":""),onClick:()=>K("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(o==="proposals"?" active":""),onClick:()=>K("proposals"),children:"My proposals"})]}),G.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>K("review"),children:[G.length," change",G.length>1?"s":""," · Review →"]})]}),o==="browse"&&e.jsx(ss,{user:L}),o==="review"?e.jsx(ws,{stagedDiff:ke,stagedRoute:Se,user:L,selectable:_e,excluded:E,onToggleObject:f=>D(y=>{const O=new Set(y);return O.has(f)?O.delete(f):O.add(f),O}),onBack:()=>K("browse"),onSubmit:f=>{b(f),M(!0)}}):o==="model"?w.previewDoc??I?e.jsx(rs,{doc:w.previewDoc??I,subjectArea:x,onSubjectArea:g,exec:V?R:w.exec,canEdit:F,direct:V,onImported:()=>K("review"),query:c,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):o==="areas"?e.jsx(ns,{exec:w.exec,canEdit:F,stagedMembers:H}):o==="proposals"?e.jsx(is,{user:L,onView:()=>K("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(x===""?" active":""),onClick:()=>g(""),children:"All subject areas"}),s.subject_areas.map(f=>e.jsx("button",{className:"sme-sa"+(x===f.id?" active":""),onClick:()=>g(f.id),title:f.definition??void 0,children:f.name},f.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(f=>{var y;return e.jsxs("li",{className:"sme-list-item"+(f.id===v?" active":""),onClick:()=>{C(f.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:f.name}),f.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((y=f.ontology)==null?void 0:y.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:f.definition??"no definition yet"})]},f.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:ne?B&&F?e.jsx(As,{term:ne,pending:k,onStage:Ce,onDone:()=>S(!1),catalogOwned:ae,catalog:i?{name:i.catalog_name,url:i.catalog_url}:null}):e.jsx(Cs,{term:ne,canEdit:F,pendingCount:k.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),_&&e.jsx(os,{changes:G,routeAdvice:T,identity:P,user:L,onUser:f=>{u(f),localStorage.setItem("mdl.sme.user",f)},onDrop:Ae,onClose:()=>M(!1),onProposed:()=>{$e(),w.clear(),D(new Set),K("proposals")}})]})}Me.createRoot(document.getElementById("root")).render(e.jsx(Re.StrictMode,{children:e.jsx(Ms,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-aoCnP9El.js b/packages/server/src/mdl_server/static/assets/sme-aoCnP9El.js new file mode 100644 index 0000000..0e47713 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/sme-aoCnP9El.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-C9iVTvG9.js","assets/client-DOmNpTln.js","assets/styles-Cn8AFsR1.js","assets/styles-BG76mhX4.css"])))=>i.map(i=>d[i]); +import{r as t,j as e,c as Re,R as De}from"./client-DOmNpTln.js";import{p as je,c as xe,d as fe,e as Pe,h as be,I as Ie,R as Oe,M as Be,O as Le,L as ze,i as We,j as qe,A as He,k as Ke,l as Ve,m as Ge,o as Ue,s as Fe,f as ce,n as Ye,q as Te,r as Je}from"./styles-Cn8AFsR1.js";/* empty css */const Qe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function oe(s,n){let a="";for(let i=0;i>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,n=new Uint8Array(10);crypto.getRandomValues(n);let a=0n;for(const i of n)a=a<<8n|BigInt(i);return oe(s,10)+oe(a,16)}const Xe={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Ze(s,n){const a=Xe[s];if(!a)return n;const i={...n};for(const l of a)i[l]||(i[l]=ge());return i}function ve(s,n){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${n.id??n.attribute_id}`;const a=n.id??n.entity_id??"",i=n.attribute_id??"";return i?`${s}:${a}:${i}`:`${s}:${a}`}function es({subjectArea:s,describe:n}){const[a,i]=t.useState([]),[l,j]=t.useState(null),[d,r]=t.useState(!1),h=t.useRef(null),N=t.useCallback(async(c,p)=>{p=Ze(c,p),c==="set_alignment"&&p.status===void 0&&(p={...p,status:"proposed"});const v=ve(c,p),{label:C,before:B,after:S}=n(c,p);return i(k=>{const A=k.filter(_=>_.key!==v);if(c.startsWith("delete_")){const _=p.id??p.entity_id;if(_&&k.some(m=>m.op.startsWith("create_")&&m.payload.id===_))return A.filter(m=>m.payload.id!==_)}return[...A,{op:c,payload:p,label:C,before:B,after:S,key:v}]}),{ok:!0,fingerprint:"",created_id:p.id??null,diagnostics:[]}},[n]);t.useEffect(()=>{if(!a.length){j(null);return}const c=setTimeout(()=>{var v;(v=h.current)==null||v.abort();const p=new AbortController;h.current=p,r(!0),je(a.map(C=>({op:C.op,payload:C.payload})),s,p.signal).then(j).catch(C=>{C.name!=="AbortError"&&j(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(c)},[a,s]);const x=t.useCallback(c=>{i(p=>p.filter(v=>v.key!==c))},[]),g=t.useCallback(()=>i([]),[]);return t.useMemo(()=>({pending:a,previewDoc:(l==null?void 0:l.model)??null,previewDiff:(l==null?void 0:l.diff)??null,diagnostics:(l==null?void 0:l.diagnostics)??[],failure:l&&l.failed_index!==null?{index:l.failed_index,error:l.error??"invalid change"}:null,busy:d,exec:N,drop:x,clear:g}),[a,l,d,N,x,g])}function ss(s){const n=new Map;for(const i of s)for(const l of["id","conceptual_id","from_fk_id"]){const j=i.payload[l];i.op.startsWith("create_")&&typeof j=="string"&&n.set(j,i.key)}if(!n.size)return new Set;const a=new Set;for(const i of s)if(!i.op.startsWith("create_"))for(const l of Object.values(i.payload))typeof l=="string"&&n.has(l)&&(a.add(i.key),a.add(n.get(l)));return a}function ns({user:s}){const[n,a]=t.useState(null);return t.useEffect(()=>{xe(s).then(a).catch(()=>{})},[s]),!(n!=null&&n.ok)||!n.git?null:n.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):n.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):n.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"your proposal branch"}),n.behind?e.jsxs("span",{className:"dim",children:["· ",n.base_branch," is ",n.behind," ahead"]}):null]})}function as({exec:s,canEdit:n,stagedMembers:a}){const[i,l]=t.useState([]),[j,d]=t.useState(""),[r,h]=t.useState(null),[N,x]=t.useState(""),[g,c]=t.useState(new Set),[p,v]=t.useState("ancestors"),[C,B]=t.useState(1),[S,k]=t.useState(null),[A,_]=t.useState(new Set),[M,m]=t.useState(null);t.useEffect(()=>{fe().then(o=>{l(o.subject_areas),d(w=>{var R;return w||((R=o.subject_areas[0])==null?void 0:R.id)||""})}).catch(o=>m(String(o)))},[]),t.useEffect(()=>{j&&(k(null),c(new Set),Pe(j).then(h).catch(o=>m(String(o))))},[j]);const $=t.useMemo(()=>a[j]??(r==null?void 0:r.members)??[],[a,j,r]),E=t.useMemo(()=>{const o=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])o.set(w.id,w);return o},[r]),P=N.trim().toLowerCase(),L=o=>!P||o.name.toLowerCase().includes(P),u=t.useMemo(()=>new Set($),[$]),I=t.useMemo(()=>$.map(o=>E.get(o)).filter(o=>!!o&&L(o)),[$,E,P]),z=t.useMemo(()=>[...E.values()].filter(o=>!u.has(o.id)&&L(o)),[E,u,P]),O=t.useCallback(o=>{if(!r)return;const w=o.filter(H=>!(r.members??[]).includes(H)).length,R=(r.members??[]).filter(H=>!o.includes(H)).length;s("set_subject_area_members",{id:r.id,members:o,__label:`${r.name}: ${o.length} objects (+${w} −${R})`})},[r,s]),q=(o,w)=>{const R=w?[...$,...o.filter(H=>!u.has(H))]:$.filter(H=>!o.includes(H));O(R),c(new Set)},K=(o,w,R)=>{const H=new Set(o);H.has(w)?H.delete(w):H.add(w),R(H)},T=()=>{if(!r)return;const o=g.size?[...g]:$;if(!o.length){m("add an object first, or select one to expand from");return}m(null),be(r.id,{seeds:o,direction:p,levels:C}).then(w=>{k(w.hops),_(new Set(w.added))}).catch(w=>m(String(w)))},F=()=>{q([...A],!0),k(null)};if(M&&!r)return e.jsx("p",{className:"sme-splash error",children:M});if(!i.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const f=((r==null?void 0:r.inconsistent)??[]).filter(o=>!u.has(o));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:i.map(o=>e.jsxs("button",{className:"sme-sa"+(o.id===j?" active":""),onClick:()=>d(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:(a[o.id]??o.members).length})]},o.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:o=>x(o.target.value)})]}),f.length>0&&n&&e.jsxs("div",{className:"sa-nudge",children:[f.length," object",f.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>q(f,!0),children:["Add ",f.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Z,{title:`Available (${z.length})`,rows:z,picked:g,onToggle:o=>K(g,o,c),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(o=>!u.has(o)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!z.length,onClick:()=>q(z.map(o=>o.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(o=>u.has(o)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!I.length,onClick:()=>q(I.map(o=>o.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Z,{title:`Included (${I.length})`,rows:I,picked:g,onToggle:o=>K(g,o,c),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:p,onChange:o=>v(o.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:o=>B(Math.max(1,Math.min(10,Number(o.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!n,children:"Preview"})]}),M&&e.jsx("p",{className:"sme-error-line",children:M}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(o=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(o.id),onChange:()=>K(A,o.id,_)}),e.jsx("span",{className:"sa-hop-name",children:o.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",o.via_name," · ",o.direction.replace(/s$/,""),", level ",o.level]})]},o.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>k(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:F,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function Z({title:s,rows:n,picked:a,onToggle:i,homedHere:l}){const j=t.useMemo(()=>new Set(l),[l]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[n.map(d=>e.jsxs("li",{className:"sa-row"+(a.has(d.id)?" picked":""),onClick:()=>i(d.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(d.id)}),e.jsx("span",{className:"sa-row-name",children:d.name}),j.has(d.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},d.id)),!n.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function ts({objects:s,onCreate:n,onClose:a}){const[i,l]=t.useState(""),[j,d]=t.useState(""),[r]=t.useState(s),[h,N]=t.useState([]),[x,g]=t.useState(new Set),[c,p]=t.useState(""),[v,C]=t.useState("ancestors"),[B,S]=t.useState(1),[k,A]=t.useState(null),[_,M]=t.useState(new Set),[m,$]=t.useState(""),[E,P]=t.useState(null);t.useEffect(()=>{fe().then(f=>{f.subject_areas[0]&&$(f.subject_areas[0].id)}).catch(()=>{})},[]);const L=t.useMemo(()=>new Set(h),[h]),u=c.trim().toLowerCase(),I=f=>!u||f.name.toLowerCase().includes(u),z=t.useMemo(()=>h.map(f=>r.find(o=>o.id===f)).filter(f=>!!f&&I(f)),[h,r,u]),O=t.useMemo(()=>r.filter(f=>!L.has(f.id)&&I(f)),[r,L,u]),q=(f,o)=>{N(w=>o?[...w,...f.filter(R=>!w.includes(R))]:w.filter(R=>!f.includes(R))),g(new Set)},K=(f,o,w)=>{const R=new Set(f);R.has(o)?R.delete(o):R.add(o),w(R)},T=()=>{const f=x.size?[...x]:h;if(!f.length){P("pick an object to expand from first");return}m&&(P(null),be(m,{seeds:f,direction:v,levels:B}).then(o=>{const w=o.hops.filter(R=>!L.has(R.id));A(w),M(new Set(w.map(R=>R.id)))}).catch(o=>P(String(o))))},F=()=>{i.trim()&&n(i.trim(),j.trim(),h)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:f=>f.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:i,placeholder:"e.g. UseCase-ApraStressTesting",onChange:f=>l(f.target.value),onKeyDown:f=>f.key==="Enter"&&h.length===0&&F()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:j,placeholder:"what this view is for",onChange:f=>d(f.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:c,onChange:f=>p(f.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Z,{title:`Available (${O.length})`,rows:O,picked:x,onToggle:f=>K(x,f,g),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(f=>!L.has(f)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!O.length,onClick:()=>q(O.map(f=>f.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(f=>L.has(f)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!z.length,onClick:()=>q(z.map(f=>f.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Z,{title:`Included (${z.length})`,rows:z,picked:x,onToggle:f=>K(x,f,g),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:v,onChange:f=>C(f.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:B,onChange:f=>S(Math.max(1,Math.min(10,Number(f.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!m,title:m?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),k&&e.jsxs("div",{className:"sa-hops",children:[k.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),k.map(f=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:_.has(f.id),onChange:()=>K(_,f.id,M)}),e.jsx("span",{className:"sa-hop-name",children:f.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",f.via_name," · ",f.direction.replace(/s$/,""),", level ",f.level]})]},f.id)),k.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!_.size,onClick:()=>{q([..._],!0),A(null)},children:["Add ",_.size]})]})]})]}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!i.trim(),onClick:F,children:["Create",h.length>0&&` with ${h.length} object${h.length===1?"":"s"}`]})]})]})})}const rs=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function is({doc:s,subjectArea:n,onSubjectArea:a,exec:i,canEdit:l,direct:j=!1,onSelectEntity:d,onImported:r,query:h="",busy:N}){var M;const[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(!1),[B,S]=t.useState(null),k=t.useRef(null),A=t.useMemo(()=>({canEdit:l,mode:j?"direct":"staged",allow:j?void 0:rs,canArbitrate:j,canCommit:j}),[l,j]),_=t.useCallback(m=>{g(m),m&&(d==null||d(m))},[d]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),l&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(n===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((M=s.counts)==null?void 0:M.entities_total)??s.entities.length})]}),s.subject_areas.map(m=>e.jsxs("button",{className:"sme-sa"+(n===m.id?" active":""),onClick:()=>a(m.id),title:m.definition??void 0,children:[m.name,e.jsx("span",{className:"sme-sa-count",children:m.member_count??0})]},m.id))]}),v&&e.jsx(ts,{objects:s.entities.flatMap(m=>m.conceptual?[{id:m.conceptual.id,kind:"conceptual_entity",name:m.conceptual.name,definition:m.conceptual.definition??null,synonyms:[],subject_area:m.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(m,$,E)=>{const P=ge();i("create_subject_area",{name:m,...$?{definition:$}:{},id:P}),E.length&&i("set_subject_area_members",{id:P,members:E}),C(!1),a(P)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),l&&e.jsx("button",{className:"erd-action",onClick:()=>{var m;return(m=k.current)==null?void 0:m.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var m;return(m=k.current)==null?void 0:m.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var m;return(m=k.current)==null?void 0:m.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(c==="ontology"?" on":""),onClick:()=>p(m=>m==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(c==="layers"?" on":""),onClick:()=>p(m=>m==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Ie,{exec:i,canEdit:l,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!l&&e.jsx("span",{className:"sme-chip",children:"read-only"}),B&&e.jsx("span",{className:"erd-error",children:B})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Oe,{children:e.jsx(Be,{doc:s,caps:A,exec:i,selectedId:x,onSelect:_,query:h,onError:S,handleRef:k})}),c&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:c==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>p(null),children:"✕"})]}),c==="ontology"?e.jsx(Le,{}):e.jsx(ze,{refreshKey:0,onFocusEntity:m=>{var $;p(null),($=k.current)==null||$.focusEntity(m)}})]})]})]})]})}function ls({user:s,onView:n}){const[a,i]=t.useState(null),[l,j]=t.useState(null);if(t.useEffect(()=>{We(s).then(i).catch(r=>j(String(r)))},[s]),l)return e.jsx("p",{className:"sme-splash error",children:l});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const d=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[d," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(os,{p:r,gh:a.gh,onView:()=>n(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function cs(s,n){var a,i;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?n&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:n&&((i=s.pr)==null?void 0:i.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function os({p:s,gh:n,onView:a}){const i=cs(s,n);return e.jsxs("div",{className:"pr-card"+(i.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+i.kind,children:i.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",n&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?n&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function ds({changes:s,routeAdvice:n,identity:a,user:i,onUser:l,onDrop:j,onClose:d,onProposed:r}){const h=!!(a&&a.source!=="anonymous"),[N,x]=t.useState(i||(localStorage.getItem("mdl.sme.user")??"")),[g,c]=t.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[p,v]=t.useState(""),[C,B]=t.useState(!1),S=Ne(C?p:g),[k,A]=t.useState(""),[_,M]=t.useState(!1),[m,$]=t.useState(null),[E,P]=t.useState(null),L=()=>{!h&&!N.trim()||(h||(localStorage.setItem("mdl.sme.user",N.trim()),l==null||l(N.trim())),M(!0),P(null),qe({user:h?"":N.trim(),slug:S,title:g,body:k.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then($).catch(u=>P(u instanceof He?u.message:String(u))).finally(()=>M(!1)))};return m!=null&&m.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(m),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:m.message}),m.pr_url&&e.jsx("p",{children:e.jsx("a",{href:m.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),m.compare_url&&e.jsx("p",{children:e.jsx("a",{href:m.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:m.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(m),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:d,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:d,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,I)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>j(I),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},I))}),h?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>x(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:g,onChange:u=>c(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?p:S,onChange:u=>{B(!0),v(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:k,onChange:u=>A(u.target.value)})]}),e.jsx(hs,{cl:n,user:h?a.name:N,changeSlug:S}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:d,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:_||!h&&!N.trim()||s.length===0,onClick:L,children:_?"Submitting…":"Submit for review"})]})]})})}function hs({cl:s,user:n,changeSlug:a}){if(!(s!=null&&s.primary))return null;const i=s.reviewers_actual??s.reviewers,l=Ne(n||"you"),j=s.routes.length>1;return e.jsxs(e.Fragment,{children:[j&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",i.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",l,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:i.join(", ")})]})]})]})}function Ne(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const ye=[{id:"all",label:"All"},{id:"additions",label:"Additions"},{id:"modifications",label:"Modifications"},{id:"moves",label:"Renames"},{id:"deletions",label:"Deletions"},{id:"conflicts",label:"Conflicts"}];function we(s,n){switch(n){case"all":return!0;case"additions":return s.change==="added";case"deletions":return s.change==="removed";case"moves":return s.renamed;case"modifications":return s.change==="modified"&&!s.renamed;case"conflicts":return s.severity==="breaking"}}function ms(s,n){return n==="all"?s.objects:s.objects.filter(a=>we(a,n))}function us(s){const n={};for(const{id:a}of ye)n[a]=a==="all"?s.objects.length:s.objects.filter(i=>we(i,a)).length;return n}function J(s,n){return`${s}\0${n}`}function ke(s){const n=s.fields.map(a=>J(s.ulid,a.field));for(const a of s.children)for(const i of a.fields)n.push(J(s.ulid,`${a.ulid}.${i.field}`));return n.length?n:[J(s.ulid,"*")]}function ps(s,n){const a=ke(s),i=a.filter(l=>n.has(l)).length;return i===0?"none":i===a.length?"all":"some"}function js(s){const n=new Set;for(const a of s.objects)for(const i of ke(a))n.add(i);return n}const xs="modulepreload",fs=function(s){return"/"+s},de={},bs=function(n,a,i){let l=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const d=document.querySelector("meta[property=csp-nonce]"),r=(d==null?void 0:d.nonce)||(d==null?void 0:d.getAttribute("nonce"));l=Promise.allSettled(a.map(h=>{if(h=fs(h),h in de)return;de[h]=!0;const N=h.endsWith(".css"),x=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${h}"]${x}`))return;const g=document.createElement("link");if(g.rel=N?"stylesheet":xs,N||(g.as="script"),g.crossOrigin="",g.href=h,r&&g.setAttribute("nonce",r),document.head.appendChild(g),N)return new Promise((c,p)=>{g.addEventListener("load",c),g.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${h}`)))})}))}function j(d){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=d,window.dispatchEvent(r),!r.defaultPrevented)throw d}return l.then(d=>{for(const r of d||[])r.status==="rejected"&&j(r.reason);return n().catch(j)})};function gs({diff:s,selectable:n,selected:a,onToggle:i,onToggleField:l}){var p;const[j,d]=t.useState("all"),r=t.useMemo(()=>ms(s,j),[s,j]),h=t.useMemo(()=>us(s),[s]),[N,x]=t.useState(((p=s.objects[0])==null?void 0:p.ulid)??null),g=t.useMemo(()=>r.find(v=>v.ulid===N)??r[0]??s.objects[0]??null,[r,s,N]),c=!!l;return s.objects.length?e.jsxs("div",{className:"rv-wrap",children:[n!==void 0&&e.jsx("div",{className:"rv-filters",role:"tablist","aria-label":"Filter changes",children:ye.filter(v=>v.id==="all"||h[v.id]>0).map(v=>e.jsxs("button",{role:"tab","aria-selected":j===v.id,className:"rv-filter"+(j===v.id?" active":""),onClick:()=>d(v.id),children:[v.label,e.jsx("span",{className:"rv-filter-count",children:h[v.id]})]},v.id))}),e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:r.length===0?e.jsx("p",{className:"sme-placeholder",children:"No changes match this filter."}):r.map(v=>e.jsx(Ns,{obj:v,active:v.ulid===((g==null?void 0:g.ulid)??""),selectable:n,perField:c,objState:c?ps(v,a??new Set):void 0,checkedObject:(a==null?void 0:a.has(v.ulid))??!0,onSelect:()=>x(v.ulid),onToggleObject:()=>i==null?void 0:i(v.ulid),onToggleField:l},v.ulid))}),e.jsx("div",{className:"rv-detail",children:g&&e.jsx(ys,{obj:g,selectable:n,perField:c,selected:a,onToggleField:l})})]})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function vs(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function Ns({obj:s,active:n,selectable:a,perField:i,objState:l,checkedObject:j,onSelect:d,onToggleObject:r,onToggleField:h}){const N=c=>{c&&i&&(c.indeterminate=l==="some")},x=i?l!=="none":j,g=()=>{if(!i)return r();for(const c of s.fields)h==null||h(s.ulid,c.field);for(const c of s.children)for(const p of c.fields)h==null||h(s.ulid,`${c.ulid}.${p.field}`);!s.fields.length&&!s.children.length&&(h==null||h(s.ulid,"*"))};return e.jsxs("div",{className:"rv-obj"+(n?" active":""),onClick:d,children:[e.jsxs("div",{className:"rv-obj-head",children:[a&&e.jsx("input",{type:"checkbox",ref:N,checked:x,onClick:c=>c.stopPropagation(),onChange:g,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",vs(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,p)=>e.jsx("li",{children:c.label},c.field+p)),s.children.flatMap(c=>c.fields.map((p,v)=>e.jsxs("li",{children:[p.label,": ",c.name_before??c.name_after]},c.ulid+v)))]})]})}function ys({obj:s,selectable:n,perField:a,selected:i,onToggleField:l}){const j=s.children.flatMap(r=>r.fields.map(h=>({field:h,on:r.name_before??r.name_after??"",key:`${r.ulid}.${h.field}`}))),d=(r,h)=>{if(!n||!a)return h;const N=J(s.ulid,r);return e.jsxs("div",{className:"rv-field-pick",children:[e.jsx("input",{type:"checkbox",checked:(i==null?void 0:i.has(N))??!0,onChange:()=>l==null?void 0:l(s.ulid,r),"aria-label":`include change: ${r}`}),e.jsx("div",{className:"rv-field-body",children:h})]})};return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((r,h)=>e.jsx("div",{children:d(r.field,e.jsx(he,{f:r}))},r.field+h)),j.map(({field:r,on:h,key:N},x)=>e.jsx("div",{children:d(N,e.jsx(he,{f:r,on:h}))},"c"+x)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function he({f:s,on:n}){var i,l,j;const a=n?`${s.label}: ${n}`:s.label;if((i=s.breaks)!=null&&i.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[n??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(d=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",d.name]}),e.jsxs("span",{className:"p",children:[d.target," · ",d.materialization]})]},d.name))})]})]});if(s.kind==="definition_changed"){const d=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:ue(d,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:ue(r,d,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const d=new Set(((l=s.before)==null?void 0:l.map(String))??[]),r=new Set(((j=s.after)==null?void 0:j.map(String))??[]),h=[...r].filter(x=>!d.has(x)),N=[...d].filter(x=>!r.has(x));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[h.map(x=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",x]},"a"+x)),N.map(x=>e.jsxs("span",{className:"rv-chip-del",children:["− ",x]},"r"+x))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:me(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:me(s.after)})]})]})}function me(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function ue(s,n,a){const i=s.split(/(\s+)/),l=n.split(/(\s+)/),j=c=>c.toLowerCase().replace(/[.,;:]/g,""),d=i.length,r=l.length,h=Array.from({length:d+1},()=>new Array(r+1).fill(0));for(let c=d-1;c>=0;c--)for(let p=r-1;p>=0;p--)h[c][p]=j(i[c])===j(l[p])?h[c+1][p+1]+1:Math.max(h[c+1][p],h[c][p+1]);const N=new Set;let x=0,g=0;for(;x=h[x][g+1]?x++:g++;return i.map((c,p)=>!c.trim()||N.has(p)?e.jsx("span",{children:c},p):a==="del"?e.jsx("del",{children:c},p):e.jsx("ins",{children:c},p))}const ws=t.lazy(()=>bs(()=>import("./ModelDiagram-C9iVTvG9.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function ks({stagedDiff:s,stagedRoute:n,user:a,onBack:i,onSubmit:l,excluded:j,onToggleField:d,selectable:r}){const[h,N]=t.useState(null),[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(null),[B,S]=t.useState(null),[k,A]=t.useState("changes"),_=t.useCallback(()=>{xe(a).then(m=>{C(m);const $=m.base_branch??"main";s?N(s):Ke("HEAD").then(E=>E.ok?N(E):S(E.error??"could not read the model")).catch(E=>S(String(E))),n?g(n):Ve("HEAD").then(g).catch(()=>{}),Ge($).then(p).catch(()=>{})}).catch(m=>S(String(m)))},[a,s,n]);if(t.useEffect(_,[_]),B)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:i,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:B})})]});if(!h)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const M=new Set([...js(h)].filter(m=>!j.has(m)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:i,diff:h,tab:k,onTab:A}),k==="changes"?e.jsx(gs,{diff:h,selectable:r,selected:M,onToggleField:d}):e.jsx(t.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(ws,{severityByUlid:Ss(h)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(_s,{cl:x}),e.jsx(Cs,{conf:c,ctx:v}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:i,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!h.objects.length||(v?!v.can_propose:!1),title:v&&!v.can_propose?"the working tree has uncommitted changes":"",onClick:()=>l(x),children:"Submit for review →"})]})]})]})}function Ss(s){const n=new Map;for(const a of s.objects)n.set(a.ulid,a.severity);return n}function pe({onBack:s,diff:n,tab:a,onTab:i}){const l=n==null?void 0:n.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),l&&e.jsxs("div",{className:"rv-sub",children:[l.objects," object",l.objects===1?"":"s"," ·"," ",(l.breaking??0)+(l.additive??0)+(l.cosmetic??0)," changes",(n==null?void 0:n.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",l.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:n==null?void 0:n.base.label})]})]}),i&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>i("changes"),children:["Changes ",(n==null?void 0:n.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>i("diagram"),children:"Diagram"})]})]})}function _s({cl:s}){if(!(s!=null&&s.primary))return null;const n=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:n.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function Cs({conf:s,ctx:n}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(n==null?void 0:n.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function As({term:s,canEdit:n,pendingCount:a,onEdit:i}){var l,j;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((l=s.subject_area)==null?void 0:l.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),n&&e.jsx("button",{className:"sme-primary",onClick:i,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(d=>e.jsx("span",{className:"sme-chip",children:d},d))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((j=s.ontology)==null?void 0:j.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(d=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:d.logical_entity}),d.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),d.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:d.physical.map(r=>r.name).join(", ")})]},d.logical_id))})]})]})}function $s({term:s,pending:n,onStage:a,onDone:i,catalogOwned:l=[],catalog:j=null}){var $,E,P,L;const d=u=>l.includes(u),r=u=>n.find(I=>I.label===u),h=(u,I)=>{const z=r(u);return z?z.after:I},[N,x]=t.useState(h("Definition",s.definition??"")),[g,c]=t.useState(s.synonyms),[p,v]=t.useState(""),[C,B]=t.useState((($=s.stewardship)==null?void 0:$.owner)??""),[S,k]=t.useState(((E=s.stewardship)==null?void 0:E.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},_=(u,I)=>{var z,O;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:I||null},label:"Stewardship",before:`owner: ${((z=s.stewardship)==null?void 0:z.owner)??"—"} · steward: ${((O=s.stewardship)==null?void 0:O.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${I||"—"}`})},M=u=>{c(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},m=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:i,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(d("definition")||d("synonyms")||d("stewardship"))&&j&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:j.name}),". Edit them there — this glossary mirrors them read-only.",j.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:j.url,target:"_blank",rel:"noreferrer",children:["Open in ",j.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),d("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>x(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),d("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[g.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>M(g.filter(I=>I!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:p,placeholder:"add + Enter",onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&p.trim()&&(M([...g,p.trim()]),v(""))}})]})]}),!m&&(d("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((P=s.stewardship)==null?void 0:P.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((L=s.stewardship)==null?void 0:L.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>B(u.target.value),onBlur:()=>_(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>k(u.target.value),onBlur:()=>_(C,S)})]})]})),e.jsx(Ms,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:n.length>0?`${n.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const Es=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function Ms({term:s,onStage:n}){var g;const[a,i]=t.useState(!1),[l,j]=t.useState(s.name),[d,r]=t.useState([]),[h,N]=t.useState("skos:closeMatch"),x=c=>{j(c),Ue(c).then(p=>r(p.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:l,onChange:c=>x(c.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:d.map(c=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var p;n({op:"set_alignment",payload:{id:s.id,aligns_to:c.prefixed,alignment:h,status:"proposed"},label:"Standard mapping",before:((p=s.ontology)==null?void 0:p.aligns_to)??"(none)",after:`${c.prefixed} (${h}, proposed)`}),i(!1)},children:[e.jsx("strong",{children:c.label})," ",e.jsx("code",{children:c.prefixed}),c.definition&&e.jsx("span",{className:"sme-align-def",children:c.definition})]},c.iri))}),e.jsx("select",{value:h,onChange:c=>N(c.target.value),children:Es.map(c=>e.jsx("option",{children:c},c))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(g=s.ontology)!=null&&g.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>i(!0),children:"Propose a mapping…"})]})]})}function Rs(){const[s,n]=t.useState(null),[a,i]=t.useState(!0),[l,j]=t.useState(null),[d,r]=t.useState(""),[h,N]=t.useState(null),[x,g]=t.useState(""),[c,p]=t.useState(""),[v,C]=t.useState(null),[B,S]=t.useState(!1),[k,A]=t.useState([]),[_,M]=t.useState(!1),[m,$]=t.useState(window.location.hash==="#proposals"?"proposals":"browse"),[E,P]=t.useState(new Set),[L,u]=t.useState(()=>localStorage.getItem("mdl.sme.user")??""),[I,z]=t.useState(null),[O,q]=t.useState(null),[K,T]=t.useState(!1),[F,f]=t.useState(null),o=t.useCallback((b,y)=>{var Y;const W=X=>{var re;const U=O==null?void 0:O.entities.find(ie=>{var le;return ie.id===X||((le=ie.conceptual)==null?void 0:le.id)===X});return((re=U==null?void 0:U.conceptual)==null?void 0:re.name)??(U==null?void 0:U.name)??"object"},D=W(y.id??y.entity_id);switch(b){case"create_entity":return{label:`New entity: ${y.name}`,before:"—",after:String(y.name??"")};case"create_relationship":return{label:`New relationship: ${W(y.from_entity)} → ${W(y.to_entity)}`,before:"—",after:String(y.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${D}`,before:D,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[y.cardinality,y.optionality,y.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${D}`,before:"",after:String(y.definition??"")};case"add_attribute":return{label:`Attribute added to ${D}`,before:"—",after:String(y.name??"")};case"delete_attribute":return{label:`Attribute removed from ${D}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${D}`,before:"",after:JSON.stringify(y)};case"rename_entity":return{label:`Renamed ${D}`,before:D,after:String(y.name??"")};case"set_subject_area":return{label:`Subject area: ${D}`,before:"",after:String(y.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${D}`,before:"",after:String(y.steward??"")};case"set_subject_area_members":{const X=y.__label,U=((Y=y.members)==null?void 0:Y.length)??0;return{label:X??"Subject area members",before:"",after:`${U} object${U===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${D}`,before:"",after:String(y.aligns_to??"")};default:return{label:`${b.replace(/_/g," ")}: ${D}`,before:"",after:""}}},[O]),w=es({subjectArea:x,describe:o}),R=t.useCallback(async(b,y)=>{const W=(O==null?void 0:O.fingerprint)??"",D=await Fe(b,y,W);return await ce(x||void 0).then(q),D},[O,x]),H=t.useMemo(()=>{const b={};for(const y of w.pending)y.op==="set_subject_area_members"&&(b[y.payload.id]=y.payload.members??[]);return b},[w.pending]),V=t.useMemo(()=>[...k,...w.pending],[k,w.pending]),[Se,ee]=t.useState(null),[_e,se]=t.useState(null);t.useEffect(()=>{if(!V.length){ee(null),se(null);return}const b=new AbortController,y=V.map(W=>({op:W.op,payload:W.payload}));return je(y,x||void 0,b.signal).then(W=>ee(W.diff??null)).catch(W=>{W.name!=="AbortError"&&ee(null)}),Ye(y).then(se).catch(()=>se(null)),()=>b.abort()},[V,x]);const Ce=t.useMemo(()=>ss(V),[V]).size{$(b),window.location.hash=b==="browse"?"":`#${b}`},[]);t.useEffect(()=>{const b=()=>{const y=window.location.hash;$(y==="#proposals"?"proposals":y==="#review"?"review":y==="#model"?"model":y==="#areas"?"areas":"browse")};return b(),window.addEventListener("hashchange",b),()=>window.removeEventListener("hashchange",b)},[]);const ne=t.useCallback(()=>{Te({subject_area:x,q:c}).then(n).catch(b=>N(String(b)))},[x,c]);t.useEffect(ne,[ne]),t.useEffect(()=>{ce(x||void 0).then(b=>{i(b.read_only),r(b.project.name),q(b),T(!!b.direct),z(b.identity??null),b.identity&&b.identity.source!=="anonymous"&&b.identity.name&&u(b.identity.name)}).catch(()=>{}),Je().then(j).catch(()=>{})},[x]);const ae=t.useMemo(()=>(s==null?void 0:s.terms.find(b=>b.id===v))??null,[s,v]),Ae=t.useCallback(b=>{const y=b.key??ve(b.op,b.payload);A(W=>[...W.filter(D=>D.key!==y),{...b,key:y}])},[]),$e=b=>A(y=>y.filter((W,D)=>D!==b)),Ee=()=>{A([]),M(!1),S(!1),ne()};if(h)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:h}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const Q=!a,te=(l==null?void 0:l.catalog_owned_fields)??[],Me=te.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:d}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Me&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${l==null?void 0:l.catalog_name}`,children:["mirrors ",l==null?void 0:l.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:c,onChange:b=>p(b.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(m==="browse"?" active":""),onClick:()=>G("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(m==="model"?" active":""),onClick:()=>G("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(m==="areas"?" active":""),onClick:()=>G("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(m==="proposals"?" active":""),onClick:()=>G("proposals"),children:"My proposals"})]}),V.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>G("review"),children:[V.length," change",V.length>1?"s":""," · Review →"]})]}),m==="browse"&&e.jsx(ns,{user:L}),m==="review"?e.jsx(ks,{stagedDiff:Se,stagedRoute:_e,user:L,selectable:Ce,excluded:E,onToggleField:(b,y)=>P(W=>{const D=new Set(W),Y=J(b,y);return D.has(Y)?D.delete(Y):D.add(Y),D}),onBack:()=>G("browse"),onSubmit:b=>{f(b),M(!0)}}):m==="model"?w.previewDoc??O?e.jsx(is,{doc:w.previewDoc??O,subjectArea:x,onSubjectArea:g,exec:K?R:w.exec,canEdit:Q,direct:K,onImported:()=>G("review"),query:c,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):m==="areas"?e.jsx(as,{exec:w.exec,canEdit:Q,stagedMembers:H}):m==="proposals"?e.jsx(ls,{user:L,onView:()=>G("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(x===""?" active":""),onClick:()=>g(""),children:"All subject areas"}),s.subject_areas.map(b=>e.jsx("button",{className:"sme-sa"+(x===b.id?" active":""),onClick:()=>g(b.id),title:b.definition??void 0,children:b.name},b.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(b=>{var y;return e.jsxs("li",{className:"sme-list-item"+(b.id===v?" active":""),onClick:()=>{C(b.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:b.name}),b.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((y=b.ontology)==null?void 0:y.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:b.definition??"no definition yet"})]},b.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:ae?B&&Q?e.jsx($s,{term:ae,pending:k,onStage:Ae,onDone:()=>S(!1),catalogOwned:te,catalog:l?{name:l.catalog_name,url:l.catalog_url}:null}):e.jsx(As,{term:ae,canEdit:Q,pendingCount:k.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),_&&e.jsx(ds,{changes:V,routeAdvice:F,identity:I,user:L,onUser:b=>{u(b),localStorage.setItem("mdl.sme.user",b)},onDrop:$e,onClose:()=>M(!1),onProposed:()=>{Ee(),w.clear(),P(new Set),G("proposals")}})]})}Re.createRoot(document.getElementById("root")).render(e.jsx(De.StrictMode,{children:e.jsx(Rs,{})})); diff --git a/packages/server/src/mdl_server/static/sme.html b/packages/server/src/mdl_server/static/sme.html index beadf8f..7886474 100644 --- a/packages/server/src/mdl_server/static/sme.html +++ b/packages/server/src/mdl_server/static/sme.html @@ -4,7 +4,7 @@ Modelith Glossary - + From 43260f90a8ee90a68e8cd9736d913bec506f5833 Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sun, 4 Oct 2026 15:13:44 +1100 Subject: [PATCH 6/6] =?UTF-8?q?feat(semantic-review):=20Phase=202=20?= =?UTF-8?q?=E2=80=94=20erwin=20import=20pre-write=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make an erwin import show the unified semantic review (filters, per-field selection, severity, break-impact) BEFORE anything is written, with an "Apply import" action that writes only the selected new objects. Reuses the Phase 1 ReviewScreen/ModelDiffDoc end to end; no change to the propose flow. Server - POST /api/import/preview (erwin only; 422 otherwise): parse without writing, diff the imported model against the current one by name, and return the enriched ModelDiffDoc (paths + break-impact) plus the import's command list and warnings for the apply action. Writes nothing. - POST /api/import/stash + GET /api/import/stash/{token}: one-shot content channel so a VS Code .xml can pre-fill the canvas review (the webview can't read local files). Core - Extract remap_head_by_name() from diff_models_by_name() so a caller needing both the diff and the ULID-remapped head (break-impact enrichment) shares one remapping. Signature of diff_models_by_name unchanged. Canvas - ReviewScreen gains an optional `action` prop; when present it renders that action's button (Apply import) over the included ULIDs, else the unchanged "Submit for review" propose button. - reviewModel: includedEntityNames (added-only) + filterImportChanges map a field selection to the import commands that should be written. - api: importPreview, importStashGet, applyImportBatch(tolerateExisting). - SmeApp routes ?import= to a pre-filled import → review → apply. VS Code - modelith.reviewImport command, framed as a Reverse Review view/title action and an .xml explorer context action; stashes file content then opens /sme?import=. Tests: import/preview diff-doc + writes-nothing, non-erwin 422, stash one-shot roundtrip; reviewModel write-filter cases. Spec phasing marks Phases 1–2 shipped. --- canvas/src/api.ts | 32 +++++- canvas/src/sme/ImportExportMenu.tsx | 50 +++++++- canvas/src/sme/ModelWorkspace.tsx | 19 ++- canvas/src/sme/ReviewScreen.tsx | 36 ++++-- canvas/src/sme/SmeApp.tsx | 79 ++++++++++++- canvas/src/sme/reviewModel.test.ts | 72 ++++++++++++ canvas/src/sme/reviewModel.ts | 69 +++++++++++ demos/fixtures/sample.erwin.xml | 108 ++++++++++++++++++ packages/core/src/mdl_core/diff.py | 12 +- packages/server/src/mdl_server/app.py | 71 ++++++++++++ ...m-oAb1enKe.js => ModelDiagram-CRUBpPVn.js} | 2 +- .../{main-DgsUduj_.js => main-DScWOCuj.js} | 2 +- .../mdl_server/static/assets/sme-CKl4JWY1.js | 2 - .../mdl_server/static/assets/sme-Zxw1Myjj.js | 2 + .../static/assets/styles-B90OKxCj.js | 23 ++++ .../static/assets/styles-BtrhR1xb.js | 23 ---- .../server/src/mdl_server/static/index.html | 4 +- .../server/src/mdl_server/static/sme.html | 4 +- .../server/tests/test_import_erwin_api.py | 48 ++++++++ specs/modelith-semantic-compare-merge-spec.md | 14 ++- vscode/package.json | 15 +++ vscode/src/canvasPanel.ts | 30 ++++- vscode/src/extension.ts | 37 ++++++ 23 files changed, 693 insertions(+), 61 deletions(-) create mode 100644 demos/fixtures/sample.erwin.xml rename packages/server/src/mdl_server/static/assets/{ModelDiagram-oAb1enKe.js => ModelDiagram-CRUBpPVn.js} (83%) rename packages/server/src/mdl_server/static/assets/{main-DgsUduj_.js => main-DScWOCuj.js} (99%) delete mode 100644 packages/server/src/mdl_server/static/assets/sme-CKl4JWY1.js create mode 100644 packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js create mode 100644 packages/server/src/mdl_server/static/assets/styles-B90OKxCj.js delete mode 100644 packages/server/src/mdl_server/static/assets/styles-BtrhR1xb.js diff --git a/canvas/src/api.ts b/canvas/src/api.ts index 7cdc107..ce123c3 100644 --- a/canvas/src/api.ts +++ b/canvas/src/api.ts @@ -185,15 +185,43 @@ export interface ImportResult { export const importModel = (format: string, content: string, dialect?: string) => post("/api/import", { format, content, dialect }); +/** A semantic diff of an import against the current model (the review view's ModelDiffDoc), + * WITHOUT writing — plus the import's change list + warnings, so the review can apply the + * selected objects afterwards. Powers "review before it lands" for erwin imports. */ +export type ImportPreviewDoc = ModelDiffDoc & { + changes: { op: string; payload: Record }[]; + warnings: string[]; +}; + +export const importPreview = (format: string, content: string) => + post("/api/import/preview", { format, content }); + +/** Fetch import content a host (VS Code) stashed by token — the webview can't read local + * files, so /sme?import= hands off the file this way. One-shot. */ +export const importStashGet = (token: string) => + get<{ ok: boolean; content?: string; error?: string }>( + `/api/import/stash/${encodeURIComponent(token)}`, + ); + /** Apply an imported change list to the working tree as one batch (direct-write * canvas). Each command is sent WITHOUT a fingerprint so the server's stale-model * check is skipped: the guard exists to catch an external edit between a human's - * view and their action, not to serialise a machine applying its own batch. */ + * view and their action, not to serialise a machine applying its own batch. + * + * `tolerateExisting` skips "already exists" errors (idempotent scaffolding): an import + * into an existing model re-emits shared domains / subject areas / categories, and a + * create for one already present is a no-op, not a failure. Any OTHER error still throws. */ export const applyImportBatch = async ( changes: { op: string; payload: Record }[], + tolerateExisting = false, ) => { for (const c of changes) { - await sendCommand(c.op, c.payload); // no fingerprint -> no stale-check + try { + await sendCommand(c.op, c.payload); // no fingerprint -> no stale-check + } catch (e) { + if (tolerateExisting && e instanceof Error && /already exists/i.test(e.message)) continue; + throw e; + } } }; diff --git a/canvas/src/sme/ImportExportMenu.tsx b/canvas/src/sme/ImportExportMenu.tsx index 3b01cb4..ac9a314 100644 --- a/canvas/src/sme/ImportExportMenu.tsx +++ b/canvas/src/sme/ImportExportMenu.tsx @@ -1,5 +1,12 @@ import { useEffect, useRef, useState } from "react"; -import { type ExportFormat, exportUrl, fetchExportFormats, importModel } from "../api"; +import { + type ExportFormat, + type ImportPreviewDoc, + exportUrl, + fetchExportFormats, + importModel, + importPreview, +} from "../api"; import type { Exec } from "../exec"; import "./ImportExportMenu.css"; @@ -17,11 +24,17 @@ export function ImportExportMenu({ buttonClass = "erd-action ghost", applyBatch, openImport = false, + prefillImport = "", + onPreview, }: { exec: Exec; canEdit: boolean; /** called after a successful import so the shell can jump to the review screen */ onImported?: (tables: number) => void; + /** when set, an erwin import is reviewed before it lands (see ImportPanel.onPreview) */ + onPreview?: (doc: ImportPreviewDoc) => void; + /** pre-fill the import content (a host-stashed .xml) */ + prefillImport?: string; /** the import button's label — "Import & review" in the staged (SME) app, plain * "Import" in the direct-write architect canvas where there is no review screen */ submitLabel?: string; @@ -126,6 +139,8 @@ export function ImportExportMenu({ setOpen(null); onImported?.(n); }} + onPreview={onPreview} + prefillImport={prefillImport} /> )}
@@ -146,6 +161,8 @@ function ImportPanel({ applyBatch, onClose, onImported, + onPreview, + prefillImport, }: { exec: Exec; submitLabel: string; @@ -153,14 +170,26 @@ function ImportPanel({ applyBatch?: (changes: { op: string; payload: Record }[]) => Promise; onClose: () => void; onImported: (tables: number) => void; + /** When set, an erwin import is sent for PRE-WRITE REVIEW instead of being applied: the + * parsed semantic diff is handed up so the host mounts the review screen with an "Apply + * import" action. Absent = apply immediately (today's behaviour / direct-write canvas). */ + onPreview?: (doc: ImportPreviewDoc) => void; + prefillImport?: string; }) { - const [format, setFormat] = useState("sql"); + // When opened for pre-write review (onPreview wired), default to erwin — the only format the + // review path supports today — so the VS Code "Review Erwin import" flow lands ready to paste. + const [format, setFormat] = useState(onPreview ? "erwin" : "sql"); const [dialect, setDialect] = useState("postgres"); - const [content, setContent] = useState(""); + const [content, setContent] = useState(prefillImport ?? ""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [warnings, setWarnings] = useState([]); + // Host-stashed content (a VS Code .xml) arrives after mount; fill the panel when it does. + useEffect(() => { + if (prefillImport) setContent(prefillImport); + }, [prefillImport]); + const onFile = (f: File | null) => { if (!f) return; f.text().then(setContent).catch(() => setError("could not read that file")); @@ -171,6 +200,21 @@ function ImportPanel({ setError(null); setWarnings([]); try { + // Pre-write review: for an erwin import in the SME app, fetch the semantic diff and hand + // it up for review BEFORE anything is written; the review's "Apply import" action does + // the write. Other formats / the direct-write canvas keep the apply-now path below. + if (onPreview && format === "erwin") { + const doc = await importPreview(format, content); + if (!doc.changes.length) { + setError(doc.warnings[0] ?? "nothing to import from that document"); + setBusy(false); + return; + } + onClose(); + onPreview(doc); + setBusy(false); + return; + } const res = await importModel(format, content, format === "sql" ? dialect : undefined); if (!res.changes.length) { setError(res.warnings[0] ?? "nothing to import from that document"); diff --git a/canvas/src/sme/ModelWorkspace.tsx b/canvas/src/sme/ModelWorkspace.tsx index 911b003..7864c0a 100644 --- a/canvas/src/sme/ModelWorkspace.tsx +++ b/canvas/src/sme/ModelWorkspace.tsx @@ -1,5 +1,6 @@ import { useCallback, useMemo, useRef, useState } from "react"; import { ReactFlowProvider } from "reactflow"; +import type { ImportPreviewDoc } from "../api"; import type { Capabilities, Exec } from "../exec"; import { ModelCanvas, type ModelCanvasHandle } from "../ModelCanvas"; import { LayersView, OntologyBrowser, type ReadOnlyPanelTab } from "../SidePanel"; @@ -54,6 +55,9 @@ export function ModelWorkspace({ direct = false, onSelectEntity, onImported, + onImportPreview, + openImport = false, + prefillImport = "", query = "", busy, }: { @@ -67,6 +71,12 @@ export function ModelWorkspace({ onSelectEntity?: (id: string) => void; /** after an import stages its changes, jump the shell to the review screen */ onImported?: (tables: number) => void; + /** when set, an erwin import is reviewed before it lands (the shell mounts the review) */ + onImportPreview?: (doc: ImportPreviewDoc) => void; + /** open the import panel on mount (VS Code "Review Erwin import" via /sme?import=1) */ + openImport?: boolean; + /** pre-fill the import panel with this content (a host-stashed .xml via /sme?import=) */ + prefillImport?: string; /** the top-bar search text — highlights matching entities and dims the rest */ query?: string; busy?: boolean; @@ -242,7 +252,14 @@ export function ModelWorkspace({
diff --git a/canvas/src/sme/SmeApp.tsx b/canvas/src/sme/SmeApp.tsx index 38bec41..b36b33b 100644 --- a/canvas/src/sme/SmeApp.tsx +++ b/canvas/src/sme/SmeApp.tsx @@ -1,9 +1,12 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { + type ImportPreviewDoc, + applyImportBatch, classifyStaged, fetchGlossary, fetchGlossaryConfig, fetchModel, + importStashGet, previewChanges, sendCommand, } from "../api"; @@ -26,7 +29,7 @@ import { ModelWorkspace } from "./ModelWorkspace"; import { SubjectAreaEditor } from "./SubjectAreaEditor"; import { ProposalsList } from "./ProposalsList"; import { ProposeDialog } from "./ProposeDialog"; -import { fieldKey } from "./reviewModel"; +import { allFieldKeys, fieldKey, filterImportChanges, includedEntityNames } from "./reviewModel"; import { ReviewScreen } from "./ReviewScreen"; import { TermCard } from "./TermCard"; import { TermEditor } from "./TermEditor"; @@ -52,13 +55,34 @@ export function SmeApp() { const [editing, setEditing] = useState(false); const [pending, setPending] = useState([]); const [proposeOpen, setProposeOpen] = useState(false); + // `?import=1` (VS Code "Review Erwin import") starts on the Model view with the import panel + // open, so an erwin import flows straight into the pre-write review. `?import=` (the + // host read a local .xml and stashed it) additionally pre-fills the panel with that content. + const importParam = useMemo( + () => new URLSearchParams(window.location.search).get("import"), + [], + ); + const openImport = !!importParam; + const importStashToken = importParam && importParam !== "1" ? importParam : null; + // content fetched from the host's one-shot stash (when ?import=) + const [prefillImport, setPrefillImport] = useState(""); + useEffect(() => { + if (!importStashToken) return; + importStashGet(importStashToken) + .then((r) => r.ok && r.content && setPrefillImport(r.content)) + .catch(() => {}); + }, [importStashToken]); // browse | review | proposals. Written to location.hash so a view is linkable // (app.py serves sme.html for `sme` and any `sme/...`, so no server change). const [view, setView] = useState<"browse" | "model" | "areas" | "review" | "proposals">( - window.location.hash === "#proposals" ? "proposals" : "browse", + openImport ? "model" : window.location.hash === "#proposals" ? "proposals" : "browse", ); // objects the SME has unticked in the review screen (selective proposal) const [excluded, setExcluded] = useState>(new Set()); + // When an erwin import is being reviewed BEFORE it lands, this holds its preview doc + + // change list; the review screen then shows an "Apply import" action instead of propose. + // Cleared when the review is left. (Reuses the "review" view, no new route.) + const [importReview, setImportReview] = useState(null); const [user, setUser] = useState(() => localStorage.getItem("mdl.sme.user") ?? ""); // Server-established identity (spec §17). When the source is proxy or git, the // acting user is known and trustworthy — the propose dialog greets them instead @@ -211,9 +235,17 @@ export function SmeApp() { }, []); // keep the view in step with the hash, so a deep link and the back button work + const firstHash = useRef(true); useEffect(() => { const onHash = () => { const h = window.location.hash; + // On the very first load with ?import=1 and no hash yet, stay on the Model view (set by + // the initializer) so the import panel opens; a hash navigation always wins thereafter. + if (firstHash.current && !h && openImport) { + firstHash.current = false; + return; + } + firstHash.current = false; setView( h === "#proposals" ? "proposals" @@ -356,8 +388,8 @@ export function SmeApp() { {view === "review" ? ( goto("browse")} + onBack={() => { + setImportReview(null); + setExcluded(new Set()); + goto("browse"); + }} onSubmit={(cl) => { setRouteAdvice(cl); setProposeOpen(true); }} + action={ + importReview + ? { + id: "apply-import", + label: "Apply import →", + primary: true, + apply: async () => { + // write only the ENTITIES the reviewer kept ticked (by name — the diff doc + // carries remapped ULIDs, so name is the stable join to the import commands) + const diff = importReview as ModelDiffDoc; + const selected = new Set( + [...allFieldKeys(diff)].filter((k) => !excluded.has(k)), + ); + const keepNames = includedEntityNames(diff, selected); + const changes = filterImportChanges(importReview.changes, keepNames); + await applyImportBatch(changes, true); // tolerate shared scaffolding + setImportReview(null); + setExcluded(new Set()); + goto("browse"); + await fetchModel(subjectArea || undefined).then(setModelDoc); + }, + } + : undefined + } /> ) : view === "model" ? ( // The previewed model when anything is staged, so an edit is visible @@ -388,6 +448,13 @@ export function SmeApp() { canEdit={canEdit} direct={direct} onImported={() => goto("review")} + onImportPreview={(doc) => { + setExcluded(new Set()); + setImportReview(doc); + goto("review"); + }} + openImport={openImport} + prefillImport={prefillImport} query={query} busy={staging.busy} /> diff --git a/canvas/src/sme/reviewModel.test.ts b/canvas/src/sme/reviewModel.test.ts index 18c262e..e6321b4 100644 --- a/canvas/src/sme/reviewModel.test.ts +++ b/canvas/src/sme/reviewModel.test.ts @@ -1,10 +1,13 @@ import { describe, expect, it } from "vitest"; import { makeDiff } from "../test/diffFixture"; +import type { ModelDiffDoc, ObjectChangeDoc } from "../types"; import { allFieldKeys, fieldKey, filterCounts, + filterImportChanges, filterObjects, + includedEntityNames, includedUlids, objectFieldKeys, objectIncluded, @@ -89,3 +92,72 @@ describe("per-field selection", () => { expect(includedUlids(diff, sel).sort()).toEqual(["E_ADD", "E_DEL", "E_REN"]); }); }); + +describe("import write-filter (apply only the kept entities, by name)", () => { + // a minimal import command list like model_to_commands emits + const changes = [ + { op: "create_subject_area", payload: { id: "SA1", name: "Core" } }, + { op: "create_entity", payload: { id: "e_cp", name: "counterparty" } }, + { op: "add_attribute", payload: { entity_id: "e_cp", id: "a1", name: "cp_id" } }, + { op: "create_entity", payload: { id: "e_tr", name: "trade" } }, + { op: "add_attribute", payload: { entity_id: "e_tr", id: "a2", name: "trade_id" } }, + { op: "create_relationship", payload: { id: "r1", from_entity: "e_tr", to_entity: "e_cp" } }, + ]; + + it("keeps scaffolding + the kept entity + its attributes; drops the rest", () => { + const out = filterImportChanges(changes, new Set(["counterparty"])); + const ids = out.map((c) => `${c.op}:${c.payload.id}`); + expect(ids).toContain("create_subject_area:SA1"); + expect(ids).toContain("create_entity:e_cp"); + expect(ids).toContain("add_attribute:a1"); // counterparty's attr (id a1) + // trade and its attribute are dropped + expect(ids).not.toContain("create_entity:e_tr"); + expect(ids).not.toContain("add_attribute:a2"); + // the relationship references trade (dropped) -> dropped + expect(out.some((c) => c.op === "create_relationship")).toBe(false); + }); + + it("keeps a relationship only when BOTH endpoints are kept", () => { + const out = filterImportChanges(changes, new Set(["counterparty", "trade"])); + expect(out.some((c) => c.op === "create_relationship")).toBe(true); + }); + + it("nothing selected writes nothing", () => { + expect(filterImportChanges(changes, new Set())).toHaveLength(0); + }); + + it("includedEntityNames reads kept logical-entity names from the diff", () => { + const entity = (ulid: string, name: string): ObjectChangeDoc => ({ + ulid, object_kind: "logical_entity", object_kind_label: "entity", + change: "added", name_before: null, name_after: name, renamed: false, + severity: "additive", path: null, + fields: [{ field: "*", kind: "added", severity: "additive", label: "", detail: "", before: null, after: null }], + children: [], + }); + const diff = { + ok: true, base: { ref: "", sha: "", label: "" }, head: { ref: null, label: "" }, + counts: {}, max_severity: "additive", has_breaking: false, + objects: [entity("x", "counterparty"), entity("y", "trade")], config: [], + } as unknown as ModelDiffDoc; + const all = allFieldKeys(diff); + expect([...includedEntityNames(diff, all)].sort()).toEqual(["counterparty", "trade"]); + }); + + it("includedEntityNames excludes a MODIFIED (already-existing) entity", () => { + const obj = (ulid: string, name: string, change: "added" | "modified"): ObjectChangeDoc => ({ + ulid, object_kind: "logical_entity", object_kind_label: "entity", + change, name_before: change === "added" ? null : name, name_after: name, renamed: false, + severity: "additive", path: null, + fields: [{ field: "*", kind: change, severity: "additive", label: "", detail: "", before: null, after: null }], + children: [], + }); + const diff = { + ok: true, base: { ref: "", sha: "", label: "" }, head: { ref: null, label: "" }, + counts: {}, max_severity: "additive", has_breaking: false, + objects: [obj("x", "counterparty", "modified"), obj("y", "trade", "added")], config: [], + } as unknown as ModelDiffDoc; + const all = allFieldKeys(diff); + // counterparty already exists (modified) -> not created; only the new entity is kept + expect([...includedEntityNames(diff, all)]).toEqual(["trade"]); + }); +}); diff --git a/canvas/src/sme/reviewModel.ts b/canvas/src/sme/reviewModel.ts index 5c7fc4a..78d0537 100644 --- a/canvas/src/sme/reviewModel.ts +++ b/canvas/src/sme/reviewModel.ts @@ -92,6 +92,75 @@ export function includedUlids(diff: ModelDiffDoc, selected: Set): string return diff.objects.filter((o) => objectIncluded(o, selected)).map((o) => o.ulid); } +/** The NEW logical-entity names the reviewer kept, for filtering an import's command list. + * Only `added` entities are returned: the import emits `create_entity` for each entity, which + * fails for one that already exists in the current model (a name-matched "modified" object), so + * an import applies only its genuinely-new entities — a modified existing entity is left as-is + * (shown in the review, not clobbered). Name is the stable join: the diff doc carries remapped + * ULIDs (base's ULID for a name-matched object) while the import commands carry fresh ULIDs. */ +export function includedEntityNames(diff: ModelDiffDoc, selected: Set): Set { + const names = new Set(); + for (const o of diff.objects) { + if (o.object_kind !== "logical_entity") continue; + if (o.change !== "added") continue; // only new entities can be created by the import + if (!objectIncluded(o, selected)) continue; + const n = o.name_after ?? o.name_before; + if (n) names.add(n); + } + return names; +} + +type ImportCommand = { op: string; payload: Record }; + +/** Filter an erwin import's command list to the entities the reviewer kept (by name). + * Scaffolding ops (subject areas, domains, categories, members) always apply; an entity and + * its attributes apply when the entity's name is kept; a relationship or key group applies + * only when EVERY entity it references is kept. `model_to_commands` emits entities before the + * attributes/relationships that reference them, so a single pass can track which entity ids + * survive. */ +export function filterImportChanges( + changes: ImportCommand[], + keepNames: Set, +): ImportCommand[] { + if (keepNames.size === 0) return []; // nothing selected -> write nothing + const keptEntityIds = new Set(); // import-ULIDs of entities we keep + const out: ImportCommand[] = []; + const refEntityIds = (p: Record): string[] => + [p.entity_id, p.from_entity, p.to_entity, p.from, p.to].filter( + (v): v is string => typeof v === "string", + ); + for (const c of changes) { + const p = c.payload; + switch (c.op) { + case "create_entity": { + const name = typeof p.name === "string" ? p.name : ""; + const id = typeof p.id === "string" ? p.id : ""; + if (keepNames.has(name)) { + keptEntityIds.add(id); + out.push(c); + } + break; + } + case "add_attribute": { + const eid = typeof p.entity_id === "string" ? p.entity_id : ""; + if (keptEntityIds.has(eid)) out.push(c); + break; + } + case "create_relationship": + case "create_key_group": { + const refs = refEntityIds(p); + if (refs.length > 0 && refs.every((id) => keptEntityIds.has(id))) out.push(c); + break; + } + default: + // scaffolding (create_subject_area, create_domain, create_category, + // set_subject_area_members, …) always applies + out.push(c); + } + } + return out; +} + /** All field keys across the whole diff — the initial "everything selected" set. */ export function allFieldKeys(diff: ModelDiffDoc): Set { const s = new Set(); diff --git a/demos/fixtures/sample.erwin.xml b/demos/fixtures/sample.erwin.xml new file mode 100644 index 0000000..ddc1a1e --- /dev/null +++ b/demos/fixtures/sample.erwin.xml @@ -0,0 +1,108 @@ + + + + TradingModel + + + id_typeBIGINT + + + + + StewardText + + + + + PhysicalUPPER + 1 + + + LogicalLOWER + 1 + + + + + CounterpartyCOUNTERPARTY + A trading partner. + + Jane Doe + + + + + counterparty_idD1 + Not_Null + + + legal_nameVARCHAR(255) + + Bob + + + + + + pk_counterpartyPrimary_Key + + + + A_CP1 + + + + + + Trade + + trade_id + + counterparty_id + R1 + A_CP1 + + + + + pk_tradePrimary_Key + + A_TR1 + + + + + Institution + + counterparty_id + + + + + + trade_has_counterparty + E_CPE_TR + Zero_One_or_More + is party to + is with + + + cpty_is_institutionSubtype_Complete + E_CPE_INST + A_CP2 + + + + + Trading + + E_CP + E_TR + + + + v_active_trades + + Main + + diff --git a/packages/core/src/mdl_core/diff.py b/packages/core/src/mdl_core/diff.py index 83b4eba..0bbd133 100644 --- a/packages/core/src/mdl_core/diff.py +++ b/packages/core/src/mdl_core/diff.py @@ -565,6 +565,16 @@ def diff_models_by_name( relationships and key groups by name), then delegates to the unchanged `diff_models` — so all the classification, severity and rendering are reused. Head-only names keep their own ULIDs and show as added; base-only names show as removed.""" + head = remap_head_by_name(base, head) + return diff_models(base, head, base_label=base_label, head_label=head_label) + + +def remap_head_by_name(base: Model, head: Model) -> Model: + """Return a COPY of `head` whose ULIDs are rewritten to `base`'s ULID for every same-named + object, so the two models can be diffed (or enriched) by shared ULID. This is the name-match + step behind `diff_models_by_name`, exposed so a caller that needs both the diff AND the + remapped head (e.g. the server's import-preview, which runs break-impact enrichment against + the head using the diff doc's ULIDs) can share one remapping instead of duplicating it.""" import copy head = copy.deepcopy(head) @@ -594,7 +604,7 @@ def diff_models_by_name( remap[a.id] = base_attr[key] _apply_remap(head, remap) - return diff_models(base, head, base_label=base_label, head_label=head_label) + return head def _apply_remap(model: Model, remap: dict[str, str]) -> None: diff --git a/packages/server/src/mdl_server/app.py b/packages/server/src/mdl_server/app.py index 8eb043c..9b12e05 100644 --- a/packages/server/src/mdl_server/app.py +++ b/packages/server/src/mdl_server/app.py @@ -81,6 +81,10 @@ def create_app( model_dir = Path(model_dir) app = FastAPI(title="Modelith", docs_url="/api/docs", openapi_url="/api/openapi.json") cache: dict = {"fingerprint": None, "repo": None} + # One-shot in-memory stash for import content handed from a host (VS Code reads a local + # .xml and POSTs it here; the canvas webview, which cannot read local files, fetches it by + # token). Consumed on read; this is a convenience channel, not durable state. + import_stash: dict[str, str] = {} # Identity is resolved per request from a trusted-proxy header (enterprise) or the # model dir's git config (solo), or falls back to anonymous — see identity.py. The @@ -260,6 +264,73 @@ def import_model(body: dict) -> JSONResponse: } ) + @app.post("/api/import/stash") + def import_stash_put(body: dict) -> JSONResponse: + """Stash import content (e.g. an erwin XML a VS Code host read from disk) and return a + one-shot token. The canvas opens /sme?import= and fetches it below — the webview + itself cannot read the host's filesystem.""" + import secrets + + content = body.get("content", "") + if not content: + return JSONResponse({"ok": False, "error": "content is empty"}, status_code=422) + token = secrets.token_urlsafe(12) + import_stash[token] = content + # cap the stash so a long-lived server can't accumulate; this is a transient handoff + if len(import_stash) > 32: + for k in list(import_stash)[:-16]: + import_stash.pop(k, None) + return JSONResponse({"ok": True, "token": token}) + + @app.get("/api/import/stash/{token}") + def import_stash_get(token: str) -> JSONResponse: + """Consume a stashed import content by token (one-shot).""" + content = import_stash.pop(token, None) + if content is None: + return JSONResponse({"ok": False, "error": "unknown or expired token"}, status_code=404) + return JSONResponse({"ok": True, "content": content}) + + @app.post("/api/import/preview") + def import_preview(body: dict) -> JSONResponse: + """Parse an import and return a SEMANTIC DIFF of it against the current model, WITHOUT + writing anything — so the change can be reviewed (filters, per-field selection, break + impact) before it lands. Returns the ModelDiffDoc shape (as /api/git/diff/model does) + plus the import's `changes` and `warnings`, so the client can apply the selected + objects afterwards without re-parsing. Erwin only for now (the flagship migration); + other formats 422 until wired.""" + from mdl_emit_erd.imports.model import model_to_commands + + from mdl_core.diff import diff_models, remap_head_by_name + from mdl_core.diff_render import render_json + from mdl_server.git_api import _attach_breaks, _attach_paths + + fmt = body.get("format", "") + content = body.get("content", "") + if not content: + return JSONResponse({"ok": False, "error": "content is empty"}, status_code=422) + if fmt != "erwin": + return JSONResponse( + {"ok": False, "error": f"import preview supports erwin only (got {fmt!r})"}, + status_code=422, + ) + from mdl_reverse.erwin import import_erwin + + result = import_erwin(content) + current = _load().model + # Imported ULIDs are fresh, so match by NAME; keep the remapped head to enrich break + # impact against the same ULIDs the diff doc carries. + head = remap_head_by_name(current, result.model) + diff = diff_models(current, head, base_label="current", head_label="import") + doc = render_json(diff) + doc["ok"] = True + doc["base"] = {"ref": "current", "sha": "", "label": "current model"} + doc["head"] = {"ref": None, "label": "erwin import"} + _attach_paths(model_dir, doc) + _attach_breaks(head, doc) + doc["changes"] = model_to_commands(result.model) + doc["warnings"] = result.warnings + return JSONResponse(doc) + @app.get("/api/entities/{ulid}") def get_entity(ulid: str) -> JSONResponse: repo = _load() diff --git a/packages/server/src/mdl_server/static/assets/ModelDiagram-oAb1enKe.js b/packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js similarity index 83% rename from packages/server/src/mdl_server/static/assets/ModelDiagram-oAb1enKe.js rename to packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js index 26d3511..2d6de82 100644 --- a/packages/server/src/mdl_server/static/assets/ModelDiagram-oAb1enKe.js +++ b/packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js @@ -1 +1 @@ -import{r,j as s}from"./client-DOmNpTln.js";import{f as T,t as _,u as O,v as R,E as D,B as P,C as z,w as A}from"./styles-BtrhR1xb.js";const V={entity:D},$={relationship:R},l={breaking:"#f87171",additive:"#34d399",cosmetic:"#fcd34d"},N=["#5eead4","#fcd34d","#a78bfa","#fb923c","#38bdf8","#f472b6","#a3e635"],d="#64748b";function Y({subjectArea:u,severityByUlid:a,focusUlid:m,onSelect:c}){const[t,g]=r.useState(null),[f,w]=r.useState(null),[h,E]=r.useState(!0);r.useEffect(()=>{g(null),T(u).then(g).catch(n=>w(String(n)))},[u]);const{nodes:k,edges:M}=r.useMemo(()=>{if(!t)return{nodes:[],edges:[]};const n=new Map;t.subject_areas.forEach(e=>{let o=2166136261;for(let i=0;i[e.id,e])),b=a&&a.size>0,C=t.entities.map(e=>{var j;const o=(a==null?void 0:a.get(e.id))??(e.conceptual?a==null?void 0:a.get(e.conceptual.id):void 0),i=b?o?l[o]??d:d:(j=e.conceptual)!=null&&j.subject_area?n.get(e.conceptual.subject_area.id)??d:d;return{id:e.id,type:"entity",position:{x:0,y:0},data:{entity:e,color:i,dimmed:!!(b&&!o),highlighted:e.id===m,showTypes:h}}}),x=t.relationships.map(e=>({id:e.id,source:e.from.entity,target:e.to.entity,type:"relationship",data:{relationship:e,dimmed:!1}}));return{nodes:_(C,x,p),edges:x}},[t,a,m,h]);return f?s.jsx("p",{className:"sme-splash error",children:f}):t?t.entities.length?s.jsxs("div",{className:"erd-wrap",children:[s.jsxs("div",{className:"erd-bar",children:[s.jsxs("span",{className:"erd-count",children:[t.entities.length," entities · ",t.relationships.length," relationships",t.scope&&" · scoped"]}),s.jsxs("label",{className:"erd-toggle",children:[s.jsx("input",{type:"checkbox",checked:h,onChange:n=>E(n.target.checked)}),"show types"]}),a&&a.size>0&&s.jsxs("span",{className:"erd-legend",children:[s.jsx("span",{className:"sw",style:{background:l.breaking}})," breaking",s.jsx("span",{className:"sw",style:{background:l.additive}})," added",s.jsx("span",{className:"sw",style:{background:l.cosmetic}})," changed",s.jsx("span",{className:"sw",style:{background:d}})," unchanged"]})]}),s.jsx("div",{className:"erd-canvas",children:s.jsxs(O,{nodes:k,edges:M,nodeTypes:V,edgeTypes:$,fitView:!0,minZoom:.1,nodesConnectable:!1,onNodeClick:(n,p)=>c==null?void 0:c(p.id),onPaneClick:()=>c==null?void 0:c(null),onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},children:[s.jsx(P,{gap:16,color:"#1f2937"}),s.jsx(z,{showInteractive:!1}),s.jsx(A,{pannable:!0,zoomable:!0})]})})]}):s.jsx("p",{className:"sme-placeholder",children:"Nothing to draw — this subject area has no entities in it yet."}):s.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"})}export{Y as ModelDiagram}; +import{r,j as s}from"./client-DOmNpTln.js";import{f as T,u as _,v as O,w as R,E as D,B as P,C as z,x as A}from"./styles-B90OKxCj.js";const V={entity:D},$={relationship:R},l={breaking:"#f87171",additive:"#34d399",cosmetic:"#fcd34d"},N=["#5eead4","#fcd34d","#a78bfa","#fb923c","#38bdf8","#f472b6","#a3e635"],d="#64748b";function Y({subjectArea:u,severityByUlid:a,focusUlid:m,onSelect:c}){const[t,g]=r.useState(null),[f,w]=r.useState(null),[h,E]=r.useState(!0);r.useEffect(()=>{g(null),T(u).then(g).catch(n=>w(String(n)))},[u]);const{nodes:k,edges:M}=r.useMemo(()=>{if(!t)return{nodes:[],edges:[]};const n=new Map;t.subject_areas.forEach(e=>{let o=2166136261;for(let i=0;i[e.id,e])),x=a&&a.size>0,C=t.entities.map(e=>{var j;const o=(a==null?void 0:a.get(e.id))??(e.conceptual?a==null?void 0:a.get(e.conceptual.id):void 0),i=x?o?l[o]??d:d:(j=e.conceptual)!=null&&j.subject_area?n.get(e.conceptual.subject_area.id)??d:d;return{id:e.id,type:"entity",position:{x:0,y:0},data:{entity:e,color:i,dimmed:!!(x&&!o),highlighted:e.id===m,showTypes:h}}}),b=t.relationships.map(e=>({id:e.id,source:e.from.entity,target:e.to.entity,type:"relationship",data:{relationship:e,dimmed:!1}}));return{nodes:_(C,b,p),edges:b}},[t,a,m,h]);return f?s.jsx("p",{className:"sme-splash error",children:f}):t?t.entities.length?s.jsxs("div",{className:"erd-wrap",children:[s.jsxs("div",{className:"erd-bar",children:[s.jsxs("span",{className:"erd-count",children:[t.entities.length," entities · ",t.relationships.length," relationships",t.scope&&" · scoped"]}),s.jsxs("label",{className:"erd-toggle",children:[s.jsx("input",{type:"checkbox",checked:h,onChange:n=>E(n.target.checked)}),"show types"]}),a&&a.size>0&&s.jsxs("span",{className:"erd-legend",children:[s.jsx("span",{className:"sw",style:{background:l.breaking}})," breaking",s.jsx("span",{className:"sw",style:{background:l.additive}})," added",s.jsx("span",{className:"sw",style:{background:l.cosmetic}})," changed",s.jsx("span",{className:"sw",style:{background:d}})," unchanged"]})]}),s.jsx("div",{className:"erd-canvas",children:s.jsxs(O,{nodes:k,edges:M,nodeTypes:V,edgeTypes:$,fitView:!0,minZoom:.1,nodesConnectable:!1,onNodeClick:(n,p)=>c==null?void 0:c(p.id),onPaneClick:()=>c==null?void 0:c(null),onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},children:[s.jsx(P,{gap:16,color:"#1f2937"}),s.jsx(z,{showInteractive:!1}),s.jsx(A,{pannable:!0,zoomable:!0})]})})]}):s.jsx("p",{className:"sme-placeholder",children:"Nothing to draw — this subject area has no entities in it yet."}):s.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"})}export{Y as ModelDiagram}; diff --git a/packages/server/src/mdl_server/static/assets/main-DgsUduj_.js b/packages/server/src/mdl_server/static/assets/main-DScWOCuj.js similarity index 99% rename from packages/server/src/mdl_server/static/assets/main-DgsUduj_.js rename to packages/server/src/mdl_server/static/assets/main-DScWOCuj.js index 53d918f..08556aa 100644 --- a/packages/server/src/mdl_server/static/assets/main-DgsUduj_.js +++ b/packages/server/src/mdl_server/static/assets/main-DScWOCuj.js @@ -1,2 +1,2 @@ -import{j as e,r as a,c as ie,R as oe}from"./client-DOmNpTln.js";import{I as le,R as de,P as Y,f as J,a as Z,g as ee,s as he,A as ue,b as xe,M as je,S as pe}from"./styles-BtrhR1xb.js";function me(s){return{canEdit:!s,mode:"direct",canArbitrate:!s,canCommit:!s}}function l({size:s=16,children:i}){return e.jsx("svg",{width:s,height:s,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.7,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false",children:i})}const fe=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M12 5v14M5 12h14"})}),ge=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M12 3l7.5 4.5v9L12 21l-7.5-4.5v-9L12 3z"})}),ye=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 3l9 5-9 5-9-5 9-5z"}),e.jsx("path",{d:"M3 12l9 5 9-5"}),e.jsx("path",{d:"M3 16l9 5 9-5"})]}),be=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"6",cy:"6",r:"2.4"}),e.jsx("circle",{cx:"6",cy:"18",r:"2.4"}),e.jsx("circle",{cx:"18",cy:"18",r:"2.4"}),e.jsx("path",{d:"M6 8.4v7.2M8.4 6H14a3 3 0 0 1 3 3v6.6"})]}),we=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 4v16M6 8h12"}),e.jsx("path",{d:"M6 8l-2.5 5a2.5 2.5 0 0 0 5 0L6 8zM18 8l-2.5 5a2.5 2.5 0 0 0 5 0L18 8z"}),e.jsx("path",{d:"M9 20h6"})]}),ve=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M8 7l-4 5 4 5M16 7l4 5-4 5"})}),Me=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M8 8l4-4 4 4M8 16l4 4 4-4"}),e.jsx("path",{d:"M4 12h16"})]}),Ce=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"3",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"14",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"3",y:"15",width:"7",height:"5",rx:"1"}),e.jsx("rect",{x:"14",y:"15",width:"7",height:"5",rx:"1"})]}),Ne=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M4 8V5a1 1 0 0 1 1-1h3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3"})}),Ee=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M20 11a8 8 0 1 0-.5 4"}),e.jsx("path",{d:"M20 4v5h-5"})]}),ke=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}),e.jsx("path",{d:"M4 9h16"})]}),Se=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M4 7h9a4 4 0 0 1 4 4v3"}),e.jsx("path",{d:"M14 11l3 3 3-3"})]}),Ie=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M8.5 12.5l2.5 2.5 4.5-5"})]}),Le=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 4l9 16H3l9-16z"}),e.jsx("path",{d:"M12 10v4M12 17.5v.5"})]}),Re=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M15 9l-6 6M9 9l6 6"})]}),Te=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M4 6h16M4 12h16M4 18h16"})}),ze=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1.5"})]}),De=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M6 6l12 12M18 6L6 18"})}),Be=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M5 12h14M13 6l6 6-6 6"})});function Fe({doc:s,saColors:i,countByArea:f,activeAreas:d,onSetActive:g,open:v,onClose:y}){const x=s.subject_areas,j=d.size>0,m=c=>g(new Set([c])),M=c=>{const p=new Set(d);p.has(c)?p.delete(c):p.add(c),g(p)},o=f.get("")??0;return e.jsxs("aside",{className:"canvas-drawer"+(v?" open":""),"aria-hidden":!v,children:[e.jsxs("div",{className:"drawer-header",children:[e.jsxs("h2",{children:[e.jsx(ze,{size:16})," Subject areas"]}),e.jsx("button",{className:"icon-btn",onClick:y,title:"Close","aria-label":"Close drawer",children:e.jsx(De,{size:16})})]}),e.jsx("div",{className:"drawer-hint",children:j?`Showing ${d.size} of ${x.length} — click a domain to focus, tick to add.`:"Click a domain to show only it. Tick several to compare."}),e.jsx("button",{className:"sa-all"+(j?"":" active"),onClick:()=>g(new Set),disabled:!j,children:"Show all"}),e.jsxs("div",{className:"sa-list",children:[x.map(c=>{const p=d.has(c.id),D=f.get(c.id)??c.member_count??0;return e.jsxs("div",{className:"sa-row"+(p?" active":""),onClick:()=>m(c.id),role:"button",tabIndex:0,onKeyDown:w=>{(w.key==="Enter"||w.key===" ")&&(w.preventDefault(),m(c.id))},title:`Focus ${c.name}`,children:[e.jsx("input",{type:"checkbox",checked:p,onClick:w=>w.stopPropagation(),onChange:()=>M(c.id),"aria-label":`Include ${c.name}`}),e.jsx("span",{className:"sa-chip",style:{background:i.get(c.id)}}),e.jsx("span",{className:"sa-name",children:c.name}),e.jsx("span",{className:"sa-count",children:D})]},c.id)}),o>0&&e.jsxs("div",{className:"sa-row muted",title:"Entities with no subject area",children:[e.jsx("span",{className:"sa-chip none"}),e.jsx("span",{className:"sa-name",children:"No subject area"}),e.jsx("span",{className:"sa-count",children:o})]})]})]})}function Pe({doc:s,diagnostics:i,query:f,onQuery:d,onSubmitQuery:g,showTypes:v,onToggleTypes:y,collapseDetail:x,onToggleCollapse:j,onFitView:m,onRelayout:M,onRefresh:o,readOnly:c,dirty:p,panelTab:D,onPanelTab:w,onNewEntity:Q,exec:A,onImported:S,onImportBatch:I,openImport:_,onToggleDrawer:$,drawerOpen:k,filterCount:K}){const B=(i==null?void 0:i.items.filter(u=>u.severity==="error").length)??0,C=(i==null?void 0:i.items.filter(u=>u.severity==="warning").length)??0,L=(u,b,R,F,q)=>e.jsxs("button",{className:"grp-btn"+(D===u?" active":""),onClick:()=>w(u),title:F,children:[b,e.jsx("span",{children:R}),q&&e.jsx("span",{className:"grp-badge","aria-label":"uncommitted changes"})]});return e.jsxs("header",{className:"topbar",children:[e.jsxs("div",{className:"topbar-row context",children:[e.jsxs("button",{className:"hamburger"+(k?" active":""),onClick:$,title:k?"Hide subject areas & layout":"Subject areas & layout","aria-label":"Toggle subject-area & layout drawer","aria-expanded":k,children:[e.jsx(Te,{size:18}),K>0&&e.jsx("span",{className:"hamburger-badge",children:K})]}),e.jsxs("div",{className:"brand",children:[e.jsx("span",{className:"logo",children:"◮"}),e.jsx("span",{className:"brand-name",children:"Modelith"}),e.jsx("span",{className:"project-name",children:s.project.name}),c&&e.jsx("span",{className:"chip",children:"read-only"})]}),e.jsx("input",{className:"search",type:"search",placeholder:"Search entities & attributes… ( / , Enter jumps )",value:f,onChange:u=>d(u.target.value),onKeyDown:u=>{u.key==="Enter"&&g()},id:"mdl-search"}),e.jsxs("div",{className:"stats",children:[e.jsxs("span",{className:"stat-item",title:"entities",children:[e.jsx(ke,{size:14}),s.counts.entities]}),e.jsxs("span",{className:"stat-item",title:"relationships",children:[e.jsx(Se,{size:14}),s.counts.relationships]}),i&&e.jsx("span",{className:"diag-chip"+(B?" err":C?" warn":" ok"),title:i.items.map(u=>`${u.code} ${u.message}`).join(` +import{j as e,r as a,c as ie,R as oe}from"./client-DOmNpTln.js";import{I as le,R as de,P as Y,f as J,a as Z,g as ee,s as he,A as ue,b as xe,M as je,S as pe}from"./styles-B90OKxCj.js";function me(s){return{canEdit:!s,mode:"direct",canArbitrate:!s,canCommit:!s}}function l({size:s=16,children:i}){return e.jsx("svg",{width:s,height:s,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.7,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false",children:i})}const fe=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M12 5v14M5 12h14"})}),ge=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M12 3l7.5 4.5v9L12 21l-7.5-4.5v-9L12 3z"})}),ye=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 3l9 5-9 5-9-5 9-5z"}),e.jsx("path",{d:"M3 12l9 5 9-5"}),e.jsx("path",{d:"M3 16l9 5 9-5"})]}),be=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"6",cy:"6",r:"2.4"}),e.jsx("circle",{cx:"6",cy:"18",r:"2.4"}),e.jsx("circle",{cx:"18",cy:"18",r:"2.4"}),e.jsx("path",{d:"M6 8.4v7.2M8.4 6H14a3 3 0 0 1 3 3v6.6"})]}),we=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 4v16M6 8h12"}),e.jsx("path",{d:"M6 8l-2.5 5a2.5 2.5 0 0 0 5 0L6 8zM18 8l-2.5 5a2.5 2.5 0 0 0 5 0L18 8z"}),e.jsx("path",{d:"M9 20h6"})]}),ve=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M8 7l-4 5 4 5M16 7l4 5-4 5"})}),Me=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M8 8l4-4 4 4M8 16l4 4 4-4"}),e.jsx("path",{d:"M4 12h16"})]}),Ce=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"3",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"14",y:"4",width:"7",height:"7",rx:"1"}),e.jsx("rect",{x:"3",y:"15",width:"7",height:"5",rx:"1"}),e.jsx("rect",{x:"14",y:"15",width:"7",height:"5",rx:"1"})]}),Ne=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M4 8V5a1 1 0 0 1 1-1h3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3"})}),Ee=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M20 11a8 8 0 1 0-.5 4"}),e.jsx("path",{d:"M20 4v5h-5"})]}),ke=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"4",y:"4",width:"16",height:"16",rx:"2"}),e.jsx("path",{d:"M4 9h16"})]}),Se=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M4 7h9a4 4 0 0 1 4 4v3"}),e.jsx("path",{d:"M14 11l3 3 3-3"})]}),Ie=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M8.5 12.5l2.5 2.5 4.5-5"})]}),Le=s=>e.jsxs(l,{...s,children:[e.jsx("path",{d:"M12 4l9 16H3l9-16z"}),e.jsx("path",{d:"M12 10v4M12 17.5v.5"})]}),Re=s=>e.jsxs(l,{...s,children:[e.jsx("circle",{cx:"12",cy:"12",r:"9"}),e.jsx("path",{d:"M15 9l-6 6M9 9l6 6"})]}),Te=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M4 6h16M4 12h16M4 18h16"})}),ze=s=>e.jsxs(l,{...s,children:[e.jsx("rect",{x:"3",y:"3",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"14",y:"3",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"3",y:"14",width:"7",height:"7",rx:"1.5"}),e.jsx("rect",{x:"14",y:"14",width:"7",height:"7",rx:"1.5"})]}),De=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M6 6l12 12M18 6L6 18"})}),Be=s=>e.jsx(l,{...s,children:e.jsx("path",{d:"M5 12h14M13 6l6 6-6 6"})});function Fe({doc:s,saColors:i,countByArea:f,activeAreas:d,onSetActive:g,open:v,onClose:y}){const x=s.subject_areas,j=d.size>0,m=c=>g(new Set([c])),M=c=>{const p=new Set(d);p.has(c)?p.delete(c):p.add(c),g(p)},o=f.get("")??0;return e.jsxs("aside",{className:"canvas-drawer"+(v?" open":""),"aria-hidden":!v,children:[e.jsxs("div",{className:"drawer-header",children:[e.jsxs("h2",{children:[e.jsx(ze,{size:16})," Subject areas"]}),e.jsx("button",{className:"icon-btn",onClick:y,title:"Close","aria-label":"Close drawer",children:e.jsx(De,{size:16})})]}),e.jsx("div",{className:"drawer-hint",children:j?`Showing ${d.size} of ${x.length} — click a domain to focus, tick to add.`:"Click a domain to show only it. Tick several to compare."}),e.jsx("button",{className:"sa-all"+(j?"":" active"),onClick:()=>g(new Set),disabled:!j,children:"Show all"}),e.jsxs("div",{className:"sa-list",children:[x.map(c=>{const p=d.has(c.id),D=f.get(c.id)??c.member_count??0;return e.jsxs("div",{className:"sa-row"+(p?" active":""),onClick:()=>m(c.id),role:"button",tabIndex:0,onKeyDown:w=>{(w.key==="Enter"||w.key===" ")&&(w.preventDefault(),m(c.id))},title:`Focus ${c.name}`,children:[e.jsx("input",{type:"checkbox",checked:p,onClick:w=>w.stopPropagation(),onChange:()=>M(c.id),"aria-label":`Include ${c.name}`}),e.jsx("span",{className:"sa-chip",style:{background:i.get(c.id)}}),e.jsx("span",{className:"sa-name",children:c.name}),e.jsx("span",{className:"sa-count",children:D})]},c.id)}),o>0&&e.jsxs("div",{className:"sa-row muted",title:"Entities with no subject area",children:[e.jsx("span",{className:"sa-chip none"}),e.jsx("span",{className:"sa-name",children:"No subject area"}),e.jsx("span",{className:"sa-count",children:o})]})]})]})}function Pe({doc:s,diagnostics:i,query:f,onQuery:d,onSubmitQuery:g,showTypes:v,onToggleTypes:y,collapseDetail:x,onToggleCollapse:j,onFitView:m,onRelayout:M,onRefresh:o,readOnly:c,dirty:p,panelTab:D,onPanelTab:w,onNewEntity:Q,exec:A,onImported:S,onImportBatch:I,openImport:_,onToggleDrawer:$,drawerOpen:k,filterCount:K}){const B=(i==null?void 0:i.items.filter(u=>u.severity==="error").length)??0,C=(i==null?void 0:i.items.filter(u=>u.severity==="warning").length)??0,L=(u,b,R,F,q)=>e.jsxs("button",{className:"grp-btn"+(D===u?" active":""),onClick:()=>w(u),title:F,children:[b,e.jsx("span",{children:R}),q&&e.jsx("span",{className:"grp-badge","aria-label":"uncommitted changes"})]});return e.jsxs("header",{className:"topbar",children:[e.jsxs("div",{className:"topbar-row context",children:[e.jsxs("button",{className:"hamburger"+(k?" active":""),onClick:$,title:k?"Hide subject areas & layout":"Subject areas & layout","aria-label":"Toggle subject-area & layout drawer","aria-expanded":k,children:[e.jsx(Te,{size:18}),K>0&&e.jsx("span",{className:"hamburger-badge",children:K})]}),e.jsxs("div",{className:"brand",children:[e.jsx("span",{className:"logo",children:"◮"}),e.jsx("span",{className:"brand-name",children:"Modelith"}),e.jsx("span",{className:"project-name",children:s.project.name}),c&&e.jsx("span",{className:"chip",children:"read-only"})]}),e.jsx("input",{className:"search",type:"search",placeholder:"Search entities & attributes… ( / , Enter jumps )",value:f,onChange:u=>d(u.target.value),onKeyDown:u=>{u.key==="Enter"&&g()},id:"mdl-search"}),e.jsxs("div",{className:"stats",children:[e.jsxs("span",{className:"stat-item",title:"entities",children:[e.jsx(ke,{size:14}),s.counts.entities]}),e.jsxs("span",{className:"stat-item",title:"relationships",children:[e.jsx(Se,{size:14}),s.counts.relationships]}),i&&e.jsx("span",{className:"diag-chip"+(B?" err":C?" warn":" ok"),title:i.items.map(u=>`${u.code} ${u.message}`).join(` `)||"model valid",children:B?e.jsxs(e.Fragment,{children:[e.jsx(Re,{size:14}),B]}):C?e.jsxs(e.Fragment,{children:[e.jsx(Le,{size:14}),C]}):e.jsxs(e.Fragment,{children:[e.jsx(Ie,{size:14}),"valid"]})})]})]}),e.jsxs("div",{className:"topbar-row actions",children:[!c&&e.jsxs("button",{className:"tool-btn primary",onClick:Q,title:"New entity (n)",children:[e.jsx(fe,{size:15}),e.jsx("span",{children:"Entity"})]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"Panels",children:[e.jsx("span",{className:"grp-tag",children:"Panels"}),L("ontology",e.jsx(ge,{size:15}),"Ontology","Ontology browser"),L("layers",e.jsx(ye,{size:15}),"Layers","Four-layer stack & coverage"),L("changes",e.jsx(be,{size:15}),"Changes",p?"Uncommitted changes":"Changes",p),L("decisions",e.jsx(we,{size:15}),"Decisions","Decision ledger")]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"View",children:[e.jsx("span",{className:"grp-tag",children:"View"}),e.jsx("button",{className:"grp-btn icon-only"+(v?" active":""),onClick:y,title:"Toggle data types","aria-label":"Toggle data types",children:e.jsx(ve,{size:15})}),e.jsx("button",{className:"grp-btn icon-only"+(x?" active":""),onClick:j,title:"Collapse to keys (PK + FK only) — readable for large models","aria-label":"Collapse to keys",children:e.jsx(Me,{size:15})}),e.jsx(_e,{onRelayout:M}),e.jsx("button",{className:"grp-btn icon-only",onClick:m,title:"Fit view","aria-label":"Fit view",children:e.jsx(Ne,{size:15})})]}),e.jsxs("div",{className:"btn-group",role:"group","aria-label":"Data",children:[e.jsx("span",{className:"grp-tag",children:"Data"}),e.jsx("button",{className:"grp-btn icon-only",onClick:o,title:"Refresh from disk (picks up terminal / external edits)","aria-label":"Refresh from disk",children:e.jsx(Ee,{size:15})}),e.jsx(le,{exec:A,canEdit:!c,onImported:S,submitLabel:"Import",buttonClass:"grp-btn",applyBatch:I,openImport:_})]})]})]})}const O=["LR","TB","RL","BT"],Ae={LR:0,TB:90,RL:180,BT:270},se={LR:"left → right",TB:"top → bottom",RL:"right → left",BT:"bottom → top"};function _e({onRelayout:s}){const[i,f]=a.useState(!1),[d,g]=a.useState("LR"),[v,y]=a.useState("auto"),x=a.useRef(null);a.useEffect(()=>{if(!i)return;const o=c=>{x.current&&!x.current.contains(c.target)&&f(!1)};return document.addEventListener("mousedown",o),()=>document.removeEventListener("mousedown",o)},[i]);const j=o=>{y(o),s(o,d)},m=o=>{j(o),f(!1)},M=()=>{const o=O[(O.indexOf(d)+1)%O.length];g(o),s(v,o)};return e.jsxs("div",{className:"layout-picker",ref:x,children:[e.jsx("button",{className:"grp-btn icon-only",onClick:()=>j("auto"),title:"Auto-layout","aria-label":"Auto-layout",children:e.jsx(Ce,{size:15})}),e.jsx("button",{className:"grp-btn icon-only caret"+(i?" active":""),onClick:()=>f(o=>!o),title:"Layout style","aria-label":"Layout style",children:"▾"}),e.jsx("button",{className:"grp-btn icon-only",onClick:M,title:`Flow direction: ${se[d]} (click to rotate)`,"aria-label":`Flow direction: ${se[d]}`,children:e.jsx("span",{className:"dir-arrow",style:{transform:`rotate(${Ae[d]}deg)`},children:e.jsx(Be,{size:15})})}),i&&e.jsxs("div",{className:"layout-menu",role:"menu",children:[e.jsx("button",{onClick:()=>m("auto"),children:"Auto (by size)"}),e.jsx("button",{onClick:()=>m("hierarchical"),children:"Hierarchical"}),e.jsx("button",{onClick:()=>m("clustered"),children:"Clustered (by area)"}),e.jsx("button",{onClick:()=>m("grid"),children:"Grid"})]})]})}function $e(){const[s,i]=a.useState(null),[f,d]=a.useState(null),[g,v]=a.useState(null),[y,x]=a.useState(null),[j,m]=a.useState(""),[M,o]=a.useState(!0),[c,p]=a.useState(!1),[D,w]=a.useState(()=>new Set),[Q,A]=a.useState(null),[S,I]=a.useState(null),[_,$]=a.useState(!1),[k,K]=a.useState(()=>new Set),[B,C]=a.useState(!1),[L,u]=a.useState(0),b=a.useRef(null),R=a.useRef(""),F=a.useRef(!1),q=a.useMemo(()=>{const t=new Map;return s==null||s.subject_areas.forEach((r,n)=>t.set(r.id,Y[n%Y.length])),t},[s]),U=a.useMemo(()=>{const t=new Map;if(!s)return t;const r=new Map;return s.subject_areas.forEach(n=>{(n.members??[]).forEach(h=>r.set(h,n.id))}),s.entities.forEach(n=>{var E,V,X;const P=((E=n.conceptual)!=null&&E.id?r.get(n.conceptual.id):void 0)??((X=(V=n.conceptual)==null?void 0:V.subject_area)==null?void 0:X.id)??"";t.set(n.id,P)}),t},[s]),te=a.useMemo(()=>{const t=new Map;return U.forEach(r=>t.set(r,(t.get(r)??0)+1)),t},[U]),T=(s==null?void 0:s.read_only)??!0,ne=a.useMemo(()=>me(T),[T]),H=a.useMemo(()=>new URLSearchParams(window.location.search),[]),z=H.get("minimal")==="1";a.useEffect(()=>{z&&p(!0)},[z]);const ae=a.useCallback(t=>{w(r=>{const n=new Set(r);return n.has(t)?n.delete(t):n.add(t),n})},[]),re=H.get("import")==="1",W=a.useRef(!1),N=a.useCallback(()=>{J().then(i).catch(t=>v(String(t))),Z().then(d).catch(()=>d(null)),ee().then(t=>C(!!(t.git&&!t.clean))).catch(()=>C(!1)),u(t=>t+1)},[]);a.useEffect(N,[N]),a.useEffect(()=>{const t=setInterval(()=>{J().then(r=>{i(n=>n&&r.fingerprint===n.fingerprint?n:(F.current||(R.current=r.fingerprint),z||(Z().then(d).catch(()=>d(null)),ee().then(h=>C(!!(h.git&&!h.clean))).catch(()=>C(!1))),u(h=>h+1),r))}).catch(()=>{})},3e3);return()=>clearInterval(t)},[z]),a.useEffect(()=>{s!=null&&s.fingerprint&&(R.current=s.fingerprint)},[s]);const G=a.useCallback(async(t,r)=>{if(s){F.current=!0;try{const n=await he(t,r,R.current||s.fingerprint);return n!=null&&n.fingerprint&&(R.current=n.fingerprint),N(),n}catch(n){throw n instanceof ue&&n.status===409?(x("Model changed on disk — view refreshed, please retry."),N()):x(n instanceof Error?n.message:String(n)),n}finally{F.current=!1}}},[s,N]);a.useEffect(()=>{if(!y)return;const t=setTimeout(()=>x(null),5e3);return()=>clearTimeout(t)},[y]),a.useEffect(()=>{if(!s||W.current)return;const t=H.get("focus");if(!t)return;const r=s.entities.find(n=>{var h;return n.name===t||((h=n.conceptual)==null?void 0:h.name)===t});r&&(W.current=!0,requestAnimationFrame(()=>{var n;return(n=b.current)==null?void 0:n.focusEntity(r.id)}))},[s,H]);const ce=a.useCallback(()=>{var n;if(!s)return;const t=j.trim().toLowerCase();if(!t)return;const r=s.entities.find(h=>{var E;return(h.name.toLowerCase()+" "+(((E=h.conceptual)==null?void 0:E.name.toLowerCase())??"")+" "+h.attributes.map(V=>V.name.toLowerCase()).join(" ")).includes(t)});r&&((n=b.current)==null||n.focusEntity(r.id))},[s,j]);return a.useEffect(()=>{const t=r=>{var h,P,E;const n=(h=document.activeElement)==null?void 0:h.tagName;n==="INPUT"||n==="TEXTAREA"||n==="SELECT"||(r.key==="/"&&(r.preventDefault(),(P=document.getElementById("mdl-search"))==null||P.focus()),r.key==="n"&&!T&&((E=b.current)==null||E.newEntity()),r.key==="Escape"&&(A(null),m(""),I(null)))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[T]),g?e.jsxs("div",{className:"splash error",children:[e.jsxs("h1",{children:["◮"," Modelith"]}),e.jsx("p",{children:"Could not load the model:"}),e.jsx("pre",{children:g}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl serve"})," pointed at a model directory?"]})]}):s?e.jsxs("div",{className:"app",children:[!z&&e.jsx(Pe,{doc:s,diagnostics:f,query:j,onQuery:m,onSubmitQuery:ce,showTypes:M,onToggleTypes:()=>o(t=>!t),collapseDetail:c,onToggleCollapse:()=>p(t=>!t),onFitView:()=>{var t;return(t=b.current)==null?void 0:t.fitView()},onRelayout:(t,r)=>{var n;return(n=b.current)==null?void 0:n.relayout(t,r)},onRefresh:N,readOnly:T,dirty:B,panelTab:S,onPanelTab:t=>I(r=>r===t?null:t),onNewEntity:()=>{var t;return(t=b.current)==null?void 0:t.newEntity()},exec:G,onImported:N,onImportBatch:xe,openImport:re,onToggleDrawer:()=>$(t=>!t),drawerOpen:_,filterCount:k.size}),e.jsxs("div",{className:"canvas-wrap",children:[e.jsx(je,{doc:s,caps:ne,exec:G,selectedId:S?null:Q,onSelect:A,query:j,showTypes:M,collapseDetail:c,expandedEntities:D,onToggleExpand:ae,activeAreas:k,onError:x,handleRef:b}),!z&&e.jsx(Fe,{doc:s,saColors:q,countByArea:te,activeAreas:k,onSetActive:K,open:_,onClose:()=>$(!1)}),S&&e.jsx(pe,{tab:S,onClose:()=>I(null),readOnly:T,refreshKey:L,onModelChanged:N,onFocusEntity:t=>{var r;I(null),(r=b.current)==null||r.focusEntity(t)}}),y&&e.jsx("div",{className:"toast",children:y})]})]}):e.jsxs("div",{className:"splash",children:["◮"," loading model…"]})}function Ke(){return e.jsx(de,{children:e.jsx($e,{})})}ie.createRoot(document.getElementById("root")).render(e.jsx(oe.StrictMode,{children:e.jsx(Ke,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-CKl4JWY1.js b/packages/server/src/mdl_server/static/assets/sme-CKl4JWY1.js deleted file mode 100644 index f89a042..0000000 --- a/packages/server/src/mdl_server/static/assets/sme-CKl4JWY1.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-oAb1enKe.js","assets/client-DOmNpTln.js","assets/styles-BtrhR1xb.js","assets/styles-DxhL4SV-.css"])))=>i.map(i=>d[i]); -import{r as t,j as e,c as Re,R as De}from"./client-DOmNpTln.js";import{p as je,c as xe,d as fe,e as Pe,h as be,I as Ie,R as Oe,M as Be,O as Le,L as ze,i as We,j as qe,A as He,k as Ke,l as Ve,m as Ge,o as Ue,s as Fe,f as ce,n as Ye,q as Te,r as Je}from"./styles-BtrhR1xb.js";/* empty css */const Qe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function oe(s,n){let a="";for(let i=0;i>=5n;return a}function ge(){const s=BigInt(Date.now())&(1n<<48n)-1n,n=new Uint8Array(10);crypto.getRandomValues(n);let a=0n;for(const i of n)a=a<<8n|BigInt(i);return oe(s,10)+oe(a,16)}const Xe={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function Ze(s,n){const a=Xe[s];if(!a)return n;const i={...n};for(const l of a)i[l]||(i[l]=ge());return i}function ve(s,n){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${n.id??n.attribute_id}`;const a=n.id??n.entity_id??"",i=n.attribute_id??"";return i?`${s}:${a}:${i}`:`${s}:${a}`}function es({subjectArea:s,describe:n}){const[a,i]=t.useState([]),[l,j]=t.useState(null),[d,r]=t.useState(!1),h=t.useRef(null),N=t.useCallback(async(c,p)=>{p=Ze(c,p),c==="set_alignment"&&p.status===void 0&&(p={...p,status:"proposed"});const v=ve(c,p),{label:C,before:B,after:S}=n(c,p);return i(k=>{const A=k.filter(_=>_.key!==v);if(c.startsWith("delete_")){const _=p.id??p.entity_id;if(_&&k.some(m=>m.op.startsWith("create_")&&m.payload.id===_))return A.filter(m=>m.payload.id!==_)}return[...A,{op:c,payload:p,label:C,before:B,after:S,key:v}]}),{ok:!0,fingerprint:"",created_id:p.id??null,diagnostics:[]}},[n]);t.useEffect(()=>{if(!a.length){j(null);return}const c=setTimeout(()=>{var v;(v=h.current)==null||v.abort();const p=new AbortController;h.current=p,r(!0),je(a.map(C=>({op:C.op,payload:C.payload})),s,p.signal).then(j).catch(C=>{C.name!=="AbortError"&&j(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(c)},[a,s]);const x=t.useCallback(c=>{i(p=>p.filter(v=>v.key!==c))},[]),g=t.useCallback(()=>i([]),[]);return t.useMemo(()=>({pending:a,previewDoc:(l==null?void 0:l.model)??null,previewDiff:(l==null?void 0:l.diff)??null,diagnostics:(l==null?void 0:l.diagnostics)??[],failure:l&&l.failed_index!==null?{index:l.failed_index,error:l.error??"invalid change"}:null,busy:d,exec:N,drop:x,clear:g}),[a,l,d,N,x,g])}function ss(s){const n=new Map;for(const i of s)for(const l of["id","conceptual_id","from_fk_id"]){const j=i.payload[l];i.op.startsWith("create_")&&typeof j=="string"&&n.set(j,i.key)}if(!n.size)return new Set;const a=new Set;for(const i of s)if(!i.op.startsWith("create_"))for(const l of Object.values(i.payload))typeof l=="string"&&n.has(l)&&(a.add(i.key),a.add(n.get(l)));return a}function ns({user:s}){const[n,a]=t.useState(null);return t.useEffect(()=>{xe(s).then(a).catch(()=>{})},[s]),!(n!=null&&n.ok)||!n.git?null:n.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):n.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):n.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"your proposal branch"}),n.behind?e.jsxs("span",{className:"dim",children:["· ",n.base_branch," is ",n.behind," ahead"]}):null]})}function as({exec:s,canEdit:n,stagedMembers:a}){const[i,l]=t.useState([]),[j,d]=t.useState(""),[r,h]=t.useState(null),[N,x]=t.useState(""),[g,c]=t.useState(new Set),[p,v]=t.useState("ancestors"),[C,B]=t.useState(1),[S,k]=t.useState(null),[A,_]=t.useState(new Set),[M,m]=t.useState(null);t.useEffect(()=>{fe().then(o=>{l(o.subject_areas),d(w=>{var R;return w||((R=o.subject_areas[0])==null?void 0:R.id)||""})}).catch(o=>m(String(o)))},[]),t.useEffect(()=>{j&&(k(null),c(new Set),Pe(j).then(h).catch(o=>m(String(o))))},[j]);const $=t.useMemo(()=>a[j]??(r==null?void 0:r.members)??[],[a,j,r]),E=t.useMemo(()=>{const o=new Map;for(const w of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])o.set(w.id,w);return o},[r]),P=N.trim().toLowerCase(),L=o=>!P||o.name.toLowerCase().includes(P),u=t.useMemo(()=>new Set($),[$]),I=t.useMemo(()=>$.map(o=>E.get(o)).filter(o=>!!o&&L(o)),[$,E,P]),z=t.useMemo(()=>[...E.values()].filter(o=>!u.has(o.id)&&L(o)),[E,u,P]),O=t.useCallback(o=>{if(!r)return;const w=o.filter(H=>!(r.members??[]).includes(H)).length,R=(r.members??[]).filter(H=>!o.includes(H)).length;s("set_subject_area_members",{id:r.id,members:o,__label:`${r.name}: ${o.length} objects (+${w} −${R})`})},[r,s]),q=(o,w)=>{const R=w?[...$,...o.filter(H=>!u.has(H))]:$.filter(H=>!o.includes(H));O(R),c(new Set)},K=(o,w,R)=>{const H=new Set(o);H.has(w)?H.delete(w):H.add(w),R(H)},T=()=>{if(!r)return;const o=g.size?[...g]:$;if(!o.length){m("add an object first, or select one to expand from");return}m(null),be(r.id,{seeds:o,direction:p,levels:C}).then(w=>{k(w.hops),_(new Set(w.added))}).catch(w=>m(String(w)))},F=()=>{q([...A],!0),k(null)};if(M&&!r)return e.jsx("p",{className:"sme-splash error",children:M});if(!i.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const f=((r==null?void 0:r.inconsistent)??[]).filter(o=>!u.has(o));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:i.map(o=>e.jsxs("button",{className:"sme-sa"+(o.id===j?" active":""),onClick:()=>d(o.id),title:o.definition??void 0,children:[o.name,e.jsx("span",{className:"sme-sa-count",children:(a[o.id]??o.members).length})]},o.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:N,onChange:o=>x(o.target.value)})]}),f.length>0&&n&&e.jsxs("div",{className:"sa-nudge",children:[f.length," object",f.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>q(f,!0),children:["Add ",f.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Z,{title:`Available (${z.length})`,rows:z,picked:g,onToggle:o=>K(g,o,c),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(o=>!u.has(o)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!z.length,onClick:()=>q(z.map(o=>o.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!g.size,onClick:()=>q([...g].filter(o=>u.has(o)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!I.length,onClick:()=>q(I.map(o=>o.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Z,{title:`Included (${I.length})`,rows:I,picked:g,onToggle:o=>K(g,o,c),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:p,onChange:o=>v(o.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:C,onChange:o=>B(Math.max(1,Math.min(10,Number(o.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!n,children:"Preview"})]}),M&&e.jsx("p",{className:"sme-error-line",children:M}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(o=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:A.has(o.id),onChange:()=>K(A,o.id,_)}),e.jsx("span",{className:"sa-hop-name",children:o.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",o.via_name," · ",o.direction.replace(/s$/,""),", level ",o.level]})]},o.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>k(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:F,disabled:!A.size,children:["Add ",A.size," object",A.size===1?"":"s"]})]})]})]})]})]})}function Z({title:s,rows:n,picked:a,onToggle:i,homedHere:l}){const j=t.useMemo(()=>new Set(l),[l]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[n.map(d=>e.jsxs("li",{className:"sa-row"+(a.has(d.id)?" picked":""),onClick:()=>i(d.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:a.has(d.id)}),e.jsx("span",{className:"sa-row-name",children:d.name}),j.has(d.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},d.id)),!n.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function ts({objects:s,onCreate:n,onClose:a}){const[i,l]=t.useState(""),[j,d]=t.useState(""),[r]=t.useState(s),[h,N]=t.useState([]),[x,g]=t.useState(new Set),[c,p]=t.useState(""),[v,C]=t.useState("ancestors"),[B,S]=t.useState(1),[k,A]=t.useState(null),[_,M]=t.useState(new Set),[m,$]=t.useState(""),[E,P]=t.useState(null);t.useEffect(()=>{fe().then(f=>{f.subject_areas[0]&&$(f.subject_areas[0].id)}).catch(()=>{})},[]);const L=t.useMemo(()=>new Set(h),[h]),u=c.trim().toLowerCase(),I=f=>!u||f.name.toLowerCase().includes(u),z=t.useMemo(()=>h.map(f=>r.find(o=>o.id===f)).filter(f=>!!f&&I(f)),[h,r,u]),O=t.useMemo(()=>r.filter(f=>!L.has(f.id)&&I(f)),[r,L,u]),q=(f,o)=>{N(w=>o?[...w,...f.filter(R=>!w.includes(R))]:w.filter(R=>!f.includes(R))),g(new Set)},K=(f,o,w)=>{const R=new Set(f);R.has(o)?R.delete(o):R.add(o),w(R)},T=()=>{const f=x.size?[...x]:h;if(!f.length){P("pick an object to expand from first");return}m&&(P(null),be(m,{seeds:f,direction:v,levels:B}).then(o=>{const w=o.hops.filter(R=>!L.has(R.id));A(w),M(new Set(w.map(R=>R.id)))}).catch(o=>P(String(o))))},F=()=>{i.trim()&&n(i.trim(),j.trim(),h)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:a,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:f=>f.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:i,placeholder:"e.g. UseCase-ApraStressTesting",onChange:f=>l(f.target.value),onKeyDown:f=>f.key==="Enter"&&h.length===0&&F()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:j,placeholder:"what this view is for",onChange:f=>d(f.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:c,onChange:f=>p(f.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(Z,{title:`Available (${O.length})`,rows:O,picked:x,onToggle:f=>K(x,f,g),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(f=>!L.has(f)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!O.length,onClick:()=>q(O.map(f=>f.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!x.size,onClick:()=>q([...x].filter(f=>L.has(f)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!z.length,onClick:()=>q(z.map(f=>f.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(Z,{title:`Included (${z.length})`,rows:z,picked:x,onToggle:f=>K(x,f,g),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:v,onChange:f=>C(f.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:B,onChange:f=>S(Math.max(1,Math.min(10,Number(f.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!m,title:m?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),k&&e.jsxs("div",{className:"sa-hops",children:[k.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),k.map(f=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:_.has(f.id),onChange:()=>K(_,f.id,M)}),e.jsx("span",{className:"sa-hop-name",children:f.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",f.via_name," · ",f.direction.replace(/s$/,""),", level ",f.level]})]},f.id)),k.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>A(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!_.size,onClick:()=>{q([..._],!0),A(null)},children:["Add ",_.size]})]})]})]}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:a,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!i.trim(),onClick:F,children:["Create",h.length>0&&` with ${h.length} object${h.length===1?"":"s"}`]})]})]})})}const rs=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function is({doc:s,subjectArea:n,onSubjectArea:a,exec:i,canEdit:l,direct:j=!1,onSelectEntity:d,onImported:r,query:h="",busy:N}){var M;const[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(!1),[B,S]=t.useState(null),k=t.useRef(null),A=t.useMemo(()=>({canEdit:l,mode:j?"direct":"staged",allow:j?void 0:rs,canArbitrate:j,canCommit:j}),[l,j]),_=t.useCallback(m=>{g(m),m&&(d==null||d(m))},[d]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),l&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>C(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(n===""?" active":""),onClick:()=>a(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((M=s.counts)==null?void 0:M.entities_total)??s.entities.length})]}),s.subject_areas.map(m=>e.jsxs("button",{className:"sme-sa"+(n===m.id?" active":""),onClick:()=>a(m.id),title:m.definition??void 0,children:[m.name,e.jsx("span",{className:"sme-sa-count",children:m.member_count??0})]},m.id))]}),v&&e.jsx(ts,{objects:s.entities.flatMap(m=>m.conceptual?[{id:m.conceptual.id,kind:"conceptual_entity",name:m.conceptual.name,definition:m.conceptual.definition??null,synonyms:[],subject_area:m.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>C(!1),onCreate:(m,$,E)=>{const P=ge();i("create_subject_area",{name:m,...$?{definition:$}:{},id:P}),E.length&&i("set_subject_area_members",{id:P,members:E}),C(!1),a(P)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),l&&e.jsx("button",{className:"erd-action",onClick:()=>{var m;return(m=k.current)==null?void 0:m.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var m;return(m=k.current)==null?void 0:m.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var m;return(m=k.current)==null?void 0:m.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(c==="ontology"?" on":""),onClick:()=>p(m=>m==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(c==="layers"?" on":""),onClick:()=>p(m=>m==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Ie,{exec:i,canEdit:l,onImported:r}),N&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!l&&e.jsx("span",{className:"sme-chip",children:"read-only"}),B&&e.jsx("span",{className:"erd-error",children:B})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Oe,{children:e.jsx(Be,{doc:s,caps:A,exec:i,selectedId:x,onSelect:_,query:h,onError:S,handleRef:k})}),c&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:c==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>p(null),children:"✕"})]}),c==="ontology"?e.jsx(Le,{}):e.jsx(ze,{refreshKey:0,onFocusEntity:m=>{var $;p(null),($=k.current)==null||$.focusEntity(m)}})]})]})]})]})}function ls({user:s,onView:n}){const[a,i]=t.useState(null),[l,j]=t.useState(null);if(t.useEffect(()=>{We(s).then(i).catch(r=>j(String(r)))},[s]),l)return e.jsx("p",{className:"sme-splash error",children:l});if(!a)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!a.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const d=a.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[d," open · base: ",a.base]})]}),!a.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),a.proposals.map(r=>e.jsx(os,{p:r,gh:a.gh,onView:()=>n(r.branch)},r.branch)),!a.gh&&a.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function cs(s,n){var a,i;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?n&&((a=s.pr)==null?void 0:a.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:n&&((i=s.pr)==null?void 0:i.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function os({p:s,gh:n,onView:a}){const i=cs(s,n);return e.jsxs("div",{className:"pr-card"+(i.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+i.kind,children:i.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",n&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?n&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:a,children:"View"})})]})]})}function ds({changes:s,routeAdvice:n,identity:a,user:i,onUser:l,onDrop:j,onClose:d,onProposed:r}){const h=!!(a&&a.source!=="anonymous"),[N,x]=t.useState(i||(localStorage.getItem("mdl.sme.user")??"")),[g,c]=t.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[p,v]=t.useState(""),[C,B]=t.useState(!1),S=Ne(C?p:g),[k,A]=t.useState(""),[_,M]=t.useState(!1),[m,$]=t.useState(null),[E,P]=t.useState(null),L=()=>{!h&&!N.trim()||(h||(localStorage.setItem("mdl.sme.user",N.trim()),l==null||l(N.trim())),M(!0),P(null),qe({user:h?"":N.trim(),slug:S,title:g,body:k.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then($).catch(u=>P(u instanceof He?u.message:String(u))).finally(()=>M(!1)))};return m!=null&&m.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(m),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:m.message}),m.pr_url&&e.jsx("p",{children:e.jsx("a",{href:m.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),m.compare_url&&e.jsx("p",{children:e.jsx("a",{href:m.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:m.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(m),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:d,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:d,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,I)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>j(I),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},I))}),h?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:a.name}),a.email?` (${a.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:N,onChange:u=>x(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:g,onChange:u=>c(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:C?p:S,onChange:u=>{B(!0),v(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:k,onChange:u=>A(u.target.value)})]}),e.jsx(hs,{cl:n,user:h?a.name:N,changeSlug:S}),E&&e.jsx("p",{className:"sme-error-line",children:E}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:d,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:_||!h&&!N.trim()||s.length===0,onClick:L,children:_?"Submitting…":"Submit for review"})]})]})})}function hs({cl:s,user:n,changeSlug:a}){if(!(s!=null&&s.primary))return null;const i=s.reviewers_actual??s.reviewers,l=Ne(n||"you"),j=s.routes.length>1;return e.jsxs(e.Fragment,{children:[j&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",i.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",l,"/",a||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:i.join(", ")})]})]})]})}function Ne(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const ye=[{id:"all",label:"All"},{id:"additions",label:"Additions"},{id:"modifications",label:"Modifications"},{id:"moves",label:"Renames"},{id:"deletions",label:"Deletions"},{id:"conflicts",label:"Conflicts"}];function we(s,n){switch(n){case"all":return!0;case"additions":return s.change==="added";case"deletions":return s.change==="removed";case"moves":return s.renamed;case"modifications":return s.change==="modified"&&!s.renamed;case"conflicts":return s.severity==="breaking"}}function ms(s,n){return n==="all"?s.objects:s.objects.filter(a=>we(a,n))}function us(s){const n={};for(const{id:a}of ye)n[a]=a==="all"?s.objects.length:s.objects.filter(i=>we(i,a)).length;return n}function J(s,n){return`${s}\0${n}`}function ke(s){const n=s.fields.map(a=>J(s.ulid,a.field));for(const a of s.children)for(const i of a.fields)n.push(J(s.ulid,`${a.ulid}.${i.field}`));return n.length?n:[J(s.ulid,"*")]}function ps(s,n){const a=ke(s),i=a.filter(l=>n.has(l)).length;return i===0?"none":i===a.length?"all":"some"}function js(s){const n=new Set;for(const a of s.objects)for(const i of ke(a))n.add(i);return n}const xs="modulepreload",fs=function(s){return"/"+s},de={},bs=function(n,a,i){let l=Promise.resolve();if(a&&a.length>0){document.getElementsByTagName("link");const d=document.querySelector("meta[property=csp-nonce]"),r=(d==null?void 0:d.nonce)||(d==null?void 0:d.getAttribute("nonce"));l=Promise.allSettled(a.map(h=>{if(h=fs(h),h in de)return;de[h]=!0;const N=h.endsWith(".css"),x=N?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${h}"]${x}`))return;const g=document.createElement("link");if(g.rel=N?"stylesheet":xs,N||(g.as="script"),g.crossOrigin="",g.href=h,r&&g.setAttribute("nonce",r),document.head.appendChild(g),N)return new Promise((c,p)=>{g.addEventListener("load",c),g.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${h}`)))})}))}function j(d){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=d,window.dispatchEvent(r),!r.defaultPrevented)throw d}return l.then(d=>{for(const r of d||[])r.status==="rejected"&&j(r.reason);return n().catch(j)})};function gs({diff:s,selectable:n,selected:a,onToggle:i,onToggleField:l}){var p;const[j,d]=t.useState("all"),r=t.useMemo(()=>ms(s,j),[s,j]),h=t.useMemo(()=>us(s),[s]),[N,x]=t.useState(((p=s.objects[0])==null?void 0:p.ulid)??null),g=t.useMemo(()=>r.find(v=>v.ulid===N)??r[0]??s.objects[0]??null,[r,s,N]),c=!!l;return s.objects.length?e.jsxs("div",{className:"rv-wrap",children:[n!==void 0&&e.jsx("div",{className:"rv-filters",role:"tablist","aria-label":"Filter changes",children:ye.filter(v=>v.id==="all"||h[v.id]>0).map(v=>e.jsxs("button",{role:"tab","aria-selected":j===v.id,className:"rv-filter"+(j===v.id?" active":""),onClick:()=>d(v.id),children:[v.label,e.jsx("span",{className:"rv-filter-count",children:h[v.id]})]},v.id))}),e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:r.length===0?e.jsx("p",{className:"sme-placeholder",children:"No changes match this filter."}):r.map(v=>e.jsx(Ns,{obj:v,active:v.ulid===((g==null?void 0:g.ulid)??""),selectable:n,perField:c,objState:c?ps(v,a??new Set):void 0,checkedObject:(a==null?void 0:a.has(v.ulid))??!0,onSelect:()=>x(v.ulid),onToggleObject:()=>i==null?void 0:i(v.ulid),onToggleField:l},v.ulid))}),e.jsx("div",{className:"rv-detail",children:g&&e.jsx(ys,{obj:g,selectable:n,perField:c,selected:a,onToggleField:l})})]})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function vs(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function Ns({obj:s,active:n,selectable:a,perField:i,objState:l,checkedObject:j,onSelect:d,onToggleObject:r,onToggleField:h}){const N=c=>{c&&i&&(c.indeterminate=l==="some")},x=i?l!=="none":j,g=()=>{if(!i)return r();for(const c of s.fields)h==null||h(s.ulid,c.field);for(const c of s.children)for(const p of c.fields)h==null||h(s.ulid,`${c.ulid}.${p.field}`);!s.fields.length&&!s.children.length&&(h==null||h(s.ulid,"*"))};return e.jsxs("div",{className:"rv-obj"+(n?" active":""),onClick:d,children:[e.jsxs("div",{className:"rv-obj-head",children:[a&&e.jsx("input",{type:"checkbox",ref:N,checked:x,onClick:c=>c.stopPropagation(),onChange:g,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",vs(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((c,p)=>e.jsx("li",{children:c.label},c.field+p)),s.children.flatMap(c=>c.fields.map((p,v)=>e.jsxs("li",{children:[p.label,": ",c.name_before??c.name_after]},c.ulid+v)))]})]})}function ys({obj:s,selectable:n,perField:a,selected:i,onToggleField:l}){const j=s.children.flatMap(r=>r.fields.map(h=>({field:h,on:r.name_before??r.name_after??"",key:`${r.ulid}.${h.field}`}))),d=(r,h)=>{if(!n||!a)return h;const N=J(s.ulid,r);return e.jsxs("div",{className:"rv-field-pick",children:[e.jsx("input",{type:"checkbox",checked:(i==null?void 0:i.has(N))??!0,onChange:()=>l==null?void 0:l(s.ulid,r),"aria-label":`include change: ${r}`}),e.jsx("div",{className:"rv-field-body",children:h})]})};return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((r,h)=>e.jsx("div",{children:d(r.field,e.jsx(he,{f:r}))},r.field+h)),j.map(({field:r,on:h,key:N},x)=>e.jsx("div",{children:d(N,e.jsx(he,{f:r,on:h}))},"c"+x)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function he({f:s,on:n}){var i,l,j;const a=n?`${s.label}: ${n}`:s.label;if((i=s.breaks)!=null&&i.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[n??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(d=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",d.name]}),e.jsxs("span",{className:"p",children:[d.target," · ",d.materialization]})]},d.name))})]})]});if(s.kind==="definition_changed"){const d=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:ue(d,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:ue(r,d,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const d=new Set(((l=s.before)==null?void 0:l.map(String))??[]),r=new Set(((j=s.after)==null?void 0:j.map(String))??[]),h=[...r].filter(x=>!d.has(x)),N=[...d].filter(x=>!r.has(x));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-chips",children:[h.map(x=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",x]},"a"+x)),N.map(x=>e.jsxs("span",{className:"rv-chip-del",children:["− ",x]},"r"+x))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:a}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:me(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:me(s.after)})]})]})}function me(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function ue(s,n,a){const i=s.split(/(\s+)/),l=n.split(/(\s+)/),j=c=>c.toLowerCase().replace(/[.,;:]/g,""),d=i.length,r=l.length,h=Array.from({length:d+1},()=>new Array(r+1).fill(0));for(let c=d-1;c>=0;c--)for(let p=r-1;p>=0;p--)h[c][p]=j(i[c])===j(l[p])?h[c+1][p+1]+1:Math.max(h[c+1][p],h[c][p+1]);const N=new Set;let x=0,g=0;for(;x=h[x][g+1]?x++:g++;return i.map((c,p)=>!c.trim()||N.has(p)?e.jsx("span",{children:c},p):a==="del"?e.jsx("del",{children:c},p):e.jsx("ins",{children:c},p))}const ws=t.lazy(()=>bs(()=>import("./ModelDiagram-oAb1enKe.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function ks({stagedDiff:s,stagedRoute:n,user:a,onBack:i,onSubmit:l,excluded:j,onToggleField:d,selectable:r}){const[h,N]=t.useState(null),[x,g]=t.useState(null),[c,p]=t.useState(null),[v,C]=t.useState(null),[B,S]=t.useState(null),[k,A]=t.useState("changes"),_=t.useCallback(()=>{xe(a).then(m=>{C(m);const $=m.base_branch??"main";s?N(s):Ke("HEAD").then(E=>E.ok?N(E):S(E.error??"could not read the model")).catch(E=>S(String(E))),n?g(n):Ve("HEAD").then(g).catch(()=>{}),Ge($).then(p).catch(()=>{})}).catch(m=>S(String(m)))},[a,s,n]);if(t.useEffect(_,[_]),B)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:i,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:B})})]});if(!h)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const M=new Set([...js(h)].filter(m=>!j.has(m)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(pe,{onBack:i,diff:h,tab:k,onTab:A}),k==="changes"?e.jsx(gs,{diff:h,selectable:r,selected:M,onToggleField:d}):e.jsx(t.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(ws,{severityByUlid:Ss(h)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(_s,{cl:x}),e.jsx(Cs,{conf:c,ctx:v}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:i,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:!h.objects.length||(v?!v.can_propose:!1),title:v&&!v.can_propose?"the working tree has uncommitted changes":"",onClick:()=>l(x),children:"Submit for review →"})]})]})]})}function Ss(s){const n=new Map;for(const a of s.objects)n.set(a.ulid,a.severity);return n}function pe({onBack:s,diff:n,tab:a,onTab:i}){const l=n==null?void 0:n.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),l&&e.jsxs("div",{className:"rv-sub",children:[l.objects," object",l.objects===1?"":"s"," ·"," ",(l.breaking??0)+(l.additive??0)+(l.cosmetic??0)," changes",(n==null?void 0:n.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",l.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:n==null?void 0:n.base.label})]})]}),i&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(a==="changes"?" active":""),onClick:()=>i("changes"),children:["Changes ",(n==null?void 0:n.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(a==="diagram"?" active":""),onClick:()=>i("diagram"),children:"Diagram"})]})]})}function _s({cl:s}){if(!(s!=null&&s.primary))return null;const n=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:n.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(a=>e.jsx("code",{children:a},a))]})]})}function Cs({conf:s,ctx:n}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(n==null?void 0:n.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(a=>a.path).join(", ")})]}):null}function As({term:s,canEdit:n,pendingCount:a,onEdit:i}){var l,j;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((l=s.subject_area)==null?void 0:l.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),n&&e.jsx("button",{className:"sme-primary",onClick:i,children:a>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(d=>e.jsx("span",{className:"sme-chip",children:d},d))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((j=s.ontology)==null?void 0:j.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(d=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:d.logical_entity}),d.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),d.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:d.physical.map(r=>r.name).join(", ")})]},d.logical_id))})]})]})}function $s({term:s,pending:n,onStage:a,onDone:i,catalogOwned:l=[],catalog:j=null}){var $,E,P,L;const d=u=>l.includes(u),r=u=>n.find(I=>I.label===u),h=(u,I)=>{const z=r(u);return z?z.after:I},[N,x]=t.useState(h("Definition",s.definition??"")),[g,c]=t.useState(s.synonyms),[p,v]=t.useState(""),[C,B]=t.useState((($=s.stewardship)==null?void 0:$.owner)??""),[S,k]=t.useState(((E=s.stewardship)==null?void 0:E.steward)??""),A=()=>{N.trim()!==(s.definition??"").trim()&&a({op:"set_definition",payload:{id:s.id,definition:N.trim()},label:"Definition",before:s.definition??"(none)",after:N.trim()||"(none)"})},_=(u,I)=>{var z,O;a({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:I||null},label:"Stewardship",before:`owner: ${((z=s.stewardship)==null?void 0:z.owner)??"—"} · steward: ${((O=s.stewardship)==null?void 0:O.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${I||"—"}`})},M=u=>{c(u),a({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},m=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:i,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(d("definition")||d("synonyms")||d("stewardship"))&&j&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:j.name}),". Edit them there — this glossary mirrors them read-only.",j.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:j.url,target:"_blank",rel:"noreferrer",children:["Open in ",j.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),d("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:N,onChange:u=>x(u.target.value),onBlur:A,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),d("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[g.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>M(g.filter(I=>I!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:p,placeholder:"add + Enter",onChange:u=>v(u.target.value),onKeyDown:u=>{u.key==="Enter"&&p.trim()&&(M([...g,p.trim()]),v(""))}})]})]}),!m&&(d("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((P=s.stewardship)==null?void 0:P.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((L=s.stewardship)==null?void 0:L.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:C,onChange:u=>B(u.target.value),onBlur:()=>_(C,S)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:S,onChange:u=>k(u.target.value),onBlur:()=>_(C,S)})]})]})),e.jsx(Ms,{term:s,onStage:a}),e.jsx("p",{className:"sme-note",children:n.length>0?`${n.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const Es=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function Ms({term:s,onStage:n}){var g;const[a,i]=t.useState(!1),[l,j]=t.useState(s.name),[d,r]=t.useState([]),[h,N]=t.useState("skos:closeMatch"),x=c=>{j(c),Ue(c).then(p=>r(p.results)).catch(()=>r([]))};return a?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:l,onChange:c=>x(c.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:d.map(c=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var p;n({op:"set_alignment",payload:{id:s.id,aligns_to:c.prefixed,alignment:h,status:"proposed"},label:"Standard mapping",before:((p=s.ontology)==null?void 0:p.aligns_to)??"(none)",after:`${c.prefixed} (${h}, proposed)`}),i(!1)},children:[e.jsx("strong",{children:c.label})," ",e.jsx("code",{children:c.prefixed}),c.definition&&e.jsx("span",{className:"sme-align-def",children:c.definition})]},c.iri))}),e.jsx("select",{value:h,onChange:c=>N(c.target.value),children:Es.map(c=>e.jsx("option",{children:c},c))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(g=s.ontology)!=null&&g.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>i(!0),children:"Propose a mapping…"})]})]})}function Rs(){const[s,n]=t.useState(null),[a,i]=t.useState(!0),[l,j]=t.useState(null),[d,r]=t.useState(""),[h,N]=t.useState(null),[x,g]=t.useState(""),[c,p]=t.useState(""),[v,C]=t.useState(null),[B,S]=t.useState(!1),[k,A]=t.useState([]),[_,M]=t.useState(!1),[m,$]=t.useState(window.location.hash==="#proposals"?"proposals":"browse"),[E,P]=t.useState(new Set),[L,u]=t.useState(()=>localStorage.getItem("mdl.sme.user")??""),[I,z]=t.useState(null),[O,q]=t.useState(null),[K,T]=t.useState(!1),[F,f]=t.useState(null),o=t.useCallback((b,y)=>{var Y;const W=X=>{var re;const U=O==null?void 0:O.entities.find(ie=>{var le;return ie.id===X||((le=ie.conceptual)==null?void 0:le.id)===X});return((re=U==null?void 0:U.conceptual)==null?void 0:re.name)??(U==null?void 0:U.name)??"object"},D=W(y.id??y.entity_id);switch(b){case"create_entity":return{label:`New entity: ${y.name}`,before:"—",after:String(y.name??"")};case"create_relationship":return{label:`New relationship: ${W(y.from_entity)} → ${W(y.to_entity)}`,before:"—",after:String(y.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${D}`,before:D,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[y.cardinality,y.optionality,y.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${D}`,before:"",after:String(y.definition??"")};case"add_attribute":return{label:`Attribute added to ${D}`,before:"—",after:String(y.name??"")};case"delete_attribute":return{label:`Attribute removed from ${D}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${D}`,before:"",after:JSON.stringify(y)};case"rename_entity":return{label:`Renamed ${D}`,before:D,after:String(y.name??"")};case"set_subject_area":return{label:`Subject area: ${D}`,before:"",after:String(y.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${D}`,before:"",after:String(y.steward??"")};case"set_subject_area_members":{const X=y.__label,U=((Y=y.members)==null?void 0:Y.length)??0;return{label:X??"Subject area members",before:"",after:`${U} object${U===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${D}`,before:"",after:String(y.aligns_to??"")};default:return{label:`${b.replace(/_/g," ")}: ${D}`,before:"",after:""}}},[O]),w=es({subjectArea:x,describe:o}),R=t.useCallback(async(b,y)=>{const W=(O==null?void 0:O.fingerprint)??"",D=await Fe(b,y,W);return await ce(x||void 0).then(q),D},[O,x]),H=t.useMemo(()=>{const b={};for(const y of w.pending)y.op==="set_subject_area_members"&&(b[y.payload.id]=y.payload.members??[]);return b},[w.pending]),V=t.useMemo(()=>[...k,...w.pending],[k,w.pending]),[Se,ee]=t.useState(null),[_e,se]=t.useState(null);t.useEffect(()=>{if(!V.length){ee(null),se(null);return}const b=new AbortController,y=V.map(W=>({op:W.op,payload:W.payload}));return je(y,x||void 0,b.signal).then(W=>ee(W.diff??null)).catch(W=>{W.name!=="AbortError"&&ee(null)}),Ye(y).then(se).catch(()=>se(null)),()=>b.abort()},[V,x]);const Ce=t.useMemo(()=>ss(V),[V]).size{$(b),window.location.hash=b==="browse"?"":`#${b}`},[]);t.useEffect(()=>{const b=()=>{const y=window.location.hash;$(y==="#proposals"?"proposals":y==="#review"?"review":y==="#model"?"model":y==="#areas"?"areas":"browse")};return b(),window.addEventListener("hashchange",b),()=>window.removeEventListener("hashchange",b)},[]);const ne=t.useCallback(()=>{Te({subject_area:x,q:c}).then(n).catch(b=>N(String(b)))},[x,c]);t.useEffect(ne,[ne]),t.useEffect(()=>{ce(x||void 0).then(b=>{i(b.read_only),r(b.project.name),q(b),T(!!b.direct),z(b.identity??null),b.identity&&b.identity.source!=="anonymous"&&b.identity.name&&u(b.identity.name)}).catch(()=>{}),Je().then(j).catch(()=>{})},[x]);const ae=t.useMemo(()=>(s==null?void 0:s.terms.find(b=>b.id===v))??null,[s,v]),Ae=t.useCallback(b=>{const y=b.key??ve(b.op,b.payload);A(W=>[...W.filter(D=>D.key!==y),{...b,key:y}])},[]),$e=b=>A(y=>y.filter((W,D)=>D!==b)),Ee=()=>{A([]),M(!1),S(!1),ne()};if(h)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:h}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const Q=!a,te=(l==null?void 0:l.catalog_owned_fields)??[],Me=te.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:d}),a&&e.jsx("span",{className:"sme-chip",children:"read-only"}),Me&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${l==null?void 0:l.catalog_name}`,children:["mirrors ",l==null?void 0:l.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:c,onChange:b=>p(b.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(m==="browse"?" active":""),onClick:()=>G("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(m==="model"?" active":""),onClick:()=>G("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(m==="areas"?" active":""),onClick:()=>G("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(m==="proposals"?" active":""),onClick:()=>G("proposals"),children:"My proposals"})]}),V.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>G("review"),children:[V.length," change",V.length>1?"s":""," · Review →"]})]}),m==="browse"&&e.jsx(ns,{user:L}),m==="review"?e.jsx(ks,{stagedDiff:Se,stagedRoute:_e,user:L,selectable:Ce,excluded:E,onToggleField:(b,y)=>P(W=>{const D=new Set(W),Y=J(b,y);return D.has(Y)?D.delete(Y):D.add(Y),D}),onBack:()=>G("browse"),onSubmit:b=>{f(b),M(!0)}}):m==="model"?w.previewDoc??O?e.jsx(is,{doc:w.previewDoc??O,subjectArea:x,onSubjectArea:g,exec:K?R:w.exec,canEdit:Q,direct:K,onImported:()=>G("review"),query:c,busy:w.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):m==="areas"?e.jsx(as,{exec:w.exec,canEdit:Q,stagedMembers:H}):m==="proposals"?e.jsx(ls,{user:L,onView:()=>G("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(x===""?" active":""),onClick:()=>g(""),children:"All subject areas"}),s.subject_areas.map(b=>e.jsx("button",{className:"sme-sa"+(x===b.id?" active":""),onClick:()=>g(b.id),title:b.definition??void 0,children:b.name},b.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(b=>{var y;return e.jsxs("li",{className:"sme-list-item"+(b.id===v?" active":""),onClick:()=>{C(b.id),S(!1)},children:[e.jsx("span",{className:"sme-term-name",children:b.name}),b.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((y=b.ontology)==null?void 0:y.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:b.definition??"no definition yet"})]},b.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:ae?B&&Q?e.jsx($s,{term:ae,pending:k,onStage:Ae,onDone:()=>S(!1),catalogOwned:te,catalog:l?{name:l.catalog_name,url:l.catalog_url}:null}):e.jsx(As,{term:ae,canEdit:Q,pendingCount:k.length,onEdit:()=>S(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),_&&e.jsx(ds,{changes:V,routeAdvice:F,identity:I,user:L,onUser:b=>{u(b),localStorage.setItem("mdl.sme.user",b)},onDrop:$e,onClose:()=>M(!1),onProposed:()=>{Ee(),w.clear(),P(new Set),G("proposals")}})]})}Re.createRoot(document.getElementById("root")).render(e.jsx(De.StrictMode,{children:e.jsx(Rs,{})})); diff --git a/packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js b/packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js new file mode 100644 index 0000000..1da7f69 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-CRUBpPVn.js","assets/client-DOmNpTln.js","assets/styles-B90OKxCj.js","assets/styles-DxhL4SV-.css"])))=>i.map(i=>d[i]); +import{r as a,j as e,c as Ge,R as Ke}from"./client-DOmNpTln.js";import{p as xe,c as be,d as ge,e as Ve,h as ve,I as Ue,R as Fe,M as Te,O as Ye,L as Je,i as Qe,j as Xe,A as Ze,k as es,l as ss,m as ns,o as ts,n as as,s as rs,f as ie,q as is,r as ls,t as cs,b as os}from"./styles-B90OKxCj.js";/* empty css */const ds="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function he(s,n){let t="";for(let i=0;i>=5n;return t}function Ne(){const s=BigInt(Date.now())&(1n<<48n)-1n,n=new Uint8Array(10);crypto.getRandomValues(n);let t=0n;for(const i of n)t=t<<8n|BigInt(i);return he(s,10)+he(t,16)}const hs={create_entity:["id","conceptual_id"],create_relationship:["id","from_fk_id"],create_subject_area:["id"],create_term:["id"],create_domain:["id"],create_code_set:["id"],create_key_group:["id"],create_category:["id"],add_attribute:["id"]};function ms(s,n){const t=hs[s];if(!t)return n;const i={...n};for(const l of t)i[l]||(i[l]=Ne());return i}function ye(s,n){if(s.startsWith("create_")||s==="add_attribute")return`${s}:${n.id??n.attribute_id}`;const t=n.id??n.entity_id??"",i=n.attribute_id??"";return i?`${s}:${t}:${i}`:`${s}:${t}`}function us({subjectArea:s,describe:n}){const[t,i]=a.useState([]),[l,m]=a.useState(null),[c,r]=a.useState(!1),d=a.useRef(null),v=a.useCallback(async(o,f)=>{f=ms(o,f),o==="set_alignment"&&f.status===void 0&&(f={...f,status:"proposed"});const N=ye(o,f),{label:_,before:B,after:E}=n(o,f);return i(S=>{const C=S.filter(R=>R.key!==N);if(o.startsWith("delete_")){const R=f.id??f.entity_id;if(R&&S.some(w=>w.op.startsWith("create_")&&w.payload.id===R))return C.filter(w=>w.payload.id!==R)}return[...C,{op:o,payload:f,label:_,before:B,after:E,key:N}]}),{ok:!0,fingerprint:"",created_id:f.id??null,diagnostics:[]}},[n]);a.useEffect(()=>{if(!t.length){m(null);return}const o=setTimeout(()=>{var N;(N=d.current)==null||N.abort();const f=new AbortController;d.current=f,r(!0),xe(t.map(_=>({op:_.op,payload:_.payload})),s,f.signal).then(m).catch(_=>{_.name!=="AbortError"&&m(null)}).finally(()=>r(!1))},250);return()=>clearTimeout(o)},[t,s]);const j=a.useCallback(o=>{i(f=>f.filter(N=>N.key!==o))},[]),b=a.useCallback(()=>i([]),[]);return a.useMemo(()=>({pending:t,previewDoc:(l==null?void 0:l.model)??null,previewDiff:(l==null?void 0:l.diff)??null,diagnostics:(l==null?void 0:l.diagnostics)??[],failure:l&&l.failed_index!==null?{index:l.failed_index,error:l.error??"invalid change"}:null,busy:c,exec:v,drop:j,clear:b}),[t,l,c,v,j,b])}function ps(s){const n=new Map;for(const i of s)for(const l of["id","conceptual_id","from_fk_id"]){const m=i.payload[l];i.op.startsWith("create_")&&typeof m=="string"&&n.set(m,i.key)}if(!n.size)return new Set;const t=new Set;for(const i of s)if(!i.op.startsWith("create_"))for(const l of Object.values(i.payload))typeof l=="string"&&n.has(l)&&(t.add(i.key),t.add(n.get(l)));return t}function js({user:s}){const[n,t]=a.useState(null);return a.useEffect(()=>{be(s).then(t).catch(()=>{})},[s]),!(n!=null&&n.ok)||!n.git?null:n.read_only?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"browse only — editing is disabled on this deployment"})]}):n.dirty?e.jsxs("div",{className:"git-banner warn",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"uncommitted changes in the working tree — a proposal will be refused until they are committed or discarded"})]}):n.on_base?e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"protected — your edits become a pull request"})]}):e.jsxs("div",{className:"git-banner",children:[e.jsx("code",{children:n.branch}),e.jsx("span",{children:"your proposal branch"}),n.behind?e.jsxs("span",{className:"dim",children:["· ",n.base_branch," is ",n.behind," ahead"]}):null]})}function fs({exec:s,canEdit:n,stagedMembers:t}){const[i,l]=a.useState([]),[m,c]=a.useState(""),[r,d]=a.useState(null),[v,j]=a.useState(""),[b,o]=a.useState(new Set),[f,N]=a.useState("ancestors"),[_,B]=a.useState(1),[E,S]=a.useState(null),[C,R]=a.useState(new Set),[A,w]=a.useState(null);a.useEffect(()=>{ge().then(h=>{l(h.subject_areas),c(k=>{var D;return k||((D=h.subject_areas[0])==null?void 0:D.id)||""})}).catch(h=>w(String(h)))},[]),a.useEffect(()=>{m&&(S(null),o(new Set),Ve(m).then(d).catch(h=>w(String(h))))},[m]);const $=a.useMemo(()=>t[m]??(r==null?void 0:r.members)??[],[t,m,r]),I=a.useMemo(()=>{const h=new Map;for(const k of[...(r==null?void 0:r.included)??[],...(r==null?void 0:r.available)??[]])h.set(k.id,k);return h},[r]),g=v.trim().toLowerCase(),O=h=>!g||h.name.toLowerCase().includes(g),u=a.useMemo(()=>new Set($),[$]),M=a.useMemo(()=>$.map(h=>I.get(h)).filter(h=>!!h&&O(h)),[$,I,g]),z=a.useMemo(()=>[...I.values()].filter(h=>!u.has(h.id)&&O(h)),[I,u,g]),W=a.useCallback(h=>{if(!r)return;const k=h.filter(q=>!(r.members??[]).includes(q)).length,D=(r.members??[]).filter(q=>!h.includes(q)).length;s("set_subject_area_members",{id:r.id,members:h,__label:`${r.name}: ${h.length} objects (+${k} −${D})`})},[r,s]),H=(h,k)=>{const D=k?[...$,...h.filter(q=>!u.has(q))]:$.filter(q=>!h.includes(q));W(D),o(new Set)},G=(h,k,D)=>{const q=new Set(h);q.has(k)?q.delete(k):q.add(k),D(q)},T=()=>{if(!r)return;const h=b.size?[...b]:$;if(!h.length){w("add an object first, or select one to expand from");return}w(null),ve(r.id,{seeds:h,direction:f,levels:_}).then(k=>{S(k.hops),R(new Set(k.added))}).catch(k=>w(String(k)))},J=()=>{H([...C],!0),S(null)};if(A&&!r)return e.jsx("p",{className:"sme-splash error",children:A});if(!i.length)return e.jsx("p",{className:"sme-placeholder",children:"No subject areas yet."});const x=((r==null?void 0:r.inconsistent)??[]).filter(h=>!u.has(h));return e.jsxs("div",{className:"sa-editor",children:[e.jsx("nav",{className:"sme-nav",children:i.map(h=>e.jsxs("button",{className:"sme-sa"+(h.id===m?" active":""),onClick:()=>c(h.id),title:h.definition??void 0,children:[h.name,e.jsx("span",{className:"sme-sa-count",children:(t[h.id]??h.members).length})]},h.id))}),e.jsxs("div",{className:"sa-main",children:[e.jsxs("div",{className:"sa-head",children:[e.jsx("h2",{children:(r==null?void 0:r.name)??"…"}),(r==null?void 0:r.definition)&&e.jsx("p",{className:"sme-muted",children:r.definition}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:v,onChange:h=>j(h.target.value)})]}),x.length>0&&n&&e.jsxs("div",{className:"sa-nudge",children:[x.length," object",x.length>1?"s are":" is"," homed in this area but not included.",e.jsxs("button",{className:"sme-link",onClick:()=>H(x,!0),children:["Add ",x.length>1?"them all":"it"]})]}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(ee,{title:`Available (${z.length})`,rows:z,picked:b,onToggle:h=>G(b,h,o),homedHere:(r==null?void 0:r.homed_here)??[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!n||!b.size,onClick:()=>H([...b].filter(h=>!u.has(h)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!z.length,onClick:()=>H(z.map(h=>h.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!b.size,onClick:()=>H([...b].filter(h=>u.has(h)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!n||!M.length,onClick:()=>H(M.map(h=>h.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(ee,{title:`Included (${M.length})`,rows:M,picked:b,onToggle:h=>G(b,h,o),homedHere:(r==null?void 0:r.homed_here)??[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:f,onChange:h=>N(h.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:_,onChange:h=>B(Math.max(1,Math.min(10,Number(h.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!n,children:"Preview"})]}),A&&e.jsx("p",{className:"sme-error-line",children:A}),E&&e.jsxs("div",{className:"sa-hops",children:[E.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),E.map(h=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:C.has(h.id),onChange:()=>G(C,h.id,R)}),e.jsx("span",{className:"sa-hop-name",children:h.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",h.via_name," · ",h.direction.replace(/s$/,""),", level ",h.level]})]},h.id)),E.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>S(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",onClick:J,disabled:!C.size,children:["Add ",C.size," object",C.size===1?"":"s"]})]})]})]})]})]})}function ee({title:s,rows:n,picked:t,onToggle:i,homedHere:l}){const m=a.useMemo(()=>new Set(l),[l]);return e.jsxs("div",{className:"sa-pane",children:[e.jsx("h3",{children:s}),e.jsxs("ul",{children:[n.map(c=>e.jsxs("li",{className:"sa-row"+(t.has(c.id)?" picked":""),onClick:()=>i(c.id),children:[e.jsx("input",{type:"checkbox",readOnly:!0,checked:t.has(c.id)}),e.jsx("span",{className:"sa-row-name",children:c.name}),m.has(c.id)?e.jsx("span",{className:"sme-badge",children:"home"}):e.jsx("span",{className:"sme-badge borrowed",children:"borrowed"})]},c.id)),!n.length&&e.jsx("li",{className:"sme-empty",children:"nothing here"})]})]})}function xs({objects:s,onCreate:n,onClose:t}){const[i,l]=a.useState(""),[m,c]=a.useState(""),[r]=a.useState(s),[d,v]=a.useState([]),[j,b]=a.useState(new Set),[o,f]=a.useState(""),[N,_]=a.useState("ancestors"),[B,E]=a.useState(1),[S,C]=a.useState(null),[R,A]=a.useState(new Set),[w,$]=a.useState(""),[I,g]=a.useState(null);a.useEffect(()=>{ge().then(x=>{x.subject_areas[0]&&$(x.subject_areas[0].id)}).catch(()=>{})},[]);const O=a.useMemo(()=>new Set(d),[d]),u=o.trim().toLowerCase(),M=x=>!u||x.name.toLowerCase().includes(u),z=a.useMemo(()=>d.map(x=>r.find(h=>h.id===x)).filter(x=>!!x&&M(x)),[d,r,u]),W=a.useMemo(()=>r.filter(x=>!O.has(x.id)&&M(x)),[r,O,u]),H=(x,h)=>{v(k=>h?[...k,...x.filter(D=>!k.includes(D))]:k.filter(D=>!x.includes(D))),b(new Set)},G=(x,h,k)=>{const D=new Set(x);D.has(h)?D.delete(h):D.add(h),k(D)},T=()=>{const x=j.size?[...j]:d;if(!x.length){g("pick an object to expand from first");return}w&&(g(null),ve(w,{seeds:x,direction:N,levels:B}).then(h=>{const k=h.hops.filter(D=>!O.has(D.id));C(k),A(new Set(k.map(D=>D.id)))}).catch(h=>g(String(h))))},J=()=>{i.trim()&&n(i.trim(),m.trim(),d)};return e.jsx("div",{className:"sme-modal-backdrop",onClick:t,children:e.jsxs("div",{className:"sme-modal sa-modal",onClick:x=>x.stopPropagation(),children:[e.jsx("h2",{children:"New subject area"}),e.jsx("p",{className:"sme-hint",children:"A subject area is a filtered view of the model. Name it and choose what it contains — both land as one proposal."}),e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{autoFocus:!0,value:i,placeholder:"e.g. UseCase-ApraStressTesting",onChange:x=>l(x.target.value),onKeyDown:x=>x.key==="Enter"&&d.length===0&&J()})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition (optional)"}),e.jsx("input",{value:m,placeholder:"what this view is for",onChange:x=>c(x.target.value)})]})]}),e.jsx("input",{className:"sme-search sa-filter",type:"search",placeholder:"Filter both lists…",value:o,onChange:x=>f(x.target.value)}),e.jsxs("div",{className:"sa-panes",children:[e.jsx(ee,{title:`Available (${W.length})`,rows:W,picked:j,onToggle:x=>G(j,x,b),homedHere:[]}),e.jsxs("div",{className:"sa-controls",children:[e.jsx("button",{className:"sme-secondary",disabled:!j.size,onClick:()=>H([...j].filter(x=>!O.has(x)),!0),title:"Add the selected objects",children:"→"}),e.jsx("button",{className:"sme-secondary",disabled:!W.length,onClick:()=>H(W.map(x=>x.id),!0),title:"Add everything matching the filter",children:"»"}),e.jsx("button",{className:"sme-secondary",disabled:!j.size,onClick:()=>H([...j].filter(x=>O.has(x)),!1),title:"Remove the selected objects",children:"←"}),e.jsx("button",{className:"sme-secondary",disabled:!z.length,onClick:()=>H(z.map(x=>x.id),!1),title:"Remove everything matching the filter",children:"«"})]}),e.jsx(ee,{title:`Included (${z.length})`,rows:z,picked:j,onToggle:x=>G(j,x,b),homedHere:[]})]}),e.jsxs("div",{className:"sa-expand",children:[e.jsx("h3",{children:"Add related objects"}),e.jsxs("div",{className:"sa-expand-row",children:[e.jsxs("select",{value:N,onChange:x=>_(x.target.value),children:[e.jsx("option",{value:"ancestors",children:"ancestors — the parents its keys point at"}),e.jsx("option",{value:"descendants",children:"descendants — what points at it"}),e.jsx("option",{value:"both",children:"both"})]}),e.jsxs("label",{children:["levels",e.jsx("input",{type:"number",min:1,max:10,value:B,onChange:x=>E(Math.max(1,Math.min(10,Number(x.target.value))))})]}),e.jsx("button",{className:"sme-secondary",onClick:T,disabled:!w,title:w?"Preview related objects to add":"Available once the model has at least one saved subject area to walk the graph from",children:"Preview"})]}),S&&e.jsxs("div",{className:"sa-hops",children:[S.length===0&&e.jsx("p",{className:"sme-muted",children:"nothing new to add"}),S.map(x=>e.jsxs("label",{className:"sa-hop",children:[e.jsx("input",{type:"checkbox",checked:R.has(x.id),onChange:()=>G(R,x.id,A)}),e.jsx("span",{className:"sa-hop-name",children:x.name}),e.jsxs("span",{className:"sa-hop-why",children:["via ",x.via_name," · ",x.direction.replace(/s$/,""),", level ",x.level]})]},x.id)),S.length>0&&e.jsxs("div",{className:"sa-hop-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:()=>C(null),children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!R.size,onClick:()=>{H([...R],!0),C(null)},children:["Add ",R.size]})]})]})]}),I&&e.jsx("p",{className:"sme-error-line",children:I}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:t,children:"Cancel"}),e.jsxs("button",{className:"sme-primary",disabled:!i.trim(),onClick:J,children:["Create",d.length>0&&` with ${d.length} object${d.length===1?"":"s"}`]})]})]})})}const bs=new Set(["set_definition","set_stewardship","set_subject_area","set_pattern","rename_entity","add_attribute","update_attribute","delete_attribute","set_alignment","clear_alignment","set_term_map","clear_term_map","update_synonyms","set_object_definition","set_subject_area_members","create_subject_area","update_subject_area","create_entity","delete_entity","create_relationship","rename_relationship","update_relationship","delete_relationship"]);function gs({doc:s,subjectArea:n,onSubjectArea:t,exec:i,canEdit:l,direct:m=!1,onSelectEntity:c,onImported:r,onImportPreview:d,openImport:v=!1,prefillImport:j="",query:b="",busy:o}){var I;const[f,N]=a.useState(null),[_,B]=a.useState(null),[E,S]=a.useState(!1),[C,R]=a.useState(null),A=a.useRef(null),w=a.useMemo(()=>({canEdit:l,mode:m?"direct":"staged",allow:m?void 0:bs,canArbitrate:m,canCommit:m}),[l,m]),$=a.useCallback(g=>{N(g),g&&(c==null||c(g))},[c]);return e.jsxs("div",{className:"sme-model-view",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsxs("div",{className:"sme-nav-head",children:[e.jsx("span",{children:"Subject areas"}),l&&e.jsx("button",{className:"sme-nav-add",title:"New subject area",onClick:()=>S(!0),children:"+"})]}),e.jsxs("button",{className:"sme-sa"+(n===""?" active":""),onClick:()=>t(""),children:["Whole model",e.jsx("span",{className:"sme-sa-count",children:((I=s.counts)==null?void 0:I.entities_total)??s.entities.length})]}),s.subject_areas.map(g=>e.jsxs("button",{className:"sme-sa"+(n===g.id?" active":""),onClick:()=>t(g.id),title:g.definition??void 0,children:[g.name,e.jsx("span",{className:"sme-sa-count",children:g.member_count??0})]},g.id))]}),E&&e.jsx(xs,{objects:s.entities.flatMap(g=>g.conceptual?[{id:g.conceptual.id,kind:"conceptual_entity",name:g.conceptual.name,definition:g.conceptual.definition??null,synonyms:[],subject_area:g.conceptual.subject_area??null,stewardship:null,ontology:null,where_used:[]}]:[]),onClose:()=>S(!1),onCreate:(g,O,u)=>{const M=Ne();i("create_subject_area",{name:g,...O?{definition:O}:{},id:M}),u.length&&i("set_subject_area_members",{id:M,members:u}),S(!1),t(M)}}),e.jsxs("div",{className:"erd-wrap",children:[e.jsxs("div",{className:"erd-bar",children:[e.jsxs("span",{className:"erd-count",children:[s.entities.length," entities · ",s.relationships.length," relationships",s.scope&&" · scoped"]}),l&&e.jsx("button",{className:"erd-action",onClick:()=>{var g;return(g=A.current)==null?void 0:g.newEntity()},title:"Add an entity (n)",children:"+ Entity"}),e.jsx("span",{className:"erd-hint",children:"drag between entities to relate them"}),e.jsxs("span",{className:"erd-group",children:[e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var g;return(g=A.current)==null?void 0:g.relayout()},title:"Auto-arrange the diagram",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⟲"}),"Re-layout"]}),e.jsxs("button",{className:"erd-action ghost",onClick:()=>{var g;return(g=A.current)==null?void 0:g.fitView()},title:"Fit the whole model in view",children:[e.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"⛶"}),"Fit"]})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsxs("span",{className:"erd-group",children:[e.jsx("button",{className:"erd-action ghost"+(_==="ontology"?" on":""),onClick:()=>B(g=>g==="ontology"?null:"ontology"),children:"Ontology"}),e.jsx("button",{className:"erd-action ghost"+(_==="layers"?" on":""),onClick:()=>B(g=>g==="layers"?null:"layers"),children:"Layers"})]}),e.jsx("span",{className:"erd-sep","aria-hidden":"true"}),e.jsx(Ue,{exec:i,canEdit:l,onImported:r,onPreview:d,openImport:v,prefillImport:j}),o&&e.jsx("span",{className:"erd-busy",children:"updating…"}),!l&&e.jsx("span",{className:"sme-chip",children:"read-only"}),C&&e.jsx("span",{className:"erd-error",children:C})]}),e.jsxs("div",{className:"erd-canvas canvas-wrap",children:[e.jsx(Fe,{children:e.jsx(Te,{doc:s,caps:w,exec:i,selectedId:f,onSelect:$,query:b,onError:R,handleRef:A})}),_&&e.jsxs("aside",{className:"side-panel",children:[e.jsxs("div",{className:"detail-header",children:[e.jsx("h2",{children:_==="ontology"?"Ontology":"Ontology layers"}),e.jsx("button",{className:"icon-btn",onClick:()=>B(null),children:"✕"})]}),_==="ontology"?e.jsx(Ye,{}):e.jsx(Je,{refreshKey:0,onFocusEntity:g=>{var O;B(null),(O=A.current)==null||O.focusEntity(g)}})]})]})]})]})}function vs({user:s,onView:n}){const[t,i]=a.useState(null),[l,m]=a.useState(null);if(a.useEffect(()=>{Qe(s).then(i).catch(r=>m(String(r)))},[s]),l)return e.jsx("p",{className:"sme-splash error",children:l});if(!t)return e.jsx("div",{className:"sme-splash",children:"◮ looking for your proposals…"});if(!t.git)return e.jsx("p",{className:"sme-placeholder",children:"This model isn't in a git repository."});const c=t.proposals.filter(r=>!r.merged).length;return e.jsxs("div",{className:"pr-wrap",children:[e.jsxs("div",{className:"pr-head",children:[e.jsx("h2",{children:"My proposals"}),e.jsxs("span",{className:"meta",children:[c," open · base: ",t.base]})]}),!t.proposals.length&&e.jsx("p",{className:"sme-placeholder",children:"Nothing proposed yet. Edits you submit for review will appear here."}),t.proposals.map(r=>e.jsx(ys,{p:r,gh:t.gh,onView:()=>n(r.branch)},r.branch)),!t.gh&&t.proposals.length>0&&e.jsxs("div",{className:"pr-degrade",children:["Review status isn't available here (",e.jsx("code",{children:"gh"})," is not installed or not signed in). Branch, contents, merge state and conflicts all still come from git — each card links out to the compare view."]})]})}function Ns(s,n){var t,i;return s.merged?{kind:"accepted",text:"✅ Accepted"}:s.pushed?n&&((t=s.pr)==null?void 0:t.state)==="CLOSED"?{kind:"declined",text:"✖ Declined"}:n&&((i=s.pr)==null?void 0:i.reviews)==="CHANGES_REQUESTED"?{kind:"changes",text:"💬 Changes requested"}:{kind:"awaiting",text:"⏳ Awaiting review"}:{kind:"local",text:"📥 Saved locally"}}function ys({p:s,gh:n,onView:t}){const i=Ns(s,n);return e.jsxs("div",{className:"pr-card"+(i.kind==="attention"?" attention":""),children:[e.jsxs("div",{className:"pr-top",children:[e.jsx("span",{className:"pr-title",children:s.title}),e.jsx("span",{className:"pr-chip "+i.kind,children:i.text})]}),e.jsxs("div",{className:"pr-branch",children:[s.branch," · ",s.created,s.behind>0&&` · ${s.behind} behind`]}),e.jsxs("div",{className:"pr-foot",children:[e.jsx("span",{className:"pr-link",children:s.merged?e.jsxs(e.Fragment,{children:["merged",n&&s.pr?` · PR #${s.pr.number}`:""]}):s.pushed?n&&s.pr?e.jsxs("a",{href:s.pr.url,target:"_blank",rel:"noreferrer",children:["PR #",s.pr.number]}):s.compare_url?e.jsx("a",{href:s.compare_url,target:"_blank",rel:"noreferrer",children:"open the compare view →"}):"pushed":"not sent yet — no connection to the shared repo"}),e.jsx("span",{className:"pr-btns",children:e.jsx("button",{className:"sme-secondary",onClick:t,children:"View"})})]})]})}function ws({changes:s,routeAdvice:n,identity:t,user:i,onUser:l,onDrop:m,onClose:c,onProposed:r}){const d=!!(t&&t.source!=="anonymous"),[v,j]=a.useState(i||(localStorage.getItem("mdl.sme.user")??"")),[b,o]=a.useState(s.length===1?`Update ${s[0].label.toLowerCase()}`:"Glossary updates"),[f,N]=a.useState(""),[_,B]=a.useState(!1),E=we(_?f:b),[S,C]=a.useState(""),[R,A]=a.useState(!1),[w,$]=a.useState(null),[I,g]=a.useState(null),O=()=>{!d&&!v.trim()||(d||(localStorage.setItem("mdl.sme.user",v.trim()),l==null||l(v.trim())),A(!0),g(null),Xe({user:d?"":v.trim(),slug:E,title:b,body:S.trim(),changes:s.map(u=>({op:u.op,payload:u.payload}))}).then($).catch(u=>g(u instanceof Ze?u.message:String(u))).finally(()=>A(!1)))};return w!=null&&w.ok?e.jsx("div",{className:"sme-modal-backdrop",onClick:()=>r(w),children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsx("h2",{children:"✓ Sent for review"}),e.jsx("p",{children:w.message}),w.pr_url&&e.jsx("p",{children:e.jsx("a",{href:w.pr_url,target:"_blank",rel:"noreferrer",children:"View pull request →"})}),w.compare_url&&e.jsx("p",{children:e.jsx("a",{href:w.compare_url,target:"_blank",rel:"noreferrer",children:"Open the pull request →"})}),e.jsxs("p",{className:"sme-muted",children:["Your suggestion is on branch ",e.jsx("code",{children:w.branch}),". A steward will review it."]}),e.jsx("div",{className:"sme-modal-foot",children:e.jsx("button",{className:"sme-primary",onClick:()=>r(w),children:"Done"})})]})}):e.jsx("div",{className:"sme-modal-backdrop",onClick:c,children:e.jsxs("div",{className:"sme-modal",onClick:u=>u.stopPropagation(),children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsx("h2",{children:"Submit for review"}),e.jsx("button",{className:"sme-secondary",onClick:c,children:"✕"})]}),e.jsx("h3",{children:"Your changes"}),e.jsx("ul",{className:"sme-diff",children:s.map((u,M)=>e.jsxs("li",{children:[e.jsxs("div",{className:"sme-diff-head",children:[e.jsx("b",{children:u.label}),e.jsx("button",{className:"sme-link danger",onClick:()=>m(M),children:"discard"})]}),e.jsx("div",{className:"sme-diff-before",children:u.before}),e.jsx("div",{className:"sme-diff-after",children:u.after})]},M))}),d?e.jsxs("p",{className:"sme-signed-in",children:["Signed in as ",e.jsx("strong",{children:t.name}),t.email?` (${t.email})`:""," — this proposal is attributed to you."]}):e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Your name (for attribution)"}),e.jsx("input",{value:v,onChange:u=>j(u.target.value),placeholder:"e.g. a.hough"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Title"}),e.jsx("input",{value:b,onChange:u=>o(u.target.value)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Branch name (edit to add a ticket id, e.g. PROJ-123)"}),e.jsx("input",{value:_?f:E,onChange:u=>{B(!0),N(u.target.value)},placeholder:"clarify-counterparty"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Note for the reviewer (optional)"}),e.jsx("textarea",{rows:2,value:S,onChange:u=>C(u.target.value)})]}),e.jsx(ks,{cl:n,user:d?t.name:v,changeSlug:E}),I&&e.jsx("p",{className:"sme-error-line",children:I}),e.jsxs("div",{className:"sme-modal-foot",children:[e.jsx("button",{className:"sme-secondary",onClick:c,children:"Keep editing"}),e.jsx("button",{className:"sme-primary",disabled:R||!d&&!v.trim()||s.length===0,onClick:O,children:R?"Submitting…":"Submit for review"})]})]})})}function ks({cl:s,user:n,changeSlug:t}){if(!(s!=null&&s.primary))return null;const i=s.reviewers_actual??s.reviewers,l=we(n||"you"),m=s.routes.length>1;return e.jsxs(e.Fragment,{children:[m&&e.jsxs("div",{className:"sub-advice",children:[e.jsx("span",{className:"i",children:"ⓘ"})," These changes span routes"," ",e.jsx("strong",{children:s.routes.join(" and ")}),". The whole proposal will be reviewed as route ",s.primary," (",s.primary_name,") — the strictest gate wins, so it needs"," ",i.join(", ")," plus ",e.jsx("code",{children:s.gates[s.gates.length-1]}),". Proposing the meaning-only changes separately would get them reviewed faster."]}),e.jsxs("div",{className:"sub-dest",children:[e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Goes to"}),e.jsxs("code",{children:["sme/",l,"/",t||"…"]})]}),e.jsxs("div",{className:"row",children:[e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:i.join(", ")})]})]})]})}function we(s){return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"change"}const ke=[{id:"all",label:"All"},{id:"additions",label:"Additions"},{id:"modifications",label:"Modifications"},{id:"moves",label:"Renames"},{id:"deletions",label:"Deletions"},{id:"conflicts",label:"Conflicts"}];function _e(s,n){switch(n){case"all":return!0;case"additions":return s.change==="added";case"deletions":return s.change==="removed";case"moves":return s.renamed;case"modifications":return s.change==="modified"&&!s.renamed;case"conflicts":return s.severity==="breaking"}}function _s(s,n){return n==="all"?s.objects:s.objects.filter(t=>_e(t,n))}function Ss(s){const n={};for(const{id:t}of ke)n[t]=t==="all"?s.objects.length:s.objects.filter(i=>_e(i,t)).length;return n}function Q(s,n){return`${s}\0${n}`}function Se(s){const n=s.fields.map(t=>Q(s.ulid,t.field));for(const t of s.children)for(const i of t.fields)n.push(Q(s.ulid,`${t.ulid}.${i.field}`));return n.length?n:[Q(s.ulid,"*")]}function Ce(s,n){const t=Se(s),i=t.filter(l=>n.has(l)).length;return i===0?"none":i===t.length?"all":"some"}function Ae(s,n){return Ce(s,n)!=="none"}function Cs(s,n){return s.objects.filter(t=>Ae(t,n)).map(t=>t.ulid)}function As(s,n){const t=new Set;for(const i of s.objects){if(i.object_kind!=="logical_entity"||i.change!=="added"||!Ae(i,n))continue;const l=i.name_after??i.name_before;l&&t.add(l)}return t}function $s(s,n){if(n.size===0)return[];const t=new Set,i=[],l=m=>[m.entity_id,m.from_entity,m.to_entity,m.from,m.to].filter(c=>typeof c=="string");for(const m of s){const c=m.payload;switch(m.op){case"create_entity":{const r=typeof c.name=="string"?c.name:"",d=typeof c.id=="string"?c.id:"";n.has(r)&&(t.add(d),i.push(m));break}case"add_attribute":{const r=typeof c.entity_id=="string"?c.entity_id:"";t.has(r)&&i.push(m);break}case"create_relationship":case"create_key_group":{const r=l(c);r.length>0&&r.every(d=>t.has(d))&&i.push(m);break}default:i.push(m)}}return i}function $e(s){const n=new Set;for(const t of s.objects)for(const i of Se(t))n.add(i);return n}const Es="modulepreload",Rs=function(s){return"/"+s},me={},Ms=function(n,t,i){let l=Promise.resolve();if(t&&t.length>0){document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),r=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));l=Promise.allSettled(t.map(d=>{if(d=Rs(d),d in me)return;me[d]=!0;const v=d.endsWith(".css"),j=v?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${d}"]${j}`))return;const b=document.createElement("link");if(b.rel=v?"stylesheet":Es,v||(b.as="script"),b.crossOrigin="",b.href=d,r&&b.setAttribute("nonce",r),document.head.appendChild(b),v)return new Promise((o,f)=>{b.addEventListener("load",o),b.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${d}`)))})}))}function m(c){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=c,window.dispatchEvent(r),!r.defaultPrevented)throw c}return l.then(c=>{for(const r of c||[])r.status==="rejected"&&m(r.reason);return n().catch(m)})};function Ds({diff:s,selectable:n,selected:t,onToggle:i,onToggleField:l}){var f;const[m,c]=a.useState("all"),r=a.useMemo(()=>_s(s,m),[s,m]),d=a.useMemo(()=>Ss(s),[s]),[v,j]=a.useState(((f=s.objects[0])==null?void 0:f.ulid)??null),b=a.useMemo(()=>r.find(N=>N.ulid===v)??r[0]??s.objects[0]??null,[r,s,v]),o=!!l;return s.objects.length?e.jsxs("div",{className:"rv-wrap",children:[n!==void 0&&e.jsx("div",{className:"rv-filters",role:"tablist","aria-label":"Filter changes",children:ke.filter(N=>N.id==="all"||d[N.id]>0).map(N=>e.jsxs("button",{role:"tab","aria-selected":m===N.id,className:"rv-filter"+(m===N.id?" active":""),onClick:()=>c(N.id),children:[N.label,e.jsx("span",{className:"rv-filter-count",children:d[N.id]})]},N.id))}),e.jsxs("div",{className:"rv-body",children:[e.jsx("div",{className:"rv-list",children:r.length===0?e.jsx("p",{className:"sme-placeholder",children:"No changes match this filter."}):r.map(N=>e.jsx(Is,{obj:N,active:N.ulid===((b==null?void 0:b.ulid)??""),selectable:n,perField:o,objState:o?Ce(N,t??new Set):void 0,checkedObject:(t==null?void 0:t.has(N.ulid))??!0,onSelect:()=>j(N.ulid),onToggleObject:()=>i==null?void 0:i(N.ulid),onToggleField:l},N.ulid))}),e.jsx("div",{className:"rv-detail",children:b&&e.jsx(Os,{obj:b,selectable:n,perField:o,selected:t,onToggleField:l})})]})]}):e.jsxs("p",{className:"sme-placeholder",children:["No model changes against ",s.base.label,"."]})}function Ps(s){return s.renamed?"renamed":s.severity==="breaking"?"breaking":s.change}function Is({obj:s,active:n,selectable:t,perField:i,objState:l,checkedObject:m,onSelect:c,onToggleObject:r,onToggleField:d}){const v=o=>{o&&i&&(o.indeterminate=l==="some")},j=i?l!=="none":m,b=()=>{if(!i)return r();for(const o of s.fields)d==null||d(s.ulid,o.field);for(const o of s.children)for(const f of o.fields)d==null||d(s.ulid,`${o.ulid}.${f.field}`);!s.fields.length&&!s.children.length&&(d==null||d(s.ulid,"*"))};return e.jsxs("div",{className:"rv-obj"+(n?" active":""),onClick:c,children:[e.jsxs("div",{className:"rv-obj-head",children:[t&&e.jsx("input",{type:"checkbox",ref:v,checked:j,onClick:o=>o.stopPropagation(),onChange:b,"aria-label":`include ${s.name_before??s.name_after}`}),e.jsx("span",{className:"rv-obj-name",children:s.name_before??s.name_after}),e.jsx("span",{className:"rv-obj-kind",children:s.object_kind_label}),e.jsxs("span",{className:"sev "+(s.renamed?"renamed":s.severity),children:[s.severity==="breaking"&&"⚠ ",Ps(s)]})]}),e.jsxs("ul",{className:"rv-obj-fields",children:[s.fields.map((o,f)=>e.jsx("li",{children:o.label},o.field+f)),s.children.flatMap(o=>o.fields.map((f,N)=>e.jsxs("li",{children:[f.label,": ",o.name_before??o.name_after]},o.ulid+N)))]})]})}function Os({obj:s,selectable:n,perField:t,selected:i,onToggleField:l}){const m=s.children.flatMap(r=>r.fields.map(d=>({field:d,on:r.name_before??r.name_after??"",key:`${r.ulid}.${d.field}`}))),c=(r,d)=>{if(!n||!t)return d;const v=Q(s.ulid,r);return e.jsxs("div",{className:"rv-field-pick",children:[e.jsx("input",{type:"checkbox",checked:(i==null?void 0:i.has(v))??!0,onChange:()=>l==null?void 0:l(s.ulid,r),"aria-label":`include change: ${r}`}),e.jsx("div",{className:"rv-field-body",children:d})]})};return e.jsxs(e.Fragment,{children:[e.jsx("h2",{children:s.renamed?`${s.name_before} → ${s.name_after}`:s.name_before??s.name_after}),e.jsxs("div",{className:"rv-detail-kind",children:[s.object_kind_label,s.renamed&&" · renamed",s.path&&e.jsxs("span",{className:"rv-src",children:[" · ",s.path]})]}),s.fields.map((r,d)=>e.jsx("div",{children:c(r.field,e.jsx(ue,{f:r}))},r.field+d)),m.map(({field:r,on:d,key:v},j)=>e.jsx("div",{children:c(v,e.jsx(ue,{f:r,on:d}))},"c"+j)),s.severity==="breaking"&&e.jsxs("div",{className:"rv-note",children:[e.jsx("span",{className:"i",children:"ⓘ"}),e.jsxs("span",{children:["This is a structural change (route B). It will need an architect's review, and CI will run ",e.jsx("code",{children:"mdl drift --check"}),"."]})]})]})}function ue({f:s,on:n}){var i,l,m;const t=n?`${s.label}: ${n}`:s.label;if((i=s.breaks)!=null&&i.length)return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:t}),e.jsxs("div",{className:"rv-break",children:[e.jsxs("div",{className:"rv-break-head",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[n??String(s.before??"")," removed"]}),s.detail&&e.jsx("span",{className:"rv-break-meta",children:s.detail})]}),e.jsxs("div",{className:"rv-break-body",children:["This is realised by ",s.breaks.length," dbt model",s.breaks.length>1?"s":"",". Removing it will break ",s.breaks.length>1?"them":"it","."]}),e.jsx("ul",{className:"rv-usage",children:s.breaks.map(c=>e.jsxs("li",{children:[e.jsxs("span",{className:"m",children:["→ ",c.name]}),e.jsxs("span",{className:"p",children:[c.target," · ",c.materialization]})]},c.name))})]})]});if(s.kind==="definition_changed"){const c=String(s.before??""),r=String(s.after??"");return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:t}),e.jsxs("div",{className:"rv-ba",children:[e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"before"}),e.jsx("div",{className:"rv-ba-text",children:je(c,r,"del")})]}),e.jsxs("div",{className:"rv-ba-col",children:[e.jsx("div",{className:"rv-ba-label",children:"after"}),e.jsx("div",{className:"rv-ba-text",children:je(r,c,"ins")})]})]})]})}if(Array.isArray(s.before)||Array.isArray(s.after)){const c=new Set(((l=s.before)==null?void 0:l.map(String))??[]),r=new Set(((m=s.after)==null?void 0:m.map(String))??[]),d=[...r].filter(j=>!c.has(j)),v=[...c].filter(j=>!r.has(j));return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:t}),e.jsxs("div",{className:"rv-chips",children:[d.map(j=>e.jsxs("span",{className:"rv-chip-add",children:["+ ",j]},"a"+j)),v.map(j=>e.jsxs("span",{className:"rv-chip-del",children:["− ",j]},"r"+j))]})]})}return e.jsxs("div",{className:"rv-section",children:[e.jsx("h3",{children:t}),e.jsxs("div",{className:"rv-scalar",children:[e.jsx("span",{className:"from",children:pe(s.before)}),e.jsx("span",{className:"arrow",children:"→"}),e.jsx("span",{className:"to",children:pe(s.after)})]})]})}function pe(s){return s==null||s===""?"—":typeof s=="boolean"?s?"yes":"no":typeof s=="object"?JSON.stringify(s):String(s)}function je(s,n,t){const i=s.split(/(\s+)/),l=n.split(/(\s+)/),m=o=>o.toLowerCase().replace(/[.,;:]/g,""),c=i.length,r=l.length,d=Array.from({length:c+1},()=>new Array(r+1).fill(0));for(let o=c-1;o>=0;o--)for(let f=r-1;f>=0;f--)d[o][f]=m(i[o])===m(l[f])?d[o+1][f+1]+1:Math.max(d[o+1][f],d[o][f+1]);const v=new Set;let j=0,b=0;for(;j=d[j][b+1]?j++:b++;return i.map((o,f)=>!o.trim()||v.has(f)?e.jsx("span",{children:o},f):t==="del"?e.jsx("del",{children:o},f):e.jsx("ins",{children:o},f))}const Bs=a.lazy(()=>Ms(()=>import("./ModelDiagram-CRUBpPVn.js"),__vite__mapDeps([0,1,2,3])).then(s=>({default:s.ModelDiagram})));function Ls({stagedDiff:s,stagedRoute:n,user:t,onBack:i,onSubmit:l,excluded:m,onToggleField:c,selectable:r,action:d}){const[v,j]=a.useState(null),[b,o]=a.useState(null),[f,N]=a.useState(null),[_,B]=a.useState(null),[E,S]=a.useState(null),[C,R]=a.useState("changes"),A=a.useCallback(()=>{be(t).then($=>{B($);const I=$.base_branch??"main";s?j(s):es("HEAD").then(g=>g.ok?j(g):S(g.error??"could not read the model")).catch(g=>S(String(g))),n?o(n):ss("HEAD").then(o).catch(()=>{}),ns(I).then(N).catch(()=>{})}).catch($=>S(String($)))},[t,s,n]);if(a.useEffect(A,[A]),E)return e.jsxs("div",{className:"rv-shell",children:[e.jsx(fe,{onBack:i,diff:null}),e.jsx("p",{className:"sme-splash error",children:e.jsx("pre",{children:E})})]});if(!v)return e.jsx("div",{className:"sme-splash",children:"◮ reading your changes…"});const w=new Set([...$e(v)].filter($=>!m.has($)));return e.jsxs("div",{className:"rv-shell",children:[e.jsx(fe,{onBack:i,diff:v,tab:C,onTab:R}),C==="changes"?e.jsx(Ds,{diff:v,selectable:r,selected:w,onToggleField:c}):e.jsx(a.Suspense,{fallback:e.jsx("div",{className:"sme-splash",children:"◮ drawing the model…"}),children:e.jsx(Bs,{severityByUlid:zs(v)})}),e.jsxs("div",{className:"rv-foot",children:[e.jsx(Hs,{cl:b}),e.jsx(Ws,{conf:f,ctx:_}),e.jsxs("div",{className:"rv-actions",children:[e.jsx("button",{className:"sme-secondary",onClick:i,children:d?"Cancel":"Keep editing"}),d?e.jsx("button",{className:"sme-primary",disabled:!v.objects.length||!!d.disabledReason,title:d.disabledReason??"",onClick:()=>d.apply(Cs(v,w)),children:d.label}):e.jsx("button",{className:"sme-primary",disabled:!v.objects.length||(_?!_.can_propose:!1),title:_&&!_.can_propose?"the working tree has uncommitted changes":"",onClick:()=>l(b),children:"Submit for review →"})]})]})]})}function zs(s){const n=new Map;for(const t of s.objects)n.set(t.ulid,t.severity);return n}function fe({onBack:s,diff:n,tab:t,onTab:i}){const l=n==null?void 0:n.counts;return e.jsxs("div",{className:"rv-head",children:[e.jsxs("div",{children:[e.jsx("button",{className:"rv-back",onClick:s,children:"‹ Back to terms"}),e.jsx("h1",{className:"rv-title",children:"Review your changes"}),l&&e.jsxs("div",{className:"rv-sub",children:[l.objects," object",l.objects===1?"":"s"," ·"," ",(l.breaking??0)+(l.additive??0)+(l.cosmetic??0)," changes",(n==null?void 0:n.has_breaking)&&e.jsxs("span",{className:"warn",children:[" · ⚠ ",l.breaking," breaking"]})," · comparing to ",e.jsx("code",{children:n==null?void 0:n.base.label})]})]}),i&&e.jsxs("div",{className:"rv-tabs",children:[e.jsxs("button",{className:"rv-tab"+(t==="changes"?" active":""),onClick:()=>i("changes"),children:["Changes ",(n==null?void 0:n.objects.length)??0]}),e.jsx("button",{className:"rv-tab"+(t==="diagram"?" active":""),onClick:()=>i("diagram"),children:"Diagram"})]})]})}function Hs({cl:s}){if(!(s!=null&&s.primary))return null;const n=s.reviewers_actual??s.reviewers;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rv-route",children:[e.jsxs("span",{className:"rv-route-badge",children:["Route ",s.primary," · ",s.primary_name]}),e.jsx("span",{className:"k",children:"Reviewers"}),e.jsx("span",{children:n.join(", ")}),s.reviewers_actual&&e.jsx("span",{className:"rv-src",children:"from .github/CODEOWNERS"})]}),e.jsxs("div",{className:"rv-gates",children:["CI gates"," ",s.gates.map(t=>e.jsx("code",{children:t},t))]})]})}function Ws({conf:s,ctx:n}){return s!=null&&s.ok?s.clean?e.jsxs("div",{className:"rv-conflict ok",children:[e.jsx("span",{children:"✓"}),e.jsxs("span",{children:["Merges cleanly onto ",s.base]}),s.behind>0&&e.jsxs("span",{className:"detail",children:["· ",s.base," is ",s.behind," commit",s.behind===1?"":"s"," ahead"]}),(n==null?void 0:n.dirty)&&e.jsx("span",{className:"detail",children:"· uncommitted changes in the working tree"})]}):e.jsxs("div",{className:"rv-conflict bad",children:[e.jsx("span",{children:"⚠"}),e.jsxs("span",{children:[s.base," has changed and ",s.files.length," file",s.files.length===1?"":"s"," now conflict"]}),e.jsx("span",{className:"detail",children:s.files.map(t=>t.path).join(", ")})]}):null}function qs({term:s,canEdit:n,pendingCount:t,onEdit:i}){var l,m;return e.jsxs("div",{className:"sme-card",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("div",{children:[e.jsx("h1",{children:s.name}),((l=s.subject_area)==null?void 0:l.name)&&e.jsx("span",{className:"sme-chip",children:s.subject_area.name}),s.kind==="term"&&e.jsx("span",{className:"sme-chip",children:"glossary term"})]}),n&&e.jsx("button",{className:"sme-primary",onClick:i,children:t>0?"Continue editing":"Suggest an edit"})]}),e.jsx("p",{className:"sme-definition",children:s.definition??e.jsx("em",{children:"No definition yet."})}),s.synonyms.length>0&&e.jsxs("section",{children:[e.jsx("h3",{children:"Also called"}),e.jsx("div",{children:s.synonyms.map(c=>e.jsx("span",{className:"sme-chip",children:c},c))})]}),s.stewardship&&(s.stewardship.owner||s.stewardship.steward)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Who owns this"}),s.stewardship.owner&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"owner"}),e.jsx("span",{children:s.stewardship.owner})]}),s.stewardship.steward&&e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"steward"}),e.jsx("span",{children:s.stewardship.steward})]})]}),((m=s.ontology)==null?void 0:m.aligns_to)&&e.jsxs("section",{children:[e.jsx("h3",{children:"Standard it maps to"}),e.jsxs("div",{className:"sme-kv",children:[e.jsx("span",{className:"k",children:"aligned to"}),e.jsx("code",{children:s.ontology.aligns_to}),s.ontology.status==="proposed"&&e.jsx("span",{className:"sme-badge proposed",children:"awaiting architect"})]})]}),e.jsxs("section",{children:[e.jsx("h3",{children:"Where it's used"}),s.where_used.length===0?e.jsx("p",{className:"sme-muted",children:s.kind==="term"?"A glossary term — not yet realised by a data model.":"Not yet realised by any data model."}):e.jsx("ul",{className:"sme-usage",children:s.where_used.map(c=>e.jsxs("li",{children:[e.jsx("span",{className:"sme-usage-model",children:c.logical_entity}),c.unmanaged&&e.jsx("span",{className:"sme-badge",children:"engineer-owned"}),c.physical.length>0&&e.jsx("span",{className:"sme-usage-phys",children:c.physical.map(r=>r.name).join(", ")})]},c.logical_id))})]})]})}function Gs({term:s,pending:n,onStage:t,onDone:i,catalogOwned:l=[],catalog:m=null}){var $,I,g,O;const c=u=>l.includes(u),r=u=>n.find(M=>M.label===u),d=(u,M)=>{const z=r(u);return z?z.after:M},[v,j]=a.useState(d("Definition",s.definition??"")),[b,o]=a.useState(s.synonyms),[f,N]=a.useState(""),[_,B]=a.useState((($=s.stewardship)==null?void 0:$.owner)??""),[E,S]=a.useState(((I=s.stewardship)==null?void 0:I.steward)??""),C=()=>{v.trim()!==(s.definition??"").trim()&&t({op:"set_definition",payload:{id:s.id,definition:v.trim()},label:"Definition",before:s.definition??"(none)",after:v.trim()||"(none)"})},R=(u,M)=>{var z,W;t({op:"set_stewardship",payload:{id:s.id,owner:u||null,steward:M||null},label:"Stewardship",before:`owner: ${((z=s.stewardship)==null?void 0:z.owner)??"—"} · steward: ${((W=s.stewardship)==null?void 0:W.steward)??"—"}`,after:`owner: ${u||"—"} · steward: ${M||"—"}`})},A=u=>{o(u),t({op:"update_synonyms",payload:{id:s.id,synonyms:u},label:"Also called",before:s.synonyms.join(", ")||"(none)",after:u.join(", ")||"(none)"})},w=s.kind==="term";return e.jsxs("div",{className:"sme-editor",children:[e.jsxs("div",{className:"sme-card-head",children:[e.jsxs("h1",{children:["Suggest an edit — ",s.name]}),e.jsx("button",{className:"sme-secondary",onClick:i,children:"Back"})]}),e.jsxs("p",{className:"sme-hint",children:["You can refine what this term ",e.jsx("em",{children:"means"}),". Structure (keys, relationships, how it's built) stays with the data team."]}),(c("definition")||c("synonyms")||c("stewardship"))&&m&&e.jsxs("div",{className:"sme-catalog-banner",children:["Definitions, synonyms, and ownership are mastered in ",e.jsx("b",{children:m.name}),". Edit them there — this glossary mirrors them read-only.",m.url&&e.jsxs(e.Fragment,{children:[" ",e.jsxs("a",{href:m.url,target:"_blank",rel:"noreferrer",children:["Open in ",m.name," →"]})]}),e.jsx("div",{className:"sme-catalog-note",children:"You can still propose a standard mapping below."})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Definition"}),c("definition")?e.jsx("p",{className:"sme-readonly-value",children:s.definition??"—"}):e.jsx("textarea",{rows:4,value:v,onChange:u=>j(u.target.value),onBlur:C,placeholder:"A plain-language business definition…"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Also called (synonyms)"}),c("synonyms")?e.jsx("div",{className:"sme-syn-row",children:s.synonyms.length?s.synonyms.map(u=>e.jsx("span",{className:"sme-chip",children:u},u)):e.jsx("span",{className:"sme-muted",children:"none"})}):e.jsxs("div",{className:"sme-syn-row",children:[b.map(u=>e.jsxs("span",{className:"sme-chip removable",onClick:()=>A(b.filter(M=>M!==u)),children:[u," ✕"]},u)),e.jsx("input",{value:f,placeholder:"add + Enter",onChange:u=>N(u.target.value),onKeyDown:u=>{u.key==="Enter"&&f.trim()&&(A([...b,f.trim()]),N(""))}})]})]}),!w&&(c("stewardship")?e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("p",{className:"sme-readonly-value",children:((g=s.stewardship)==null?void 0:g.owner)??"—"})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("p",{className:"sme-readonly-value",children:((O=s.stewardship)==null?void 0:O.steward)??"—"})]})]}):e.jsxs("div",{className:"sme-field-row",children:[e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Owner"}),e.jsx("input",{value:_,onChange:u=>B(u.target.value),onBlur:()=>R(_,E)})]}),e.jsxs("label",{className:"sme-field",children:[e.jsx("span",{children:"Steward"}),e.jsx("input",{value:E,onChange:u=>S(u.target.value),onBlur:()=>R(_,E)})]})]})),e.jsx(Vs,{term:s,onStage:t}),e.jsx("p",{className:"sme-note",children:n.length>0?`${n.length} change(s) staged — use “Submit for review” at the top when you're done.`:"Changes you make are collected until you submit them for review."})]})}const Ks=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch"];function Vs({term:s,onStage:n}){var b;const[t,i]=a.useState(!1),[l,m]=a.useState(s.name),[c,r]=a.useState([]),[d,v]=a.useState("skos:closeMatch"),j=o=>{m(o),ts(o).then(f=>r(f.results)).catch(()=>r([]))};return t?e.jsxs("div",{className:"sme-field sme-align",children:[e.jsx("span",{children:"Propose a standard mapping (a suggestion; the architect confirms)"}),e.jsx("input",{value:l,onChange:o=>j(o.target.value),placeholder:"Search standards…",autoFocus:!0}),e.jsx("div",{className:"sme-align-results",children:c.map(o=>e.jsxs("button",{className:"sme-align-card",onClick:()=>{var f;n({op:"set_alignment",payload:{id:s.id,aligns_to:o.prefixed,alignment:d,status:"proposed"},label:"Standard mapping",before:((f=s.ontology)==null?void 0:f.aligns_to)??"(none)",after:`${o.prefixed} (${d}, proposed)`}),i(!1)},children:[e.jsx("strong",{children:o.label})," ",e.jsx("code",{children:o.prefixed}),o.definition&&e.jsx("span",{className:"sme-align-def",children:o.definition})]},o.iri))}),e.jsx("select",{value:d,onChange:o=>v(o.target.value),children:Ks.map(o=>e.jsx("option",{children:o},o))})]}):e.jsxs("div",{className:"sme-field",children:[e.jsx("span",{children:"Standard mapping"}),e.jsxs("div",{children:[(b=s.ontology)!=null&&b.aligns_to?e.jsx("code",{className:"sme-current-align",children:s.ontology.aligns_to}):e.jsx("span",{className:"sme-muted",children:"not mapped to a standard"}),e.jsx("button",{className:"sme-link",onClick:()=>i(!0),children:"Propose a mapping…"})]})]})}function Us(){const[s,n]=a.useState(null),[t,i]=a.useState(!0),[l,m]=a.useState(null),[c,r]=a.useState(""),[d,v]=a.useState(null),[j,b]=a.useState(""),[o,f]=a.useState(""),[N,_]=a.useState(null),[B,E]=a.useState(!1),[S,C]=a.useState([]),[R,A]=a.useState(!1),w=a.useMemo(()=>new URLSearchParams(window.location.search).get("import"),[]),$=!!w,I=w&&w!=="1"?w:null,[g,O]=a.useState("");a.useEffect(()=>{I&&as(I).then(p=>p.ok&&p.content&&O(p.content)).catch(()=>{})},[I]);const[u,M]=a.useState($?"model":window.location.hash==="#proposals"?"proposals":"browse"),[z,W]=a.useState(new Set),[H,G]=a.useState(null),[T,J]=a.useState(()=>localStorage.getItem("mdl.sme.user")??""),[x,h]=a.useState(null),[k,D]=a.useState(null),[q,Ee]=a.useState(!1),[Re,Me]=a.useState(null),De=a.useCallback((p,y)=>{var Y;const L=Z=>{var ce;const F=k==null?void 0:k.entities.find(oe=>{var de;return oe.id===Z||((de=oe.conceptual)==null?void 0:de.id)===Z});return((ce=F==null?void 0:F.conceptual)==null?void 0:ce.name)??(F==null?void 0:F.name)??"object"},P=L(y.id??y.entity_id);switch(p){case"create_entity":return{label:`New entity: ${y.name}`,before:"—",after:String(y.name??"")};case"create_relationship":return{label:`New relationship: ${L(y.from_entity)} → ${L(y.to_entity)}`,before:"—",after:String(y.cardinality??"many_to_one")};case"delete_entity":return{label:`Deleted ${P}`,before:P,after:"—"};case"delete_relationship":return{label:"Relationship removed",before:"",after:"—"};case"update_relationship":return{label:"Relationship changed",before:"",after:[y.cardinality,y.optionality,y.identifying?"identifying":""].filter(Boolean).join(", ")};case"set_definition":return{label:`Definition: ${P}`,before:"",after:String(y.definition??"")};case"add_attribute":return{label:`Attribute added to ${P}`,before:"—",after:String(y.name??"")};case"delete_attribute":return{label:`Attribute removed from ${P}`,before:"",after:"—"};case"update_attribute":return{label:`Attribute changed on ${P}`,before:"",after:JSON.stringify(y)};case"rename_entity":return{label:`Renamed ${P}`,before:P,after:String(y.name??"")};case"set_subject_area":return{label:`Subject area: ${P}`,before:"",after:String(y.subject_area??"none")};case"set_stewardship":return{label:`Stewardship: ${P}`,before:"",after:String(y.steward??"")};case"set_subject_area_members":{const Z=y.__label,F=((Y=y.members)==null?void 0:Y.length)??0;return{label:Z??"Subject area members",before:"",after:`${F} object${F===1?"":"s"}`}}case"set_alignment":return{label:`Alignment: ${P}`,before:"",after:String(y.aligns_to??"")};default:return{label:`${p.replace(/_/g," ")}: ${P}`,before:"",after:""}}},[k]),V=us({subjectArea:j,describe:De}),Pe=a.useCallback(async(p,y)=>{const L=(k==null?void 0:k.fingerprint)??"",P=await rs(p,y,L);return await ie(j||void 0).then(D),P},[k,j]),Ie=a.useMemo(()=>{const p={};for(const y of V.pending)y.op==="set_subject_area_members"&&(p[y.payload.id]=y.payload.members??[]);return p},[V.pending]),U=a.useMemo(()=>[...S,...V.pending],[S,V.pending]),[Oe,se]=a.useState(null),[Be,ne]=a.useState(null);a.useEffect(()=>{if(!U.length){se(null),ne(null);return}const p=new AbortController,y=U.map(L=>({op:L.op,payload:L.payload}));return xe(y,j||void 0,p.signal).then(L=>se(L.diff??null)).catch(L=>{L.name!=="AbortError"&&se(null)}),is(y).then(ne).catch(()=>ne(null)),()=>p.abort()},[U,j]);const Le=a.useMemo(()=>ps(U),[U]).size{M(p),window.location.hash=p==="browse"?"":`#${p}`},[]),te=a.useRef(!0);a.useEffect(()=>{const p=()=>{const y=window.location.hash;if(te.current&&!y&&$){te.current=!1;return}te.current=!1,M(y==="#proposals"?"proposals":y==="#review"?"review":y==="#model"?"model":y==="#areas"?"areas":"browse")};return p(),window.addEventListener("hashchange",p),()=>window.removeEventListener("hashchange",p)},[]);const ae=a.useCallback(()=>{ls({subject_area:j,q:o}).then(n).catch(p=>v(String(p)))},[j,o]);a.useEffect(ae,[ae]),a.useEffect(()=>{ie(j||void 0).then(p=>{i(p.read_only),r(p.project.name),D(p),Ee(!!p.direct),h(p.identity??null),p.identity&&p.identity.source!=="anonymous"&&p.identity.name&&J(p.identity.name)}).catch(()=>{}),cs().then(m).catch(()=>{})},[j]);const re=a.useMemo(()=>(s==null?void 0:s.terms.find(p=>p.id===N))??null,[s,N]),ze=a.useCallback(p=>{const y=p.key??ye(p.op,p.payload);C(L=>[...L.filter(P=>P.key!==y),{...p,key:y}])},[]),He=p=>C(y=>y.filter((L,P)=>P!==p)),We=()=>{C([]),A(!1),E(!1),ae()};if(d)return e.jsxs("div",{className:"sme-splash error",children:[e.jsx("h1",{children:"◮ Glossary"}),e.jsx("pre",{children:d}),e.jsxs("p",{children:["Is ",e.jsx("code",{children:"mdl glossary"})," pointed at a model?"]})]});if(!s)return e.jsx("div",{className:"sme-splash",children:"◮ loading glossary…"});const X=!t,le=(l==null?void 0:l.catalog_owned_fields)??[],qe=le.length>0;return e.jsxs("div",{className:"sme",children:[e.jsxs("header",{className:"sme-top",children:[e.jsxs("div",{className:"sme-brand",children:[e.jsx("span",{className:"sme-logo",children:"◮"}),e.jsx("span",{children:"Glossary"}),e.jsx("span",{className:"sme-project",children:c}),t&&e.jsx("span",{className:"sme-chip",children:"read-only"}),qe&&e.jsxs("span",{className:"sme-chip catalog",title:`Definitions are mastered in ${l==null?void 0:l.catalog_name}`,children:["mirrors ",l==null?void 0:l.catalog_name]})]}),e.jsx("input",{className:"sme-search",type:"search",placeholder:"Search terms & definitions…",value:o,onChange:p=>f(p.target.value)}),e.jsxs("nav",{className:"sme-tabs",children:[e.jsx("button",{className:"sme-tab"+(u==="browse"?" active":""),onClick:()=>K("browse"),children:"Terms"}),e.jsx("button",{className:"sme-tab"+(u==="model"?" active":""),onClick:()=>K("model"),children:"Model"}),e.jsx("button",{className:"sme-tab"+(u==="areas"?" active":""),onClick:()=>K("areas"),children:"Subject areas"}),e.jsx("button",{className:"sme-tab"+(u==="proposals"?" active":""),onClick:()=>K("proposals"),children:"My proposals"})]}),U.length>0&&e.jsxs("button",{className:"sme-tray",onClick:()=>K("review"),children:[U.length," change",U.length>1?"s":""," · Review →"]})]}),u==="browse"&&e.jsx(js,{user:T}),u==="review"?e.jsx(Ls,{stagedDiff:H??Oe,stagedRoute:H?null:Be,user:T,selectable:Le,excluded:z,onToggleField:(p,y)=>W(L=>{const P=new Set(L),Y=Q(p,y);return P.has(Y)?P.delete(Y):P.add(Y),P}),onBack:()=>{G(null),W(new Set),K("browse")},onSubmit:p=>{Me(p),A(!0)},action:H?{id:"apply-import",label:"Apply import →",primary:!0,apply:async()=>{const p=H,y=new Set([...$e(p)].filter(Y=>!z.has(Y))),L=As(p,y),P=$s(H.changes,L);await os(P,!0),G(null),W(new Set),K("browse"),await ie(j||void 0).then(D)}}:void 0}):u==="model"?V.previewDoc??k?e.jsx(gs,{doc:V.previewDoc??k,subjectArea:j,onSubjectArea:b,exec:q?Pe:V.exec,canEdit:X,direct:q,onImported:()=>K("review"),onImportPreview:p=>{W(new Set),G(p),K("review")},openImport:$,prefillImport:g,query:o,busy:V.busy}):e.jsx("div",{className:"sme-splash",children:"◮ loading the model…"}):u==="areas"?e.jsx(fs,{exec:V.exec,canEdit:X,stagedMembers:Ie}):u==="proposals"?e.jsx(vs,{user:T,onView:()=>K("review")}):e.jsxs("div",{className:"sme-body",children:[e.jsxs("nav",{className:"sme-nav",children:[e.jsx("button",{className:"sme-sa"+(j===""?" active":""),onClick:()=>b(""),children:"All subject areas"}),s.subject_areas.map(p=>e.jsx("button",{className:"sme-sa"+(j===p.id?" active":""),onClick:()=>b(p.id),title:p.definition??void 0,children:p.name},p.id))]}),e.jsxs("ul",{className:"sme-list",children:[s.terms.map(p=>{var y;return e.jsxs("li",{className:"sme-list-item"+(p.id===N?" active":""),onClick:()=>{_(p.id),E(!1)},children:[e.jsx("span",{className:"sme-term-name",children:p.name}),p.kind==="term"&&e.jsx("span",{className:"sme-kind",children:"term"}),((y=p.ontology)==null?void 0:y.status)==="proposed"&&e.jsx("span",{className:"sme-badge proposed",title:"alignment awaiting architect",children:"proposed"}),e.jsx("span",{className:"sme-term-def",children:p.definition??"no definition yet"})]},p.id)}),s.terms.length===0&&e.jsx("li",{className:"sme-empty",children:"no terms match"})]}),e.jsx("section",{className:"sme-detail",children:re?B&&X?e.jsx(Gs,{term:re,pending:S,onStage:ze,onDone:()=>E(!1),catalogOwned:le,catalog:l?{name:l.catalog_name,url:l.catalog_url}:null}):e.jsx(qs,{term:re,canEdit:X,pendingCount:S.length,onEdit:()=>E(!0)}):e.jsx("div",{className:"sme-placeholder",children:"Select a term to see its definition."})})]}),R&&e.jsx(ws,{changes:U,routeAdvice:Re,identity:x,user:T,onUser:p=>{J(p),localStorage.setItem("mdl.sme.user",p)},onDrop:He,onClose:()=>A(!1),onProposed:()=>{We(),V.clear(),W(new Set),K("proposals")}})]})}Ge.createRoot(document.getElementById("root")).render(e.jsx(Ke.StrictMode,{children:e.jsx(Us,{})})); diff --git a/packages/server/src/mdl_server/static/assets/styles-B90OKxCj.js b/packages/server/src/mdl_server/static/assets/styles-B90OKxCj.js new file mode 100644 index 0000000..6cbaefa --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/styles-B90OKxCj.js @@ -0,0 +1,23 @@ +var tl=Object.defineProperty;var nl=(e,t,n)=>t in e?tl(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ue=(e,t,n)=>nl(e,typeof t!="symbol"?t+"":t,n);import{r as w,g as fi,R as M,j as h}from"./client-DOmNpTln.js";function ye(e){if(typeof e=="string"||typeof e=="number")return""+e;let t="";if(Array.isArray(e))for(let n=0,r;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?ul:cl;gi.useSyncExternalStore=Et.useSyncExternalStore!==void 0?Et.useSyncExternalStore:dl;pi.exports=gi;var fl=pi.exports;/** + * @license React + * use-sync-external-store-shim/with-selector.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var kn=w,hl=fl;function ml(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var pl=typeof Object.is=="function"?Object.is:ml,gl=hl.useSyncExternalStore,yl=kn.useRef,xl=kn.useEffect,wl=kn.useMemo,bl=kn.useDebugValue;mi.useSyncExternalStoreWithSelector=function(e,t,n,r,s){var i=yl(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=wl(function(){function l(f){if(!c){if(c=!0,u=f,f=r(f),s!==void 0&&o.hasValue){var x=o.value;if(s(x,f))return d=x}return d=f}if(x=d,pl(u,f))return x;var y=r(f);return s!==void 0&&s(x,y)?(u=f,x):(u=f,d=y)}var c=!1,u,d,m=n===void 0?null:n;return[function(){return l(t())},m===null?void 0:function(){return l(m())}]},[t,n,r,s]);var a=gl(e,i[0],i[1]);return xl(function(){o.hasValue=!0,o.value=a},[a]),bl(a),a};hi.exports=mi;var vl=hi.exports;const _l=fi(vl),El={},Wr=e=>{let t;const n=new Set,r=(u,d)=>{const m=typeof u=="function"?u(t):u;if(!Object.is(m,t)){const f=t;t=d??(typeof m!="object"||m===null)?m:Object.assign({},t,m),n.forEach(x=>x(t,f))}},s=()=>t,l={setState:r,getState:s,getInitialState:()=>c,subscribe:u=>(n.add(u),()=>n.delete(u)),destroy:()=>{(El?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),n.clear()}},c=t=e(r,s,l);return l},Nl=e=>e?Wr(e):Wr,{useDebugValue:Sl}=M,{useSyncExternalStoreWithSelector:kl}=_l,jl=e=>e;function yi(e,t=jl,n){const r=kl(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return Sl(r),r}const Ur=(e,t)=>{const n=Nl(e),r=(s,i=t)=>yi(n,s,i);return Object.assign(r,n),r},Cl=(e,t)=>e?Ur(e,t):Ur;function ge(e,t){if(Object.is(e,t))return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return!1;if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,s]of e)if(!Object.is(s,t.get(r)))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}const n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(const r of n)if(!Object.prototype.hasOwnProperty.call(t,r)||!Object.is(e[r],t[r]))return!1;return!0}var $l={value:()=>{}};function jn(){for(var e=0,t=arguments.length,n={},r;e=0&&(r=n.slice(s+1),n=n.slice(0,s)),n&&!t.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:r}})}cn.prototype=jn.prototype={constructor:cn,on:function(e,t){var n=this._,r=Ml(e+"",n),s,i=-1,o=r.length;if(arguments.length<2){for(;++i0)for(var n=new Array(s),r=0,s,i;r=0&&(t=e.slice(0,n))!=="xmlns"&&(e=e.slice(n+1)),qr.hasOwnProperty(t)?{space:qr[t],local:e}:e}function Il(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===nr&&t.documentElement.namespaceURI===nr?t.createElement(e):t.createElementNS(n,e)}}function Rl(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function xi(e){var t=Cn(e);return(t.local?Rl:Il)(t)}function Tl(){}function wr(e){return e==null?Tl:function(){return this.querySelector(e)}}function Ol(e){typeof e!="function"&&(e=wr(e));for(var t=this._groups,n=t.length,r=new Array(n),s=0;s=$&&($=E+1);!(A=v[$])&&++$=0;)(o=r[s])&&(i&&o.compareDocumentPosition(i)^4&&i.parentNode.insertBefore(o,i),i=o);return this}function oc(e){e||(e=ac);function t(d,m){return d&&m?e(d.__data__,m.__data__):!d-!m}for(var n=this._groups,r=n.length,s=new Array(r),i=0;it?1:e>=t?0:NaN}function lc(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}function cc(){return Array.from(this)}function uc(){for(var e=this._groups,t=0,n=e.length;t1?this.each((t==null?vc:typeof t=="function"?Ec:_c)(e,t,n??"")):Nt(this.node(),e)}function Nt(e,t){return e.style.getPropertyValue(t)||Ei(e).getComputedStyle(e,null).getPropertyValue(t)}function Sc(e){return function(){delete this[e]}}function kc(e,t){return function(){this[e]=t}}function jc(e,t){return function(){var n=t.apply(this,arguments);n==null?delete this[e]:this[e]=n}}function Cc(e,t){return arguments.length>1?this.each((t==null?Sc:typeof t=="function"?jc:kc)(e,t)):this.node()[e]}function Ni(e){return e.trim().split(/^|\s+/)}function br(e){return e.classList||new Si(e)}function Si(e){this._node=e,this._names=Ni(e.getAttribute("class")||"")}Si.prototype={add:function(e){var t=this._names.indexOf(e);t<0&&(this._names.push(e),this._node.setAttribute("class",this._names.join(" ")))},remove:function(e){var t=this._names.indexOf(e);t>=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(e){return this._names.indexOf(e)>=0}};function ki(e,t){for(var n=br(e),r=-1,s=t.length;++r=0&&(n=t.slice(r+1),t=t.slice(0,r)),{type:t,name:n}})}function nu(e){return function(){var t=this.__on;if(t){for(var n=0,r=-1,s=t.length,i;n()=>e;function rr(e,{sourceEvent:t,subject:n,target:r,identifier:s,active:i,x:o,y:a,dx:l,dy:c,dispatch:u}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:s,enumerable:!0,configurable:!0},active:{value:i,enumerable:!0,configurable:!0},x:{value:o,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:c,enumerable:!0,configurable:!0},_:{value:u}})}rr.prototype.on=function(){var e=this._.on.apply(this._,arguments);return e===this._?this:e};function fu(e){return!e.ctrlKey&&!e.button}function hu(){return this.parentNode}function mu(e,t){return t??{x:e.x,y:e.y}}function pu(){return navigator.maxTouchPoints||"ontouchstart"in this}function gu(){var e=fu,t=hu,n=mu,r=pu,s={},i=jn("start","drag","end"),o=0,a,l,c,u,d=0;function m(b){b.on("mousedown.drag",f).filter(r).on("touchstart.drag",v).on("touchmove.drag",p,du).on("touchend.drag touchcancel.drag",E).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(b,A){if(!(u||!e.call(this,b,A))){var j=$(this,t.call(this,b,A),b,A,"mouse");j&&(Ne(b.view).on("mousemove.drag",x,zt).on("mouseup.drag",y,zt),Mi(b.view),Hn(b),c=!1,a=b.clientX,l=b.clientY,j("start",b))}}function x(b){if(wt(b),!c){var A=b.clientX-a,j=b.clientY-l;c=A*A+j*j>d}s.mouse("drag",b)}function y(b){Ne(b.view).on("mousemove.drag mouseup.drag",null),Ai(b.view,c),wt(b),s.mouse("end",b)}function v(b,A){if(e.call(this,b,A)){var j=b.changedTouches,_=t.call(this,b,A),I=j.length,S,D;for(S=0;S>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?Jt(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?Jt(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=xu.exec(e))?new ve(t[1],t[2],t[3],1):(t=wu.exec(e))?new ve(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=bu.exec(e))?Jt(t[1],t[2],t[3],t[4]):(t=vu.exec(e))?Jt(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=_u.exec(e))?ns(t[1],t[2]/100,t[3]/100,1):(t=Eu.exec(e))?ns(t[1],t[2]/100,t[3]/100,t[4]):Kr.hasOwnProperty(e)?Jr(Kr[e]):e==="transparent"?new ve(NaN,NaN,NaN,0):null}function Jr(e){return new ve(e>>16&255,e>>8&255,e&255,1)}function Jt(e,t,n,r){return r<=0&&(e=t=n=NaN),new ve(e,t,n,r)}function ku(e){return e instanceof Xt||(e=Vt(e)),e?(e=e.rgb(),new ve(e.r,e.g,e.b,e.opacity)):new ve}function sr(e,t,n,r){return arguments.length===1?ku(e):new ve(e,t,n,r??1)}function ve(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}vr(ve,sr,Ii(Xt,{brighter(e){return e=e==null?mn:Math.pow(mn,e),new ve(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Ht:Math.pow(Ht,e),new ve(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new ve(at(this.r),at(this.g),at(this.b),pn(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:es,formatHex:es,formatHex8:ju,formatRgb:ts,toString:ts}));function es(){return`#${it(this.r)}${it(this.g)}${it(this.b)}`}function ju(){return`#${it(this.r)}${it(this.g)}${it(this.b)}${it((isNaN(this.opacity)?1:this.opacity)*255)}`}function ts(){const e=pn(this.opacity);return`${e===1?"rgb(":"rgba("}${at(this.r)}, ${at(this.g)}, ${at(this.b)}${e===1?")":`, ${e})`}`}function pn(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function at(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function it(e){return e=at(e),(e<16?"0":"")+e.toString(16)}function ns(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new Me(e,t,n,r)}function Ri(e){if(e instanceof Me)return new Me(e.h,e.s,e.l,e.opacity);if(e instanceof Xt||(e=Vt(e)),!e)return new Me;if(e instanceof Me)return e;e=e.rgb();var t=e.r/255,n=e.g/255,r=e.b/255,s=Math.min(t,n,r),i=Math.max(t,n,r),o=NaN,a=i-s,l=(i+s)/2;return a?(t===i?o=(n-r)/a+(n0&&l<1?0:o,new Me(o,a,l,e.opacity)}function Cu(e,t,n,r){return arguments.length===1?Ri(e):new Me(e,t,n,r??1)}function Me(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}vr(Me,Cu,Ii(Xt,{brighter(e){return e=e==null?mn:Math.pow(mn,e),new Me(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Ht:Math.pow(Ht,e),new Me(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*t,s=2*n-r;return new ve(Fn(e>=240?e-240:e+120,s,r),Fn(e,s,r),Fn(e<120?e+240:e-120,s,r),this.opacity)},clamp(){return new Me(rs(this.h),en(this.s),en(this.l),pn(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const e=pn(this.opacity);return`${e===1?"hsl(":"hsla("}${rs(this.h)}, ${en(this.s)*100}%, ${en(this.l)*100}%${e===1?")":`, ${e})`}`}}));function rs(e){return e=(e||0)%360,e<0?e+360:e}function en(e){return Math.max(0,Math.min(1,e||0))}function Fn(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}const Ti=e=>()=>e;function $u(e,t){return function(n){return e+n*t}}function Mu(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(r){return Math.pow(e+r*t,n)}}function Au(e){return(e=+e)==1?Oi:function(t,n){return n-t?Mu(t,n,e):Ti(isNaN(t)?n:t)}}function Oi(e,t){var n=t-e;return n?$u(e,n):Ti(isNaN(e)?t:e)}const ss=function e(t){var n=Au(t);function r(s,i){var o=n((s=sr(s)).r,(i=sr(i)).r),a=n(s.g,i.g),l=n(s.b,i.b),c=Oi(s.opacity,i.opacity);return function(u){return s.r=o(u),s.g=a(u),s.b=l(u),s.opacity=c(u),s+""}}return r.gamma=e,r}(1);function Ue(e,t){return e=+e,t=+t,function(n){return e*(1-n)+t*n}}var ir=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Vn=new RegExp(ir.source,"g");function Iu(e){return function(){return e}}function Ru(e){return function(t){return e(t)+""}}function Tu(e,t){var n=ir.lastIndex=Vn.lastIndex=0,r,s,i,o=-1,a=[],l=[];for(e=e+"",t=t+"";(r=ir.exec(e))&&(s=Vn.exec(t));)(i=s.index)>n&&(i=t.slice(n,i),a[o]?a[o]+=i:a[++o]=i),(r=r[0])===(s=s[0])?a[o]?a[o]+=s:a[++o]=s:(a[++o]=null,l.push({i:o,x:Ue(r,s)})),n=Vn.lastIndex;return n180?u+=360:u-c>180&&(c+=360),m.push({i:d.push(s(d)+"rotate(",null,r)-2,x:Ue(c,u)})):u&&d.push(s(d)+"rotate("+u+r)}function a(c,u,d,m){c!==u?m.push({i:d.push(s(d)+"skewX(",null,r)-2,x:Ue(c,u)}):u&&d.push(s(d)+"skewX("+u+r)}function l(c,u,d,m,f,x){if(c!==d||u!==m){var y=f.push(s(f)+"scale(",null,",",null,")");x.push({i:y-4,x:Ue(c,d)},{i:y-2,x:Ue(u,m)})}else(d!==1||m!==1)&&f.push(s(f)+"scale("+d+","+m+")")}return function(c,u){var d=[],m=[];return c=e(c),u=e(u),i(c.translateX,c.translateY,u.translateX,u.translateY,d,m),o(c.rotate,u.rotate,d,m),a(c.skewX,u.skewX,d,m),l(c.scaleX,c.scaleY,u.scaleX,u.scaleY,d,m),c=u=null,function(f){for(var x=-1,y=m.length,v;++x=0&&e._call.call(void 0,t),e=e._next;--St}function as(){ct=(yn=Bt.now())+$n,St=Ot=0;try{Gu()}finally{St=0,Wu(),ct=0}}function Yu(){var e=Bt.now(),t=e-yn;t>Pi&&($n-=t,yn=e)}function Wu(){for(var e,t=gn,n,r=1/0;t;)t._call?(r>t._time&&(r=t._time),e=t,t=t._next):(n=t._next,t._next=null,t=e?e._next=n:gn=n);Dt=e,ar(r)}function ar(e){if(!St){Ot&&(Ot=clearTimeout(Ot));var t=e-ct;t>24?(e<1/0&&(Ot=setTimeout(as,e-Bt.now()-$n)),Mt&&(Mt=clearInterval(Mt))):(Mt||(yn=Bt.now(),Mt=setInterval(Yu,Pi)),St=1,zi(as))}}function ls(e,t,n){var r=new xn;return t=t==null?0:+t,r.restart(s=>{r.stop(),e(s+t)},t,n),r}var Uu=jn("start","end","cancel","interrupt"),Xu=[],Fi=0,cs=1,lr=2,un=3,us=4,cr=5,dn=6;function Mn(e,t,n,r,s,i){var o=e.__transition;if(!o)e.__transition={};else if(n in o)return;qu(e,n,{name:t,index:r,group:s,on:Uu,tween:Xu,time:i.time,delay:i.delay,duration:i.duration,ease:i.ease,timer:null,state:Fi})}function Er(e,t){var n=Ae(e,t);if(n.state>Fi)throw new Error("too late; already scheduled");return n}function Oe(e,t){var n=Ae(e,t);if(n.state>un)throw new Error("too late; already running");return n}function Ae(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw new Error("transition not found");return n}function qu(e,t,n){var r=e.__transition,s;r[t]=n,n.timer=Hi(i,0,n.time);function i(c){n.state=cs,n.timer.restart(o,n.delay,n.time),n.delay<=c&&o(c-n.delay)}function o(c){var u,d,m,f;if(n.state!==cs)return l();for(u in r)if(f=r[u],f.name===n.name){if(f.state===un)return ls(o);f.state===us?(f.state=dn,f.timer.stop(),f.on.call("interrupt",e,e.__data__,f.index,f.group),delete r[u]):+ulr&&r.state=0&&(t=t.slice(0,n)),!t||t==="start"})}function Sd(e,t,n){var r,s,i=Nd(t)?Er:Oe;return function(){var o=i(this,e),a=o.on;a!==r&&(s=(r=a).copy()).on(t,n),o.on=s}}function kd(e,t){var n=this._id;return arguments.length<2?Ae(this.node(),n).on.on(e):this.each(Sd(n,e,t))}function jd(e){return function(){var t=this.parentNode;for(var n in this.__transition)if(+n!==e)return;t&&t.removeChild(this)}}function Cd(){return this.on("end.remove",jd(this._id))}function $d(e){var t=this._name,n=this._id;typeof e!="function"&&(e=wr(e));for(var r=this._groups,s=r.length,i=new Array(s),o=0;o()=>e;function ef(e,{sourceEvent:t,target:n,transform:r,dispatch:s}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:s}})}function He(e,t,n){this.k=e,this.x=t,this.y=n}He.prototype={constructor:He,scale:function(e){return e===1?this:new He(this.k*e,this.x,this.y)},translate:function(e,t){return e===0&t===0?this:new He(this.k,this.x+this.k*e,this.y+this.k*t)},apply:function(e){return[e[0]*this.k+this.x,e[1]*this.k+this.y]},applyX:function(e){return e*this.k+this.x},applyY:function(e){return e*this.k+this.y},invert:function(e){return[(e[0]-this.x)/this.k,(e[1]-this.y)/this.k]},invertX:function(e){return(e-this.x)/this.k},invertY:function(e){return(e-this.y)/this.k},rescaleX:function(e){return e.copy().domain(e.range().map(this.invertX,this).map(e.invert,e))},rescaleY:function(e){return e.copy().domain(e.range().map(this.invertY,this).map(e.invert,e))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Fe=new He(1,0,0);He.prototype;function Bn(e){e.stopImmediatePropagation()}function At(e){e.preventDefault(),e.stopImmediatePropagation()}function tf(e){return(!e.ctrlKey||e.type==="wheel")&&!e.button}function nf(){var e=this;return e instanceof SVGElement?(e=e.ownerSVGElement||e,e.hasAttribute("viewBox")?(e=e.viewBox.baseVal,[[e.x,e.y],[e.x+e.width,e.y+e.height]]):[[0,0],[e.width.baseVal.value,e.height.baseVal.value]]):[[0,0],[e.clientWidth,e.clientHeight]]}function ds(){return this.__zoom||Fe}function rf(e){return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*(e.ctrlKey?10:1)}function sf(){return navigator.maxTouchPoints||"ontouchstart"in this}function of(e,t,n){var r=e.invertX(t[0][0])-n[0][0],s=e.invertX(t[1][0])-n[1][0],i=e.invertY(t[0][1])-n[0][1],o=e.invertY(t[1][1])-n[1][1];return e.translate(s>r?(r+s)/2:Math.min(0,r)||Math.max(0,s),o>i?(i+o)/2:Math.min(0,i)||Math.max(0,o))}function Yi(){var e=tf,t=nf,n=of,r=rf,s=sf,i=[0,1/0],o=[[-1/0,-1/0],[1/0,1/0]],a=250,l=Vu,c=jn("start","zoom","end"),u,d,m,f=500,x=150,y=0,v=10;function p(g){g.property("__zoom",ds).on("wheel.zoom",I,{passive:!1}).on("mousedown.zoom",S).on("dblclick.zoom",D).filter(s).on("touchstart.zoom",P).on("touchmove.zoom",V).on("touchend.zoom touchcancel.zoom",F).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}p.transform=function(g,C,N,R){var H=g.selection?g.selection():g;H.property("__zoom",ds),g!==H?A(g,C,N,R):H.interrupt().each(function(){j(this,arguments).event(R).start().zoom(null,typeof C=="function"?C.apply(this,arguments):C).end()})},p.scaleBy=function(g,C,N,R){p.scaleTo(g,function(){var H=this.__zoom.k,k=typeof C=="function"?C.apply(this,arguments):C;return H*k},N,R)},p.scaleTo=function(g,C,N,R){p.transform(g,function(){var H=t.apply(this,arguments),k=this.__zoom,T=N==null?b(H):typeof N=="function"?N.apply(this,arguments):N,L=k.invert(T),z=typeof C=="function"?C.apply(this,arguments):C;return n($(E(k,z),T,L),H,o)},N,R)},p.translateBy=function(g,C,N,R){p.transform(g,function(){return n(this.__zoom.translate(typeof C=="function"?C.apply(this,arguments):C,typeof N=="function"?N.apply(this,arguments):N),t.apply(this,arguments),o)},null,R)},p.translateTo=function(g,C,N,R,H){p.transform(g,function(){var k=t.apply(this,arguments),T=this.__zoom,L=R==null?b(k):typeof R=="function"?R.apply(this,arguments):R;return n(Fe.translate(L[0],L[1]).scale(T.k).translate(typeof C=="function"?-C.apply(this,arguments):-C,typeof N=="function"?-N.apply(this,arguments):-N),k,o)},R,H)};function E(g,C){return C=Math.max(i[0],Math.min(i[1],C)),C===g.k?g:new He(C,g.x,g.y)}function $(g,C,N){var R=C[0]-N[0]*g.k,H=C[1]-N[1]*g.k;return R===g.x&&H===g.y?g:new He(g.k,R,H)}function b(g){return[(+g[0][0]+ +g[1][0])/2,(+g[0][1]+ +g[1][1])/2]}function A(g,C,N,R){g.on("start.zoom",function(){j(this,arguments).event(R).start()}).on("interrupt.zoom end.zoom",function(){j(this,arguments).event(R).end()}).tween("zoom",function(){var H=this,k=arguments,T=j(H,k).event(R),L=t.apply(H,k),z=N==null?b(L):typeof N=="function"?N.apply(H,k):N,Y=Math.max(L[1][0]-L[0][0],L[1][1]-L[0][1]),G=H.__zoom,K=typeof C=="function"?C.apply(H,k):C,J=l(G.invert(z).concat(Y/G.k),K.invert(z).concat(Y/K.k));return function(ee){if(ee===1)ee=K;else{var ie=J(ee),te=Y/ie[2];ee=new He(te,z[0]-ie[0]*te,z[1]-ie[1]*te)}T.zoom(null,ee)}})}function j(g,C,N){return!N&&g.__zooming||new _(g,C)}function _(g,C){this.that=g,this.args=C,this.active=0,this.sourceEvent=null,this.extent=t.apply(g,C),this.taps=0}_.prototype={event:function(g){return g&&(this.sourceEvent=g),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(g,C){return this.mouse&&g!=="mouse"&&(this.mouse[1]=C.invert(this.mouse[0])),this.touch0&&g!=="touch"&&(this.touch0[1]=C.invert(this.touch0[0])),this.touch1&&g!=="touch"&&(this.touch1[1]=C.invert(this.touch1[0])),this.that.__zoom=C,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(g){var C=Ne(this.that).datum();c.call(g,this.that,new ef(g,{sourceEvent:this.sourceEvent,target:p,transform:this.that.__zoom,dispatch:c}),C)}};function I(g,...C){if(!e.apply(this,arguments))return;var N=j(this,C).event(g),R=this.__zoom,H=Math.max(i[0],Math.min(i[1],R.k*Math.pow(2,r.apply(this,arguments)))),k=$e(g);if(N.wheel)(N.mouse[0][0]!==k[0]||N.mouse[0][1]!==k[1])&&(N.mouse[1]=R.invert(N.mouse[0]=k)),clearTimeout(N.wheel);else{if(R.k===H)return;N.mouse=[k,R.invert(k)],fn(this),N.start()}At(g),N.wheel=setTimeout(T,x),N.zoom("mouse",n($(E(R,H),N.mouse[0],N.mouse[1]),N.extent,o));function T(){N.wheel=null,N.end()}}function S(g,...C){if(m||!e.apply(this,arguments))return;var N=g.currentTarget,R=j(this,C,!0).event(g),H=Ne(g.view).on("mousemove.zoom",z,!0).on("mouseup.zoom",Y,!0),k=$e(g,N),T=g.clientX,L=g.clientY;Mi(g.view),Bn(g),R.mouse=[k,this.__zoom.invert(k)],fn(this),R.start();function z(G){if(At(G),!R.moved){var K=G.clientX-T,J=G.clientY-L;R.moved=K*K+J*J>y}R.event(G).zoom("mouse",n($(R.that.__zoom,R.mouse[0]=$e(G,N),R.mouse[1]),R.extent,o))}function Y(G){H.on("mousemove.zoom mouseup.zoom",null),Ai(G.view,R.moved),At(G),R.event(G).end()}}function D(g,...C){if(e.apply(this,arguments)){var N=this.__zoom,R=$e(g.changedTouches?g.changedTouches[0]:g,this),H=N.invert(R),k=N.k*(g.shiftKey?.5:2),T=n($(E(N,k),R,H),t.apply(this,C),o);At(g),a>0?Ne(this).transition().duration(a).call(A,T,R,g):Ne(this).call(p.transform,T,R,g)}}function P(g,...C){if(e.apply(this,arguments)){var N=g.touches,R=N.length,H=j(this,C,g.changedTouches.length===R).event(g),k,T,L,z;for(Bn(g),T=0;T"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:e=>`Node type "${e}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:e=>`The old edge with id=${e} does not exist.`,error009:e=>`Marker type "${e}" doesn't exist.`,error008:(e,t)=>`Couldn't create edge for ${e?"target":"source"} handle id: "${e?t.targetHandle:t.sourceHandle}", edge id: ${t.id}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:e=>`Edge type "${e}" not found. Using fallback type "default".`,error012:e=>`Node with id "${e}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`},Wi=Be.error001();function ae(e,t){const n=w.useContext(An);if(n===null)throw new Error(Wi);return yi(n,e,t)}const pe=()=>{const e=w.useContext(An);if(e===null)throw new Error(Wi);return w.useMemo(()=>({getState:e.getState,setState:e.setState,subscribe:e.subscribe,destroy:e.destroy}),[e])},lf=e=>e.userSelectionActive?"none":"all";function Sr({position:e,children:t,className:n,style:r,...s}){const i=ae(lf),o=`${e}`.split("-");return M.createElement("div",{className:ye(["react-flow__panel",n,...o]),style:{...r,pointerEvents:i},...s},t)}function cf({proOptions:e,position:t="bottom-right"}){return e!=null&&e.hideAttribution?null:M.createElement(Sr,{position:t,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://reactflow.dev/pro"},M.createElement("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution"},"React Flow"))}const uf=({x:e,y:t,label:n,labelStyle:r={},labelShowBg:s=!0,labelBgStyle:i={},labelBgPadding:o=[2,4],labelBgBorderRadius:a=2,children:l,className:c,...u})=>{const d=w.useRef(null),[m,f]=w.useState({x:0,y:0,width:0,height:0}),x=ye(["react-flow__edge-textwrapper",c]);return w.useEffect(()=>{if(d.current){const y=d.current.getBBox();f({x:y.x,y:y.y,width:y.width,height:y.height})}},[n]),typeof n>"u"||!n?null:M.createElement("g",{transform:`translate(${e-m.width/2} ${t-m.height/2})`,className:x,visibility:m.width?"visible":"hidden",...u},s&&M.createElement("rect",{width:m.width+2*o[0],x:-o[0],y:-o[1],height:m.height+2*o[1],className:"react-flow__edge-textbg",style:i,rx:a,ry:a}),M.createElement("text",{className:"react-flow__edge-text",y:m.height/2,dy:"0.3em",ref:d,style:r},n),l)};var df=w.memo(uf);const kr=e=>({width:e.offsetWidth,height:e.offsetHeight}),kt=(e,t=0,n=1)=>Math.min(Math.max(e,t),n),jr=(e={x:0,y:0},t)=>({x:kt(e.x,t[0][0],t[1][0]),y:kt(e.y,t[0][1],t[1][1])}),fs=(e,t,n)=>en?-kt(Math.abs(e-n),1,50)/50:0,Ui=(e,t)=>{const n=fs(e.x,35,t.width-35)*20,r=fs(e.y,35,t.height-35)*20;return[n,r]},Xi=e=>{var t;return((t=e.getRootNode)==null?void 0:t.call(e))||(window==null?void 0:window.document)},qi=(e,t)=>({x:Math.min(e.x,t.x),y:Math.min(e.y,t.y),x2:Math.max(e.x2,t.x2),y2:Math.max(e.y2,t.y2)}),Gt=({x:e,y:t,width:n,height:r})=>({x:e,y:t,x2:e+n,y2:t+r}),Ki=({x:e,y:t,x2:n,y2:r})=>({x:e,y:t,width:n-e,height:r-t}),hs=e=>({...e.positionAbsolute||{x:0,y:0},width:e.width||0,height:e.height||0}),ff=(e,t)=>Ki(qi(Gt(e),Gt(t))),ur=(e,t)=>{const n=Math.max(0,Math.min(e.x+e.width,t.x+t.width)-Math.max(e.x,t.x)),r=Math.max(0,Math.min(e.y+e.height,t.y+t.height)-Math.max(e.y,t.y));return Math.ceil(n*r)},hf=e=>Se(e.width)&&Se(e.height)&&Se(e.x)&&Se(e.y),Se=e=>!isNaN(e)&&isFinite(e),le=Symbol.for("internals"),Zi=["Enter"," ","Escape"],mf=(e,t)=>{},pf=e=>"nativeEvent"in e;function dr(e){var s,i;const t=pf(e)?e.nativeEvent:e,n=((i=(s=t.composedPath)==null?void 0:s.call(t))==null?void 0:i[0])||e.target;return["INPUT","SELECT","TEXTAREA"].includes(n==null?void 0:n.nodeName)||(n==null?void 0:n.hasAttribute("contenteditable"))||!!(n!=null&&n.closest(".nokey"))}const Qi=e=>"clientX"in e,Ze=(e,t)=>{var i,o;const n=Qi(e),r=n?e.clientX:(i=e.touches)==null?void 0:i[0].clientX,s=n?e.clientY:(o=e.touches)==null?void 0:o[0].clientY;return{x:r-((t==null?void 0:t.left)??0),y:s-((t==null?void 0:t.top)??0)}},wn=()=>{var e;return typeof navigator<"u"&&((e=navigator==null?void 0:navigator.userAgent)==null?void 0:e.indexOf("Mac"))>=0},qt=({id:e,path:t,labelX:n,labelY:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f=20})=>M.createElement(M.Fragment,null,M.createElement("path",{id:e,style:u,d:t,fill:"none",className:"react-flow__edge-path",markerEnd:d,markerStart:m}),f&&M.createElement("path",{d:t,fill:"none",strokeOpacity:0,strokeWidth:f,className:"react-flow__edge-interaction"}),s&&Se(n)&&Se(r)?M.createElement(df,{x:n,y:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c}):null);qt.displayName="BaseEdge";function It(e,t,n){return n===void 0?n:r=>{const s=t().edges.find(i=>i.id===e);s&&n(r,{...s})}}function Ji({sourceX:e,sourceY:t,targetX:n,targetY:r}){const s=Math.abs(n-e)/2,i=n{const[v,p,E]=to({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:i});return M.createElement(qt,{path:v,labelX:p,labelY:E,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:x,interactionWidth:y})});Cr.displayName="SimpleBezierEdge";const ps={[Z.Left]:{x:-1,y:0},[Z.Right]:{x:1,y:0},[Z.Top]:{x:0,y:-1},[Z.Bottom]:{x:0,y:1}},gf=({source:e,sourcePosition:t=Z.Bottom,target:n})=>t===Z.Left||t===Z.Right?e.xMath.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2));function yf({source:e,sourcePosition:t=Z.Bottom,target:n,targetPosition:r=Z.Top,center:s,offset:i}){const o=ps[t],a=ps[r],l={x:e.x+o.x*i,y:e.y+o.y*i},c={x:n.x+a.x*i,y:n.y+a.y*i},u=gf({source:l,sourcePosition:t,target:c}),d=u.x!==0?"x":"y",m=u[d];let f=[],x,y;const v={x:0,y:0},p={x:0,y:0},[E,$,b,A]=Ji({sourceX:e.x,sourceY:e.y,targetX:n.x,targetY:n.y});if(o[d]*a[d]===-1){x=s.x??E,y=s.y??$;const _=[{x,y:l.y},{x,y:c.y}],I=[{x:l.x,y},{x:c.x,y}];o[d]===m?f=d==="x"?_:I:f=d==="x"?I:_}else{const _=[{x:l.x,y:c.y}],I=[{x:c.x,y:l.y}];if(d==="x"?f=o.x===m?I:_:f=o.y===m?_:I,t===r){const F=Math.abs(e[d]-n[d]);if(F<=i){const g=Math.min(i-1,i-F);o[d]===m?v[d]=(l[d]>e[d]?-1:1)*g:p[d]=(c[d]>n[d]?-1:1)*g}}if(t!==r){const F=d==="x"?"y":"x",g=o[d]===a[F],C=l[F]>c[F],N=l[F]=V?(x=(S.x+D.x)/2,y=f[0].y):(x=f[0].x,y=(S.y+D.y)/2)}return[[e,{x:l.x+v.x,y:l.y+v.y},...f,{x:c.x+p.x,y:c.y+p.y},n],x,y,b,A]}function xf(e,t,n,r){const s=Math.min(gs(e,t)/2,gs(t,n)/2,r),{x:i,y:o}=t;if(e.x===i&&i===n.x||e.y===o&&o===n.y)return`L${i} ${o}`;if(e.y===o){const c=e.x{let $="";return E>0&&E{const[p,E,$]=vn({sourceX:e,sourceY:t,sourcePosition:d,targetX:n,targetY:r,targetPosition:m,borderRadius:y==null?void 0:y.borderRadius,offset:y==null?void 0:y.offset});return M.createElement(qt,{path:p,labelX:E,labelY:$,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:f,markerStart:x,interactionWidth:v})});In.displayName="SmoothStepEdge";const $r=w.memo(e=>{var t;return M.createElement(In,{...e,pathOptions:w.useMemo(()=>{var n;return{borderRadius:0,offset:(n=e.pathOptions)==null?void 0:n.offset}},[(t=e.pathOptions)==null?void 0:t.offset])})});$r.displayName="StepEdge";function wf({sourceX:e,sourceY:t,targetX:n,targetY:r}){const[s,i,o,a]=Ji({sourceX:e,sourceY:t,targetX:n,targetY:r});return[`M ${e},${t}L ${n},${r}`,s,i,o,a]}const Mr=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f})=>{const[x,y,v]=wf({sourceX:e,sourceY:t,targetX:n,targetY:r});return M.createElement(qt,{path:x,labelX:y,labelY:v,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f})});Mr.displayName="StraightEdge";function rn(e,t){return e>=0?.5*e:t*25*Math.sqrt(-e)}function ys({pos:e,x1:t,y1:n,x2:r,y2:s,c:i}){switch(e){case Z.Left:return[t-rn(t-r,i),n];case Z.Right:return[t+rn(r-t,i),n];case Z.Top:return[t,n-rn(n-s,i)];case Z.Bottom:return[t,n+rn(s-n,i)]}}function no({sourceX:e,sourceY:t,sourcePosition:n=Z.Bottom,targetX:r,targetY:s,targetPosition:i=Z.Top,curvature:o=.25}){const[a,l]=ys({pos:n,x1:e,y1:t,x2:r,y2:s,c:o}),[c,u]=ys({pos:i,x1:r,y1:s,x2:e,y2:t,c:o}),[d,m,f,x]=eo({sourceX:e,sourceY:t,targetX:r,targetY:s,sourceControlX:a,sourceControlY:l,targetControlX:c,targetControlY:u});return[`M${e},${t} C${a},${l} ${c},${u} ${r},${s}`,d,m,f,x]}const _n=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,sourcePosition:s=Z.Bottom,targetPosition:i=Z.Top,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:x,pathOptions:y,interactionWidth:v})=>{const[p,E,$]=no({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:i,curvature:y==null?void 0:y.curvature});return M.createElement(qt,{path:p,labelX:E,labelY:$,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:x,interactionWidth:v})});_n.displayName="BezierEdge";const Ar=w.createContext(null),bf=Ar.Provider;Ar.Consumer;const vf=()=>w.useContext(Ar),_f=e=>"id"in e&&"source"in e&&"target"in e,Ef=({source:e,sourceHandle:t,target:n,targetHandle:r})=>`reactflow__edge-${e}${t||""}-${n}${r||""}`,fr=(e,t)=>typeof e>"u"?"":typeof e=="string"?e:`${t?`${t}__`:""}${Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("&")}`,Nf=(e,t)=>t.some(n=>n.source===e.source&&n.target===e.target&&(n.sourceHandle===e.sourceHandle||!n.sourceHandle&&!e.sourceHandle)&&(n.targetHandle===e.targetHandle||!n.targetHandle&&!e.targetHandle)),Sf=(e,t)=>{if(!e.source||!e.target)return t;let n;return _f(e)?n={...e}:n={...e,id:Ef(e)},Nf(n,t)?t:t.concat(n)},hr=({x:e,y:t},[n,r,s],i,[o,a])=>{const l={x:(e-n)/s,y:(t-r)/s};return i?{x:o*Math.round(l.x/o),y:a*Math.round(l.y/a)}:l},ro=({x:e,y:t},[n,r,s])=>({x:e*s+n,y:t*s+r}),lt=(e,t=[0,0])=>{if(!e)return{x:0,y:0,positionAbsolute:{x:0,y:0}};const n=(e.width??0)*t[0],r=(e.height??0)*t[1],s={x:e.position.x-n,y:e.position.y-r};return{...s,positionAbsolute:e.positionAbsolute?{x:e.positionAbsolute.x-n,y:e.positionAbsolute.y-r}:s}},Kt=(e,t=[0,0])=>{if(e.length===0)return{x:0,y:0,width:0,height:0};const n=e.reduce((r,s)=>{const{x:i,y:o}=lt(s,t).positionAbsolute;return qi(r,Gt({x:i,y:o,width:s.width||0,height:s.height||0}))},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Ki(n)},kf=(e,t=[0,0])=>(console.warn("[DEPRECATED] `getRectOfNodes` is deprecated. Instead use `getNodesBounds` https://reactflow.dev/api-reference/utils/get-nodes-bounds."),Kt(e,t)),so=(e,t,[n,r,s]=[0,0,1],i=!1,o=!1,a=[0,0])=>{const l={x:(t.x-n)/s,y:(t.y-r)/s,width:t.width/s,height:t.height/s},c=[];return e.forEach(u=>{const{width:d,height:m,selectable:f=!0,hidden:x=!1}=u;if(o&&!f||x)return!1;const{positionAbsolute:y}=lt(u,a),v={x:y.x,y:y.y,width:d||0,height:m||0},p=ur(l,v),E=typeof d>"u"||typeof m>"u"||d===null||m===null,$=i&&p>0,b=(d||0)*(m||0);(E||$||p>=b||u.dragging)&&c.push(u)}),c},io=(e,t)=>{const n=e.map(r=>r.id);return t.filter(r=>n.includes(r.source)||n.includes(r.target))},oo=(e,t,n,r,s,i=.1)=>{const o=t/(e.width*(1+i)),a=n/(e.height*(1+i)),l=Math.min(o,a),c=kt(l,r,s),u=e.x+e.width/2,d=e.y+e.height/2,m=t/2-u*c,f=n/2-d*c;return{x:m,y:f,zoom:c}},st=(e,t=0)=>e.transition().duration(t);function xs(e,t,n,r){return(t[n]||[]).reduce((s,i)=>{var o,a;return`${e.id}-${i.id}-${n}`!==r&&s.push({id:i.id||null,type:n,nodeId:e.id,x:(((o=e.positionAbsolute)==null?void 0:o.x)??0)+i.x+i.width/2,y:(((a=e.positionAbsolute)==null?void 0:a.y)??0)+i.y+i.height/2}),s},[])}function jf(e,t,n,r,s,i){const{x:o,y:a}=Ze(e),c=t.elementsFromPoint(o,a).find(x=>x.classList.contains("react-flow__handle"));if(c){const x=c.getAttribute("data-nodeid");if(x){const y=Ir(void 0,c),v=c.getAttribute("data-handleid"),p=i({nodeId:x,id:v,type:y});if(p){const E=s.find($=>$.nodeId===x&&$.type===y&&$.id===v);return{handle:{id:v,type:y,nodeId:x,x:(E==null?void 0:E.x)||n.x,y:(E==null?void 0:E.y)||n.y},validHandleResult:p}}}}let u=[],d=1/0;if(s.forEach(x=>{const y=Math.sqrt((x.x-n.x)**2+(x.y-n.y)**2);if(y<=r){const v=i(x);y<=d&&(yx.isValid),f=u.some(({handle:x})=>x.type==="target");return u.find(({handle:x,validHandleResult:y})=>f?x.type==="target":m?y.isValid:!0)||u[0]}const Cf={source:null,target:null,sourceHandle:null,targetHandle:null},ao=()=>({handleDomNode:null,isValid:!1,connection:Cf,endHandle:null});function lo(e,t,n,r,s,i,o){const a=s==="target",l=o.querySelector(`.react-flow__handle[data-id="${e==null?void 0:e.nodeId}-${e==null?void 0:e.id}-${e==null?void 0:e.type}"]`),c={...ao(),handleDomNode:l};if(l){const u=Ir(void 0,l),d=l.getAttribute("data-nodeid"),m=l.getAttribute("data-handleid"),f=l.classList.contains("connectable"),x=l.classList.contains("connectableend"),y={source:a?d:n,sourceHandle:a?m:r,target:a?n:d,targetHandle:a?r:m};c.connection=y,f&&x&&(t===ut.Strict?a&&u==="source"||!a&&u==="target":d!==n||m!==r)&&(c.endHandle={nodeId:d,handleId:m,type:u},c.isValid=i(y))}return c}function $f({nodes:e,nodeId:t,handleId:n,handleType:r}){return e.reduce((s,i)=>{if(i[le]){const{handleBounds:o}=i[le];let a=[],l=[];o&&(a=xs(i,o,"source",`${t}-${n}-${r}`),l=xs(i,o,"target",`${t}-${n}-${r}`)),s.push(...a,...l)}return s},[])}function Ir(e,t){return e||(t!=null&&t.classList.contains("target")?"target":t!=null&&t.classList.contains("source")?"source":null)}function Gn(e){e==null||e.classList.remove("valid","connecting","react-flow__handle-valid","react-flow__handle-connecting")}function Mf(e,t){let n=null;return t?n="valid":e&&!t&&(n="invalid"),n}function co({event:e,handleId:t,nodeId:n,onConnect:r,isTarget:s,getState:i,setState:o,isValidConnection:a,edgeUpdaterType:l,onReconnectEnd:c}){const u=Xi(e.target),{connectionMode:d,domNode:m,autoPanOnConnect:f,connectionRadius:x,onConnectStart:y,panBy:v,getNodes:p,cancelConnection:E}=i();let $=0,b;const{x:A,y:j}=Ze(e),_=u==null?void 0:u.elementFromPoint(A,j),I=Ir(l,_),S=m==null?void 0:m.getBoundingClientRect();if(!S||!I)return;let D,P=Ze(e,S),V=!1,F=null,g=!1,C=null;const N=$f({nodes:p(),nodeId:n,handleId:t,handleType:I}),R=()=>{if(!f)return;const[T,L]=Ui(P,S);v({x:T,y:L}),$=requestAnimationFrame(R)};o({connectionPosition:P,connectionStatus:null,connectionNodeId:n,connectionHandleId:t,connectionHandleType:I,connectionStartHandle:{nodeId:n,handleId:t,type:I},connectionEndHandle:null}),y==null||y(e,{nodeId:n,handleId:t,handleType:I});function H(T){const{transform:L}=i();P=Ze(T,S);const{handle:z,validHandleResult:Y}=jf(T,u,hr(P,L,!1,[1,1]),x,N,G=>lo(G,d,n,t,s?"target":"source",a,u));if(b=z,V||(R(),V=!0),C=Y.handleDomNode,F=Y.connection,g=Y.isValid,o({connectionPosition:b&&g?ro({x:b.x,y:b.y},L):P,connectionStatus:Mf(!!b,g),connectionEndHandle:Y.endHandle}),!b&&!g&&!C)return Gn(D);F.source!==F.target&&C&&(Gn(D),D=C,C.classList.add("connecting","react-flow__handle-connecting"),C.classList.toggle("valid",g),C.classList.toggle("react-flow__handle-valid",g))}function k(T){var L,z;(b||C)&&F&&g&&(r==null||r(F)),(z=(L=i()).onConnectEnd)==null||z.call(L,T),l&&(c==null||c(T)),Gn(D),E(),cancelAnimationFrame($),V=!1,g=!1,F=null,C=null,u.removeEventListener("mousemove",H),u.removeEventListener("mouseup",k),u.removeEventListener("touchmove",H),u.removeEventListener("touchend",k)}u.addEventListener("mousemove",H),u.addEventListener("mouseup",k),u.addEventListener("touchmove",H),u.addEventListener("touchend",k)}const ws=()=>!0,Af=e=>({connectionStartHandle:e.connectionStartHandle,connectOnClick:e.connectOnClick,noPanClassName:e.noPanClassName}),If=(e,t,n)=>r=>{const{connectionStartHandle:s,connectionEndHandle:i,connectionClickStartHandle:o}=r;return{connecting:(s==null?void 0:s.nodeId)===e&&(s==null?void 0:s.handleId)===t&&(s==null?void 0:s.type)===n||(i==null?void 0:i.nodeId)===e&&(i==null?void 0:i.handleId)===t&&(i==null?void 0:i.type)===n,clickConnecting:(o==null?void 0:o.nodeId)===e&&(o==null?void 0:o.handleId)===t&&(o==null?void 0:o.type)===n}},uo=w.forwardRef(({type:e="source",position:t=Z.Top,isValidConnection:n,isConnectable:r=!0,isConnectableStart:s=!0,isConnectableEnd:i=!0,id:o,onConnect:a,children:l,className:c,onMouseDown:u,onTouchStart:d,...m},f)=>{var S,D;const x=o||null,y=e==="target",v=pe(),p=vf(),{connectOnClick:E,noPanClassName:$}=ae(Af,ge),{connecting:b,clickConnecting:A}=ae(If(p,x,e),ge);p||(D=(S=v.getState()).onError)==null||D.call(S,"010",Be.error010());const j=P=>{const{defaultEdgeOptions:V,onConnect:F,hasDefaultEdges:g}=v.getState(),C={...V,...P};if(g){const{edges:N,setEdges:R}=v.getState();R(Sf(C,N))}F==null||F(C),a==null||a(C)},_=P=>{if(!p)return;const V=Qi(P);s&&(V&&P.button===0||!V)&&co({event:P,handleId:x,nodeId:p,onConnect:j,isTarget:y,getState:v.getState,setState:v.setState,isValidConnection:n||v.getState().isValidConnection||ws}),V?u==null||u(P):d==null||d(P)},I=P=>{const{onClickConnectStart:V,onClickConnectEnd:F,connectionClickStartHandle:g,connectionMode:C,isValidConnection:N}=v.getState();if(!p||!g&&!s)return;if(!g){V==null||V(P,{nodeId:p,handleId:x,handleType:e}),v.setState({connectionClickStartHandle:{nodeId:p,type:e,handleId:x}});return}const R=Xi(P.target),H=n||N||ws,{connection:k,isValid:T}=lo({nodeId:p,id:x,type:e},C,g.nodeId,g.handleId||null,g.type,H,R);T&&j(k),F==null||F(P),v.setState({connectionClickStartHandle:null})};return M.createElement("div",{"data-handleid":x,"data-nodeid":p,"data-handlepos":t,"data-id":`${p}-${x}-${e}`,className:ye(["react-flow__handle",`react-flow__handle-${t}`,"nodrag",$,c,{source:!y,target:y,connectable:r,connectablestart:s,connectableend:i,connecting:A,connectionindicator:r&&(s&&!b||i&&b)}]),onMouseDown:_,onTouchStart:_,onClick:E?I:void 0,ref:f,...m},l)});uo.displayName="Handle";var Ke=w.memo(uo);const fo=({data:e,isConnectable:t,targetPosition:n=Z.Top,sourcePosition:r=Z.Bottom})=>M.createElement(M.Fragment,null,M.createElement(Ke,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label,M.createElement(Ke,{type:"source",position:r,isConnectable:t}));fo.displayName="DefaultNode";var mr=w.memo(fo);const ho=({data:e,isConnectable:t,sourcePosition:n=Z.Bottom})=>M.createElement(M.Fragment,null,e==null?void 0:e.label,M.createElement(Ke,{type:"source",position:n,isConnectable:t}));ho.displayName="InputNode";var mo=w.memo(ho);const po=({data:e,isConnectable:t,targetPosition:n=Z.Top})=>M.createElement(M.Fragment,null,M.createElement(Ke,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label);po.displayName="OutputNode";var go=w.memo(po);const Rr=()=>null;Rr.displayName="GroupNode";const Rf=e=>({selectedNodes:e.getNodes().filter(t=>t.selected),selectedEdges:e.edges.filter(t=>t.selected).map(t=>({...t}))}),sn=e=>e.id;function Tf(e,t){return ge(e.selectedNodes.map(sn),t.selectedNodes.map(sn))&&ge(e.selectedEdges.map(sn),t.selectedEdges.map(sn))}const yo=w.memo(({onSelectionChange:e})=>{const t=pe(),{selectedNodes:n,selectedEdges:r}=ae(Rf,Tf);return w.useEffect(()=>{const s={nodes:n,edges:r};e==null||e(s),t.getState().onSelectionChange.forEach(i=>i(s))},[n,r,e]),null});yo.displayName="SelectionListener";const Of=e=>!!e.onSelectionChange;function Df({onSelectionChange:e}){const t=ae(Of);return e||t?M.createElement(yo,{onSelectionChange:e}):null}const Lf=e=>({setNodes:e.setNodes,setEdges:e.setEdges,setDefaultNodesAndEdges:e.setDefaultNodesAndEdges,setMinZoom:e.setMinZoom,setMaxZoom:e.setMaxZoom,setTranslateExtent:e.setTranslateExtent,setNodeExtent:e.setNodeExtent,reset:e.reset});function mt(e,t){w.useEffect(()=>{typeof e<"u"&&t(e)},[e])}function ne(e,t,n){w.useEffect(()=>{typeof t<"u"&&n({[e]:t})},[t])}const Pf=({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:s,onConnectStart:i,onConnectEnd:o,onClickConnectStart:a,onClickConnectEnd:l,nodesDraggable:c,nodesConnectable:u,nodesFocusable:d,edgesFocusable:m,edgesUpdatable:f,elevateNodesOnSelect:x,minZoom:y,maxZoom:v,nodeExtent:p,onNodesChange:E,onEdgesChange:$,elementsSelectable:b,connectionMode:A,snapGrid:j,snapToGrid:_,translateExtent:I,connectOnClick:S,defaultEdgeOptions:D,fitView:P,fitViewOptions:V,onNodesDelete:F,onEdgesDelete:g,onNodeDrag:C,onNodeDragStart:N,onNodeDragStop:R,onSelectionDrag:H,onSelectionDragStart:k,onSelectionDragStop:T,noPanClassName:L,nodeOrigin:z,rfId:Y,autoPanOnConnect:G,autoPanOnNodeDrag:K,onError:J,connectionRadius:ee,isValidConnection:ie,nodeDragThreshold:te})=>{const{setNodes:re,setEdges:fe,setDefaultNodesAndEdges:B,setMinZoom:q,setMaxZoom:W,setTranslateExtent:Q,setNodeExtent:U,reset:X}=ae(Lf,ge),O=pe();return w.useEffect(()=>{const se=r==null?void 0:r.map(ce=>({...ce,...D}));return B(n,se),()=>{X()}},[]),ne("defaultEdgeOptions",D,O.setState),ne("connectionMode",A,O.setState),ne("onConnect",s,O.setState),ne("onConnectStart",i,O.setState),ne("onConnectEnd",o,O.setState),ne("onClickConnectStart",a,O.setState),ne("onClickConnectEnd",l,O.setState),ne("nodesDraggable",c,O.setState),ne("nodesConnectable",u,O.setState),ne("nodesFocusable",d,O.setState),ne("edgesFocusable",m,O.setState),ne("edgesUpdatable",f,O.setState),ne("elementsSelectable",b,O.setState),ne("elevateNodesOnSelect",x,O.setState),ne("snapToGrid",_,O.setState),ne("snapGrid",j,O.setState),ne("onNodesChange",E,O.setState),ne("onEdgesChange",$,O.setState),ne("connectOnClick",S,O.setState),ne("fitViewOnInit",P,O.setState),ne("fitViewOnInitOptions",V,O.setState),ne("onNodesDelete",F,O.setState),ne("onEdgesDelete",g,O.setState),ne("onNodeDrag",C,O.setState),ne("onNodeDragStart",N,O.setState),ne("onNodeDragStop",R,O.setState),ne("onSelectionDrag",H,O.setState),ne("onSelectionDragStart",k,O.setState),ne("onSelectionDragStop",T,O.setState),ne("noPanClassName",L,O.setState),ne("nodeOrigin",z,O.setState),ne("rfId",Y,O.setState),ne("autoPanOnConnect",G,O.setState),ne("autoPanOnNodeDrag",K,O.setState),ne("onError",J,O.setState),ne("connectionRadius",ee,O.setState),ne("isValidConnection",ie,O.setState),ne("nodeDragThreshold",te,O.setState),mt(e,re),mt(t,fe),mt(y,q),mt(v,W),mt(I,Q),mt(p,U),null},bs={display:"none"},zf={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},xo="react-flow__node-desc",wo="react-flow__edge-desc",Hf="react-flow__aria-live",Ff=e=>e.ariaLiveMessage;function Vf({rfId:e}){const t=ae(Ff);return M.createElement("div",{id:`${Hf}-${e}`,"aria-live":"assertive","aria-atomic":"true",style:zf},t)}function Bf({rfId:e,disableKeyboardA11y:t}){return M.createElement(M.Fragment,null,M.createElement("div",{id:`${xo}-${e}`,style:bs},"Press enter or space to select a node.",!t&&"You can then use the arrow keys to move the node around."," Press delete to remove it and escape to cancel."," "),M.createElement("div",{id:`${wo}-${e}`,style:bs},"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel."),!t&&M.createElement(Vf,{rfId:e}))}var Wt=(e=null,t={actInsideInputWithModifier:!0})=>{const[n,r]=w.useState(!1),s=w.useRef(!1),i=w.useRef(new Set([])),[o,a]=w.useMemo(()=>{if(e!==null){const c=(Array.isArray(e)?e:[e]).filter(d=>typeof d=="string").map(d=>d.split("+")),u=c.reduce((d,m)=>d.concat(...m),[]);return[c,u]}return[[],[]]},[e]);return w.useEffect(()=>{const l=typeof document<"u"?document:null,c=(t==null?void 0:t.target)||l;if(e!==null){const u=f=>{if(s.current=f.ctrlKey||f.metaKey||f.shiftKey,(!s.current||s.current&&!t.actInsideInputWithModifier)&&dr(f))return!1;const y=_s(f.code,a);i.current.add(f[y]),vs(o,i.current,!1)&&(f.preventDefault(),r(!0))},d=f=>{if((!s.current||s.current&&!t.actInsideInputWithModifier)&&dr(f))return!1;const y=_s(f.code,a);vs(o,i.current,!0)?(r(!1),i.current.clear()):i.current.delete(f[y]),f.key==="Meta"&&i.current.clear(),s.current=!1},m=()=>{i.current.clear(),r(!1)};return c==null||c.addEventListener("keydown",u),c==null||c.addEventListener("keyup",d),window.addEventListener("blur",m),()=>{c==null||c.removeEventListener("keydown",u),c==null||c.removeEventListener("keyup",d),window.removeEventListener("blur",m)}}},[e,r]),n};function vs(e,t,n){return e.filter(r=>n||r.length===t.size).some(r=>r.every(s=>t.has(s)))}function _s(e,t){return t.includes(e)?"code":"key"}function bo(e,t,n,r){var a,l;const s=e.parentNode||e.parentId;if(!s)return n;const i=t.get(s),o=lt(i,r);return bo(i,t,{x:(n.x??0)+o.x,y:(n.y??0)+o.y,z:(((a=i[le])==null?void 0:a.z)??0)>(n.z??0)?((l=i[le])==null?void 0:l.z)??0:n.z??0},r)}function vo(e,t,n){e.forEach(r=>{var i;const s=r.parentNode||r.parentId;if(s&&!e.has(s))throw new Error(`Parent node ${s} not found`);if(s||n!=null&&n[r.id]){const{x:o,y:a,z:l}=bo(r,e,{...r.position,z:((i=r[le])==null?void 0:i.z)??0},t);r.positionAbsolute={x:o,y:a},r[le].z=l,n!=null&&n[r.id]&&(r[le].isParent=!0)}})}function Yn(e,t,n,r){const s=new Map,i={},o=r?1e3:0;return e.forEach(a=>{var f;const l=(Se(a.zIndex)?a.zIndex:0)+(a.selected?o:0),c=t.get(a.id),u={...a,positionAbsolute:{x:a.position.x,y:a.position.y}},d=a.parentNode||a.parentId;d&&(i[d]=!0);const m=(c==null?void 0:c.type)&&(c==null?void 0:c.type)!==a.type;Object.defineProperty(u,le,{enumerable:!1,value:{handleBounds:m||(f=c==null?void 0:c[le])==null?void 0:f.handleBounds,z:l}}),s.set(a.id,u)}),vo(s,n,i),s}function _o(e,t={}){const{getNodes:n,width:r,height:s,minZoom:i,maxZoom:o,d3Zoom:a,d3Selection:l,fitViewOnInitDone:c,fitViewOnInit:u,nodeOrigin:d}=e(),m=t.initial&&!c&&u;if(a&&l&&(m||!t.initial)){const x=n().filter(v=>{var E;const p=t.includeHiddenNodes?v.width&&v.height:!v.hidden;return(E=t.nodes)!=null&&E.length?p&&t.nodes.some($=>$.id===v.id):p}),y=x.every(v=>v.width&&v.height);if(x.length>0&&y){const v=Kt(x,d),{x:p,y:E,zoom:$}=oo(v,r,s,t.minZoom??i,t.maxZoom??o,t.padding??.1),b=Fe.translate(p,E).scale($);return typeof t.duration=="number"&&t.duration>0?a.transform(st(l,t.duration),b):a.transform(l,b),!0}}return!1}function Gf(e,t){return e.forEach(n=>{const r=t.get(n.id);r&&t.set(r.id,{...r,[le]:r[le],selected:n.selected})}),new Map(t)}function Yf(e,t){return t.map(n=>{const r=e.find(s=>s.id===n.id);return r&&(n.selected=r.selected),n})}function on({changedNodes:e,changedEdges:t,get:n,set:r}){const{nodeInternals:s,edges:i,onNodesChange:o,onEdgesChange:a,hasDefaultNodes:l,hasDefaultEdges:c}=n();e!=null&&e.length&&(l&&r({nodeInternals:Gf(e,s)}),o==null||o(e)),t!=null&&t.length&&(c&&r({edges:Yf(t,i)}),a==null||a(t))}const pt=()=>{},Wf={zoomIn:pt,zoomOut:pt,zoomTo:pt,getZoom:()=>1,setViewport:pt,getViewport:()=>({x:0,y:0,zoom:1}),fitView:()=>!1,setCenter:pt,fitBounds:pt,project:e=>e,screenToFlowPosition:e=>e,flowToScreenPosition:e=>e,viewportInitialized:!1},Uf=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection}),Xf=()=>{const e=pe(),{d3Zoom:t,d3Selection:n}=ae(Uf,ge);return w.useMemo(()=>n&&t?{zoomIn:s=>t.scaleBy(st(n,s==null?void 0:s.duration),1.2),zoomOut:s=>t.scaleBy(st(n,s==null?void 0:s.duration),1/1.2),zoomTo:(s,i)=>t.scaleTo(st(n,i==null?void 0:i.duration),s),getZoom:()=>e.getState().transform[2],setViewport:(s,i)=>{const[o,a,l]=e.getState().transform,c=Fe.translate(s.x??o,s.y??a).scale(s.zoom??l);t.transform(st(n,i==null?void 0:i.duration),c)},getViewport:()=>{const[s,i,o]=e.getState().transform;return{x:s,y:i,zoom:o}},fitView:s=>_o(e.getState,s),setCenter:(s,i,o)=>{const{width:a,height:l,maxZoom:c}=e.getState(),u=typeof(o==null?void 0:o.zoom)<"u"?o.zoom:c,d=a/2-s*u,m=l/2-i*u,f=Fe.translate(d,m).scale(u);t.transform(st(n,o==null?void 0:o.duration),f)},fitBounds:(s,i)=>{const{width:o,height:a,minZoom:l,maxZoom:c}=e.getState(),{x:u,y:d,zoom:m}=oo(s,o,a,l,c,(i==null?void 0:i.padding)??.1),f=Fe.translate(u,d).scale(m);t.transform(st(n,i==null?void 0:i.duration),f)},project:s=>{const{transform:i,snapToGrid:o,snapGrid:a}=e.getState();return console.warn("[DEPRECATED] `project` is deprecated. Instead use `screenToFlowPosition`. There is no need to subtract the react flow bounds anymore! https://reactflow.dev/api-reference/types/react-flow-instance#screen-to-flow-position"),hr(s,i,o,a)},screenToFlowPosition:s=>{const{transform:i,snapToGrid:o,snapGrid:a,domNode:l}=e.getState();if(!l)return s;const{x:c,y:u}=l.getBoundingClientRect(),d={x:s.x-c,y:s.y-u};return hr(d,i,o,a)},flowToScreenPosition:s=>{const{transform:i,domNode:o}=e.getState();if(!o)return s;const{x:a,y:l}=o.getBoundingClientRect(),c=ro(s,i);return{x:c.x+a,y:c.y+l}},viewportInitialized:!0}:Wf,[t,n])};function Rn(){const e=Xf(),t=pe(),n=w.useCallback(()=>t.getState().getNodes().map(y=>({...y})),[]),r=w.useCallback(y=>t.getState().nodeInternals.get(y),[]),s=w.useCallback(()=>{const{edges:y=[]}=t.getState();return y.map(v=>({...v}))},[]),i=w.useCallback(y=>{const{edges:v=[]}=t.getState();return v.find(p=>p.id===y)},[]),o=w.useCallback(y=>{const{getNodes:v,setNodes:p,hasDefaultNodes:E,onNodesChange:$}=t.getState(),b=v(),A=typeof y=="function"?y(b):y;if(E)p(A);else if($){const j=A.length===0?b.map(_=>({type:"remove",id:_.id})):A.map(_=>({item:_,type:"reset"}));$(j)}},[]),a=w.useCallback(y=>{const{edges:v=[],setEdges:p,hasDefaultEdges:E,onEdgesChange:$}=t.getState(),b=typeof y=="function"?y(v):y;if(E)p(b);else if($){const A=b.length===0?v.map(j=>({type:"remove",id:j.id})):b.map(j=>({item:j,type:"reset"}));$(A)}},[]),l=w.useCallback(y=>{const v=Array.isArray(y)?y:[y],{getNodes:p,setNodes:E,hasDefaultNodes:$,onNodesChange:b}=t.getState();if($){const j=[...p(),...v];E(j)}else if(b){const A=v.map(j=>({item:j,type:"add"}));b(A)}},[]),c=w.useCallback(y=>{const v=Array.isArray(y)?y:[y],{edges:p=[],setEdges:E,hasDefaultEdges:$,onEdgesChange:b}=t.getState();if($)E([...p,...v]);else if(b){const A=v.map(j=>({item:j,type:"add"}));b(A)}},[]),u=w.useCallback(()=>{const{getNodes:y,edges:v=[],transform:p}=t.getState(),[E,$,b]=p;return{nodes:y().map(A=>({...A})),edges:v.map(A=>({...A})),viewport:{x:E,y:$,zoom:b}}},[]),d=w.useCallback(({nodes:y,edges:v})=>{const{nodeInternals:p,getNodes:E,edges:$,hasDefaultNodes:b,hasDefaultEdges:A,onNodesDelete:j,onEdgesDelete:_,onNodesChange:I,onEdgesChange:S}=t.getState(),D=(y||[]).map(C=>C.id),P=(v||[]).map(C=>C.id),V=E().reduce((C,N)=>{const R=N.parentNode||N.parentId,H=!D.includes(N.id)&&R&&C.find(T=>T.id===R);return(typeof N.deletable=="boolean"?N.deletable:!0)&&(D.includes(N.id)||H)&&C.push(N),C},[]),F=$.filter(C=>typeof C.deletable=="boolean"?C.deletable:!0),g=F.filter(C=>P.includes(C.id));if(V||g){const C=io(V,F),N=[...g,...C],R=N.reduce((H,k)=>(H.includes(k.id)||H.push(k.id),H),[]);if((A||b)&&(A&&t.setState({edges:$.filter(H=>!R.includes(H.id))}),b&&(V.forEach(H=>{p.delete(H.id)}),t.setState({nodeInternals:new Map(p)}))),R.length>0&&(_==null||_(N),S&&S(R.map(H=>({id:H,type:"remove"})))),V.length>0&&(j==null||j(V),I)){const H=V.map(k=>({id:k.id,type:"remove"}));I(H)}}},[]),m=w.useCallback(y=>{const v=hf(y),p=v?null:t.getState().nodeInternals.get(y.id);return!v&&!p?[null,null,v]:[v?y:hs(p),p,v]},[]),f=w.useCallback((y,v=!0,p)=>{const[E,$,b]=m(y);return E?(p||t.getState().getNodes()).filter(A=>{if(!b&&(A.id===$.id||!A.positionAbsolute))return!1;const j=hs(A),_=ur(j,E);return v&&_>0||_>=E.width*E.height}):[]},[]),x=w.useCallback((y,v,p=!0)=>{const[E]=m(y);if(!E)return!1;const $=ur(E,v);return p&&$>0||$>=E.width*E.height},[]);return w.useMemo(()=>({...e,getNodes:n,getNode:r,getEdges:s,getEdge:i,setNodes:o,setEdges:a,addNodes:l,addEdges:c,toObject:u,deleteElements:d,getIntersectingNodes:f,isNodeIntersecting:x}),[e,n,r,s,i,o,a,l,c,u,d,f,x])}const qf={actInsideInputWithModifier:!1};var Kf=({deleteKeyCode:e,multiSelectionKeyCode:t})=>{const n=pe(),{deleteElements:r}=Rn(),s=Wt(e,qf),i=Wt(t);w.useEffect(()=>{if(s){const{edges:o,getNodes:a}=n.getState(),l=a().filter(u=>u.selected),c=o.filter(u=>u.selected);r({nodes:l,edges:c}),n.setState({nodesSelectionActive:!1})}},[s]),w.useEffect(()=>{n.setState({multiSelectionActive:i})},[i])};function Zf(e){const t=pe();w.useEffect(()=>{let n;const r=()=>{var i,o;if(!e.current)return;const s=kr(e.current);(s.height===0||s.width===0)&&((o=(i=t.getState()).onError)==null||o.call(i,"004",Be.error004())),t.setState({width:s.width||500,height:s.height||500})};return r(),window.addEventListener("resize",r),e.current&&(n=new ResizeObserver(()=>r()),n.observe(e.current)),()=>{window.removeEventListener("resize",r),n&&e.current&&n.unobserve(e.current)}},[])}const Tr={position:"absolute",width:"100%",height:"100%",top:0,left:0},Qf=(e,t)=>e.x!==t.x||e.y!==t.y||e.zoom!==t.k,an=e=>({x:e.x,y:e.y,zoom:e.k}),gt=(e,t)=>e.target.closest(`.${t}`),Es=(e,t)=>t===2&&Array.isArray(e)&&e.includes(2),Ns=e=>{const t=e.ctrlKey&&wn()?10:1;return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*t},Jf=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection,d3ZoomHandler:e.d3ZoomHandler,userSelectionActive:e.userSelectionActive}),eh=({onMove:e,onMoveStart:t,onMoveEnd:n,onPaneContextMenu:r,zoomOnScroll:s=!0,zoomOnPinch:i=!0,panOnScroll:o=!1,panOnScrollSpeed:a=.5,panOnScrollMode:l=ot.Free,zoomOnDoubleClick:c=!0,elementsSelectable:u,panOnDrag:d=!0,defaultViewport:m,translateExtent:f,minZoom:x,maxZoom:y,zoomActivationKeyCode:v,preventScrolling:p=!0,children:E,noWheelClassName:$,noPanClassName:b})=>{const A=w.useRef(),j=pe(),_=w.useRef(!1),I=w.useRef(!1),S=w.useRef(null),D=w.useRef({x:0,y:0,zoom:0}),{d3Zoom:P,d3Selection:V,d3ZoomHandler:F,userSelectionActive:g}=ae(Jf,ge),C=Wt(v),N=w.useRef(0),R=w.useRef(!1),H=w.useRef();return Zf(S),w.useEffect(()=>{if(S.current){const k=S.current.getBoundingClientRect(),T=Yi().scaleExtent([x,y]).translateExtent(f),L=Ne(S.current).call(T),z=Fe.translate(m.x,m.y).scale(kt(m.zoom,x,y)),Y=[[0,0],[k.width,k.height]],G=T.constrain()(z,Y,f);T.transform(L,G),T.wheelDelta(Ns),j.setState({d3Zoom:T,d3Selection:L,d3ZoomHandler:L.on("wheel.zoom"),transform:[G.x,G.y,G.k],domNode:S.current.closest(".react-flow")})}},[]),w.useEffect(()=>{V&&P&&(o&&!C&&!g?V.on("wheel.zoom",k=>{if(gt(k,$))return!1;k.preventDefault(),k.stopImmediatePropagation();const T=V.property("__zoom").k||1;if(k.ctrlKey&&i){const ie=$e(k),te=Ns(k),re=T*Math.pow(2,te);P.scaleTo(V,re,ie,k);return}const L=k.deltaMode===1?20:1;let z=l===ot.Vertical?0:k.deltaX*L,Y=l===ot.Horizontal?0:k.deltaY*L;!wn()&&k.shiftKey&&l!==ot.Vertical&&(z=k.deltaY*L,Y=0),P.translateBy(V,-(z/T)*a,-(Y/T)*a,{internal:!0});const G=an(V.property("__zoom")),{onViewportChangeStart:K,onViewportChange:J,onViewportChangeEnd:ee}=j.getState();clearTimeout(H.current),R.current||(R.current=!0,t==null||t(k,G),K==null||K(G)),R.current&&(e==null||e(k,G),J==null||J(G),H.current=setTimeout(()=>{n==null||n(k,G),ee==null||ee(G),R.current=!1},150))},{passive:!1}):typeof F<"u"&&V.on("wheel.zoom",function(k,T){if(!p&&k.type==="wheel"&&!k.ctrlKey||gt(k,$))return null;k.preventDefault(),F.call(this,k,T)},{passive:!1}))},[g,o,l,V,P,F,C,i,p,$,t,e,n]),w.useEffect(()=>{P&&P.on("start",k=>{var z,Y;if(!k.sourceEvent||k.sourceEvent.internal)return null;N.current=(z=k.sourceEvent)==null?void 0:z.button;const{onViewportChangeStart:T}=j.getState(),L=an(k.transform);_.current=!0,D.current=L,((Y=k.sourceEvent)==null?void 0:Y.type)==="mousedown"&&j.setState({paneDragging:!0}),T==null||T(L),t==null||t(k.sourceEvent,L)})},[P,t]),w.useEffect(()=>{P&&(g&&!_.current?P.on("zoom",null):g||P.on("zoom",k=>{var L;const{onViewportChange:T}=j.getState();if(j.setState({transform:[k.transform.x,k.transform.y,k.transform.k]}),I.current=!!(r&&Es(d,N.current??0)),(e||T)&&!((L=k.sourceEvent)!=null&&L.internal)){const z=an(k.transform);T==null||T(z),e==null||e(k.sourceEvent,z)}}))},[g,P,e,d,r]),w.useEffect(()=>{P&&P.on("end",k=>{if(!k.sourceEvent||k.sourceEvent.internal)return null;const{onViewportChangeEnd:T}=j.getState();if(_.current=!1,j.setState({paneDragging:!1}),r&&Es(d,N.current??0)&&!I.current&&r(k.sourceEvent),I.current=!1,(n||T)&&Qf(D.current,k.transform)){const L=an(k.transform);D.current=L,clearTimeout(A.current),A.current=setTimeout(()=>{T==null||T(L),n==null||n(k.sourceEvent,L)},o?150:0)}})},[P,o,d,n,r]),w.useEffect(()=>{P&&P.filter(k=>{const T=C||s,L=i&&k.ctrlKey;if((d===!0||Array.isArray(d)&&d.includes(1))&&k.button===1&&k.type==="mousedown"&&(gt(k,"react-flow__node")||gt(k,"react-flow__edge")))return!0;if(!d&&!T&&!o&&!c&&!i||g||!c&&k.type==="dblclick"||gt(k,$)&&k.type==="wheel"||gt(k,b)&&(k.type!=="wheel"||o&&k.type==="wheel"&&!C)||!i&&k.ctrlKey&&k.type==="wheel"||!T&&!o&&!L&&k.type==="wheel"||!d&&(k.type==="mousedown"||k.type==="touchstart")||Array.isArray(d)&&!d.includes(k.button)&&k.type==="mousedown")return!1;const z=Array.isArray(d)&&d.includes(k.button)||!k.button||k.button<=1;return(!k.ctrlKey||k.type==="wheel")&&z})},[g,P,s,i,o,c,d,u,C]),M.createElement("div",{className:"react-flow__renderer",ref:S,style:Tr},E)},th=e=>({userSelectionActive:e.userSelectionActive,userSelectionRect:e.userSelectionRect});function nh(){const{userSelectionActive:e,userSelectionRect:t}=ae(th,ge);return e&&t?M.createElement("div",{className:"react-flow__selection react-flow__container",style:{width:t.width,height:t.height,transform:`translate(${t.x}px, ${t.y}px)`}}):null}function Ss(e,t){const n=t.parentNode||t.parentId,r=e.find(s=>s.id===n);if(r){const s=t.position.x+t.width-r.width,i=t.position.y+t.height-r.height;if(s>0||i>0||t.position.x<0||t.position.y<0){if(r.style={...r.style},r.style.width=r.style.width??r.width,r.style.height=r.style.height??r.height,s>0&&(r.style.width+=s),i>0&&(r.style.height+=i),t.position.x<0){const o=Math.abs(t.position.x);r.position.x=r.position.x-o,r.style.width+=o,t.position.x=0}if(t.position.y<0){const o=Math.abs(t.position.y);r.position.y=r.position.y-o,r.style.height+=o,t.position.y=0}r.width=r.style.width,r.height=r.style.height}}}function rh(e,t){if(e.some(r=>r.type==="reset"))return e.filter(r=>r.type==="reset").map(r=>r.item);const n=e.filter(r=>r.type==="add").map(r=>r.item);return t.reduce((r,s)=>{const i=e.filter(a=>a.id===s.id);if(i.length===0)return r.push(s),r;const o={...s};for(const a of i)if(a)switch(a.type){case"select":{o.selected=a.selected;break}case"position":{typeof a.position<"u"&&(o.position=a.position),typeof a.positionAbsolute<"u"&&(o.positionAbsolute=a.positionAbsolute),typeof a.dragging<"u"&&(o.dragging=a.dragging),o.expandParent&&Ss(r,o);break}case"dimensions":{typeof a.dimensions<"u"&&(o.width=a.dimensions.width,o.height=a.dimensions.height),typeof a.updateStyle<"u"&&(o.style={...o.style||{},...a.dimensions}),typeof a.resizing=="boolean"&&(o.resizing=a.resizing),o.expandParent&&Ss(r,o);break}case"remove":return r}return r.push(o),r},n)}function sh(e,t){return rh(e,t)}const Xe=(e,t)=>({id:e,type:"select",selected:t});function xt(e,t){return e.reduce((n,r)=>{const s=t.includes(r.id);return!r.selected&&s?(r.selected=!0,n.push(Xe(r.id,!0))):r.selected&&!s&&(r.selected=!1,n.push(Xe(r.id,!1))),n},[])}const Wn=(e,t)=>n=>{n.target===t.current&&(e==null||e(n))},ih=e=>({userSelectionActive:e.userSelectionActive,elementsSelectable:e.elementsSelectable,dragging:e.paneDragging}),Eo=w.memo(({isSelecting:e,selectionMode:t=Yt.Full,panOnDrag:n,onSelectionStart:r,onSelectionEnd:s,onPaneClick:i,onPaneContextMenu:o,onPaneScroll:a,onPaneMouseEnter:l,onPaneMouseMove:c,onPaneMouseLeave:u,children:d})=>{const m=w.useRef(null),f=pe(),x=w.useRef(0),y=w.useRef(0),v=w.useRef(),{userSelectionActive:p,elementsSelectable:E,dragging:$}=ae(ih,ge),b=()=>{f.setState({userSelectionActive:!1,userSelectionRect:null}),x.current=0,y.current=0},A=F=>{i==null||i(F),f.getState().resetSelectedElements(),f.setState({nodesSelectionActive:!1})},j=F=>{if(Array.isArray(n)&&(n!=null&&n.includes(2))){F.preventDefault();return}o==null||o(F)},_=a?F=>a(F):void 0,I=F=>{const{resetSelectedElements:g,domNode:C}=f.getState();if(v.current=C==null?void 0:C.getBoundingClientRect(),!E||!e||F.button!==0||F.target!==m.current||!v.current)return;const{x:N,y:R}=Ze(F,v.current);g(),f.setState({userSelectionRect:{width:0,height:0,startX:N,startY:R,x:N,y:R}}),r==null||r(F)},S=F=>{const{userSelectionRect:g,nodeInternals:C,edges:N,transform:R,onNodesChange:H,onEdgesChange:k,nodeOrigin:T,getNodes:L}=f.getState();if(!e||!v.current||!g)return;f.setState({userSelectionActive:!0,nodesSelectionActive:!1});const z=Ze(F,v.current),Y=g.startX??0,G=g.startY??0,K={...g,x:z.xre.id),te=ee.map(re=>re.id);if(x.current!==te.length){x.current=te.length;const re=xt(J,te);re.length&&(H==null||H(re))}if(y.current!==ie.length){y.current=ie.length;const re=xt(N,ie);re.length&&(k==null||k(re))}f.setState({userSelectionRect:K})},D=F=>{if(F.button!==0)return;const{userSelectionRect:g}=f.getState();!p&&g&&F.target===m.current&&(A==null||A(F)),f.setState({nodesSelectionActive:x.current>0}),b(),s==null||s(F)},P=F=>{p&&(f.setState({nodesSelectionActive:x.current>0}),s==null||s(F)),b()},V=E&&(e||p);return M.createElement("div",{className:ye(["react-flow__pane",{dragging:$,selection:e}]),onClick:V?void 0:Wn(A,m),onContextMenu:Wn(j,m),onWheel:Wn(_,m),onMouseEnter:V?void 0:l,onMouseDown:V?I:void 0,onMouseMove:V?S:c,onMouseUp:V?D:void 0,onMouseLeave:V?P:u,ref:m,style:Tr},d,M.createElement(nh,null))});Eo.displayName="Pane";function No(e,t){const n=e.parentNode||e.parentId;if(!n)return!1;const r=t.get(n);return r?r.selected?!0:No(r,t):!1}function ks(e,t,n){let r=e;do{if(r!=null&&r.matches(t))return!0;if(r===n.current)return!1;r=r.parentElement}while(r);return!1}function oh(e,t,n,r){return Array.from(e.values()).filter(s=>(s.selected||s.id===r)&&(!s.parentNode||s.parentId||!No(s,e))&&(s.draggable||t&&typeof s.draggable>"u")).map(s=>{var i,o;return{id:s.id,position:s.position||{x:0,y:0},positionAbsolute:s.positionAbsolute||{x:0,y:0},distance:{x:n.x-(((i=s.positionAbsolute)==null?void 0:i.x)??0),y:n.y-(((o=s.positionAbsolute)==null?void 0:o.y)??0)},delta:{x:0,y:0},extent:s.extent,parentNode:s.parentNode||s.parentId,parentId:s.parentNode||s.parentId,width:s.width,height:s.height,expandParent:s.expandParent}})}function ah(e,t){return!t||t==="parent"?t:[t[0],[t[1][0]-(e.width||0),t[1][1]-(e.height||0)]]}function So(e,t,n,r,s=[0,0],i){const o=ah(e,e.extent||r);let a=o;const l=e.parentNode||e.parentId;if(e.extent==="parent"&&!e.expandParent)if(l&&e.width&&e.height){const d=n.get(l),{x:m,y:f}=lt(d,s).positionAbsolute;a=d&&Se(m)&&Se(f)&&Se(d.width)&&Se(d.height)?[[m+e.width*s[0],f+e.height*s[1]],[m+d.width-e.width+e.width*s[0],f+d.height-e.height+e.height*s[1]]]:a}else i==null||i("005",Be.error005()),a=o;else if(e.extent&&l&&e.extent!=="parent"){const d=n.get(l),{x:m,y:f}=lt(d,s).positionAbsolute;a=[[e.extent[0][0]+m,e.extent[0][1]+f],[e.extent[1][0]+m,e.extent[1][1]+f]]}let c={x:0,y:0};if(l){const d=n.get(l);c=lt(d,s).positionAbsolute}const u=a&&a!=="parent"?jr(t,a):t;return{position:{x:u.x-c.x,y:u.y-c.y},positionAbsolute:u}}function Un({nodeId:e,dragItems:t,nodeInternals:n}){const r=t.map(s=>({...n.get(s.id),position:s.position,positionAbsolute:s.positionAbsolute}));return[e?r.find(s=>s.id===e):r[0],r]}const js=(e,t,n,r)=>{const s=t.querySelectorAll(e);if(!s||!s.length)return null;const i=Array.from(s),o=t.getBoundingClientRect(),a={x:o.width*r[0],y:o.height*r[1]};return i.map(l=>{const c=l.getBoundingClientRect();return{id:l.getAttribute("data-handleid"),position:l.getAttribute("data-handlepos"),x:(c.left-o.left-a.x)/n,y:(c.top-o.top-a.y)/n,...kr(l)}})};function Rt(e,t,n){return n===void 0?n:r=>{const s=t().nodeInternals.get(e);s&&n(r,{...s})}}function pr({id:e,store:t,unselect:n=!1,nodeRef:r}){const{addSelectedNodes:s,unselectNodesAndEdges:i,multiSelectionActive:o,nodeInternals:a,onError:l}=t.getState(),c=a.get(e);if(!c){l==null||l("012",Be.error012(e));return}t.setState({nodesSelectionActive:!1}),c.selected?(n||c.selected&&o)&&(i({nodes:[c],edges:[]}),requestAnimationFrame(()=>{var u;return(u=r==null?void 0:r.current)==null?void 0:u.blur()})):s([e])}function lh(){const e=pe();return w.useCallback(({sourceEvent:n})=>{const{transform:r,snapGrid:s,snapToGrid:i}=e.getState(),o=n.touches?n.touches[0].clientX:n.clientX,a=n.touches?n.touches[0].clientY:n.clientY,l={x:(o-r[0])/r[2],y:(a-r[1])/r[2]};return{xSnapped:i?s[0]*Math.round(l.x/s[0]):l.x,ySnapped:i?s[1]*Math.round(l.y/s[1]):l.y,...l}},[])}function Xn(e){return(t,n,r)=>e==null?void 0:e(t,r)}function ko({nodeRef:e,disabled:t=!1,noDragClassName:n,handleSelector:r,nodeId:s,isSelectable:i,selectNodesOnDrag:o}){const a=pe(),[l,c]=w.useState(!1),u=w.useRef([]),d=w.useRef({x:null,y:null}),m=w.useRef(0),f=w.useRef(null),x=w.useRef({x:0,y:0}),y=w.useRef(null),v=w.useRef(!1),p=w.useRef(!1),E=w.useRef(!1),$=lh();return w.useEffect(()=>{if(e!=null&&e.current){const b=Ne(e.current),A=({x:I,y:S})=>{const{nodeInternals:D,onNodeDrag:P,onSelectionDrag:V,updateNodePositions:F,nodeExtent:g,snapGrid:C,snapToGrid:N,nodeOrigin:R,onError:H}=a.getState();d.current={x:I,y:S};let k=!1,T={x:0,y:0,x2:0,y2:0};if(u.current.length>1&&g){const z=Kt(u.current,R);T=Gt(z)}if(u.current=u.current.map(z=>{const Y={x:I-z.distance.x,y:S-z.distance.y};N&&(Y.x=C[0]*Math.round(Y.x/C[0]),Y.y=C[1]*Math.round(Y.y/C[1]));const G=[[g[0][0],g[0][1]],[g[1][0],g[1][1]]];u.current.length>1&&g&&!z.extent&&(G[0][0]=z.positionAbsolute.x-T.x+g[0][0],G[1][0]=z.positionAbsolute.x+(z.width??0)-T.x2+g[1][0],G[0][1]=z.positionAbsolute.y-T.y+g[0][1],G[1][1]=z.positionAbsolute.y+(z.height??0)-T.y2+g[1][1]);const K=So(z,Y,D,G,R,H);return k=k||z.position.x!==K.position.x||z.position.y!==K.position.y,z.position=K.position,z.positionAbsolute=K.positionAbsolute,z}),!k)return;F(u.current,!0,!0),c(!0);const L=s?P:Xn(V);if(L&&y.current){const[z,Y]=Un({nodeId:s,dragItems:u.current,nodeInternals:D});L(y.current,z,Y)}},j=()=>{if(!f.current)return;const[I,S]=Ui(x.current,f.current);if(I!==0||S!==0){const{transform:D,panBy:P}=a.getState();d.current.x=(d.current.x??0)-I/D[2],d.current.y=(d.current.y??0)-S/D[2],P({x:I,y:S})&&A(d.current)}m.current=requestAnimationFrame(j)},_=I=>{var R;const{nodeInternals:S,multiSelectionActive:D,nodesDraggable:P,unselectNodesAndEdges:V,onNodeDragStart:F,onSelectionDragStart:g}=a.getState();p.current=!0;const C=s?F:Xn(g);(!o||!i)&&!D&&s&&((R=S.get(s))!=null&&R.selected||V()),s&&i&&o&&pr({id:s,store:a,nodeRef:e});const N=$(I);if(d.current=N,u.current=oh(S,P,N,s),C&&u.current){const[H,k]=Un({nodeId:s,dragItems:u.current,nodeInternals:S});C(I.sourceEvent,H,k)}};if(t)b.on(".drag",null);else{const I=gu().on("start",S=>{const{domNode:D,nodeDragThreshold:P}=a.getState();P===0&&_(S),E.current=!1;const V=$(S);d.current=V,f.current=(D==null?void 0:D.getBoundingClientRect())||null,x.current=Ze(S.sourceEvent,f.current)}).on("drag",S=>{var F,g;const D=$(S),{autoPanOnNodeDrag:P,nodeDragThreshold:V}=a.getState();if(S.sourceEvent.type==="touchmove"&&S.sourceEvent.touches.length>1&&(E.current=!0),!E.current){if(!v.current&&p.current&&P&&(v.current=!0,j()),!p.current){const C=D.xSnapped-(((F=d==null?void 0:d.current)==null?void 0:F.x)??0),N=D.ySnapped-(((g=d==null?void 0:d.current)==null?void 0:g.y)??0);Math.sqrt(C*C+N*N)>V&&_(S)}(d.current.x!==D.xSnapped||d.current.y!==D.ySnapped)&&u.current&&p.current&&(y.current=S.sourceEvent,x.current=Ze(S.sourceEvent,f.current),A(D))}}).on("end",S=>{if(!(!p.current||E.current)&&(c(!1),v.current=!1,p.current=!1,cancelAnimationFrame(m.current),u.current)){const{updateNodePositions:D,nodeInternals:P,onNodeDragStop:V,onSelectionDragStop:F}=a.getState(),g=s?V:Xn(F);if(D(u.current,!1,!1),g){const[C,N]=Un({nodeId:s,dragItems:u.current,nodeInternals:P});g(S.sourceEvent,C,N)}}}).filter(S=>{const D=S.target;return!S.button&&(!n||!ks(D,`.${n}`,e))&&(!r||ks(D,r,e))});return b.call(I),()=>{b.on(".drag",null)}}}},[e,t,n,r,i,a,s,o,$]),l}function jo(){const e=pe();return w.useCallback(n=>{const{nodeInternals:r,nodeExtent:s,updateNodePositions:i,getNodes:o,snapToGrid:a,snapGrid:l,onError:c,nodesDraggable:u}=e.getState(),d=o().filter(E=>E.selected&&(E.draggable||u&&typeof E.draggable>"u")),m=a?l[0]:5,f=a?l[1]:5,x=n.isShiftPressed?4:1,y=n.x*m*x,v=n.y*f*x,p=d.map(E=>{if(E.positionAbsolute){const $={x:E.positionAbsolute.x+y,y:E.positionAbsolute.y+v};a&&($.x=l[0]*Math.round($.x/l[0]),$.y=l[1]*Math.round($.y/l[1]));const{positionAbsolute:b,position:A}=So(E,$,r,s,void 0,c);E.position=A,E.positionAbsolute=b}return E});i(p,!0,!1)},[])}const vt={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}};var Tt=e=>{const t=({id:n,type:r,data:s,xPos:i,yPos:o,xPosOrigin:a,yPosOrigin:l,selected:c,onClick:u,onMouseEnter:d,onMouseMove:m,onMouseLeave:f,onContextMenu:x,onDoubleClick:y,style:v,className:p,isDraggable:E,isSelectable:$,isConnectable:b,isFocusable:A,selectNodesOnDrag:j,sourcePosition:_,targetPosition:I,hidden:S,resizeObserver:D,dragHandle:P,zIndex:V,isParent:F,noDragClassName:g,noPanClassName:C,initialized:N,disableKeyboardA11y:R,ariaLabel:H,rfId:k,hasHandleBounds:T})=>{const L=pe(),z=w.useRef(null),Y=w.useRef(null),G=w.useRef(_),K=w.useRef(I),J=w.useRef(r),ee=$||E||u||d||m||f,ie=jo(),te=Rt(n,L.getState,d),re=Rt(n,L.getState,m),fe=Rt(n,L.getState,f),B=Rt(n,L.getState,x),q=Rt(n,L.getState,y),W=X=>{const{nodeDragThreshold:O}=L.getState();if($&&(!j||!E||O>0)&&pr({id:n,store:L,nodeRef:z}),u){const se=L.getState().nodeInternals.get(n);se&&u(X,{...se})}},Q=X=>{if(!dr(X)&&!R)if(Zi.includes(X.key)&&$){const O=X.key==="Escape";pr({id:n,store:L,unselect:O,nodeRef:z})}else E&&c&&Object.prototype.hasOwnProperty.call(vt,X.key)&&(L.setState({ariaLiveMessage:`Moved selected node ${X.key.replace("Arrow","").toLowerCase()}. New position, x: ${~~i}, y: ${~~o}`}),ie({x:vt[X.key].x,y:vt[X.key].y,isShiftPressed:X.shiftKey}))};w.useEffect(()=>()=>{Y.current&&(D==null||D.unobserve(Y.current),Y.current=null)},[]),w.useEffect(()=>{if(z.current&&!S){const X=z.current;(!N||!T||Y.current!==X)&&(Y.current&&(D==null||D.unobserve(Y.current)),D==null||D.observe(X),Y.current=X)}},[S,N,T]),w.useEffect(()=>{const X=J.current!==r,O=G.current!==_,se=K.current!==I;z.current&&(X||O||se)&&(X&&(J.current=r),O&&(G.current=_),se&&(K.current=I),L.getState().updateNodeDimensions([{id:n,nodeElement:z.current,forceUpdate:!0}]))},[n,r,_,I]);const U=ko({nodeRef:z,disabled:S||!E,noDragClassName:g,handleSelector:P,nodeId:n,isSelectable:$,selectNodesOnDrag:j});return S?null:M.createElement("div",{className:ye(["react-flow__node",`react-flow__node-${r}`,{[C]:E},p,{selected:c,selectable:$,parent:F,dragging:U}]),ref:z,style:{zIndex:V,transform:`translate(${a}px,${l}px)`,pointerEvents:ee?"all":"none",visibility:N?"visible":"hidden",...v},"data-id":n,"data-testid":`rf__node-${n}`,onMouseEnter:te,onMouseMove:re,onMouseLeave:fe,onContextMenu:B,onClick:W,onDoubleClick:q,onKeyDown:A?Q:void 0,tabIndex:A?0:void 0,role:A?"button":void 0,"aria-describedby":R?void 0:`${xo}-${k}`,"aria-label":H},M.createElement(bf,{value:n},M.createElement(e,{id:n,data:s,type:r,xPos:i,yPos:o,selected:c,isConnectable:b,sourcePosition:_,targetPosition:I,dragging:U,dragHandle:P,zIndex:V})))};return t.displayName="NodeWrapper",w.memo(t)};const ch=e=>{const t=e.getNodes().filter(n=>n.selected);return{...Kt(t,e.nodeOrigin),transformString:`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`,userSelectionActive:e.userSelectionActive}};function uh({onSelectionContextMenu:e,noPanClassName:t,disableKeyboardA11y:n}){const r=pe(),{width:s,height:i,x:o,y:a,transformString:l,userSelectionActive:c}=ae(ch,ge),u=jo(),d=w.useRef(null);if(w.useEffect(()=>{var x;n||(x=d.current)==null||x.focus({preventScroll:!0})},[n]),ko({nodeRef:d}),c||!s||!i)return null;const m=e?x=>{const y=r.getState().getNodes().filter(v=>v.selected);e(x,y)}:void 0,f=x=>{Object.prototype.hasOwnProperty.call(vt,x.key)&&u({x:vt[x.key].x,y:vt[x.key].y,isShiftPressed:x.shiftKey})};return M.createElement("div",{className:ye(["react-flow__nodesselection","react-flow__container",t]),style:{transform:l}},M.createElement("div",{ref:d,className:"react-flow__nodesselection-rect",onContextMenu:m,tabIndex:n?void 0:-1,onKeyDown:n?void 0:f,style:{width:s,height:i,top:a,left:o}}))}var dh=w.memo(uh);const fh=e=>e.nodesSelectionActive,Co=({children:e,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:i,onPaneScroll:o,deleteKeyCode:a,onMove:l,onMoveStart:c,onMoveEnd:u,selectionKeyCode:d,selectionOnDrag:m,selectionMode:f,onSelectionStart:x,onSelectionEnd:y,multiSelectionKeyCode:v,panActivationKeyCode:p,zoomActivationKeyCode:E,elementsSelectable:$,zoomOnScroll:b,zoomOnPinch:A,panOnScroll:j,panOnScrollSpeed:_,panOnScrollMode:I,zoomOnDoubleClick:S,panOnDrag:D,defaultViewport:P,translateExtent:V,minZoom:F,maxZoom:g,preventScrolling:C,onSelectionContextMenu:N,noWheelClassName:R,noPanClassName:H,disableKeyboardA11y:k})=>{const T=ae(fh),L=Wt(d),z=Wt(p),Y=z||D,G=z||j,K=L||m&&Y!==!0;return Kf({deleteKeyCode:a,multiSelectionKeyCode:v}),M.createElement(eh,{onMove:l,onMoveStart:c,onMoveEnd:u,onPaneContextMenu:i,elementsSelectable:$,zoomOnScroll:b,zoomOnPinch:A,panOnScroll:G,panOnScrollSpeed:_,panOnScrollMode:I,zoomOnDoubleClick:S,panOnDrag:!L&&Y,defaultViewport:P,translateExtent:V,minZoom:F,maxZoom:g,zoomActivationKeyCode:E,preventScrolling:C,noWheelClassName:R,noPanClassName:H},M.createElement(Eo,{onSelectionStart:x,onSelectionEnd:y,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:i,onPaneScroll:o,panOnDrag:Y,isSelecting:!!K,selectionMode:f},e,T&&M.createElement(dh,{onSelectionContextMenu:N,noPanClassName:H,disableKeyboardA11y:k})))};Co.displayName="FlowRenderer";var hh=w.memo(Co);function mh(e){return ae(w.useCallback(n=>e?so(n.nodeInternals,{x:0,y:0,width:n.width,height:n.height},n.transform,!0):n.getNodes(),[e]))}function ph(e){const t={input:Tt(e.input||mo),default:Tt(e.default||mr),output:Tt(e.output||go),group:Tt(e.group||Rr)},n={},r=Object.keys(e).filter(s=>!["input","default","output","group"].includes(s)).reduce((s,i)=>(s[i]=Tt(e[i]||mr),s),n);return{...t,...r}}const gh=({x:e,y:t,width:n,height:r,origin:s})=>!n||!r?{x:e,y:t}:s[0]<0||s[1]<0||s[0]>1||s[1]>1?{x:e,y:t}:{x:e-n*s[0],y:t-r*s[1]},yh=e=>({nodesDraggable:e.nodesDraggable,nodesConnectable:e.nodesConnectable,nodesFocusable:e.nodesFocusable,elementsSelectable:e.elementsSelectable,updateNodeDimensions:e.updateNodeDimensions,onError:e.onError}),$o=e=>{const{nodesDraggable:t,nodesConnectable:n,nodesFocusable:r,elementsSelectable:s,updateNodeDimensions:i,onError:o}=ae(yh,ge),a=mh(e.onlyRenderVisibleElements),l=w.useRef(),c=w.useMemo(()=>{if(typeof ResizeObserver>"u")return null;const u=new ResizeObserver(d=>{const m=d.map(f=>({id:f.target.getAttribute("data-id"),nodeElement:f.target,forceUpdate:!0}));i(m)});return l.current=u,u},[]);return w.useEffect(()=>()=>{var u;(u=l==null?void 0:l.current)==null||u.disconnect()},[]),M.createElement("div",{className:"react-flow__nodes",style:Tr},a.map(u=>{var A,j,_;let d=u.type||"default";e.nodeTypes[d]||(o==null||o("003",Be.error003(d)),d="default");const m=e.nodeTypes[d]||e.nodeTypes.default,f=!!(u.draggable||t&&typeof u.draggable>"u"),x=!!(u.selectable||s&&typeof u.selectable>"u"),y=!!(u.connectable||n&&typeof u.connectable>"u"),v=!!(u.focusable||r&&typeof u.focusable>"u"),p=e.nodeExtent?jr(u.positionAbsolute,e.nodeExtent):u.positionAbsolute,E=(p==null?void 0:p.x)??0,$=(p==null?void 0:p.y)??0,b=gh({x:E,y:$,width:u.width??0,height:u.height??0,origin:e.nodeOrigin});return M.createElement(m,{key:u.id,id:u.id,className:u.className,style:u.style,type:d,data:u.data,sourcePosition:u.sourcePosition||Z.Bottom,targetPosition:u.targetPosition||Z.Top,hidden:u.hidden,xPos:E,yPos:$,xPosOrigin:b.x,yPosOrigin:b.y,selectNodesOnDrag:e.selectNodesOnDrag,onClick:e.onNodeClick,onMouseEnter:e.onNodeMouseEnter,onMouseMove:e.onNodeMouseMove,onMouseLeave:e.onNodeMouseLeave,onContextMenu:e.onNodeContextMenu,onDoubleClick:e.onNodeDoubleClick,selected:!!u.selected,isDraggable:f,isSelectable:x,isConnectable:y,isFocusable:v,resizeObserver:c,dragHandle:u.dragHandle,zIndex:((A=u[le])==null?void 0:A.z)??0,isParent:!!((j=u[le])!=null&&j.isParent),noDragClassName:e.noDragClassName,noPanClassName:e.noPanClassName,initialized:!!u.width&&!!u.height,rfId:e.rfId,disableKeyboardA11y:e.disableKeyboardA11y,ariaLabel:u.ariaLabel,hasHandleBounds:!!((_=u[le])!=null&&_.handleBounds)})}))};$o.displayName="NodeRenderer";var xh=w.memo($o);const wh=(e,t,n)=>n===Z.Left?e-t:n===Z.Right?e+t:e,bh=(e,t,n)=>n===Z.Top?e-t:n===Z.Bottom?e+t:e,Cs="react-flow__edgeupdater",$s=({position:e,centerX:t,centerY:n,radius:r=10,onMouseDown:s,onMouseEnter:i,onMouseOut:o,type:a})=>M.createElement("circle",{onMouseDown:s,onMouseEnter:i,onMouseOut:o,className:ye([Cs,`${Cs}-${a}`]),cx:wh(t,r,e),cy:bh(n,r,e),r,stroke:"transparent",fill:"transparent"}),vh=()=>!0;var yt=e=>{const t=({id:n,className:r,type:s,data:i,onClick:o,onEdgeDoubleClick:a,selected:l,animated:c,label:u,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:x,labelBgBorderRadius:y,style:v,source:p,target:E,sourceX:$,sourceY:b,targetX:A,targetY:j,sourcePosition:_,targetPosition:I,elementsSelectable:S,hidden:D,sourceHandleId:P,targetHandleId:V,onContextMenu:F,onMouseEnter:g,onMouseMove:C,onMouseLeave:N,reconnectRadius:R,onReconnect:H,onReconnectStart:k,onReconnectEnd:T,markerEnd:L,markerStart:z,rfId:Y,ariaLabel:G,isFocusable:K,isReconnectable:J,pathOptions:ee,interactionWidth:ie,disableKeyboardA11y:te})=>{const re=w.useRef(null),[fe,B]=w.useState(!1),[q,W]=w.useState(!1),Q=pe(),U=w.useMemo(()=>`url('#${fr(z,Y)}')`,[z,Y]),X=w.useMemo(()=>`url('#${fr(L,Y)}')`,[L,Y]);if(D)return null;const O=xe=>{var Re;const{edges:Ce,addSelectedEdges:Je,unselectNodesAndEdges:et,multiSelectionActive:tt}=Q.getState(),De=Ce.find(Ct=>Ct.id===n);De&&(S&&(Q.setState({nodesSelectionActive:!1}),De.selected&&tt?(et({nodes:[],edges:[De]}),(Re=re.current)==null||Re.blur()):Je([n])),o&&o(xe,De))},se=It(n,Q.getState,a),ce=It(n,Q.getState,F),me=It(n,Q.getState,g),we=It(n,Q.getState,C),be=It(n,Q.getState,N),Ee=(xe,Ce)=>{if(xe.button!==0)return;const{edges:Je,isValidConnection:et}=Q.getState(),tt=Ce?E:p,De=(Ce?V:P)||null,Re=Ce?"target":"source",Ct=et||vh,Dn=Ce,$t=Je.find(nt=>nt.id===n);W(!0),k==null||k(xe,$t,Re);const Ln=nt=>{W(!1),T==null||T(nt,$t,Re)};co({event:xe,handleId:De,nodeId:tt,onConnect:nt=>H==null?void 0:H($t,nt),isTarget:Dn,getState:Q.getState,setState:Q.setState,isValidConnection:Ct,edgeUpdaterType:Re,onReconnectEnd:Ln})},Ye=xe=>Ee(xe,!0),oe=xe=>Ee(xe,!1),Qe=()=>B(!0),ft=()=>B(!1),ht=!S&&!o,jt=xe=>{var Ce;if(!te&&Zi.includes(xe.key)&&S){const{unselectNodesAndEdges:Je,addSelectedEdges:et,edges:tt}=Q.getState();xe.key==="Escape"?((Ce=re.current)==null||Ce.blur(),Je({edges:[tt.find(Re=>Re.id===n)]})):et([n])}};return M.createElement("g",{className:ye(["react-flow__edge",`react-flow__edge-${s}`,r,{selected:l,animated:c,inactive:ht,updating:fe}]),onClick:O,onDoubleClick:se,onContextMenu:ce,onMouseEnter:me,onMouseMove:we,onMouseLeave:be,onKeyDown:K?jt:void 0,tabIndex:K?0:void 0,role:K?"button":"img","data-testid":`rf__edge-${n}`,"aria-label":G===null?void 0:G||`Edge from ${p} to ${E}`,"aria-describedby":K?`${wo}-${Y}`:void 0,ref:re},!q&&M.createElement(e,{id:n,source:p,target:E,selected:l,animated:c,label:u,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:x,labelBgBorderRadius:y,data:i,style:v,sourceX:$,sourceY:b,targetX:A,targetY:j,sourcePosition:_,targetPosition:I,sourceHandleId:P,targetHandleId:V,markerStart:U,markerEnd:X,pathOptions:ee,interactionWidth:ie}),J&&M.createElement(M.Fragment,null,(J==="source"||J===!0)&&M.createElement($s,{position:_,centerX:$,centerY:b,radius:R,onMouseDown:Ye,onMouseEnter:Qe,onMouseOut:ft,type:"source"}),(J==="target"||J===!0)&&M.createElement($s,{position:I,centerX:A,centerY:j,radius:R,onMouseDown:oe,onMouseEnter:Qe,onMouseOut:ft,type:"target"})))};return t.displayName="EdgeWrapper",w.memo(t)};function _h(e){const t={default:yt(e.default||_n),straight:yt(e.bezier||Mr),step:yt(e.step||$r),smoothstep:yt(e.step||In),simplebezier:yt(e.simplebezier||Cr)},n={},r=Object.keys(e).filter(s=>!["default","bezier"].includes(s)).reduce((s,i)=>(s[i]=yt(e[i]||_n),s),n);return{...t,...r}}function Ms(e,t,n=null){const r=((n==null?void 0:n.x)||0)+t.x,s=((n==null?void 0:n.y)||0)+t.y,i=(n==null?void 0:n.width)||t.width,o=(n==null?void 0:n.height)||t.height;switch(e){case Z.Top:return{x:r+i/2,y:s};case Z.Right:return{x:r+i,y:s+o/2};case Z.Bottom:return{x:r+i/2,y:s+o};case Z.Left:return{x:r,y:s+o/2}}}function As(e,t){return e?e.length===1||!t?e[0]:t&&e.find(n=>n.id===t)||null:null}const Eh=(e,t,n,r,s,i)=>{const o=Ms(n,e,t),a=Ms(i,r,s);return{sourceX:o.x,sourceY:o.y,targetX:a.x,targetY:a.y}};function Nh({sourcePos:e,targetPos:t,sourceWidth:n,sourceHeight:r,targetWidth:s,targetHeight:i,width:o,height:a,transform:l}){const c={x:Math.min(e.x,t.x),y:Math.min(e.y,t.y),x2:Math.max(e.x+n,t.x+s),y2:Math.max(e.y+r,t.y+i)};c.x===c.x2&&(c.x2+=1),c.y===c.y2&&(c.y2+=1);const u=Gt({x:(0-l[0])/l[2],y:(0-l[1])/l[2],width:o/l[2],height:a/l[2]}),d=Math.max(0,Math.min(u.x2,c.x2)-Math.max(u.x,c.x)),m=Math.max(0,Math.min(u.y2,c.y2)-Math.max(u.y,c.y));return Math.ceil(d*m)>0}function Is(e){var r,s,i,o,a;const t=((r=e==null?void 0:e[le])==null?void 0:r.handleBounds)||null,n=t&&(e==null?void 0:e.width)&&(e==null?void 0:e.height)&&typeof((s=e==null?void 0:e.positionAbsolute)==null?void 0:s.x)<"u"&&typeof((i=e==null?void 0:e.positionAbsolute)==null?void 0:i.y)<"u";return[{x:((o=e==null?void 0:e.positionAbsolute)==null?void 0:o.x)||0,y:((a=e==null?void 0:e.positionAbsolute)==null?void 0:a.y)||0,width:(e==null?void 0:e.width)||0,height:(e==null?void 0:e.height)||0},t,!!n]}const Sh=[{level:0,isMaxLevel:!0,edges:[]}];function kh(e,t,n=!1){let r=-1;const s=e.reduce((o,a)=>{var u,d;const l=Se(a.zIndex);let c=l?a.zIndex:0;if(n){const m=t.get(a.target),f=t.get(a.source),x=a.selected||(m==null?void 0:m.selected)||(f==null?void 0:f.selected),y=Math.max(((u=f==null?void 0:f[le])==null?void 0:u.z)||0,((d=m==null?void 0:m[le])==null?void 0:d.z)||0,1e3);c=(l?a.zIndex:0)+(x?y:0)}return o[c]?o[c].push(a):o[c]=[a],r=c>r?c:r,o},{}),i=Object.entries(s).map(([o,a])=>{const l=+o;return{edges:a,level:l,isMaxLevel:l===r}});return i.length===0?Sh:i}function jh(e,t,n){const r=ae(w.useCallback(s=>e?s.edges.filter(i=>{const o=t.get(i.source),a=t.get(i.target);return(o==null?void 0:o.width)&&(o==null?void 0:o.height)&&(a==null?void 0:a.width)&&(a==null?void 0:a.height)&&Nh({sourcePos:o.positionAbsolute||{x:0,y:0},targetPos:a.positionAbsolute||{x:0,y:0},sourceWidth:o.width,sourceHeight:o.height,targetWidth:a.width,targetHeight:a.height,width:s.width,height:s.height,transform:s.transform})}):s.edges,[e,t]));return kh(r,t,n)}const Ch=({color:e="none",strokeWidth:t=1})=>M.createElement("polyline",{style:{stroke:e,strokeWidth:t},strokeLinecap:"round",strokeLinejoin:"round",fill:"none",points:"-5,-4 0,0 -5,4"}),$h=({color:e="none",strokeWidth:t=1})=>M.createElement("polyline",{style:{stroke:e,fill:e,strokeWidth:t},strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"}),Rs={[bn.Arrow]:Ch,[bn.ArrowClosed]:$h};function Mh(e){const t=pe();return w.useMemo(()=>{var s,i;return Object.prototype.hasOwnProperty.call(Rs,e)?Rs[e]:((i=(s=t.getState()).onError)==null||i.call(s,"009",Be.error009(e)),null)},[e])}const Ah=({id:e,type:t,color:n,width:r=12.5,height:s=12.5,markerUnits:i="strokeWidth",strokeWidth:o,orient:a="auto-start-reverse"})=>{const l=Mh(t);return l?M.createElement("marker",{className:"react-flow__arrowhead",id:e,markerWidth:`${r}`,markerHeight:`${s}`,viewBox:"-10 -10 20 20",markerUnits:i,orient:a,refX:"0",refY:"0"},M.createElement(l,{color:n,strokeWidth:o})):null},Ih=({defaultColor:e,rfId:t})=>n=>{const r=[];return n.edges.reduce((s,i)=>([i.markerStart,i.markerEnd].forEach(o=>{if(o&&typeof o=="object"){const a=fr(o,t);r.includes(a)||(s.push({id:a,color:o.color||e,...o}),r.push(a))}}),s),[]).sort((s,i)=>s.id.localeCompare(i.id))},Mo=({defaultColor:e,rfId:t})=>{const n=ae(w.useCallback(Ih({defaultColor:e,rfId:t}),[e,t]),(r,s)=>!(r.length!==s.length||r.some((i,o)=>i.id!==s[o].id)));return M.createElement("defs",null,n.map(r=>M.createElement(Ah,{id:r.id,key:r.id,type:r.type,color:r.color,width:r.width,height:r.height,markerUnits:r.markerUnits,strokeWidth:r.strokeWidth,orient:r.orient})))};Mo.displayName="MarkerDefinitions";var Rh=w.memo(Mo);const Th=e=>({nodesConnectable:e.nodesConnectable,edgesFocusable:e.edgesFocusable,edgesUpdatable:e.edgesUpdatable,elementsSelectable:e.elementsSelectable,width:e.width,height:e.height,connectionMode:e.connectionMode,nodeInternals:e.nodeInternals,onError:e.onError}),Ao=({defaultMarkerColor:e,onlyRenderVisibleElements:t,elevateEdgesOnSelect:n,rfId:r,edgeTypes:s,noPanClassName:i,onEdgeContextMenu:o,onEdgeMouseEnter:a,onEdgeMouseMove:l,onEdgeMouseLeave:c,onEdgeClick:u,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:x,reconnectRadius:y,children:v,disableKeyboardA11y:p})=>{const{edgesFocusable:E,edgesUpdatable:$,elementsSelectable:b,width:A,height:j,connectionMode:_,nodeInternals:I,onError:S}=ae(Th,ge),D=jh(t,I,n);return A?M.createElement(M.Fragment,null,D.map(({level:P,edges:V,isMaxLevel:F})=>M.createElement("svg",{key:P,style:{zIndex:P},width:A,height:j,className:"react-flow__edges react-flow__container"},F&&M.createElement(Rh,{defaultColor:e,rfId:r}),M.createElement("g",null,V.map(g=>{const[C,N,R]=Is(I.get(g.source)),[H,k,T]=Is(I.get(g.target));if(!R||!T)return null;let L=g.type||"default";s[L]||(S==null||S("011",Be.error011(L)),L="default");const z=s[L]||s.default,Y=_===ut.Strict?k.target:(k.target??[]).concat(k.source??[]),G=As(N.source,g.sourceHandle),K=As(Y,g.targetHandle),J=(G==null?void 0:G.position)||Z.Bottom,ee=(K==null?void 0:K.position)||Z.Top,ie=!!(g.focusable||E&&typeof g.focusable>"u"),te=g.reconnectable||g.updatable,re=typeof m<"u"&&(te||$&&typeof te>"u");if(!G||!K)return S==null||S("008",Be.error008(G,g)),null;const{sourceX:fe,sourceY:B,targetX:q,targetY:W}=Eh(C,G,J,H,K,ee);return M.createElement(z,{key:g.id,id:g.id,className:ye([g.className,i]),type:L,data:g.data,selected:!!g.selected,animated:!!g.animated,hidden:!!g.hidden,label:g.label,labelStyle:g.labelStyle,labelShowBg:g.labelShowBg,labelBgStyle:g.labelBgStyle,labelBgPadding:g.labelBgPadding,labelBgBorderRadius:g.labelBgBorderRadius,style:g.style,source:g.source,target:g.target,sourceHandleId:g.sourceHandle,targetHandleId:g.targetHandle,markerEnd:g.markerEnd,markerStart:g.markerStart,sourceX:fe,sourceY:B,targetX:q,targetY:W,sourcePosition:J,targetPosition:ee,elementsSelectable:b,onContextMenu:o,onMouseEnter:a,onMouseMove:l,onMouseLeave:c,onClick:u,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:x,reconnectRadius:y,rfId:r,ariaLabel:g.ariaLabel,isFocusable:ie,isReconnectable:re,pathOptions:"pathOptions"in g?g.pathOptions:void 0,interactionWidth:g.interactionWidth,disableKeyboardA11y:p})})))),v):null};Ao.displayName="EdgeRenderer";var Oh=w.memo(Ao);const Dh=e=>`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`;function Lh({children:e}){const t=ae(Dh);return M.createElement("div",{className:"react-flow__viewport react-flow__container",style:{transform:t}},e)}function Ph(e){const t=Rn(),n=w.useRef(!1);w.useEffect(()=>{!n.current&&t.viewportInitialized&&e&&(setTimeout(()=>e(t),1),n.current=!0)},[e,t.viewportInitialized])}const zh={[Z.Left]:Z.Right,[Z.Right]:Z.Left,[Z.Top]:Z.Bottom,[Z.Bottom]:Z.Top},Io=({nodeId:e,handleType:t,style:n,type:r=qe.Bezier,CustomComponent:s,connectionStatus:i})=>{var j,_,I;const{fromNode:o,handleId:a,toX:l,toY:c,connectionMode:u}=ae(w.useCallback(S=>({fromNode:S.nodeInternals.get(e),handleId:S.connectionHandleId,toX:(S.connectionPosition.x-S.transform[0])/S.transform[2],toY:(S.connectionPosition.y-S.transform[1])/S.transform[2],connectionMode:S.connectionMode}),[e]),ge),d=(j=o==null?void 0:o[le])==null?void 0:j.handleBounds;let m=d==null?void 0:d[t];if(u===ut.Loose&&(m=m||(d==null?void 0:d[t==="source"?"target":"source"])),!o||!m)return null;const f=a?m.find(S=>S.id===a):m[0],x=f?f.x+f.width/2:(o.width??0)/2,y=f?f.y+f.height/2:o.height??0,v=(((_=o.positionAbsolute)==null?void 0:_.x)??0)+x,p=(((I=o.positionAbsolute)==null?void 0:I.y)??0)+y,E=f==null?void 0:f.position,$=E?zh[E]:null;if(!E||!$)return null;if(s)return M.createElement(s,{connectionLineType:r,connectionLineStyle:n,fromNode:o,fromHandle:f,fromX:v,fromY:p,toX:l,toY:c,fromPosition:E,toPosition:$,connectionStatus:i});let b="";const A={sourceX:v,sourceY:p,sourcePosition:E,targetX:l,targetY:c,targetPosition:$};return r===qe.Bezier?[b]=no(A):r===qe.Step?[b]=vn({...A,borderRadius:0}):r===qe.SmoothStep?[b]=vn(A):r===qe.SimpleBezier?[b]=to(A):b=`M${v},${p} ${l},${c}`,M.createElement("path",{d:b,fill:"none",className:"react-flow__connection-path",style:n})};Io.displayName="ConnectionLine";const Hh=e=>({nodeId:e.connectionNodeId,handleType:e.connectionHandleType,nodesConnectable:e.nodesConnectable,connectionStatus:e.connectionStatus,width:e.width,height:e.height});function Fh({containerStyle:e,style:t,type:n,component:r}){const{nodeId:s,handleType:i,nodesConnectable:o,width:a,height:l,connectionStatus:c}=ae(Hh,ge);return!(s&&i&&a&&o)?null:M.createElement("svg",{style:e,width:a,height:l,className:"react-flow__edges react-flow__connectionline react-flow__container"},M.createElement("g",{className:ye(["react-flow__connection",c])},M.createElement(Io,{nodeId:s,handleType:i,style:t,type:n,CustomComponent:r,connectionStatus:c})))}function Ts(e,t){return w.useRef(null),pe(),w.useMemo(()=>t(e),[e])}const Ro=({nodeTypes:e,edgeTypes:t,onMove:n,onMoveStart:r,onMoveEnd:s,onInit:i,onNodeClick:o,onEdgeClick:a,onNodeDoubleClick:l,onEdgeDoubleClick:c,onNodeMouseEnter:u,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,onSelectionContextMenu:x,onSelectionStart:y,onSelectionEnd:v,connectionLineType:p,connectionLineStyle:E,connectionLineComponent:$,connectionLineContainerStyle:b,selectionKeyCode:A,selectionOnDrag:j,selectionMode:_,multiSelectionKeyCode:I,panActivationKeyCode:S,zoomActivationKeyCode:D,deleteKeyCode:P,onlyRenderVisibleElements:V,elementsSelectable:F,selectNodesOnDrag:g,defaultViewport:C,translateExtent:N,minZoom:R,maxZoom:H,preventScrolling:k,defaultMarkerColor:T,zoomOnScroll:L,zoomOnPinch:z,panOnScroll:Y,panOnScrollSpeed:G,panOnScrollMode:K,zoomOnDoubleClick:J,panOnDrag:ee,onPaneClick:ie,onPaneMouseEnter:te,onPaneMouseMove:re,onPaneMouseLeave:fe,onPaneScroll:B,onPaneContextMenu:q,onEdgeContextMenu:W,onEdgeMouseEnter:Q,onEdgeMouseMove:U,onEdgeMouseLeave:X,onReconnect:O,onReconnectStart:se,onReconnectEnd:ce,reconnectRadius:me,noDragClassName:we,noWheelClassName:be,noPanClassName:Ee,elevateEdgesOnSelect:Ye,disableKeyboardA11y:oe,nodeOrigin:Qe,nodeExtent:ft,rfId:ht})=>{const jt=Ts(e,ph),xe=Ts(t,_h);return Ph(i),M.createElement(hh,{onPaneClick:ie,onPaneMouseEnter:te,onPaneMouseMove:re,onPaneMouseLeave:fe,onPaneContextMenu:q,onPaneScroll:B,deleteKeyCode:P,selectionKeyCode:A,selectionOnDrag:j,selectionMode:_,onSelectionStart:y,onSelectionEnd:v,multiSelectionKeyCode:I,panActivationKeyCode:S,zoomActivationKeyCode:D,elementsSelectable:F,onMove:n,onMoveStart:r,onMoveEnd:s,zoomOnScroll:L,zoomOnPinch:z,zoomOnDoubleClick:J,panOnScroll:Y,panOnScrollSpeed:G,panOnScrollMode:K,panOnDrag:ee,defaultViewport:C,translateExtent:N,minZoom:R,maxZoom:H,onSelectionContextMenu:x,preventScrolling:k,noDragClassName:we,noWheelClassName:be,noPanClassName:Ee,disableKeyboardA11y:oe},M.createElement(Lh,null,M.createElement(Oh,{edgeTypes:xe,onEdgeClick:a,onEdgeDoubleClick:c,onlyRenderVisibleElements:V,onEdgeContextMenu:W,onEdgeMouseEnter:Q,onEdgeMouseMove:U,onEdgeMouseLeave:X,onReconnect:O,onReconnectStart:se,onReconnectEnd:ce,reconnectRadius:me,defaultMarkerColor:T,noPanClassName:Ee,elevateEdgesOnSelect:!!Ye,disableKeyboardA11y:oe,rfId:ht},M.createElement(Fh,{style:E,type:p,component:$,containerStyle:b})),M.createElement("div",{className:"react-flow__edgelabel-renderer"}),M.createElement(xh,{nodeTypes:jt,onNodeClick:o,onNodeDoubleClick:l,onNodeMouseEnter:u,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,selectNodesOnDrag:g,onlyRenderVisibleElements:V,noPanClassName:Ee,noDragClassName:we,disableKeyboardA11y:oe,nodeOrigin:Qe,nodeExtent:ft,rfId:ht})))};Ro.displayName="GraphView";var Vh=w.memo(Ro);const gr=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],We={rfId:"1",width:0,height:0,transform:[0,0,1],nodeInternals:new Map,edges:[],onNodesChange:null,onEdgesChange:null,hasDefaultNodes:!1,hasDefaultEdges:!1,d3Zoom:null,d3Selection:null,d3ZoomHandler:void 0,minZoom:.5,maxZoom:2,translateExtent:gr,nodeExtent:gr,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionNodeId:null,connectionHandleId:null,connectionHandleType:"source",connectionPosition:{x:0,y:0},connectionStatus:null,connectionMode:ut.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:[0,0],nodeDragThreshold:0,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesUpdatable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,fitViewOnInit:!1,fitViewOnInitDone:!1,fitViewOnInitOptions:void 0,onSelectionChange:[],multiSelectionActive:!1,connectionStartHandle:null,connectionEndHandle:null,connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,connectionRadius:20,onError:mf,isValidConnection:void 0},Bh=()=>Cl((e,t)=>({...We,setNodes:n=>{const{nodeInternals:r,nodeOrigin:s,elevateNodesOnSelect:i}=t();e({nodeInternals:Yn(n,r,s,i)})},getNodes:()=>Array.from(t().nodeInternals.values()),setEdges:n=>{const{defaultEdgeOptions:r={}}=t();e({edges:n.map(s=>({...r,...s}))})},setDefaultNodesAndEdges:(n,r)=>{const s=typeof n<"u",i=typeof r<"u",o=s?Yn(n,new Map,t().nodeOrigin,t().elevateNodesOnSelect):new Map;e({nodeInternals:o,edges:i?r:[],hasDefaultNodes:s,hasDefaultEdges:i})},updateNodeDimensions:n=>{const{onNodesChange:r,nodeInternals:s,fitViewOnInit:i,fitViewOnInitDone:o,fitViewOnInitOptions:a,domNode:l,nodeOrigin:c}=t(),u=l==null?void 0:l.querySelector(".react-flow__viewport");if(!u)return;const d=window.getComputedStyle(u),{m22:m}=new window.DOMMatrixReadOnly(d.transform),f=n.reduce((y,v)=>{const p=s.get(v.id);if(p!=null&&p.hidden)s.set(p.id,{...p,[le]:{...p[le],handleBounds:void 0}});else if(p){const E=kr(v.nodeElement);!!(E.width&&E.height&&(p.width!==E.width||p.height!==E.height||v.forceUpdate))&&(s.set(p.id,{...p,[le]:{...p[le],handleBounds:{source:js(".source",v.nodeElement,m,c),target:js(".target",v.nodeElement,m,c)}},...E}),y.push({id:p.id,type:"dimensions",dimensions:E}))}return y},[]);vo(s,c);const x=o||i&&!o&&_o(t,{initial:!0,...a});e({nodeInternals:new Map(s),fitViewOnInitDone:x}),(f==null?void 0:f.length)>0&&(r==null||r(f))},updateNodePositions:(n,r=!0,s=!1)=>{const{triggerNodeChanges:i}=t(),o=n.map(a=>{const l={id:a.id,type:"position",dragging:s};return r&&(l.positionAbsolute=a.positionAbsolute,l.position=a.position),l});i(o)},triggerNodeChanges:n=>{const{onNodesChange:r,nodeInternals:s,hasDefaultNodes:i,nodeOrigin:o,getNodes:a,elevateNodesOnSelect:l}=t();if(n!=null&&n.length){if(i){const c=sh(n,a()),u=Yn(c,s,o,l);e({nodeInternals:u})}r==null||r(n)}},addSelectedNodes:n=>{const{multiSelectionActive:r,edges:s,getNodes:i}=t();let o,a=null;r?o=n.map(l=>Xe(l,!0)):(o=xt(i(),n),a=xt(s,[])),on({changedNodes:o,changedEdges:a,get:t,set:e})},addSelectedEdges:n=>{const{multiSelectionActive:r,edges:s,getNodes:i}=t();let o,a=null;r?o=n.map(l=>Xe(l,!0)):(o=xt(s,n),a=xt(i(),[])),on({changedNodes:a,changedEdges:o,get:t,set:e})},unselectNodesAndEdges:({nodes:n,edges:r}={})=>{const{edges:s,getNodes:i}=t(),o=n||i(),a=r||s,l=o.map(u=>(u.selected=!1,Xe(u.id,!1))),c=a.map(u=>Xe(u.id,!1));on({changedNodes:l,changedEdges:c,get:t,set:e})},setMinZoom:n=>{const{d3Zoom:r,maxZoom:s}=t();r==null||r.scaleExtent([n,s]),e({minZoom:n})},setMaxZoom:n=>{const{d3Zoom:r,minZoom:s}=t();r==null||r.scaleExtent([s,n]),e({maxZoom:n})},setTranslateExtent:n=>{var r;(r=t().d3Zoom)==null||r.translateExtent(n),e({translateExtent:n})},resetSelectedElements:()=>{const{edges:n,getNodes:r}=t(),i=r().filter(a=>a.selected).map(a=>Xe(a.id,!1)),o=n.filter(a=>a.selected).map(a=>Xe(a.id,!1));on({changedNodes:i,changedEdges:o,get:t,set:e})},setNodeExtent:n=>{const{nodeInternals:r}=t();r.forEach(s=>{s.positionAbsolute=jr(s.position,n)}),e({nodeExtent:n,nodeInternals:new Map(r)})},panBy:n=>{const{transform:r,width:s,height:i,d3Zoom:o,d3Selection:a,translateExtent:l}=t();if(!o||!a||!n.x&&!n.y)return!1;const c=Fe.translate(r[0]+n.x,r[1]+n.y).scale(r[2]),u=[[0,0],[s,i]],d=o==null?void 0:o.constrain()(c,u,l);return o.transform(a,d),r[0]!==d.x||r[1]!==d.y||r[2]!==d.k},cancelConnection:()=>e({connectionNodeId:We.connectionNodeId,connectionHandleId:We.connectionHandleId,connectionHandleType:We.connectionHandleType,connectionStatus:We.connectionStatus,connectionStartHandle:We.connectionStartHandle,connectionEndHandle:We.connectionEndHandle}),reset:()=>e({...We})}),Object.is),To=({children:e})=>{const t=w.useRef(null);return t.current||(t.current=Bh()),M.createElement(af,{value:t.current},e)};To.displayName="ReactFlowProvider";const Oo=({children:e})=>w.useContext(An)?M.createElement(M.Fragment,null,e):M.createElement(To,null,e);Oo.displayName="ReactFlowWrapper";const Gh={input:mo,default:mr,output:go,group:Rr},Yh={default:_n,straight:Mr,step:$r,smoothstep:In,simplebezier:Cr},Wh=[0,0],Uh=[15,15],Xh={x:0,y:0,zoom:1},qh={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0},Do=w.forwardRef(({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,className:s,nodeTypes:i=Gh,edgeTypes:o=Yh,onNodeClick:a,onEdgeClick:l,onInit:c,onMove:u,onMoveStart:d,onMoveEnd:m,onConnect:f,onConnectStart:x,onConnectEnd:y,onClickConnectStart:v,onClickConnectEnd:p,onNodeMouseEnter:E,onNodeMouseMove:$,onNodeMouseLeave:b,onNodeContextMenu:A,onNodeDoubleClick:j,onNodeDragStart:_,onNodeDrag:I,onNodeDragStop:S,onNodesDelete:D,onEdgesDelete:P,onSelectionChange:V,onSelectionDragStart:F,onSelectionDrag:g,onSelectionDragStop:C,onSelectionContextMenu:N,onSelectionStart:R,onSelectionEnd:H,connectionMode:k=ut.Strict,connectionLineType:T=qe.Bezier,connectionLineStyle:L,connectionLineComponent:z,connectionLineContainerStyle:Y,deleteKeyCode:G="Backspace",selectionKeyCode:K="Shift",selectionOnDrag:J=!1,selectionMode:ee=Yt.Full,panActivationKeyCode:ie="Space",multiSelectionKeyCode:te=wn()?"Meta":"Control",zoomActivationKeyCode:re=wn()?"Meta":"Control",snapToGrid:fe=!1,snapGrid:B=Uh,onlyRenderVisibleElements:q=!1,selectNodesOnDrag:W=!0,nodesDraggable:Q,nodesConnectable:U,nodesFocusable:X,nodeOrigin:O=Wh,edgesFocusable:se,edgesUpdatable:ce,elementsSelectable:me,defaultViewport:we=Xh,minZoom:be=.5,maxZoom:Ee=2,translateExtent:Ye=gr,preventScrolling:oe=!0,nodeExtent:Qe,defaultMarkerColor:ft="#b1b1b7",zoomOnScroll:ht=!0,zoomOnPinch:jt=!0,panOnScroll:xe=!1,panOnScrollSpeed:Ce=.5,panOnScrollMode:Je=ot.Free,zoomOnDoubleClick:et=!0,panOnDrag:tt=!0,onPaneClick:De,onPaneMouseEnter:Re,onPaneMouseMove:Ct,onPaneMouseLeave:Dn,onPaneScroll:$t,onPaneContextMenu:Ln,children:Vr,onEdgeContextMenu:nt,onEdgeDoubleClick:_a,onEdgeMouseEnter:Ea,onEdgeMouseMove:Na,onEdgeMouseLeave:Sa,onEdgeUpdate:ka,onEdgeUpdateStart:ja,onEdgeUpdateEnd:Ca,onReconnect:$a,onReconnectStart:Ma,onReconnectEnd:Aa,reconnectRadius:Ia=10,edgeUpdaterRadius:Ra=10,onNodesChange:Ta,onEdgesChange:Oa,noDragClassName:Da="nodrag",noWheelClassName:La="nowheel",noPanClassName:Br="nopan",fitView:Pa=!1,fitViewOptions:za,connectOnClick:Ha=!0,attributionPosition:Fa,proOptions:Va,defaultEdgeOptions:Ba,elevateNodesOnSelect:Ga=!0,elevateEdgesOnSelect:Ya=!1,disableKeyboardA11y:Gr=!1,autoPanOnConnect:Wa=!0,autoPanOnNodeDrag:Ua=!0,connectionRadius:Xa=20,isValidConnection:qa,onError:Ka,style:Za,id:Yr,nodeDragThreshold:Qa,...Ja},el)=>{const Pn=Yr||"1";return M.createElement("div",{...Ja,style:{...Za,...qh},ref:el,className:ye(["react-flow",s]),"data-testid":"rf__wrapper",id:Yr},M.createElement(Oo,null,M.createElement(Vh,{onInit:c,onMove:u,onMoveStart:d,onMoveEnd:m,onNodeClick:a,onEdgeClick:l,onNodeMouseEnter:E,onNodeMouseMove:$,onNodeMouseLeave:b,onNodeContextMenu:A,onNodeDoubleClick:j,nodeTypes:i,edgeTypes:o,connectionLineType:T,connectionLineStyle:L,connectionLineComponent:z,connectionLineContainerStyle:Y,selectionKeyCode:K,selectionOnDrag:J,selectionMode:ee,deleteKeyCode:G,multiSelectionKeyCode:te,panActivationKeyCode:ie,zoomActivationKeyCode:re,onlyRenderVisibleElements:q,selectNodesOnDrag:W,defaultViewport:we,translateExtent:Ye,minZoom:be,maxZoom:Ee,preventScrolling:oe,zoomOnScroll:ht,zoomOnPinch:jt,zoomOnDoubleClick:et,panOnScroll:xe,panOnScrollSpeed:Ce,panOnScrollMode:Je,panOnDrag:tt,onPaneClick:De,onPaneMouseEnter:Re,onPaneMouseMove:Ct,onPaneMouseLeave:Dn,onPaneScroll:$t,onPaneContextMenu:Ln,onSelectionContextMenu:N,onSelectionStart:R,onSelectionEnd:H,onEdgeContextMenu:nt,onEdgeDoubleClick:_a,onEdgeMouseEnter:Ea,onEdgeMouseMove:Na,onEdgeMouseLeave:Sa,onReconnect:$a??ka,onReconnectStart:Ma??ja,onReconnectEnd:Aa??Ca,reconnectRadius:Ia??Ra,defaultMarkerColor:ft,noDragClassName:Da,noWheelClassName:La,noPanClassName:Br,elevateEdgesOnSelect:Ya,rfId:Pn,disableKeyboardA11y:Gr,nodeOrigin:O,nodeExtent:Qe}),M.createElement(Pf,{nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:f,onConnectStart:x,onConnectEnd:y,onClickConnectStart:v,onClickConnectEnd:p,nodesDraggable:Q,nodesConnectable:U,nodesFocusable:X,edgesFocusable:se,edgesUpdatable:ce,elementsSelectable:me,elevateNodesOnSelect:Ga,minZoom:be,maxZoom:Ee,nodeExtent:Qe,onNodesChange:Ta,onEdgesChange:Oa,snapToGrid:fe,snapGrid:B,connectionMode:k,translateExtent:Ye,connectOnClick:Ha,defaultEdgeOptions:Ba,fitView:Pa,fitViewOptions:za,onNodesDelete:D,onEdgesDelete:P,onNodeDragStart:_,onNodeDrag:I,onNodeDragStop:S,onSelectionDrag:g,onSelectionDragStart:F,onSelectionDragStop:C,noPanClassName:Br,nodeOrigin:O,rfId:Pn,autoPanOnConnect:Wa,autoPanOnNodeDrag:Ua,onError:Ka,connectionRadius:Xa,isValidConnection:qa,nodeDragThreshold:Qa}),M.createElement(Df,{onSelectionChange:V}),Vr,M.createElement(cf,{proOptions:Va,position:Fa}),M.createElement(Bf,{rfId:Pn,disableKeyboardA11y:Gr})))});Do.displayName="ReactFlow";const Lo=({id:e,x:t,y:n,width:r,height:s,style:i,color:o,strokeColor:a,strokeWidth:l,className:c,borderRadius:u,shapeRendering:d,onClick:m,selected:f})=>{const{background:x,backgroundColor:y}=i||{},v=o||x||y;return M.createElement("rect",{className:ye(["react-flow__minimap-node",{selected:f},c]),x:t,y:n,rx:u,ry:u,width:r,height:s,fill:v,stroke:a,strokeWidth:l,shapeRendering:d,onClick:m?p=>m(p,e):void 0})};Lo.displayName="MiniMapNode";var Kh=w.memo(Lo);const Zh=e=>e.nodeOrigin,Qh=e=>e.getNodes().filter(t=>!t.hidden&&t.width&&t.height),qn=e=>e instanceof Function?e:()=>e;function Jh({nodeStrokeColor:e="transparent",nodeColor:t="#e2e2e2",nodeClassName:n="",nodeBorderRadius:r=5,nodeStrokeWidth:s=2,nodeComponent:i=Kh,onClick:o}){const a=ae(Qh,ge),l=ae(Zh),c=qn(t),u=qn(e),d=qn(n),m=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return M.createElement(M.Fragment,null,a.map(f=>{const{x,y}=lt(f,l).positionAbsolute;return M.createElement(i,{key:f.id,x,y,width:f.width,height:f.height,style:f.style,selected:f.selected,className:d(f),color:c(f),borderRadius:r,strokeColor:u(f),strokeWidth:s,shapeRendering:m,onClick:o,id:f.id})}))}var em=w.memo(Jh);const tm=200,nm=150,rm=e=>{const t=e.getNodes(),n={x:-e.transform[0]/e.transform[2],y:-e.transform[1]/e.transform[2],width:e.width/e.transform[2],height:e.height/e.transform[2]};return{viewBB:n,boundingRect:t.length>0?ff(Kt(t,e.nodeOrigin),n):n,rfId:e.rfId}},sm="react-flow__minimap-desc";function Po({style:e,className:t,nodeStrokeColor:n="transparent",nodeColor:r="#e2e2e2",nodeClassName:s="",nodeBorderRadius:i=5,nodeStrokeWidth:o=2,nodeComponent:a,maskColor:l="rgb(240, 240, 240, 0.6)",maskStrokeColor:c="none",maskStrokeWidth:u=1,position:d="bottom-right",onClick:m,onNodeClick:f,pannable:x=!1,zoomable:y=!1,ariaLabel:v="React Flow mini map",inversePan:p=!1,zoomStep:E=10,offsetScale:$=5}){const b=pe(),A=w.useRef(null),{boundingRect:j,viewBB:_,rfId:I}=ae(rm,ge),S=(e==null?void 0:e.width)??tm,D=(e==null?void 0:e.height)??nm,P=j.width/S,V=j.height/D,F=Math.max(P,V),g=F*S,C=F*D,N=$*F,R=j.x-(g-j.width)/2-N,H=j.y-(C-j.height)/2-N,k=g+N*2,T=C+N*2,L=`${sm}-${I}`,z=w.useRef(0);z.current=F,w.useEffect(()=>{if(A.current){const K=Ne(A.current),J=te=>{const{transform:re,d3Selection:fe,d3Zoom:B}=b.getState();if(te.sourceEvent.type!=="wheel"||!fe||!B)return;const q=-te.sourceEvent.deltaY*(te.sourceEvent.deltaMode===1?.05:te.sourceEvent.deltaMode?1:.002)*E,W=re[2]*Math.pow(2,q);B.scaleTo(fe,W)},ee=te=>{const{transform:re,d3Selection:fe,d3Zoom:B,translateExtent:q,width:W,height:Q}=b.getState();if(te.sourceEvent.type!=="mousemove"||!fe||!B)return;const U=z.current*Math.max(1,re[2])*(p?-1:1),X={x:re[0]-te.sourceEvent.movementX*U,y:re[1]-te.sourceEvent.movementY*U},O=[[0,0],[W,Q]],se=Fe.translate(X.x,X.y).scale(re[2]),ce=B.constrain()(se,O,q);B.transform(fe,ce)},ie=Yi().on("zoom",x?ee:null).on("zoom.wheel",y?J:null);return K.call(ie),()=>{K.on("zoom",null)}}},[x,y,p,E]);const Y=m?K=>{const J=$e(K);m(K,{x:J[0],y:J[1]})}:void 0,G=f?(K,J)=>{const ee=b.getState().nodeInternals.get(J);f(K,ee)}:void 0;return M.createElement(Sr,{position:d,style:e,className:ye(["react-flow__minimap",t]),"data-testid":"rf__minimap"},M.createElement("svg",{width:S,height:D,viewBox:`${R} ${H} ${k} ${T}`,role:"img","aria-labelledby":L,ref:A,onClick:Y},v&&M.createElement("title",{id:L},v),M.createElement(em,{onClick:G,nodeColor:r,nodeStrokeColor:n,nodeBorderRadius:i,nodeClassName:s,nodeStrokeWidth:o,nodeComponent:a}),M.createElement("path",{className:"react-flow__minimap-mask",d:`M${R-N},${H-N}h${k+N*2}v${T+N*2}h${-k-N*2}z + M${_.x},${_.y}h${_.width}v${_.height}h${-_.width}z`,fill:l,fillRule:"evenodd",stroke:c,strokeWidth:u,pointerEvents:"none"})))}Po.displayName="MiniMap";var im=w.memo(Po);function om(){return M.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32"},M.createElement("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"}))}function am(){return M.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5"},M.createElement("path",{d:"M0 0h32v4.2H0z"}))}function lm(){return M.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30"},M.createElement("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"}))}function cm(){return M.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32"},M.createElement("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"}))}function um(){return M.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32"},M.createElement("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"}))}const Lt=({children:e,className:t,...n})=>M.createElement("button",{type:"button",className:ye(["react-flow__controls-button",t]),...n},e);Lt.displayName="ControlButton";const dm=e=>({isInteractive:e.nodesDraggable||e.nodesConnectable||e.elementsSelectable,minZoomReached:e.transform[2]<=e.minZoom,maxZoomReached:e.transform[2]>=e.maxZoom}),zo=({style:e,showZoom:t=!0,showFitView:n=!0,showInteractive:r=!0,fitViewOptions:s,onZoomIn:i,onZoomOut:o,onFitView:a,onInteractiveChange:l,className:c,children:u,position:d="bottom-left"})=>{const m=pe(),[f,x]=w.useState(!1),{isInteractive:y,minZoomReached:v,maxZoomReached:p}=ae(dm,ge),{zoomIn:E,zoomOut:$,fitView:b}=Rn();if(w.useEffect(()=>{x(!0)},[]),!f)return null;const A=()=>{E(),i==null||i()},j=()=>{$(),o==null||o()},_=()=>{b(s),a==null||a()},I=()=>{m.setState({nodesDraggable:!y,nodesConnectable:!y,elementsSelectable:!y}),l==null||l(!y)};return M.createElement(Sr,{className:ye(["react-flow__controls",c]),position:d,style:e,"data-testid":"rf__controls"},t&&M.createElement(M.Fragment,null,M.createElement(Lt,{onClick:A,className:"react-flow__controls-zoomin",title:"zoom in","aria-label":"zoom in",disabled:p},M.createElement(om,null)),M.createElement(Lt,{onClick:j,className:"react-flow__controls-zoomout",title:"zoom out","aria-label":"zoom out",disabled:v},M.createElement(am,null))),n&&M.createElement(Lt,{className:"react-flow__controls-fitview",onClick:_,title:"fit view","aria-label":"fit view"},M.createElement(lm,null)),r&&M.createElement(Lt,{className:"react-flow__controls-interactive",onClick:I,title:"toggle interactivity","aria-label":"toggle interactivity"},y?M.createElement(um,null):M.createElement(cm,null)),u)};zo.displayName="Controls";var fm=w.memo(zo),ke;(function(e){e.Lines="lines",e.Dots="dots",e.Cross="cross"})(ke||(ke={}));function hm({color:e,dimensions:t,lineWidth:n}){return M.createElement("path",{stroke:e,strokeWidth:n,d:`M${t[0]/2} 0 V${t[1]} M0 ${t[1]/2} H${t[0]}`})}function mm({color:e,radius:t}){return M.createElement("circle",{cx:t,cy:t,r:t,fill:e})}const pm={[ke.Dots]:"#91919a",[ke.Lines]:"#eee",[ke.Cross]:"#e2e2e2"},gm={[ke.Dots]:1,[ke.Lines]:1,[ke.Cross]:6},ym=e=>({transform:e.transform,patternId:`pattern-${e.rfId}`});function Ho({id:e,variant:t=ke.Dots,gap:n=20,size:r,lineWidth:s=1,offset:i=2,color:o,style:a,className:l}){const c=w.useRef(null),{transform:u,patternId:d}=ae(ym,ge),m=o||pm[t],f=r||gm[t],x=t===ke.Dots,y=t===ke.Cross,v=Array.isArray(n)?n:[n,n],p=[v[0]*u[2]||1,v[1]*u[2]||1],E=f*u[2],$=y?[E,E]:p,b=x?[E/i,E/i]:[$[0]/i,$[1]/i];return M.createElement("svg",{className:ye(["react-flow__background",l]),style:{...a,position:"absolute",width:"100%",height:"100%",top:0,left:0},ref:c,"data-testid":"rf__background"},M.createElement("pattern",{id:d+e,x:u[0]%p[0],y:u[1]%p[1],width:p[0],height:p[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${b[0]},-${b[1]})`},x?M.createElement(mm,{color:m,radius:E/i}):M.createElement(hm,{dimensions:$,color:m,lineWidth:s})),M.createElement("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${d+e})`}))}Ho.displayName="Background";var xm=w.memo(Ho);const Or=(()=>{const e=window.location.pathname.match(/^(\/view\/[^/]+)(?:\/|$)/);return e?e[1]:""})();function Fo(e){return Or+e}async function de(e){const t=await fetch(Fo(e));if(!t.ok)throw new Error(`${e}: ${t.status} ${await t.text()}`);return await t.json()}async function Ge(e,t){const n=await fetch(Fo(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json();if(!n.ok)throw new Vo(r.error??`${e}: ${n.status}`,n.status);return r}class Vo extends Error{constructor(n,r){super(n);ue(this,"status");this.status=r}}const xx=e=>de(e?`/api/model?subject_area=${encodeURIComponent(e)}`:"/api/model"),wx=()=>de("/api/diagnostics"),Bo=(e,t)=>de(`/api/ontology/search?q=${encodeURIComponent(e)}`+(t?`&within=${encodeURIComponent(t)}`:"")),Go=()=>de("/api/ontology/ontologies"),wm=e=>de(`/api/ontology/term?ref=${encodeURIComponent(e)}`),bm=()=>de("/api/ontology/stack"),vm=()=>de("/api/ontology/coverage"),_m=(e,t,n)=>Ge("/api/command",{op:e,payload:t,fingerprint:n??""}),Em=()=>de("/api/git/status"),Nm=()=>de("/api/git/diff"),Sm=e=>Ge("/api/git/commit",{message:e}),km=()=>Ge("/api/git/discard",{}),jm=()=>de("/api/decisions"),Os=(e,t)=>Ge(`/api/decisions/${e}/verdict`,{verdict:t}),bx=(e={})=>{const t=new URLSearchParams;return e.subject_area&&t.set("subject_area",e.subject_area),e.q&&t.set("q",e.q),de(`/api/glossary/terms?${t.toString()}`)},vx=()=>de("/api/glossary/config"),_x=e=>Ge("/api/git/propose",e),Ex=(e="HEAD")=>de(`/api/git/diff/model?base=${encodeURIComponent(e)}`),Nx=(e="HEAD")=>de(`/api/git/classify?base=${encodeURIComponent(e)}`),Sx=e=>Ge("/api/git/classify",{changes:e}),kx=e=>de(`/api/git/conflicts?base=${encodeURIComponent(e)}`),jx=(e="")=>de(`/api/git/context?user=${encodeURIComponent(e)}`),Cx=(e="")=>de(`/api/git/proposals?user=${encodeURIComponent(e)}`),Cm=()=>de("/api/export"),$m=(e,t)=>{const n=t?`?dialect=${encodeURIComponent(t)}`:"";return`${Or}/api/export/${encodeURIComponent(e)}${n}`},Mm=(e,t,n)=>Ge("/api/import",{format:e,content:t,dialect:n}),Am=(e,t)=>Ge("/api/import/preview",{format:e,content:t}),$x=e=>de(`/api/import/stash/${encodeURIComponent(e)}`),Mx=async(e,t=!1)=>{for(const n of e)try{await _m(n.op,n.payload)}catch(r){if(t&&r instanceof Error&&/already exists/i.test(r.message))continue;throw r}},Ax=(e,t,n)=>fetch(`${Or}/api/preview`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({changes:e,subject_area:t??""}),signal:n}).then(async r=>{if(!r.ok)throw new Vo(await r.text(),r.status);return await r.json()}),Ix=()=>de("/api/glossary/subject-areas"),Rx=e=>de(`/api/glossary/subject-area/${e}`),Tx=(e,t)=>Ge(`/api/glossary/subject-area/${e}/expand`,t),Im={business_key:"🔑",surrogate_key:"#",measure:"Σ",attribute:""};function Rm(e){return e??""}const Dr="__entity_source",Lr="__entity_target",Tm=w.memo(function({data:t,selected:n}){var j;const{entity:r,color:s,dimmed:i,highlighted:o,showTypes:a,collapseDetail:l,expanded:c,onToggleExpand:u,endpointAttrs:d,highlightAttrs:m,dimAttrs:f,onHoverAttr:x}=t,y=_=>_.role==="business_key"?0:d!=null&&d.has(_.id)?1:2,v=[...r.attributes].map((_,I)=>({a:_,i:I})).sort((_,I)=>y(_.a)-y(I.a)||_.i-I.i).map(_=>_.a),p=v.filter(_=>_.role==="business_key");let E=v.filter(_=>_.role!=="business_key");const $=!!l&&!c;let b=0;if($){const _=E.filter(I=>d==null?void 0:d.has(I.id));b=E.length-_.length,E=_}const A=(_,I)=>{const S="attr-row"+(m!=null&&m.has(_.id)?" rel-hit":"")+(f!=null&&f.has(_.id)?" rel-dim":"")+(d!=null&&d.has(_.id)?" endpoint":"");return h.jsxs("div",{className:S,onMouseEnter:x?()=>x(_.id):void 0,onMouseLeave:x?()=>x(null):void 0,children:[h.jsx(Ke,{id:_.id,type:"target",position:Z.Left,className:"port attr-port"}),h.jsx(Ke,{id:_.id,type:"source",position:Z.Right,className:"port attr-port"}),h.jsx("span",{className:"attr-icon",children:Im[_.role]}),h.jsx("span",{className:"attr-name"+(I?" pk":""),children:_.name}),a&&h.jsx("span",{className:"attr-type",children:Rm(_.domain)}),!_.nullable&&h.jsx("span",{className:"attr-notnull",title:"not null",children:"●"})]},_.id)};return h.jsxs("div",{className:"entity-node"+(n?" selected":"")+(i?" dimmed":"")+(o?" highlighted":""),style:{borderTopColor:s},children:[h.jsx(Ke,{id:Lr,type:"target",position:Z.Left,className:"port"}),h.jsx(Ke,{id:Dr,type:"source",position:Z.Right,className:"port"}),h.jsxs("div",{className:"entity-header",children:[h.jsx("span",{className:"entity-name",title:((j=r.conceptual)==null?void 0:j.definition)??void 0,children:r.name}),h.jsxs("span",{className:"entity-badges",children:[r.pattern&&h.jsx("span",{className:"badge pattern",children:r.pattern}),(()=>{var S;const _=(((S=r.conceptual)==null?void 0:S.ontology_refs)??[]).filter(D=>D.uri);if(_.length===0)return null;const I=_.map(D=>D.uri).join(` +`);return h.jsxs("span",{className:"badge ontology",title:I,children:["⚙",_.length>1?` ${_.length}`:""]})})()]})]}),p.length>0&&h.jsx("div",{className:"attr-section keys",children:p.map(_=>A(_,!0))}),h.jsxs("div",{className:"attr-section",children:[E.map(_=>A(_,!1)),r.attributes.length===0&&h.jsx("div",{className:"attr-row empty",children:"no attributes"}),b>0&&h.jsx("div",{className:"attr-row more",title:"Show all columns",onClick:u?()=>u(r.id):void 0,children:`+${b} more`}),c&&!!l&&h.jsx("div",{className:"attr-row more",title:"Collapse to keys",onClick:u?()=>u(r.id):void 0,children:"− collapse"})]})]})}),Om=["string","integer","bigint","decimal","boolean","date","timestamp"];function Dm({entity:e,doc:t,caps:n,exec:r,onClose:s,onFocusEntity:i,onAlign:o,onEditMapping:a}){var m,f,x,y,v,p,E,$,b,A,j,_,I;const l=!n.canEdit,c=e.conceptual,u=t.relationships.filter(S=>S.from.entity===e.id||S.to.entity===e.id),d=S=>{var D;return((D=t.entities.find(P=>P.id===S))==null?void 0:D.name)??S};return h.jsxs("aside",{className:"detail-panel",children:[h.jsxs("div",{className:"detail-header",children:[h.jsxs("div",{style:{flex:1},children:[h.jsx(Pe,{value:e.name,className:"entity-title-input",readOnly:l,onCommit:S=>r("rename_entity",{id:e.id,name:S})}),(e.physical_name||!l)&&h.jsxs("div",{className:"physical-name-row",title:"Physical table name (erwin Physical_Name)",children:[h.jsx("span",{className:"physical-label",children:"physical"}),h.jsx(Pe,{value:e.physical_name??"",className:"physical-name-input",placeholder:l?"—":"physical table name…",readOnly:l,onCommit:S=>r("rename_entity",{id:e.id,physical_name:S})})]}),h.jsxs("div",{children:[h.jsx(Pm,{entity:e,doc:t,readOnly:l,exec:r}),h.jsx(Hm,{entity:e,readOnly:l,exec:r}),e.category&&h.jsx("span",{className:"cat-badge "+e.category.role,title:e.category.role==="supertype"?`Supertype of category "${e.category.category}"`:`Subtype in "${e.category.category}" (${e.category.materialization})`,children:e.category.role==="supertype"?"◆ supertype":"◇ subtype"})]})]}),h.jsx("button",{className:"icon-btn",onClick:s,title:"Close",children:"✕"})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Definition"}),h.jsx(Pe,{value:((m=c==null?void 0:c.definition)==null?void 0:m.trim())??"",multiline:!0,placeholder:l?"—":"add a business definition…",className:"definition-input",readOnly:l,onCommit:S=>r("set_definition",{id:e.id,definition:S})})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Ontology",!l&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>o(e),children:(((f=c==null?void 0:c.ontology_refs)==null?void 0:f.length)??0)>0?"Add / re-align…":"Align…"}),(((x=c==null?void 0:c.ontology_refs)==null?void 0:x.length)??0)>0&&h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_alignment",{id:e.id}),title:"Clear every alignment on this object",children:"Clear all"})]})]}),(c==null?void 0:c.ontology_layer)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"layer"}),h.jsx("span",{className:`chip layer-${c.ontology_layer}`,children:c.ontology_layer}),c.no_industry_equivalent&&h.jsx("span",{className:"chip",title:"reviewed: no industry equivalent",children:"no industry equivalent"})]}),h.jsx(Lm,{entityId:e.id,refs:(c==null?void 0:c.ontology_refs)??[],caps:n,exec:r})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Knowledge Graph mapping",!l&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>a(e),children:(y=e.term_map)!=null&&y.subject_template||(v=e.term_map)!=null&&v.class_iri?"Edit mapping…":"Map…"}),(((p=e.term_map)==null?void 0:p.subject_template)||((E=e.term_map)==null?void 0:E.class_iri))&&h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_term_map",{id:e.id}),children:"Clear"})]})]}),($=e.term_map)!=null&&$.subject_template||(b=e.term_map)!=null&&b.class_iri?h.jsxs(h.Fragment,{children:[((A=e.term_map)==null?void 0:A.class_iri)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"class IRI"}),h.jsx("code",{className:"v",children:e.term_map.class_iri})]}),((j=e.term_map)==null?void 0:j.subject_template)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"subject"}),h.jsx("code",{className:"v",children:e.term_map.subject_template})]})]}):h.jsxs("p",{className:"empty-hint",children:["uses the default R2RML term-map (base IRI",t.project.kg_base_iri?` ${t.project.kg_base_iri}`:""," + primary key)"]})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Stewardship"}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"owner"}),h.jsx(Pe,{value:((_=c==null?void 0:c.stewardship)==null?void 0:_.owner)??"",placeholder:"—",readOnly:l,onCommit:S=>{var D;return r("set_stewardship",{id:e.id,owner:S,steward:((D=c==null?void 0:c.stewardship)==null?void 0:D.steward)??null})}})]}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"steward"}),h.jsx(Pe,{value:((I=c==null?void 0:c.stewardship)==null?void 0:I.steward)??"",placeholder:"—",readOnly:l,onCommit:S=>{var D;return r("set_stewardship",{id:e.id,owner:((D=c==null?void 0:c.stewardship)==null?void 0:D.owner)??null,steward:S})}})]})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Attributes (",e.attributes.length,")"]}),h.jsx("table",{className:"attr-table",children:h.jsx("tbody",{children:e.attributes.map(S=>h.jsx(Vm,{a:S,entity:e,doc:t,readOnly:l,exec:r},S.id))})}),!l&&h.jsx(Bm,{entity:e,exec:r})]}),e.key_groups&&e.key_groups.length>0&&h.jsxs("section",{children:[h.jsxs("h3",{children:["Keys (",e.key_groups.length,")"]}),e.key_groups.map(S=>{const D=S.members.map(P=>{var V;return((V=e.attributes.find(F=>F.id===P))==null?void 0:V.name)??"?"}).join(", ");return h.jsxs("div",{className:"key-row",children:[h.jsx("span",{className:"key-badge "+S.type,children:S.type.toUpperCase()}),h.jsx("span",{className:"key-name",children:S.name}),h.jsx("span",{className:"key-cols",children:D})]},S.id)})]}),e.udp&&Object.keys(e.udp).length>0&&h.jsxs("section",{children:[h.jsx("h3",{children:"Properties (UDP)"}),h.jsx("table",{className:"attr-table",children:h.jsx("tbody",{children:Object.entries(e.udp).map(([S,D])=>h.jsxs("tr",{className:"udp-row",children:[h.jsx("td",{className:"udp-key",children:S}),h.jsx("td",{className:"udp-val",children:String(D)})]},S))})})]}),u.length>0&&h.jsxs("section",{children:[h.jsxs("h3",{children:["Relationships (",u.length,")"]}),u.map(S=>{const D=S.from.entity===e.id?S.to.entity:S.from.entity;return h.jsxs("div",{className:"rel-row",children:[h.jsxs("div",{className:"rel-main",children:[h.jsxs("button",{className:"rel-link",onClick:()=>i(D),children:[h.jsx("span",{className:"rel-card",children:Gm(S,e.id)}),d(D)]}),(S.verb_phrase||S.inverse_verb_phrase||!l)&&h.jsxs("div",{className:"verb-phrases",title:"erwin verb phrases (parent → child, and inverse)",children:[h.jsx(Pe,{value:S.verb_phrase??"",className:"verb-input",placeholder:l?"":"verb phrase…",readOnly:l,onCommit:P=>r("update_relationship",{id:S.id,verb_phrase:P})}),h.jsx("span",{className:"verb-sep",children:"/"}),h.jsx(Pe,{value:S.inverse_verb_phrase??"",className:"verb-input",placeholder:l?"":"inverse…",readOnly:l,onCommit:P=>r("update_relationship",{id:S.id,inverse_verb_phrase:P})})]})]}),!l&&h.jsx("button",{className:"mini-btn danger",title:`delete ${S.name}`,onClick:()=>r("delete_relationship",{id:S.id}),children:"✕"})]},S.id)})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Identity"}),h.jsx("code",{className:"ulid",title:"Immutable ULID — click to copy",onClick:()=>{var S;return(S=navigator.clipboard)==null?void 0:S.writeText(e.id)},children:e.id}),!l&&h.jsx("div",{style:{marginTop:12},children:h.jsx("button",{className:"mini-btn danger",onClick:()=>{confirm(`Delete entity '${e.name}' (and its relationships)?`)&&r("delete_entity",{id:e.id,cascade:!0}).then(s)},children:"Delete entity…"})})]})]})}function Lm({entityId:e,refs:t,caps:n,exec:r}){const s=!n.canEdit,i=t.filter(o=>o.uri);return i.length===0?h.jsx("p",{className:"empty-hint",children:"not aligned to any ontology term"}):h.jsx("div",{className:"ref-list",children:i.map((o,a)=>{const l=o.status==="proposed";return h.jsxs("div",{className:"ref-card"+(l?" proposed":""),children:[h.jsxs("div",{className:"ref-head",children:[h.jsx("code",{className:"ref-uri",title:o.uri??void 0,children:o.uri}),h.jsx("span",{className:"ref-status "+(l?"proposed":"accepted"),children:l?"proposed":"accepted"})]}),h.jsxs("div",{className:"ref-meta",children:[o.predicate&&h.jsx("span",{className:"ref-pill",children:o.predicate}),o.layer&&h.jsx("span",{className:`chip layer-${o.layer}`,children:o.layer}),o.resolved_via&&h.jsxs("span",{className:"ref-via",title:"resolver that found this term",children:["via ",o.resolved_via]}),typeof o.confidence=="number"&&h.jsxs("span",{className:"ref-via",children:["conf ",Math.round(o.confidence*100),"%"]})]}),!s&&h.jsxs("div",{className:"ref-actions",children:[l&&n.canArbitrate&&h.jsx("button",{className:"mini-btn",onClick:()=>r("promote_alignment",{id:e,uri:o.uri}),title:"Accept this proposed alignment (architect action)",children:"Promote"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_alignment",{id:e,uri:o.uri}),title:"Remove this alignment",children:"Remove"})]})]},`${o.uri}-${a}`)})})}function Pe({value:e,onCommit:t,readOnly:n,multiline:r=!1,placeholder:s="",className:i=""}){const[o,a]=w.useState(e);w.useEffect(()=>a(e),[e]);const l=()=>{o.trim()!==e.trim()&&t(o.trim())};return n?h.jsx("span",{className:i,children:e||s}):r?h.jsx("textarea",{className:`edit-input ${i}`,value:o,placeholder:s,rows:3,onChange:c=>a(c.target.value),onBlur:l}):h.jsx("input",{className:`edit-input ${i}`,value:o,placeholder:s,onChange:c=>a(c.target.value),onBlur:l,onKeyDown:c=>c.key==="Enter"&&c.target.blur()})}function Pm({entity:e,doc:t,readOnly:n,exec:r}){var i,o,a;const s=((o=(i=e.conceptual)==null?void 0:i.subject_area)==null?void 0:o.id)??"";return n?(a=e.conceptual)!=null&&a.subject_area?h.jsx("span",{className:"chip",children:e.conceptual.subject_area.name}):null:h.jsxs("select",{className:"chip-select",value:s,onChange:l=>r("set_subject_area",{id:e.id,subject_area:l.target.value||null}),children:[h.jsx("option",{value:"",children:"no subject area"}),t.subject_areas.map(l=>h.jsx("option",{value:l.id,children:l.name},l.id))]})}const zm=["","scd2","hub","link","satellite","bridge"];function Hm({entity:e,readOnly:t,exec:n}){return t?e.pattern?h.jsx("span",{className:"chip pattern",children:e.pattern}):null:h.jsx("select",{className:"chip-select",value:e.pattern??"",onChange:r=>n("set_pattern",{id:e.id,pattern:r.target.value||null}),children:zm.map(r=>h.jsx("option",{value:r,children:r||"no pattern"},r))})}const Fm=["attribute","business_key","measure","surrogate_key"];function Vm({a:e,entity:t,doc:n,readOnly:r,exec:s}){if(r)return h.jsxs("tr",{children:[h.jsxs("td",{className:e.role==="business_key"?"pk":"",children:[e.name,e.physical_name&&e.physical_name!==e.name&&h.jsx("span",{className:"physical-col",title:"Physical column name",children:e.physical_name}),e.enum_values&&e.enum_values.length>0&&h.jsx("span",{className:"enum-badge",title:`Allowed: ${e.enum_values.join(", ")}`,children:"enum"})]}),h.jsx("td",{className:"type",children:e.domain??""}),h.jsx("td",{className:"null",children:e.nullable?"":"NN"})]});const i=o=>s("update_attribute",{entity_id:t.id,attribute_id:e.id,...o});return h.jsxs("tr",{children:[h.jsxs("td",{className:e.role==="business_key"?"pk":"",children:[h.jsx(Pe,{value:e.name,readOnly:!1,onCommit:o=>i({name:o})}),(e.physical_name||!1)&&h.jsx(Pe,{value:e.physical_name??"",className:"physical-col-input",placeholder:"physical…",readOnly:!1,onCommit:o=>i({physical_name:o})}),e.enum_values&&e.enum_values.length>0&&h.jsx("span",{className:"enum-badge",title:`Allowed: ${e.enum_values.join(", ")}`,children:"enum"})]}),h.jsx("td",{className:"type",children:h.jsx("select",{className:"cell-select",value:e.domain??"string",onChange:o=>i({domain:o.target.value}),children:[...new Set([...Om,...n.domains??[],e.domain??"string"])].sort().map(o=>h.jsx("option",{value:o,children:o},o))})}),h.jsx("td",{children:h.jsx("select",{className:"cell-select",value:e.role,onChange:o=>i({role:o.target.value}),title:"role",children:Fm.map(o=>h.jsx("option",{value:o,children:o==="business_key"?"🔑 key":o==="measure"?"Σ measure":o},o))})}),h.jsx("td",{className:"null",children:h.jsx("input",{type:"checkbox",checked:!e.nullable,title:"not null",onChange:o=>i({nullable:!o.target.checked})})}),h.jsx("td",{children:h.jsx("button",{className:"mini-btn danger",title:"delete attribute",onClick:()=>s("delete_attribute",{entity_id:t.id,attribute_id:e.id}),children:"✕"})})]})}function Bm({entity:e,exec:t}){const[n,r]=w.useState(""),s=()=>{n.trim()&&t("add_attribute",{entity_id:e.id,name:n,domain:"string"}).then(()=>r(""))};return h.jsxs("div",{className:"add-attr",children:[h.jsx("input",{className:"edit-input",placeholder:"+ add attribute…",value:n,onChange:i=>r(i.target.value),onKeyDown:i=>i.key==="Enter"&&s()}),n&&h.jsx("button",{className:"mini-btn",onClick:s,children:"Add"})]})}function Gm(e,t){const n=e.from.entity===t;switch(e.cardinality){case"many_to_one":return n?"N:1 →":"1:N ←";case"one_to_many":return n?"1:N →":"N:1 ←";case"one_to_one":return"1:1";default:return"N:M"}}var Ym="\0",rt="\0",Ds="";let Wm=class{constructor(t){ue(this,"_isDirected",!0);ue(this,"_isMultigraph",!1);ue(this,"_isCompound",!1);ue(this,"_label");ue(this,"_defaultNodeLabelFn",()=>{});ue(this,"_defaultEdgeLabelFn",()=>{});ue(this,"_nodes",{});ue(this,"_in",{});ue(this,"_preds",{});ue(this,"_out",{});ue(this,"_sucs",{});ue(this,"_edgeObjs",{});ue(this,"_edgeLabels",{});ue(this,"_nodeCount",0);ue(this,"_edgeCount",0);ue(this,"_parent");ue(this,"_children");t&&(this._isDirected=Object.hasOwn(t,"directed")?t.directed:!0,this._isMultigraph=Object.hasOwn(t,"multigraph")?t.multigraph:!1,this._isCompound=Object.hasOwn(t,"compound")?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children[rt]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return this._defaultNodeLabelFn=t,typeof t!="function"&&(this._defaultNodeLabelFn=()=>t),this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){var t=this;return this.nodes().filter(n=>Object.keys(t._in[n]).length===0)}sinks(){var t=this;return this.nodes().filter(n=>Object.keys(t._out[n]).length===0)}setNodes(t,n){var r=arguments,s=this;return t.forEach(function(i){r.length>1?s.setNode(i,n):s.setNode(i)}),this}setNode(t,n){return Object.hasOwn(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=n),this):(this._nodes[t]=arguments.length>1?n:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]=rt,this._children[t]={},this._children[rt][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return Object.hasOwn(this._nodes,t)}removeNode(t){var n=this;if(Object.hasOwn(this._nodes,t)){var r=s=>n.removeEdge(n._edgeObjs[s]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(function(s){n.setParent(s)}),delete this._children[t]),Object.keys(this._in[t]).forEach(r),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(r),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,n){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(n===void 0)n=rt;else{n+="";for(var r=n;r!==void 0;r=this.parent(r))if(r===t)throw new Error("Setting "+n+" as parent of "+t+" would create a cycle");this.setNode(n)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=n,this._children[n][t]=!0,this}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}parent(t){if(this._isCompound){var n=this._parent[t];if(n!==rt)return n}}children(t=rt){if(this._isCompound){var n=this._children[t];if(n)return Object.keys(n)}else{if(t===rt)return this.nodes();if(this.hasNode(t))return[]}}predecessors(t){var n=this._preds[t];if(n)return Object.keys(n)}successors(t){var n=this._sucs[t];if(n)return Object.keys(n)}neighbors(t){var n=this.predecessors(t);if(n){const s=new Set(n);for(var r of this.successors(t))s.add(r);return Array.from(s.values())}}isLeaf(t){var n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),n.length===0}filterNodes(t){var n=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});n.setGraph(this.graph());var r=this;Object.entries(this._nodes).forEach(function([o,a]){t(o)&&n.setNode(o,a)}),Object.values(this._edgeObjs).forEach(function(o){n.hasNode(o.v)&&n.hasNode(o.w)&&n.setEdge(o,r.edge(o))});var s={};function i(o){var a=r.parent(o);return a===void 0||n.hasNode(a)?(s[o]=a,a):a in s?s[a]:i(a)}return this._isCompound&&n.nodes().forEach(o=>n.setParent(o,i(o))),n}setDefaultEdgeLabel(t){return this._defaultEdgeLabelFn=t,typeof t!="function"&&(this._defaultEdgeLabelFn=()=>t),this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,n){var r=this,s=arguments;return t.reduce(function(i,o){return s.length>1?r.setEdge(i,o,n):r.setEdge(i,o),o}),this}setEdge(){var t,n,r,s,i=!1,o=arguments[0];typeof o=="object"&&o!==null&&"v"in o?(t=o.v,n=o.w,r=o.name,arguments.length===2&&(s=arguments[1],i=!0)):(t=o,n=arguments[1],r=arguments[3],arguments.length>2&&(s=arguments[2],i=!0)),t=""+t,n=""+n,r!==void 0&&(r=""+r);var a=Pt(this._isDirected,t,n,r);if(Object.hasOwn(this._edgeLabels,a))return i&&(this._edgeLabels[a]=s),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(n),this._edgeLabels[a]=i?s:this._defaultEdgeLabelFn(t,n,r);var l=Um(this._isDirected,t,n,r);return t=l.v,n=l.w,Object.freeze(l),this._edgeObjs[a]=l,Ls(this._preds[n],t),Ls(this._sucs[t],n),this._in[n][a]=l,this._out[t][a]=l,this._edgeCount++,this}edge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r);return this._edgeLabels[s]}edgeAsObj(){const t=this.edge(...arguments);return typeof t!="object"?{label:t}:t}hasEdge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r);return Object.hasOwn(this._edgeLabels,s)}removeEdge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r),i=this._edgeObjs[s];return i&&(t=i.v,n=i.w,delete this._edgeLabels[s],delete this._edgeObjs[s],Ps(this._preds[n],t),Ps(this._sucs[t],n),delete this._in[n][s],delete this._out[t][s],this._edgeCount--),this}inEdges(t,n){var r=this._in[t];if(r){var s=Object.values(r);return n?s.filter(i=>i.v===n):s}}outEdges(t,n){var r=this._out[t];if(r){var s=Object.values(r);return n?s.filter(i=>i.w===n):s}}nodeEdges(t,n){var r=this.inEdges(t,n);if(r)return r.concat(this.outEdges(t,n))}};function Ls(e,t){e[t]?e[t]++:e[t]=1}function Ps(e,t){--e[t]||delete e[t]}function Pt(e,t,n,r){var s=""+t,i=""+n;if(!e&&s>i){var o=s;s=i,i=o}return s+Ds+i+Ds+(r===void 0?Ym:r)}function Um(e,t,n,r){var s=""+t,i=""+n;if(!e&&s>i){var o=s;s=i,i=o}var a={v:s,w:i};return r&&(a.name=r),a}function Kn(e,t){return Pt(e,t.v,t.w,t.name)}var Pr=Wm,Xm="2.2.4",qm={Graph:Pr,version:Xm},Km=Pr,Zm={write:Qm,read:tp};function Qm(e){var t={options:{directed:e.isDirected(),multigraph:e.isMultigraph(),compound:e.isCompound()},nodes:Jm(e),edges:ep(e)};return e.graph()!==void 0&&(t.value=structuredClone(e.graph())),t}function Jm(e){return e.nodes().map(function(t){var n=e.node(t),r=e.parent(t),s={v:t};return n!==void 0&&(s.value=n),r!==void 0&&(s.parent=r),s})}function ep(e){return e.edges().map(function(t){var n=e.edge(t),r={v:t.v,w:t.w};return t.name!==void 0&&(r.name=t.name),n!==void 0&&(r.value=n),r})}function tp(e){var t=new Km(e.options).setGraph(e.value);return e.nodes.forEach(function(n){t.setNode(n.v,n.value),n.parent&&t.setParent(n.v,n.parent)}),e.edges.forEach(function(n){t.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),t}var np=rp;function rp(e){var t={},n=[],r;function s(i){Object.hasOwn(t,i)||(t[i]=!0,r.push(i),e.successors(i).forEach(s),e.predecessors(i).forEach(s))}return e.nodes().forEach(function(i){r=[],s(i),r.length&&n.push(r)}),n}let sp=class{constructor(){ue(this,"_arr",[]);ue(this,"_keyIndices",{})}size(){return this._arr.length}keys(){return this._arr.map(function(t){return t.key})}has(t){return Object.hasOwn(this._keyIndices,t)}priority(t){var n=this._keyIndices[t];if(n!==void 0)return this._arr[n].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,n){var r=this._keyIndices;if(t=String(t),!Object.hasOwn(r,t)){var s=this._arr,i=s.length;return r[t]=i,s.push({key:t,priority:n}),this._decrease(i),!0}return!1}removeMin(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,n){var r=this._keyIndices[t];if(n>this._arr[r].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[r].priority+" New: "+n);this._arr[r].priority=n,this._decrease(r)}_heapify(t){var n=this._arr,r=2*t,s=r+1,i=t;r>1,!(n[s].priority1;function ap(e,t,n,r){return lp(e,String(t),n||op,r||function(s){return e.outEdges(s)})}function lp(e,t,n,r){var s={},i=new ip,o,a,l=function(c){var u=c.v!==o?c.v:c.w,d=s[u],m=n(c),f=a.distance+m;if(m<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+c+" Weight: "+m);f0&&(o=i.removeMin(),a=s[o],a.distance!==Number.POSITIVE_INFINITY);)r(o).forEach(l);return s}var cp=Wo,up=dp;function dp(e,t,n){return e.nodes().reduce(function(r,s){return r[s]=cp(e,s,t,n),r},{})}var Uo=fp;function fp(e){var t=0,n=[],r={},s=[];function i(o){var a=r[o]={onStack:!0,lowlink:t,index:t++};if(n.push(o),e.successors(o).forEach(function(u){Object.hasOwn(r,u)?r[u].onStack&&(a.lowlink=Math.min(a.lowlink,r[u].index)):(i(u),a.lowlink=Math.min(a.lowlink,r[u].lowlink))}),a.lowlink===a.index){var l=[],c;do c=n.pop(),r[c].onStack=!1,l.push(c);while(o!==c);s.push(l)}}return e.nodes().forEach(function(o){Object.hasOwn(r,o)||i(o)}),s}var hp=Uo,mp=pp;function pp(e){return hp(e).filter(function(t){return t.length>1||t.length===1&&e.hasEdge(t[0],t[0])})}var gp=xp,yp=()=>1;function xp(e,t,n){return wp(e,t||yp,n||function(r){return e.outEdges(r)})}function wp(e,t,n){var r={},s=e.nodes();return s.forEach(function(i){r[i]={},r[i][i]={distance:0},s.forEach(function(o){i!==o&&(r[i][o]={distance:Number.POSITIVE_INFINITY})}),n(i).forEach(function(o){var a=o.v===i?o.w:o.v,l=t(o);r[i][a]={distance:l,predecessor:i}})}),s.forEach(function(i){var o=r[i];s.forEach(function(a){var l=r[a];s.forEach(function(c){var u=l[i],d=o[c],m=l[c],f=u.distance+d.distance;fe.successors(a):a=>e.neighbors(a),s=n==="post"?Ep:Np,i=[],o={};return t.forEach(a=>{if(!e.hasNode(a))throw new Error("Graph does not have node: "+a);s(a,r,o,i)}),i}function Ep(e,t,n,r){for(var s=[[e,!1]];s.length>0;){var i=s.pop();i[1]?r.push(i[0]):Object.hasOwn(n,i[0])||(n[i[0]]=!0,s.push([i[0],!0]),Zo(t(i[0]),o=>s.push([o,!1])))}}function Np(e,t,n,r){for(var s=[e];s.length>0;){var i=s.pop();Object.hasOwn(n,i)||(n[i]=!0,r.push(i),Zo(t(i),o=>s.push(o)))}}function Zo(e,t){for(var n=e.length;n--;)t(e[n],n,e);return e}var Sp=Ko,kp=jp;function jp(e,t){return Sp(e,t,"post")}var Cp=Ko,$p=Mp;function Mp(e,t){return Cp(e,t,"pre")}var Ap=Pr,Ip=Yo,Rp=Tp;function Tp(e,t){var n=new Ap,r={},s=new Ip,i;function o(l){var c=l.v===i?l.w:l.v,u=s.priority(c);if(u!==void 0){var d=t(l);d0;){if(i=s.removeMin(),Object.hasOwn(r,i))n.setEdge(i,r[i]);else{if(a)throw new Error("Input graph is not connected: "+e);a=!0}e.nodeEdges(i).forEach(o)}return n}var Op={components:np,dijkstra:Wo,dijkstraAll:up,findCycles:mp,floydWarshall:gp,isAcyclic:bp,postorder:kp,preorder:$p,prim:Rp,tarjan:Uo,topsort:qo},Hs=qm,Ie={Graph:Hs.Graph,json:Zm,alg:Op,version:Hs.version};let Dp=class{constructor(){let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,n=t._prev;if(n!==t)return Fs(n),n}enqueue(t){let n=this._sentinel;t._prev&&t._next&&Fs(t),t._next=n._next,n._next._prev=t,n._next=t,t._prev=n}toString(){let t=[],n=this._sentinel,r=n._prev;for(;r!==n;)t.push(JSON.stringify(r,Lp)),r=r._prev;return"["+t.join(", ")+"]"}};function Fs(e){e._prev._next=e._next,e._next._prev=e._prev,delete e._next,delete e._prev}function Lp(e,t){if(e!=="_next"&&e!=="_prev")return t}var Pp=Dp;let zp=Ie.Graph,Hp=Pp;var Fp=Bp;let Vp=()=>1;function Bp(e,t){if(e.nodeCount()<=1)return[];let n=Yp(e,t||Vp);return Gp(n.graph,n.buckets,n.zeroIdx).flatMap(s=>e.outEdges(s.v,s.w))}function Gp(e,t,n){let r=[],s=t[t.length-1],i=t[0],o;for(;e.nodeCount();){for(;o=i.dequeue();)Zn(e,t,n,o);for(;o=s.dequeue();)Zn(e,t,n,o);if(e.nodeCount()){for(let a=t.length-2;a>0;--a)if(o=t[a].dequeue(),o){r=r.concat(Zn(e,t,n,o,!0));break}}}return r}function Zn(e,t,n,r,s){let i=s?[]:void 0;return e.inEdges(r.v).forEach(o=>{let a=e.edge(o),l=e.node(o.v);s&&i.push({v:o.v,w:o.w}),l.out-=a,xr(t,n,l)}),e.outEdges(r.v).forEach(o=>{let a=e.edge(o),l=o.w,c=e.node(l);c.in-=a,xr(t,n,c)}),e.removeNode(r.v),i}function Yp(e,t){let n=new zp,r=0,s=0;e.nodes().forEach(a=>{n.setNode(a,{v:a,in:0,out:0})}),e.edges().forEach(a=>{let l=n.edge(a.v,a.w)||0,c=t(a),u=l+c;n.setEdge(a.v,a.w,u),s=Math.max(s,n.node(a.v).out+=c),r=Math.max(r,n.node(a.w).in+=c)});let i=Wp(s+r+3).map(()=>new Hp),o=r+1;return n.nodes().forEach(a=>{xr(i,o,n.node(a))}),{graph:n,buckets:i,zeroIdx:o}}function xr(e,t,n){n.out?n.in?e[n.out-n.in+t].enqueue(n):e[e.length-1].enqueue(n):e[0].enqueue(n)}function Wp(e){const t=[];for(let n=0;nt.setNode(n,e.node(n))),e.edges().forEach(n=>{let r=t.edge(n.v,n.w)||{weight:0,minlen:1},s=e.edge(n);t.setEdge(n.v,n.w,{weight:r.weight+s.weight,minlen:Math.max(r.minlen,s.minlen)})}),t}function Xp(e){let t=new Qo({multigraph:e.isMultigraph()}).setGraph(e.graph());return e.nodes().forEach(n=>{e.children(n).length||t.setNode(n,e.node(n))}),e.edges().forEach(n=>{t.setEdge(n,e.edge(n))}),t}function qp(e){let t=e.nodes().map(n=>{let r={};return e.outEdges(n).forEach(s=>{r[s.w]=(r[s.w]||0)+e.edge(s).weight}),r});return zr(e.nodes(),t)}function Kp(e){let t=e.nodes().map(n=>{let r={};return e.inEdges(n).forEach(s=>{r[s.v]=(r[s.v]||0)+e.edge(s).weight}),r});return zr(e.nodes(),t)}function Zp(e,t){let n=e.x,r=e.y,s=t.x-n,i=t.y-r,o=e.width/2,a=e.height/2;if(!s&&!i)throw new Error("Not possible to find intersection inside of the rectangle");let l,c;return Math.abs(i)*o>Math.abs(s)*a?(i<0&&(a=-a),l=a*s/i,c=a):(s<0&&(o=-o),l=o,c=o*i/s),{x:n+l,y:r+c}}function Qp(e){let t=ra(ta(e)+1).map(()=>[]);return e.nodes().forEach(n=>{let r=e.node(n),s=r.rank;s!==void 0&&(t[s][r.order]=n)}),t}function Jp(e){let t=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MAX_VALUE:s}),n=Tn(Math.min,t);e.nodes().forEach(r=>{let s=e.node(r);Object.hasOwn(s,"rank")&&(s.rank-=n)})}function eg(e){let t=e.nodes().map(o=>e.node(o).rank),n=Tn(Math.min,t),r=[];e.nodes().forEach(o=>{let a=e.node(o).rank-n;r[a]||(r[a]=[]),r[a].push(o)});let s=0,i=e.graph().nodeRankFactor;Array.from(r).forEach((o,a)=>{o===void 0&&a%i!==0?--s:o!==void 0&&s&&o.forEach(l=>e.node(l).rank+=s)})}function tg(e,t,n,r){let s={width:0,height:0};return arguments.length>=4&&(s.rank=n,s.order=r),Jo(e,"border",s,t)}function ng(e,t=ea){const n=[];for(let r=0;rea){const n=ng(t);return e.apply(null,n.map(r=>e.apply(null,r)))}else return e.apply(null,t)}function ta(e){const n=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MIN_VALUE:s});return Tn(Math.max,n)}function rg(e,t){let n={lhs:[],rhs:[]};return e.forEach(r=>{t(r)?n.lhs.push(r):n.rhs.push(r)}),n}function sg(e,t){let n=Date.now();try{return t()}finally{console.log(e+" time: "+(Date.now()-n)+"ms")}}function ig(e,t){return t()}let og=0;function na(e){var t=++og;return e+(""+t)}function ra(e,t,n=1){t==null&&(t=e,e=0);let r=i=>itr[t]),Object.entries(e).reduce((r,[s,i])=>(r[s]=n(i,s),r),{})}function zr(e,t){return e.reduce((n,r,s)=>(n[r]=t[s],n),{})}let cg=Fp,ug=he.uniqueId;var dg={run:fg,undo:mg};function fg(e){(e.graph().acyclicer==="greedy"?cg(e,n(e)):hg(e)).forEach(r=>{let s=e.edge(r);e.removeEdge(r),s.forwardName=r.name,s.reversed=!0,e.setEdge(r.w,r.v,s,ug("rev"))});function n(r){return s=>r.edge(s).weight}}function hg(e){let t=[],n={},r={};function s(i){Object.hasOwn(r,i)||(r[i]=!0,n[i]=!0,e.outEdges(i).forEach(o=>{Object.hasOwn(n,o.w)?t.push(o):s(o.w)}),delete n[i])}return e.nodes().forEach(s),t}function mg(e){e.edges().forEach(t=>{let n=e.edge(t);if(n.reversed){e.removeEdge(t);let r=n.forwardName;delete n.reversed,delete n.forwardName,e.setEdge(t.w,t.v,n,r)}})}let pg=he;var gg={run:yg,undo:wg};function yg(e){e.graph().dummyChains=[],e.edges().forEach(t=>xg(e,t))}function xg(e,t){let n=t.v,r=e.node(n).rank,s=t.w,i=e.node(s).rank,o=t.name,a=e.edge(t),l=a.labelRank;if(i===r+1)return;e.removeEdge(t);let c,u,d;for(d=0,++r;r{let n=e.node(t),r=n.edgeLabel,s;for(e.setEdge(n.edgeObj,r);n.dummy;)s=e.successors(t)[0],e.removeNode(t),r.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(r.x=n.x,r.y=n.y,r.width=n.width,r.height=n.height),t=s,n=e.node(t)})}const{applyWithChunking:bg}=he;var On={longestPath:vg,slack:_g};function vg(e){var t={};function n(r){var s=e.node(r);if(Object.hasOwn(t,r))return s.rank;t[r]=!0;let i=e.outEdges(r).map(a=>a==null?Number.POSITIVE_INFINITY:n(a.w)-e.edge(a).minlen);var o=bg(Math.min,i);return o===Number.POSITIVE_INFINITY&&(o=0),s.rank=o}e.sources().forEach(n)}function _g(e,t){return e.node(t.w).rank-e.node(t.v).rank-e.edge(t).minlen}var Eg=Ie.Graph,En=On.slack,sa=Ng;function Ng(e){var t=new Eg({directed:!1}),n=e.nodes()[0],r=e.nodeCount();t.setNode(n,{});for(var s,i;Sg(t,e){var i=s.v,o=r===i?s.w:i;!e.hasNode(o)&&!En(t,s)&&(e.setNode(o,{}),e.setEdge(r,o,{}),n(o))})}return e.nodes().forEach(n),e.nodeCount()}function kg(e,t){return t.edges().reduce((r,s)=>{let i=Number.POSITIVE_INFINITY;return e.hasNode(s.v)!==e.hasNode(s.w)&&(i=En(t,s)),it.node(r).rank+=n)}var Cg=sa,Vs=On.slack,$g=On.longestPath,Mg=Ie.alg.preorder,Ag=Ie.alg.postorder,Ig=he.simplify,Rg=dt;dt.initLowLimValues=Fr;dt.initCutValues=Hr;dt.calcCutValue=ia;dt.leaveEdge=aa;dt.enterEdge=la;dt.exchangeEdges=ca;function dt(e){e=Ig(e),$g(e);var t=Cg(e);Fr(t),Hr(t,e);for(var n,r;n=aa(t);)r=la(t,e,n),ca(t,e,n,r)}function Hr(e,t){var n=Ag(e,e.nodes());n=n.slice(0,n.length-1),n.forEach(r=>Tg(e,t,r))}function Tg(e,t,n){var r=e.node(n),s=r.parent;e.edge(n,s).cutvalue=ia(e,t,n)}function ia(e,t,n){var r=e.node(n),s=r.parent,i=!0,o=t.edge(n,s),a=0;return o||(i=!1,o=t.edge(s,n)),a=o.weight,t.nodeEdges(n).forEach(l=>{var c=l.v===n,u=c?l.w:l.v;if(u!==s){var d=c===i,m=t.edge(l).weight;if(a+=d?m:-m,Dg(e,n,u)){var f=e.edge(n,u).cutvalue;a+=d?-f:f}}}),a}function Fr(e,t){arguments.length<2&&(t=e.nodes()[0]),oa(e,{},1,t)}function oa(e,t,n,r,s){var i=n,o=e.node(r);return t[r]=!0,e.neighbors(r).forEach(a=>{Object.hasOwn(t,a)||(n=oa(e,t,n,a,r))}),o.low=i,o.lim=n++,s?o.parent=s:delete o.parent,n}function aa(e){return e.edges().find(t=>e.edge(t).cutvalue<0)}function la(e,t,n){var r=n.v,s=n.w;t.hasEdge(r,s)||(r=n.w,s=n.v);var i=e.node(r),o=e.node(s),a=i,l=!1;i.lim>o.lim&&(a=o,l=!0);var c=t.edges().filter(u=>l===Bs(e,e.node(u.v),a)&&l!==Bs(e,e.node(u.w),a));return c.reduce((u,d)=>Vs(t,d)!t.node(s).parent),r=Mg(e,n);r=r.slice(1),r.forEach(s=>{var i=e.node(s).parent,o=t.edge(s,i),a=!1;o||(o=t.edge(i,s),a=!0),t.node(s).rank=t.node(i).rank+(a?o.minlen:-o.minlen)})}function Dg(e,t,n){return e.hasEdge(t,n)}function Bs(e,t,n){return n.low<=t.lim&&t.lim<=n.lim}var Lg=On,ua=Lg.longestPath,Pg=sa,zg=Rg,Hg=Fg;function Fg(e){var t=e.graph().ranker;if(t instanceof Function)return t(e);switch(e.graph().ranker){case"network-simplex":Gs(e);break;case"tight-tree":Bg(e);break;case"longest-path":Vg(e);break;case"none":break;default:Gs(e)}}var Vg=ua;function Bg(e){ua(e),Pg(e)}function Gs(e){zg(e)}var Gg=Yg;function Yg(e){let t=Ug(e);e.graph().dummyChains.forEach(n=>{let r=e.node(n),s=r.edgeObj,i=Wg(e,t,s.v,s.w),o=i.path,a=i.lca,l=0,c=o[l],u=!0;for(;n!==s.w;){if(r=e.node(n),u){for(;(c=o[l])!==a&&e.node(c).maxRanko||a>t[l].lim));for(c=l,l=r;(l=e.parent(l))!==c;)i.push(l);return{path:s.concat(i.reverse()),lca:c}}function Ug(e){let t={},n=0;function r(s){let i=n;e.children(s).forEach(r),t[s]={low:i,lim:n++}}return e.children().forEach(r),t}let Nn=he;var Xg={run:qg,cleanup:Qg};function qg(e){let t=Nn.addDummyNode(e,"root",{},"_root"),n=Kg(e),r=Object.values(n),s=Nn.applyWithChunking(Math.max,r)-1,i=2*s+1;e.graph().nestingRoot=t,e.edges().forEach(a=>e.edge(a).minlen*=i);let o=Zg(e)+1;e.children().forEach(a=>da(e,t,i,o,s,n,a)),e.graph().nodeRankFactor=i}function da(e,t,n,r,s,i,o){let a=e.children(o);if(!a.length){o!==t&&e.setEdge(t,o,{weight:0,minlen:n});return}let l=Nn.addBorderNode(e,"_bt"),c=Nn.addBorderNode(e,"_bb"),u=e.node(o);e.setParent(l,o),u.borderTop=l,e.setParent(c,o),u.borderBottom=c,a.forEach(d=>{da(e,t,n,r,s,i,d);let m=e.node(d),f=m.borderTop?m.borderTop:d,x=m.borderBottom?m.borderBottom:d,y=m.borderTop?r:2*r,v=f!==x?1:s-i[o]+1;e.setEdge(l,f,{weight:y,minlen:v,nestingEdge:!0}),e.setEdge(x,c,{weight:y,minlen:v,nestingEdge:!0})}),e.parent(o)||e.setEdge(t,l,{weight:0,minlen:s+i[o]})}function Kg(e){var t={};function n(r,s){var i=e.children(r);i&&i.length&&i.forEach(o=>n(o,s+1)),t[r]=s}return e.children().forEach(r=>n(r,1)),t}function Zg(e){return e.edges().reduce((t,n)=>t+e.edge(n).weight,0)}function Qg(e){var t=e.graph();e.removeNode(t.nestingRoot),delete t.nestingRoot,e.edges().forEach(n=>{var r=e.edge(n);r.nestingEdge&&e.removeEdge(n)})}let Jg=he;var e0=t0;function t0(e){function t(n){let r=e.children(n),s=e.node(n);if(r.length&&r.forEach(t),Object.hasOwn(s,"minRank")){s.borderLeft=[],s.borderRight=[];for(let i=s.minRank,o=s.maxRank+1;iWs(e.node(t))),e.edges().forEach(t=>Ws(e.edge(t)))}function Ws(e){let t=e.width;e.width=e.height,e.height=t}function i0(e){e.nodes().forEach(t=>Qn(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(Qn),Object.hasOwn(n,"y")&&Qn(n)})}function Qn(e){e.y=-e.y}function o0(e){e.nodes().forEach(t=>Jn(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(Jn),Object.hasOwn(n,"x")&&Jn(n)})}function Jn(e){let t=e.x;e.x=e.y,e.y=t}let Us=he;var a0=l0;function l0(e){let t={},n=e.nodes().filter(l=>!e.children(l).length),r=n.map(l=>e.node(l).rank),s=Us.applyWithChunking(Math.max,r),i=Us.range(s+1).map(()=>[]);function o(l){if(t[l])return;t[l]=!0;let c=e.node(l);i[c.rank].push(l),e.successors(l).forEach(o)}return n.sort((l,c)=>e.node(l).rank-e.node(c).rank).forEach(o),i}let c0=he.zipObject;var u0=d0;function d0(e,t){let n=0;for(let r=1;ru)),s=t.flatMap(c=>e.outEdges(c).map(u=>({pos:r[u.w],weight:e.edge(u).weight})).sort((u,d)=>u.pos-d.pos)),i=1;for(;i{let u=c.pos+i;a[u]+=c.weight;let d=0;for(;u>0;)u%2&&(d+=a[u+1]),u=u-1>>1,a[u]+=c.weight;l+=c.weight*d}),l}var h0=m0;function m0(e,t=[]){return t.map(n=>{let r=e.inEdges(n);if(r.length){let s=r.reduce((i,o)=>{let a=e.edge(o),l=e.node(o.v);return{sum:i.sum+a.weight*l.order,weight:i.weight+a.weight}},{sum:0,weight:0});return{v:n,barycenter:s.sum/s.weight,weight:s.weight}}else return{v:n}})}let p0=he;var g0=y0;function y0(e,t){let n={};e.forEach((s,i)=>{let o=n[s.v]={indegree:0,in:[],out:[],vs:[s.v],i};s.barycenter!==void 0&&(o.barycenter=s.barycenter,o.weight=s.weight)}),t.edges().forEach(s=>{let i=n[s.v],o=n[s.w];i!==void 0&&o!==void 0&&(o.indegree++,i.out.push(n[s.w]))});let r=Object.values(n).filter(s=>!s.indegree);return x0(r)}function x0(e){let t=[];function n(s){return i=>{i.merged||(i.barycenter===void 0||s.barycenter===void 0||i.barycenter>=s.barycenter)&&w0(s,i)}}function r(s){return i=>{i.in.push(s),--i.indegree===0&&e.push(i)}}for(;e.length;){let s=e.pop();t.push(s),s.in.reverse().forEach(n(s)),s.out.forEach(r(s))}return t.filter(s=>!s.merged).map(s=>p0.pick(s,["vs","i","barycenter","weight"]))}function w0(e,t){let n=0,r=0;e.weight&&(n+=e.barycenter*e.weight,r+=e.weight),t.weight&&(n+=t.barycenter*t.weight,r+=t.weight),e.vs=t.vs.concat(e.vs),e.barycenter=n/r,e.weight=r,e.i=Math.min(t.i,e.i),t.merged=!0}let b0=he;var v0=_0;function _0(e,t){let n=b0.partition(e,u=>Object.hasOwn(u,"barycenter")),r=n.lhs,s=n.rhs.sort((u,d)=>d.i-u.i),i=[],o=0,a=0,l=0;r.sort(E0(!!t)),l=Xs(i,s,l),r.forEach(u=>{l+=u.vs.length,i.push(u.vs),o+=u.barycenter*u.weight,a+=u.weight,l=Xs(i,s,l)});let c={vs:i.flat(!0)};return a&&(c.barycenter=o/a,c.weight=a),c}function Xs(e,t,n){let r;for(;t.length&&(r=t[t.length-1]).i<=n;)t.pop(),e.push(r.vs),n++;return n}function E0(e){return(t,n)=>t.barycentern.barycenter?1:e?n.i-t.i:t.i-n.i}let N0=h0,S0=g0,k0=v0;var j0=ha;function ha(e,t,n,r){let s=e.children(t),i=e.node(t),o=i?i.borderLeft:void 0,a=i?i.borderRight:void 0,l={};o&&(s=s.filter(m=>m!==o&&m!==a));let c=N0(e,s);c.forEach(m=>{if(e.children(m.v).length){let f=ha(e,m.v,n,r);l[m.v]=f,Object.hasOwn(f,"barycenter")&&$0(m,f)}});let u=S0(c,n);C0(u,l);let d=k0(u,r);if(o&&(d.vs=[o,d.vs,a].flat(!0),e.predecessors(o).length)){let m=e.node(e.predecessors(o)[0]),f=e.node(e.predecessors(a)[0]);Object.hasOwn(d,"barycenter")||(d.barycenter=0,d.weight=0),d.barycenter=(d.barycenter*d.weight+m.order+f.order)/(d.weight+2),d.weight+=2}return d}function C0(e,t){e.forEach(n=>{n.vs=n.vs.flatMap(r=>t[r]?t[r].vs:r)})}function $0(e,t){e.barycenter!==void 0?(e.barycenter=(e.barycenter*e.weight+t.barycenter*t.weight)/(e.weight+t.weight),e.weight+=t.weight):(e.barycenter=t.barycenter,e.weight=t.weight)}let M0=Ie.Graph,A0=he;var I0=R0;function R0(e,t,n,r){r||(r=e.nodes());let s=T0(e),i=new M0({compound:!0}).setGraph({root:s}).setDefaultNodeLabel(o=>e.node(o));return r.forEach(o=>{let a=e.node(o),l=e.parent(o);(a.rank===t||a.minRank<=t&&t<=a.maxRank)&&(i.setNode(o),i.setParent(o,l||s),e[n](o).forEach(c=>{let u=c.v===o?c.w:c.v,d=i.edge(u,o),m=d!==void 0?d.weight:0;i.setEdge(u,o,{weight:e.edge(c).weight+m})}),Object.hasOwn(a,"minRank")&&i.setNode(o,{borderLeft:a.borderLeft[t],borderRight:a.borderRight[t]}))}),i}function T0(e){for(var t;e.hasNode(t=A0.uniqueId("_root")););return t}var O0=D0;function D0(e,t,n){let r={},s;n.forEach(i=>{let o=e.parent(i),a,l;for(;o;){if(a=e.parent(o),a?(l=r[a],r[a]=o):(l=s,s=o),l&&l!==o){t.setEdge(l,o);return}o=a}})}let L0=a0,P0=u0,z0=j0,H0=I0,F0=O0,V0=Ie.Graph,ln=he;var B0=ma;function ma(e,t){if(t&&typeof t.customOrder=="function"){t.customOrder(e,ma);return}let n=ln.maxRank(e),r=qs(e,ln.range(1,n+1),"inEdges"),s=qs(e,ln.range(n-1,-1,-1),"outEdges"),i=L0(e);if(Ks(e,i),t&&t.disableOptimalOrderHeuristic)return;let o=Number.POSITIVE_INFINITY,a;for(let l=0,c=0;c<4;++l,++c){G0(l%2?r:s,l%4>=2),i=ln.buildLayerMatrix(e);let u=P0(e,i);u{r.has(i)||r.set(i,[]),r.get(i).push(o)};for(const i of e.nodes()){const o=e.node(i);if(typeof o.rank=="number"&&s(o.rank,i),typeof o.minRank=="number"&&typeof o.maxRank=="number")for(let a=o.minRank;a<=o.maxRank;a++)a!==o.rank&&s(a,i)}return t.map(function(i){return H0(e,i,n,r.get(i)||[])})}function G0(e,t){let n=new V0;e.forEach(function(r){let s=r.graph().root,i=z0(r,s,n,t);i.vs.forEach((o,a)=>r.node(o).order=a),F0(r,n,i.vs)})}function Ks(e,t){Object.values(t).forEach(n=>n.forEach((r,s)=>e.node(r).order=s))}let Y0=Ie.Graph,ze=he;var W0={positionX:ry};function U0(e,t){let n={};function r(s,i){let o=0,a=0,l=s.length,c=i[i.length-1];return i.forEach((u,d)=>{let m=q0(e,u),f=m?e.node(m).order:l;(m||u===c)&&(i.slice(a,d+1).forEach(x=>{e.predecessors(x).forEach(y=>{let v=e.node(y),p=v.order;(p{u=i[d],e.node(u).dummy&&e.predecessors(u).forEach(m=>{let f=e.node(m);f.dummy&&(f.orderc)&&pa(n,m,u)})})}function s(i,o){let a=-1,l,c=0;return o.forEach((u,d)=>{if(e.node(u).dummy==="border"){let m=e.predecessors(u);m.length&&(l=e.node(m[0]).order,r(o,c,d,a,l),c=d,a=l)}r(o,c,o.length,l,i.length)}),o}return t.length&&t.reduce(s),n}function q0(e,t){if(e.node(t).dummy)return e.predecessors(t).find(n=>e.node(n).dummy)}function pa(e,t,n){if(t>n){let s=t;t=n,n=s}let r=e[t];r||(e[t]=r={}),r[n]=!0}function K0(e,t,n){if(t>n){let r=t;t=n,n=r}return!!e[t]&&Object.hasOwn(e[t],n)}function Z0(e,t,n,r){let s={},i={},o={};return t.forEach(a=>{a.forEach((l,c)=>{s[l]=l,i[l]=l,o[l]=c})}),t.forEach(a=>{let l=-1;a.forEach(c=>{let u=r(c);if(u.length){u=u.sort((m,f)=>o[m]-o[f]);let d=(u.length-1)/2;for(let m=Math.floor(d),f=Math.ceil(d);m<=f;++m){let x=u[m];i[c]===c&&lMath.max(m,i[f.v]+o.edge(f)),0)}function u(d){let m=o.outEdges(d).reduce((x,y)=>Math.min(x,i[y.w]-o.edge(y)),Number.POSITIVE_INFINITY),f=e.node(d);m!==Number.POSITIVE_INFINITY&&f.borderType!==a&&(i[d]=Math.max(i[d],m))}return l(c,o.predecessors.bind(o)),l(u,o.successors.bind(o)),Object.keys(r).forEach(d=>i[d]=i[n[d]]),i}function J0(e,t,n,r){let s=new Y0,i=e.graph(),o=sy(i.nodesep,i.edgesep,r);return t.forEach(a=>{let l;a.forEach(c=>{let u=n[c];if(s.setNode(u),l){var d=n[l],m=s.edge(d,u);s.setEdge(d,u,Math.max(o(e,c,l),m||0))}l=c})}),s}function ey(e,t){return Object.values(t).reduce((n,r)=>{let s=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;Object.entries(r).forEach(([a,l])=>{let c=iy(e,a)/2;s=Math.max(l+c,s),i=Math.min(l-c,i)});const o=s-i;return o{["l","r"].forEach(o=>{let a=i+o,l=e[a];if(l===t)return;let c=Object.values(l),u=r-ze.applyWithChunking(Math.min,c);o!=="l"&&(u=s-ze.applyWithChunking(Math.max,c)),u&&(e[a]=ze.mapValues(l,d=>d+u))})})}function ny(e,t){return ze.mapValues(e.ul,(n,r)=>{if(t)return e[t.toLowerCase()][r];{let s=Object.values(e).map(i=>i[r]).sort((i,o)=>i-o);return(s[1]+s[2])/2}})}function ry(e){let t=ze.buildLayerMatrix(e),n=Object.assign(U0(e,t),X0(e,t)),r={},s;["u","d"].forEach(o=>{s=o==="u"?t:Object.values(t).reverse(),["l","r"].forEach(a=>{a==="r"&&(s=s.map(d=>Object.values(d).reverse()));let l=(o==="u"?e.predecessors:e.successors).bind(e),c=Z0(e,s,n,l),u=Q0(e,s,c.root,c.align,a==="r");a==="r"&&(u=ze.mapValues(u,d=>-d)),r[o+a]=u})});let i=ey(e,r);return ty(r,i),ny(r,e.graph().align)}function sy(e,t,n){return(r,s,i)=>{let o=r.node(s),a=r.node(i),l=0,c;if(l+=o.width/2,Object.hasOwn(o,"labelpos"))switch(o.labelpos.toLowerCase()){case"l":c=-o.width/2;break;case"r":c=o.width/2;break}if(c&&(l+=n?c:-c),c=0,l+=(o.dummy?t:e)/2,l+=(a.dummy?t:e)/2,l+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":c=a.width/2;break;case"r":c=-a.width/2;break}return c&&(l+=n?c:-c),c=0,l}}function iy(e,t){return e.node(t).width}let ga=he,oy=W0.positionX;var ay=ly;function ly(e){e=ga.asNonCompoundGraph(e),cy(e),Object.entries(oy(e)).forEach(([t,n])=>e.node(t).x=n)}function cy(e){let t=ga.buildLayerMatrix(e),n=e.graph().ranksep,r=0;t.forEach(s=>{const i=s.reduce((o,a)=>{const l=e.node(a).height;return o>l?o:l},0);s.forEach(o=>e.node(o).y=r+i/2),r+=i+n})}let Zs=dg,Qs=gg,uy=Hg,dy=he.normalizeRanks,fy=Gg,hy=he.removeEmptyRanks,Js=Xg,my=e0,ei=n0,py=B0,gy=ay,je=he,yy=Ie.Graph;var xy=wy;function wy(e,t){let n=t&&t.debugTiming?je.time:je.notime;n("layout",()=>{let r=n(" buildLayoutGraph",()=>$y(e));n(" runLayout",()=>by(r,n,t)),n(" updateInputGraph",()=>vy(e,r))})}function by(e,t,n){t(" makeSpaceForEdgeLabels",()=>My(e)),t(" removeSelfEdges",()=>zy(e)),t(" acyclic",()=>Zs.run(e)),t(" nestingGraph.run",()=>Js.run(e)),t(" rank",()=>uy(je.asNonCompoundGraph(e))),t(" injectEdgeLabelProxies",()=>Ay(e)),t(" removeEmptyRanks",()=>hy(e)),t(" nestingGraph.cleanup",()=>Js.cleanup(e)),t(" normalizeRanks",()=>dy(e)),t(" assignRankMinMax",()=>Iy(e)),t(" removeEdgeLabelProxies",()=>Ry(e)),t(" normalize.run",()=>Qs.run(e)),t(" parentDummyChains",()=>fy(e)),t(" addBorderSegments",()=>my(e)),t(" order",()=>py(e,n)),t(" insertSelfEdges",()=>Hy(e)),t(" adjustCoordinateSystem",()=>ei.adjust(e)),t(" position",()=>gy(e)),t(" positionSelfEdges",()=>Fy(e)),t(" removeBorderNodes",()=>Py(e)),t(" normalize.undo",()=>Qs.undo(e)),t(" fixupEdgeLabelCoords",()=>Dy(e)),t(" undoCoordinateSystem",()=>ei.undo(e)),t(" translateGraph",()=>Ty(e)),t(" assignNodeIntersects",()=>Oy(e)),t(" reversePoints",()=>Ly(e)),t(" acyclic.undo",()=>Zs.undo(e))}function vy(e,t){e.nodes().forEach(n=>{let r=e.node(n),s=t.node(n);r&&(r.x=s.x,r.y=s.y,r.rank=s.rank,t.children(n).length&&(r.width=s.width,r.height=s.height))}),e.edges().forEach(n=>{let r=e.edge(n),s=t.edge(n);r.points=s.points,Object.hasOwn(s,"x")&&(r.x=s.x,r.y=s.y)}),e.graph().width=t.graph().width,e.graph().height=t.graph().height}let _y=["nodesep","edgesep","ranksep","marginx","marginy"],Ey={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},Ny=["acyclicer","ranker","rankdir","align"],Sy=["width","height","rank"],ti={width:0,height:0},ky=["minlen","weight","width","height","labeloffset"],jy={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},Cy=["labelpos"];function $y(e){let t=new yy({multigraph:!0,compound:!0}),n=tr(e.graph());return t.setGraph(Object.assign({},Ey,er(n,_y),je.pick(n,Ny))),e.nodes().forEach(r=>{let s=tr(e.node(r));const i=er(s,Sy);Object.keys(ti).forEach(o=>{i[o]===void 0&&(i[o]=ti[o])}),t.setNode(r,i),t.setParent(r,e.parent(r))}),e.edges().forEach(r=>{let s=tr(e.edge(r));t.setEdge(r,Object.assign({},jy,er(s,ky),je.pick(s,Cy)))}),t}function My(e){let t=e.graph();t.ranksep/=2,e.edges().forEach(n=>{let r=e.edge(n);r.minlen*=2,r.labelpos.toLowerCase()!=="c"&&(t.rankdir==="TB"||t.rankdir==="BT"?r.width+=r.labeloffset:r.height+=r.labeloffset)})}function Ay(e){e.edges().forEach(t=>{let n=e.edge(t);if(n.width&&n.height){let r=e.node(t.v),i={rank:(e.node(t.w).rank-r.rank)/2+r.rank,e:t};je.addDummyNode(e,"edge-proxy",i,"_ep")}})}function Iy(e){let t=0;e.nodes().forEach(n=>{let r=e.node(n);r.borderTop&&(r.minRank=e.node(r.borderTop).rank,r.maxRank=e.node(r.borderBottom).rank,t=Math.max(t,r.maxRank))}),e.graph().maxRank=t}function Ry(e){e.nodes().forEach(t=>{let n=e.node(t);n.dummy==="edge-proxy"&&(e.edge(n.e).labelRank=n.rank,e.removeNode(t))})}function Ty(e){let t=Number.POSITIVE_INFINITY,n=0,r=Number.POSITIVE_INFINITY,s=0,i=e.graph(),o=i.marginx||0,a=i.marginy||0;function l(c){let u=c.x,d=c.y,m=c.width,f=c.height;t=Math.min(t,u-m/2),n=Math.max(n,u+m/2),r=Math.min(r,d-f/2),s=Math.max(s,d+f/2)}e.nodes().forEach(c=>l(e.node(c))),e.edges().forEach(c=>{let u=e.edge(c);Object.hasOwn(u,"x")&&l(u)}),t-=o,r-=a,e.nodes().forEach(c=>{let u=e.node(c);u.x-=t,u.y-=r}),e.edges().forEach(c=>{let u=e.edge(c);u.points.forEach(d=>{d.x-=t,d.y-=r}),Object.hasOwn(u,"x")&&(u.x-=t),Object.hasOwn(u,"y")&&(u.y-=r)}),i.width=n-t+o,i.height=s-r+a}function Oy(e){e.edges().forEach(t=>{let n=e.edge(t),r=e.node(t.v),s=e.node(t.w),i,o;n.points?(i=n.points[0],o=n.points[n.points.length-1]):(n.points=[],i=s,o=r),n.points.unshift(je.intersectRect(r,i)),n.points.push(je.intersectRect(s,o))})}function Dy(e){e.edges().forEach(t=>{let n=e.edge(t);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function Ly(e){e.edges().forEach(t=>{let n=e.edge(t);n.reversed&&n.points.reverse()})}function Py(e){e.nodes().forEach(t=>{if(e.children(t).length){let n=e.node(t),r=e.node(n.borderTop),s=e.node(n.borderBottom),i=e.node(n.borderLeft[n.borderLeft.length-1]),o=e.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(o.x-i.x),n.height=Math.abs(s.y-r.y),n.x=i.x+n.width/2,n.y=r.y+n.height/2}}),e.nodes().forEach(t=>{e.node(t).dummy==="border"&&e.removeNode(t)})}function zy(e){e.edges().forEach(t=>{if(t.v===t.w){var n=e.node(t.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e:t,label:e.edge(t)}),e.removeEdge(t)}})}function Hy(e){var t=je.buildLayerMatrix(e);t.forEach(n=>{var r=0;n.forEach((s,i)=>{var o=e.node(s);o.order=i+r,(o.selfEdges||[]).forEach(a=>{je.addDummyNode(e,"selfedge",{width:a.label.width,height:a.label.height,rank:o.rank,order:i+ ++r,e:a.e,label:a.label},"_se")}),delete o.selfEdges})})}function Fy(e){e.nodes().forEach(t=>{var n=e.node(t);if(n.dummy==="selfedge"){var r=e.node(n.e.v),s=r.x+r.width/2,i=r.y,o=n.x-s,a=r.height/2;e.setEdge(n.e,n.label),e.removeNode(t),n.label.points=[{x:s+2*o/3,y:i-a},{x:s+5*o/6,y:i-a},{x:s+o,y:i},{x:s+5*o/6,y:i+a},{x:s+2*o/3,y:i+a}],n.label.x=n.x,n.label.y=n.y}})}function er(e,t){return je.mapValues(je.pick(e,t),Number)}function tr(e){var t={};return e&&Object.entries(e).forEach(([n,r])=>{typeof n=="string"&&(n=n.toLowerCase()),t[n]=r}),t}let Vy=he,By=Ie.Graph;var Gy={debugOrdering:Yy};function Yy(e){let t=Vy.buildLayerMatrix(e),n=new By({compound:!0,multigraph:!0}).setGraph({});return e.nodes().forEach(r=>{n.setNode(r,{label:r}),n.setParent(r,"layer"+e.node(r).rank)}),e.edges().forEach(r=>n.setEdge(r.v,r.w,{},r.name)),t.forEach((r,s)=>{let i="layer"+s;n.setNode(i,{rank:"same"}),r.reduce((o,a)=>(n.setEdge(o,a,{style:"invis"}),a))}),n}var Wy="1.1.8",Uy={graphlib:Ie,layout:xy,debug:Gy,util:{time:he.time,notime:he.notime},version:Wy};const ni=fi(Uy),_t=264,Xy=44,qy=26,Ky=14;function Sn(e){const t=Math.max(e.attributes.length,1);return Xy+t*qy+Ky}const Zy=40;function ri(e,t,n,r="auto",s,i="LR"){const o=Qy(n,s);return r==="grid"?ai(e,n,o):r==="clustered"?oi(e,t,n,o,i):r==="hierarchical"||e.length<=Zy?si(e,t,n,i):new Set(e.map(l=>o(l.id)).filter(Boolean)).size>1?oi(e,t,n,o,i):ai(e,n,o)}function Qy(e,t){return n=>{var r,s,i;if(t){const o=t.get(n);if(o)return o.name}return((i=(s=(r=e.get(n))==null?void 0:r.conceptual)==null?void 0:s.subject_area)==null?void 0:i.name)??""}}function ya(e,t,n,r=!1,s="LR"){const i=new ni.graphlib.Graph;i.setGraph({rankdir:s,nodesep:r?45:70,ranksep:r?110:150,edgesep:r?20:30,marginx:24,marginy:24,ranker:"network-simplex"}),i.setDefaultEdgeLabel(()=>({}));const o=new Set(e.map(u=>u.id));for(const u of e){const d=n.get(u.id);i.setNode(u.id,{width:_t,height:d?Sn(d):120})}for(const u of t)o.has(u.source)&&o.has(u.target)&&i.setEdge(u.source,u.target,{minlen:1,weight:2});ni.layout(i);let a=0,l=0;return{nodes:e.map(u=>{const d=i.node(u.id),m=n.get(u.id),f=m?Sn(m):120,x=d.x-_t/2,y=d.y-f/2;return a=Math.max(a,x+_t),l=Math.max(l,y+f),{...u,position:{x,y}}}),width:a,height:l}}function si(e,t,n,r="LR"){return ya(e,t,n,!1,r).nodes}function Jy(e,t){const n=a=>{const l=t.get(a);return l?Sn(l):120},r=Math.max(1,Math.round(Math.sqrt(e.length))),s=[];let i=0,o=0;for(let a=0;a{const d=u*(_t+xa);s.push({...c,position:{x:d,y:i}}),o=Math.max(o,d+_t)}),i+=Math.max(...l.map(c=>n(c.id)))+wa}return{nodes:s,width:o,height:i}}const ii=120;function oi(e,t,n,r,s="LR"){const i=new Map;for(const m of e){const f=r(m.id);(i.get(f)??i.set(f,[]).get(f)).push(m)}const a=[...i.keys()].sort((m,f)=>m===""?1:f===""?-1:m.localeCompare(f)).map(m=>{const f=i.get(m),x=new Set(f.map(E=>E.id));let y=0;for(const E of t)x.has(E.source)&&x.has(E.target)&&y++;const p=f.length<=8&&y>=f.length-1?ya(f,t,n,!0,s):Jy(f,n);return{name:m,...p}}),l=Math.max(1,Math.ceil(Math.sqrt(a.length*1.6))),c=[];let u=0;for(let m=0;m[m.id,m.position]));return e.map(m=>({...m,position:d.get(m.id)??m.position}))}const xa=40,wa=40;function ai(e,t,n){const r=c=>{const u=t.get(c);return u?Sn(u):120},s=[...e].sort((c,u)=>{var y,v;const d=n(c.id),m=n(u.id);if(d!==m)return d.localeCompare(m);const f=((y=t.get(c.id))==null?void 0:y.name)??c.id,x=((v=t.get(u.id))==null?void 0:v.name)??u.id;return f.localeCompare(x)}),i=Math.max(1,Math.round(Math.sqrt(s.length))),o=Array.from({length:i},(c,u)=>u*(_t+xa)),a=new Map;let l=0;for(let c=0;ca.set(m.id,{x:o[f],y:l}));const d=Math.max(...u.map(m=>r(m.id)));l+=d+wa}return e.map(c=>({...c,position:a.get(c.id)??c.position}))}function Zt({title:e,onClose:t,children:n,wide:r}){return h.jsx("div",{className:"modal-backdrop",onClick:t,children:h.jsxs("div",{className:"modal"+(r?" wide":""),onClick:s=>s.stopPropagation(),children:[h.jsxs("div",{className:"modal-header",children:[h.jsx("h2",{children:e}),h.jsx("button",{className:"icon-btn",onClick:t,children:"✕"})]}),n]})})}const ex=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch","skos:relatedMatch"],ba=["industry","core","domain","specialised"];function tx({entity:e,exec:t,onClose:n}){var b,A;const[r,s]=w.useState(((b=e.conceptual)==null?void 0:b.name)??e.name),[i,o]=w.useState([]),[a,l]=w.useState(null),[c,u]=w.useState(null),[d,m]=w.useState("skos:exactMatch"),[f,x]=w.useState(((A=e.conceptual)==null?void 0:A.ontology_layer)??"core"),[y,v]=w.useState([]),[p,E]=w.useState("");w.useEffect(()=>{Go().then(j=>v(j.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const j=setTimeout(()=>{if(!r.trim())return o([]);Bo(r,p||void 0).then(_=>{o(_.results),l(_.loaded_terms)}).catch(()=>o([]))},200);return()=>clearTimeout(j)},[r,p]);const $=()=>{c&&t("set_alignment",{id:e.id,aligns_to:c.prefixed,alignment:d,layer:f,resolved_via:c.source}).then(n)};return h.jsxs(Zt,{title:`Align '${e.name}' to ontology`,onClose:n,wide:!0,children:[y.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(p===""?" active":""),onClick:()=>E(""),children:"all sources"}),y.map(j=>h.jsx("button",{className:"src-chip"+(p===j.id?" active":""),title:j.count!=null?`${j.count} terms`:"live resolver",onClick:()=>E(j.id),children:j.name},j.id))]}),h.jsx("input",{autoFocus:!0,className:"edit-input modal-search",placeholder:p?`Search within ${p}…`:"Search vocabulary terms…",value:r,onChange:j=>s(j.target.value)}),a===0&&y.length===0&&h.jsx("p",{className:"empty-hint",children:"No vocabulary loaded. Declare one in mdl-project.yaml's ontology_stack (or run `mdl ontology vendor fibo`)."}),h.jsxs("div",{className:"term-results",children:[i.map(j=>h.jsxs("button",{className:"term-card"+((c==null?void 0:c.iri)===j.iri?" picked":""),onClick:()=>u(j),children:[h.jsxs("div",{className:"term-head",children:[h.jsx("span",{className:"term-label",children:j.label}),h.jsxs("span",{className:"term-source",children:[j.source_kind==="glossary-term"&&h.jsx("span",{className:"kind-badge",children:"glossary"}),j.source]})]}),h.jsx("code",{className:"term-iri",children:j.prefixed}),j.definition&&h.jsx("p",{className:"term-def",children:j.definition})]},j.iri)),r&&i.length===0&&a!==0&&h.jsxs("p",{className:"empty-hint",children:["no matches for “",r,"”"]})]}),h.jsxs("div",{className:"modal-footer",children:[h.jsxs("label",{children:["predicate",h.jsx("select",{value:d,onChange:j=>m(j.target.value),children:ex.map(j=>h.jsx("option",{children:j},j))})]}),h.jsxs("label",{children:["layer",h.jsx("select",{value:f,onChange:j=>x(j.target.value),children:ba.map(j=>h.jsx("option",{children:j},j))})]}),h.jsxs("button",{className:"primary-btn",disabled:!c,onClick:$,children:["Align",c?` to ${c.label}`:""]})]})]})}function nx({entity:e,doc:t,exec:n,onClose:r}){var b,A,j;const[s,i]=w.useState(((b=e.term_map)==null?void 0:b.subject_template)??""),[o,a]=w.useState(((A=e.term_map)==null?void 0:A.class_iri)??""),l=new Set(e.attributes.map(_=>_.name)),u=Array.from(s.matchAll(/\{([^}]+)\}/g)).map(_=>_[1]).filter(_=>!l.has(_)),d=_=>!_||_.startsWith("http://")||_.startsWith("https://")||/^[^\s:]+:[^\s:]+/.test(_),m=u.length===0&&d(o),f=()=>{n("set_term_map",{id:e.id,subject_template:s.trim()||null,class_iri:o.trim()||null}).then(r)},x=(t.project.name||"").replace(/[^0-9a-zA-Z]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase()||"modelith-model",y=t.project.kg_base_iri||`urn:${x}:`,v=(j=(e.key_groups??[]).find(_=>_.type==="pk"))==null?void 0:j.members.map(_=>{var I;return(I=e.attributes.find(S=>S.id===_))==null?void 0:I.name}).filter(Boolean),p=v&&v.length?`${y}${e.id}/${v.map(_=>`{${_}}`).join("_")}`:"(blank-node: entity has no primary key)",E=o.trim()||`${y}${e.id}`,$=s.trim()||p;return h.jsxs(Zt,{title:`Knowledge Graph mapping — ${e.name}`,onClose:r,wide:!0,children:[h.jsx("p",{className:"empty-hint",style:{marginBottom:14},children:"Override the R2RML term-map for this entity. Leave a field blank to use the default (project base IRI + primary key)."}),h.jsxs("div",{className:"form-row",children:[h.jsx("label",{htmlFor:"tm-class",children:"Class IRI"}),h.jsx("input",{id:"tm-class",className:"edit-input",value:o,placeholder:"https://acme.com/Customer or fibo-fnd:Party",onChange:_=>a(_.target.value)}),d(o)?h.jsxs("code",{className:"form-preview",children:["rr:class ",E]}):h.jsx("span",{className:"form-error",children:"not a valid IRI or prefix:local CURIE"})]}),h.jsxs("div",{className:"form-row",children:[h.jsx("label",{htmlFor:"tm-subject",children:"Subject template"}),h.jsx("input",{id:"tm-subject",className:"edit-input",value:s,placeholder:"https://acme.com/id/customer/{customer_id}",onChange:_=>i(_.target.value)}),u.length>0?h.jsxs("span",{className:"form-error",children:["unknown column",u.length>1?"s":"",": ",u.join(", ")]}):h.jsxs("code",{className:"form-preview",children:["rr:template ",$]}),h.jsxs("span",{className:"form-hint",children:["Wrap a column in ","{ }"," to fill it from each row, e.g."," ","{"+((v==null?void 0:v[0])??"id")+"}",". Columns:"," ",e.attributes.map(_=>_.name).join(", ")]})]}),h.jsxs("div",{className:"modal-footer",children:[h.jsx("button",{className:"mini-btn",onClick:r,children:"Cancel"}),h.jsx("button",{className:"primary-btn",disabled:!m,onClick:f,children:"Save mapping"})]})]})}function rx({doc:e,exec:t,onClose:n,onCreated:r}){const[s,i]=w.useState(""),[o,a]=w.useState(""),[l,c]=w.useState(""),[u,d]=w.useState(""),m=()=>{s.trim()&&t("create_entity",{name:s,definition:o||null,subject_area:l||null,layer:u||null}).then(f=>{const x=f==null?void 0:f.created_id;n(),x&&r(x)})};return h.jsxs(Zt,{title:"New entity",onClose:n,children:[h.jsxs("label",{className:"form-row",children:["name",h.jsx("input",{autoFocus:!0,className:"edit-input",placeholder:"e.g. custody_account",value:s,onChange:f=>i(f.target.value),onKeyDown:f=>f.key==="Enter"&&m()})]}),h.jsxs("label",{className:"form-row",children:["definition",h.jsx("textarea",{className:"edit-input",rows:2,placeholder:"business definition…",value:o,onChange:f=>a(f.target.value)})]}),h.jsxs("label",{className:"form-row",children:["subject area",h.jsxs("select",{value:l,onChange:f=>c(f.target.value),children:[h.jsx("option",{value:"",children:"none"}),e.subject_areas.map(f=>h.jsx("option",{value:f.id,children:f.name},f.id))]})]}),h.jsxs("label",{className:"form-row",children:["ontology layer",h.jsxs("select",{value:u,onChange:f=>d(f.target.value),children:[h.jsx("option",{value:"",children:"none"}),ba.map(f=>h.jsx("option",{children:f},f))]})]}),h.jsx("div",{className:"modal-footer",children:h.jsx("button",{className:"primary-btn",disabled:!s.trim(),onClick:m,children:"Create"})})]})}const va=["many_to_one","one_to_many","one_to_one","many_to_many"];function sx({doc:e,fromId:t,toId:n,initialFromAttr:r,initialToAttr:s,exec:i,onClose:o}){const a=e.entities.find(b=>b.id===t),l=e.entities.find(b=>b.id===n),c=w.useMemo(()=>l==null?void 0:l.attributes.find(b=>b.role==="business_key"),[l]),[u,d]=w.useState(r??""),[m,f]=w.useState(s??(c==null?void 0:c.id)??""),[x,y]=w.useState("many_to_one"),[v,p]=w.useState("mandatory");if(!a||!l)return null;const E=u==="__new__",$=()=>i("create_relationship",{from_entity:a.id,to_entity:l.id,from_attribute:E?null:u||null,to_attribute:m||null,create_from_fk:E,cardinality:x,optionality:v}).then(o);return h.jsxs(Zt,{title:`${a.name} → ${l.name}`,onClose:o,children:[h.jsxs("label",{className:"form-row",children:["cardinality (",a.name," : ",l.name,")",h.jsx("select",{value:x,onChange:b=>y(b.target.value),children:va.map(b=>h.jsx("option",{children:b},b))})]}),h.jsxs("label",{className:"form-row",children:["foreign key on ",a.name,h.jsxs("select",{value:u,onChange:b=>d(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),h.jsxs("option",{value:"__new__",children:["➕ create column",c?` "${c.name}"`:""]}),a.attributes.map(b=>h.jsx("option",{value:b.id,children:b.name},b.id))]})]}),h.jsxs("label",{className:"form-row",children:["references on ",l.name,h.jsxs("select",{value:m,onChange:b=>f(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),l.attributes.map(b=>h.jsxs("option",{value:b.id,children:[b.name,b.role==="business_key"?" 🔑":""]},b.id))]})]}),h.jsxs("label",{className:"form-row",children:["optionality",h.jsxs("select",{value:v,onChange:b=>p(b.target.value),children:[h.jsx("option",{children:"mandatory"}),h.jsx("option",{children:"optional"})]})]}),h.jsx("div",{className:"modal-footer",children:h.jsx("button",{className:"primary-btn",onClick:$,children:"Create relationship"})})]})}function ix({rel:e,doc:t,exec:n,onClose:r}){const s=p=>{var E;return((E=t.entities.find($=>$.id===p))==null?void 0:E.name)??"?"},[i,o]=w.useState(e.name),[a,l]=w.useState(e.cardinality),[c,u]=w.useState(e.optionality),[d,m]=w.useState(e.identifying),[f,x]=w.useState(!1),y=async()=>{i.trim()&&i.trim()!==e.name&&await n("rename_relationship",{id:e.id,name:i.trim()}),(a!==e.cardinality||c!==e.optionality||d!==e.identifying)&&await n("update_relationship",{id:e.id,cardinality:a,optionality:c,identifying:d}),r()},v=()=>n("delete_relationship",{id:e.id}).then(r);return h.jsxs(Zt,{title:`${s(e.from.entity)} → ${s(e.to.entity)}`,onClose:r,children:[h.jsxs("label",{className:"form-row",children:["relationship name",h.jsx("input",{value:i,onChange:p=>o(p.target.value),autoFocus:!0})]}),h.jsxs("label",{className:"form-row",children:["cardinality",h.jsx("select",{value:a,onChange:p=>l(p.target.value),children:va.map(p=>h.jsx("option",{children:p},p))})]}),h.jsxs("label",{className:"form-row",children:["optionality",h.jsxs("select",{value:c,onChange:p=>u(p.target.value),children:[h.jsx("option",{value:"mandatory",children:"mandatory"}),h.jsx("option",{value:"optional",children:"optional"})]})]}),h.jsxs("label",{className:"form-row",children:["identifying",h.jsxs("select",{value:d?"yes":"no",onChange:p=>m(p.target.value==="yes"),children:[h.jsx("option",{value:"no",children:"no — parent key is a plain reference"}),h.jsx("option",{value:"yes",children:"yes — parent key is part of the child's identity"})]})]}),h.jsxs("div",{className:"modal-footer",style:{justifyContent:"space-between"},children:[f?h.jsx("button",{className:"danger-btn",onClick:v,children:"Confirm delete"}):h.jsx("button",{className:"mini-btn danger",onClick:()=>x(!0),children:"Delete relationship"}),h.jsx("button",{className:"primary-btn",onClick:y,children:"Save"})]})]})}const ox=w.memo(function({id:t,sourceX:n,sourceY:r,targetX:s,targetY:i,sourcePosition:o,targetPosition:a,data:l,selected:c}){const[u]=vn({sourceX:n,sourceY:r,sourcePosition:o,targetX:s,targetY:i,targetPosition:a,borderRadius:10}),d=l==null?void 0:l.relationship,m=(l==null?void 0:l.dimmed)??!1,f=(d==null?void 0:d.cardinality)??"many_to_one",x=(d==null?void 0:d.optionality)==="optional",y=(d==null?void 0:d.identifying)??!1,v=f==="many_to_one"||f==="many_to_many",p=f==="one_to_many"||f==="many_to_many",E="rel-edge"+(c?" selected":"")+(m?" dimmed":""),$=((l==null?void 0:l.memberCount)??0)>1,b=l==null?void 0:l.onHoverEdge;return h.jsxs("g",{className:E,children:[h.jsx("path",{id:t,className:"rel-path",d:u,fill:"none",strokeDasharray:y?void 0:"6 4"}),h.jsx("path",{d:u,fill:"none",strokeWidth:14,stroke:"transparent",onMouseEnter:b?()=>b(d.id):void 0,onMouseLeave:b?()=>b(null):void 0}),h.jsx(li,{x:n,y:r,side:o,many:v,optional:x}),h.jsx(li,{x:s,y:i,side:a,many:p,optional:!1}),(c||$)&&d&&h.jsxs("text",{className:"rel-label",x:(n+s)/2,y:(r+i)/2-8,children:[c?d.verb_phrase||d.name:"",$?`${c?" ":""}(${l.memberCount})`:""]})]})});function li({x:e,y:t,side:n,many:r,optional:s}){const i=n===Z.Left?-1:1,o=7,a=12,l=[];return r?l.push(h.jsx("path",{className:"rel-glyph",d:`M ${e+i*a} ${t} L ${e} ${t-o} M ${e+i*a} ${t} L ${e} ${t} M ${e+i*a} ${t} L ${e} ${t+o}`,fill:"none"},"foot")):l.push(h.jsx("line",{className:"rel-glyph",x1:e+i*(a-3),y1:t-o,x2:e+i*(a-3),y2:t+o},"one")),s?l.push(h.jsx("circle",{className:"rel-glyph ring",cx:e+i*(a+6),cy:t,r:4},"opt")):l.push(h.jsx("line",{className:"rel-glyph",x1:e+i*(a+5),y1:t-o,x2:e+i*(a+5),y2:t+o},"mand")),h.jsx(h.Fragment,{children:l})}const ax={entity:Tm},lx={relationship:ox},ci=["#e0a63a","#e8825a","#d98ba8","#c4a3e0","#7fb0d8","#7fc9a0","#e6c14a"],ui="#8a7d6a";function cx(e,t){const n=(i,o)=>{var a;return!!o&&!!((a=t.get(i))!=null&&a.attributes.some(l=>l.id===o))},r=e.from.attributes[0],s=e.to.attributes[0];return{sourceHandle:n(e.from.entity,r)?r:Dr,targetHandle:n(e.to.entity,s)?s:Lr}}function Px({doc:e,caps:t,exec:n,selectedId:r,onSelect:s,query:i="",showTypes:o=!0,collapseDetail:a=!1,expandedEntities:l,onToggleExpand:c,activeAreas:u,onError:d,handleRef:m}){const{fitView:f,fitBounds:x,getNodes:y}=Rn(),[v,p]=w.useState([]),[E,$]=w.useState([]),b=w.useRef(!1),[A,j]=w.useState(null),[_,I]=w.useState(null),[S,D]=w.useState(!1),[P,V]=w.useState(null),[F,g]=w.useState(null),[C,N]=w.useState(null),[R,H]=w.useState(null),k=!t.canEdit,T=w.useMemo(()=>{const B=new Map;return e.subject_areas.forEach((q,W)=>B.set(q.id,ci[W%ci.length])),B},[e]),L=w.useMemo(()=>{const B=new Map;return e.entities.forEach(q=>B.set(q.id,q)),B},[e]),z=w.useMemo(()=>{const B=new Map,q=new Map;return e.subject_areas.forEach(W=>{(W.members??[]).forEach(Q=>q.set(Q,{id:W.id,name:W.name}))}),e.entities.forEach(W=>{var O,se;const Q=(O=W.conceptual)!=null&&O.id?q.get(W.conceptual.id):void 0,U=(se=W.conceptual)==null?void 0:se.subject_area,X=Q??(U?{id:U.id,name:U.name??U.id}:void 0);X&&B.set(W.id,X)}),B},[e]),Y=w.useCallback(B=>{var W;if(!u||u.size===0)return!1;const q=(W=z.get(B))==null?void 0:W.id;return!(q&&u.has(q))},[u,z]),G=w.useMemo(()=>{const B=new Map,q=(W,Q)=>{if(!Q.length)return;const U=B.get(W)??new Set;Q.forEach(X=>U.add(X)),B.set(W,U)};for(const W of e.relationships)q(W.from.entity,W.from.attributes),q(W.to.entity,W.to.attributes);return B},[e]);w.useEffect(()=>{var X;const B=i.trim().toLowerCase(),q=new Set;if(B)for(const O of e.entities)(O.name.toLowerCase()+" "+(((X=O.conceptual)==null?void 0:X.name.toLowerCase())??"")+" "+O.attributes.map(ce=>ce.name.toLowerCase()).join(" ")).includes(B)&&q.add(O.id);const W=new Set;if(r){W.add(r);for(const O of e.relationships)O.from.entity===r&&W.add(O.to.entity),O.to.entity===r&&W.add(O.from.entity)}const Q=e.entities.map(O=>{const se=B!==""&&!q.has(O.id),ce=r!==null&&!W.has(O.id);return{id:O.id,type:"entity",position:{x:0,y:0},hidden:Y(O.id),data:{entity:O,color:(()=>{const me=z.get(O.id);return me?T.get(me.id)??ui:ui})(),dimmed:se||ce,highlighted:B!==""&&q.has(O.id),showTypes:o,collapseDetail:a,expanded:(l==null?void 0:l.has(O.id))??!1,onToggleExpand:c,endpointAttrs:G.get(O.id),highlightAttrs:void 0,onHoverAttr:N}}}),U=e.relationships.filter(O=>L.has(O.from.entity)&&L.has(O.to.entity)).map(O=>{const{sourceHandle:se,targetHandle:ce}=cx(O,L),me=r!==null&&O.from.entity!==r&&O.to.entity!==r;return{id:O.id,source:O.from.entity,target:O.to.entity,sourceHandle:se,targetHandle:ce,type:"relationship",data:{relationship:O,dimmed:me,memberCount:Math.max(O.from.attributes.length,O.to.attributes.length),onHoverEdge:H}}});p(O=>{const se=new Map(O.map(oe=>[oe.id,oe.position])),ce=Q.every(oe=>se.has(oe.id)),me=((u==null?void 0:u.size)??0)>0;if(b.current&&ce&&O.length>=Q.length&&!me)return Q.map(oe=>({...oe,position:se.get(oe.id)??oe.position}));const we=me?Q.filter(oe=>!oe.hidden):Q,be=ri(we,U,L,"auto",z),Ee=new Map(be.map(oe=>[oe.id,oe.position])),Ye=Q.map(oe=>Ee.has(oe.id)?{...oe,position:Ee.get(oe.id)}:se.has(oe.id)&&b.current?{...oe,position:se.get(oe.id)}:oe);return b.current=!0,requestAnimationFrame(()=>f({padding:.15,duration:300})),Ye}),$(U)},[e,i,r,o,a,l,c,T,L,z,Y,u,G,f]);const K=w.useMemo(()=>{const B=new Set,q=new Set,W=new Map,Q=(U,X)=>{if(!X.length)return;const O=W.get(U)??new Set;X.forEach(se=>O.add(se)),W.set(U,O)};if(C)for(const U of e.relationships)(U.from.attributes.includes(C)||U.to.attributes.includes(C))&&(B.add(U.id),q.add(U.from.entity),q.add(U.to.entity),Q(U.from.entity,U.from.attributes),Q(U.to.entity,U.to.attributes));if(R){const U=e.relationships.find(X=>X.id===R);U&&(Q(U.from.entity,U.from.attributes),Q(U.to.entity,U.to.attributes))}return{active:C!==null,relsForAttr:B,entsForAttr:q,hitAttrsByEntity:W}},[e,C,R]);w.useEffect(()=>{var Q;const B=i.trim().toLowerCase(),q=new Set;if(B)for(const U of e.entities)(U.name.toLowerCase()+" "+(((Q=U.conceptual)==null?void 0:Q.name.toLowerCase())??"")+" "+U.attributes.map(O=>O.name.toLowerCase()).join(" ")).includes(B)&&q.add(U.id);const W=new Set;if(r){W.add(r);for(const U of e.relationships)U.from.entity===r&&W.add(U.to.entity),U.to.entity===r&&W.add(U.from.entity)}p(U=>U.map(X=>{const O=B!==""&&!q.has(X.id),se=r!==null&&!W.has(X.id),ce=K.active&&!K.entsForAttr.has(X.id),me=O||se||ce,we=B!==""&&q.has(X.id)||K.active&&K.entsForAttr.has(X.id),be=K.hitAttrsByEntity.get(X.id);return X.data.dimmed===me&&X.data.highlighted===we&&X.data.highlightAttrs===be?X:{...X,data:{...X.data,dimmed:me,highlighted:we,highlightAttrs:be}}})),$(U=>U.map(X=>{var we,be;const O=(we=X.data)==null?void 0:we.relationship,se=r!==null&&O!==void 0&&O.from.entity!==r&&O.to.entity!==r,ce=K.active&&!K.relsForAttr.has(X.id),me=se||ce;return((be=X.data)==null?void 0:be.dimmed)===me?X:{...X,data:{...X.data,dimmed:me}}}))},[K,i,r,e]),w.useEffect(()=>{var Q;const B=i.trim().toLowerCase();if(!B)return;const q=new Set;for(const U of e.entities)(U.name.toLowerCase()+" "+(((Q=U.conceptual)==null?void 0:Q.name.toLowerCase())??"")+" "+U.attributes.map(O=>O.name.toLowerCase()).join(" ")).includes(B)&&q.add(U.id);if(!q.size)return;const W=requestAnimationFrame(()=>{const U=y().filter(O=>q.has(O.id));if(!U.length)return;const X=kf(U);x(X,{padding:.35,duration:400})});return()=>cancelAnimationFrame(W)},[i,e,y,x]);const J=w.useCallback((B="auto",q="LR")=>{p(W=>ri(W,E,L,B,z,q)),requestAnimationFrame(()=>f({padding:.15,duration:300}))},[E,L,z,f]),ee=w.useCallback(B=>{s(B),p(q=>(q.find(Q=>Q.id===B)&&requestAnimationFrame(()=>f({nodes:[{id:B}],padding:.4,duration:400})),q))},[f,s]);w.useImperativeHandle(m,()=>({relayout:J,fitView:()=>f({padding:.15,duration:300}),focusEntity:ee,newEntity:()=>D(!0)}),[J,f,ee]);const ie=w.useCallback(B=>{if(k||!B.source||!B.target||B.source===B.target)return;const q=B.sourceHandle&&B.sourceHandle!==Dr?B.sourceHandle:void 0,W=B.targetHandle&&B.targetHandle!==Lr?B.targetHandle:void 0;V({from:B.source,to:B.target,fromAttr:q,toAttr:W})},[k]),te=w.useCallback(async(B,q)=>{try{return await n(B,q)}catch(W){throw d==null||d(W instanceof Error?W.message:String(W)),W}},[n,d]),re=r?L.get(r)??null:null,fe=e.relationships.find(B=>B.id===F)??null;return h.jsxs(h.Fragment,{children:[h.jsxs(Do,{nodes:v,edges:E,nodeTypes:ax,edgeTypes:lx,onNodeClick:(B,q)=>s(q.id),onEdgeClick:(B,q)=>!k&&g(q.id),onPaneClick:()=>{s(null),g(null)},onNodeDragStop:(B,q)=>p(W=>W.map(Q=>Q.id===q.id?{...Q,position:q.position}:Q)),onConnect:ie,nodesConnectable:!k,minZoom:.05,onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},fitView:!0,children:[h.jsx(xm,{variant:ke.Dots,gap:22,size:1.5,color:"#33291f"}),h.jsx(im,{pannable:!0,zoomable:!0,nodeColor:B=>{var q;return((q=B.data)==null?void 0:q.color)??"#5a4f42"},maskColor:"rgba(20, 17, 15, 0.75)"}),h.jsx(fm,{showInteractive:!1})]}),re&&h.jsx(Dm,{entity:re,doc:e,caps:t,exec:te,onClose:()=>s(null),onFocusEntity:ee,onAlign:j,onEditMapping:I}),A&&h.jsx(tx,{entity:A,exec:te,onClose:()=>j(null)}),_&&h.jsx(nx,{entity:_,doc:e,exec:te,onClose:()=>I(null)}),S&&h.jsx(rx,{doc:e,exec:te,onClose:()=>D(!1),onCreated:ee}),P&&h.jsx(sx,{doc:e,fromId:P.from,toId:P.to,initialFromAttr:P.fromAttr,initialToAttr:P.toAttr,exec:te,onClose:()=>V(null)}),fe&&h.jsx(ix,{rel:fe,doc:e,exec:te,onClose:()=>g(null)})]})}function zx({tab:e,onClose:t,readOnly:n,refreshKey:r,onModelChanged:s,onFocusEntity:i}){return h.jsxs("aside",{className:"side-panel",children:[h.jsxs("div",{className:"detail-header",children:[h.jsxs("h2",{children:[e==="ontology"&&"Ontology",e==="layers"&&"Ontology layers",e==="changes"&&"Uncommitted changes",e==="decisions"&&"Decision ledger"]}),h.jsx("button",{className:"icon-btn",onClick:t,children:"✕"})]}),e==="ontology"&&h.jsx(ux,{}),e==="layers"&&h.jsx(fx,{refreshKey:r,onFocusEntity:i}),e==="changes"&&h.jsx(hx,{readOnly:n,refreshKey:r,onModelChanged:s}),e==="decisions"&&h.jsx(mx,{readOnly:n})]})}function ux(){const[e,t]=w.useState(""),[n,r]=w.useState([]),[s,i]=w.useState({loaded:0,vocabs:[]}),[o,a]=w.useState(null),[l,c]=w.useState([]),[u,d]=w.useState("");w.useEffect(()=>{Go().then(f=>c(f.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const f=setTimeout(()=>{Bo(e||"a",u||void 0).then(x=>{r(e?x.results:[]),i({loaded:x.loaded_terms,vocabs:x.vocabularies})}).catch(()=>{})},200);return()=>clearTimeout(f)},[e,u]);const m=w.useCallback(f=>{wm(f).then(a).catch(()=>{})},[]);return o?h.jsxs("div",{className:"panel-body",children:[h.jsx("button",{className:"mini-btn",onClick:()=>a(null),children:"← back"}),h.jsx("h3",{style:{marginTop:12},children:o.label}),h.jsx("code",{className:"term-iri",children:o.prefixed}),o.definition&&h.jsx("p",{className:"term-def",children:o.definition}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"source"}),h.jsx("span",{className:"v",children:o.source})]}),o.broader.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"broader"}),o.broader.map(f=>h.jsxs("button",{className:"rel-link",onClick:()=>m(f.iri),children:["↑ ",f.label]},f.iri))]}),o.narrower.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"narrower"}),o.narrower.map(f=>h.jsxs("button",{className:"rel-link",onClick:()=>m(f.iri),children:["↓ ",f.label]},f.iri))]})]}):h.jsxs("div",{className:"panel-body",children:[l.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(u===""?" active":""),onClick:()=>d(""),children:"all sources"}),l.map(f=>h.jsxs("button",{className:"src-chip"+(u===f.id?" active":""),title:(f.description??"")+(f.count!=null?` · ${f.count} terms`:" · live"),onClick:()=>d(f.id),children:[f.name,f.layer&&h.jsx("span",{className:`src-layer layer-${f.layer}`,children:f.layer})]},f.id))]}),h.jsx("input",{className:"edit-input",placeholder:u?`Search within ${u}…`:"Search vocabulary terms…",value:e,onChange:f=>t(f.target.value)}),h.jsx("p",{className:"meta-line",children:l.length>0?`${l.length} source${l.length>1?"s":""}: ${s.vocabs.join(", ")}`+(s.loaded>0?` · ${s.loaded} local terms indexed`:""):"no vocabulary loaded — declare ontology_stack in mdl-project.yaml or run `mdl ontology vendor fibo`"}),h.jsx("div",{className:"term-results",children:n.map(f=>h.jsxs("button",{className:"term-card",onClick:()=>m(f.prefixed),children:[h.jsxs("div",{className:"term-head",children:[h.jsx("span",{className:"term-label",children:f.label}),h.jsxs("span",{className:"term-source",children:[f.source_kind==="glossary-term"&&h.jsx("span",{className:"kind-badge",title:"glossary term",children:"glossary"}),f.source]})]}),h.jsx("code",{className:"term-iri",children:f.prefixed}),f.definition&&h.jsx("p",{className:"term-def",children:f.definition})]},f.iri))})]})}const dx=["industry","core","domain","specialised"];function fx({refreshKey:e,onFocusEntity:t}){const[n,r]=w.useState(null),[s,i]=w.useState(null);if(w.useEffect(()=>{bm().then(r).catch(()=>{}),vm().then(i).catch(()=>{})},[e]),!n)return h.jsx("div",{className:"panel-body",children:"loading…"});const o=new Set((s==null?void 0:s.core_uncovered)??[]);return h.jsxs("div",{className:"panel-body",children:[s&&h.jsxs("div",{className:"coverage-banner"+(s.coverage_pct<100?" warn":""),children:["industry alignment coverage: ",h.jsxs("b",{children:[s.coverage_pct,"%"]})," (",s.core_with_industry,"+",s.core_exempt,"/",s.total_core," core terms)"]}),h.jsxs("div",{className:"layer-band external",children:[h.jsx("h4",{children:"industry (external vocabularies)"}),n.external_terms.length===0&&h.jsx("p",{className:"empty-hint",children:"no aligned terms yet"}),n.external_terms.map(a=>h.jsxs("div",{className:"stack-term external",title:a.definition??void 0,children:[a.label,h.jsx("span",{className:"term-source",children:a.source})]},a.iri))]}),dx.slice(1).map(a=>h.jsxs("div",{className:"layer-band",children:[h.jsx("h4",{children:a}),(n.layers[a]??[]).length===0&&h.jsx("p",{className:"empty-hint",children:"—"}),(n.layers[a]??[]).map(l=>{var c;return h.jsxs("button",{className:"stack-term"+(o.has(l.name)?" uncovered":""),title:l.definition??void 0,onClick:()=>t(l.id),children:[l.name,(l.aligned_refs??(l.aligned_to?[l.aligned_to]:[])).map((u,d)=>h.jsxs("span",{className:"align-arrow"+(u.resolved?"":" unresolved")+(u.status==="proposed"?" proposed":""),title:u.ref+(u.resolved_via?` (via ${u.resolved_via})`:""),children:["↑ ",u.label,u.status==="proposed"?" ·prop":""]},`${u.ref}-${d}`)),!l.aligned_to&&(((c=l.aligned_refs)==null?void 0:c.length)??0)===0&&!l.no_industry_equivalent&&h.jsx("span",{className:"align-arrow missing",children:"unaligned"})]},l.id)})]},a))]})}function hx({readOnly:e,refreshKey:t,onModelChanged:n}){const[r,s]=w.useState(null),[i,o]=w.useState(""),[a,l]=w.useState(""),[c,u]=w.useState(!1),d=w.useCallback(()=>{Em().then(s).catch(()=>s(null)),Nm().then(m=>o([m.diff,...m.untracked].filter(Boolean).join(` + +`))).catch(()=>o(""))},[]);return w.useEffect(d,[d,t]),r!=null&&r.git?r.clean?h.jsx("div",{className:"panel-body ok",children:"✓ working tree clean"}):h.jsxs("div",{className:"panel-body",children:[h.jsx("ul",{className:"dirty-list",children:r.dirty.map(m=>h.jsxs("li",{children:[h.jsx("code",{className:"dirty-state",children:m.state})," ",m.path]},m.path))}),h.jsx("pre",{className:"diff-view",children:i.split(` +`).map((m,f)=>h.jsx("span",{className:m.startsWith("+")&&!m.startsWith("+++")?"diff-add":m.startsWith("-")&&!m.startsWith("---")?"diff-del":m.startsWith("@@")?"diff-hunk":"",children:m+` +`},f))}),!e&&h.jsxs(h.Fragment,{children:[h.jsx("input",{className:"edit-input",placeholder:"commit message…",value:a,onChange:m=>l(m.target.value)}),h.jsxs("div",{className:"modal-footer",children:[h.jsx("button",{className:"mini-btn danger",disabled:c,onClick:()=>{confirm("Discard ALL uncommitted model changes?")&&(u(!0),km().then(()=>{u(!1),d(),n()}))},children:"Discard all"}),h.jsx("button",{className:"primary-btn",disabled:c||!a.trim(),onClick:()=>{u(!0),Sm(a).then(()=>{u(!1),l(""),d()})},children:"Commit"})]})]})]}):h.jsx("div",{className:"panel-body",children:"model directory is not in a git repo"})}function mx({readOnly:e}){const[t,n]=w.useState([]),r=w.useCallback(()=>{jm().then(i=>n(i.decisions)).catch(()=>n([]))},[]);if(w.useEffect(r,[r]),t.length===0)return h.jsx("div",{className:"panel-body",children:"no recorded decisions (run `mdl reverse` to propose)"});const s={accepted:"✓",rejected:"✗",proposed:"?"};return h.jsx("div",{className:"panel-body",children:t.map(i=>h.jsxs("div",{className:"decision "+i.verdict,children:[h.jsxs("div",{className:"decision-head",children:[h.jsx("span",{className:"decision-mark",children:s[i.verdict]}),h.jsx("span",{className:"chip",children:i.confidence}),h.jsx("span",{className:"chip",children:i.signal})]}),h.jsx("p",{className:"decision-subject",children:i.subject}),!e&&i.verdict==="proposed"&&h.jsxs("div",{className:"decision-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>Os(i.signal_key,"accepted").then(r),children:"Accept"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>Os(i.signal_key,"rejected").then(r),children:"Reject"})]})]},i.signal_key))})}function Hx({exec:e,canEdit:t,onImported:n,submitLabel:r="Import & review",buttonClass:s="erd-action ghost",applyBatch:i,openImport:o=!1,prefillImport:a="",onPreview:l}){const[c,u]=w.useState(o&&t?"import":null),[d,m]=w.useState([]),[f,x]=w.useState("postgres"),y=w.useRef(null);w.useEffect(()=>{Cm().then(p=>m(p.formats)).catch(()=>m([]))},[]),w.useEffect(()=>{if(!c)return;const p=E=>{y.current&&!y.current.contains(E.target)&&u(null)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[c]);const v=d.find(p=>p.id==="sql");return h.jsxs("div",{className:"ie-wrap",ref:y,children:[h.jsxs("button",{className:s+(c==="export"?" on":""),onClick:()=>u(c==="export"?null:"export"),children:[h.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"↧"}),"Export ",h.jsx("span",{className:"erd-caret",children:"▾"})]}),t&&h.jsxs("button",{className:s+(c==="import"?" on":""),onClick:()=>u(c==="import"?null:"import"),children:[h.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"↥"}),"Import ",h.jsx("span",{className:"erd-caret",children:"▾"})]}),c==="export"&&h.jsxs("div",{className:"ie-menu",role:"menu",children:[(v==null?void 0:v.dialects)&&h.jsxs("label",{className:"ie-dialect",children:[h.jsx("span",{children:"SQL dialect"}),h.jsx("select",{value:f,onChange:p=>x(p.target.value),children:v.dialects.map(p=>h.jsx("option",{value:p,children:p},p))})]}),d.map(p=>h.jsx("a",{className:"ie-item",href:$m(p.id,p.id==="sql"?f:void 0),download:!0,onClick:()=>u(null),children:p.label},p.id))]}),c==="import"&&h.jsx(px,{exec:e,submitLabel:r,buttonClass:s,applyBatch:i,onClose:()=>u(null),onImported:p=>{u(null),n==null||n(p)},onPreview:l,prefillImport:a})]})}const di=[{id:"sql",label:"SQL DDL",hint:"CREATE TABLE … PRIMARY KEY / FOREIGN KEY"},{id:"mermaid",label:"Mermaid erDiagram",hint:"structural: entities, attributes, relationships"},{id:"json-schema",label:"JSON Schema",hint:"each object definition becomes an entity"},{id:"erwin",label:"erwin XML",hint:"entities, keys, relationships, subject areas, subtypes"}];function px({exec:e,submitLabel:t,buttonClass:n,applyBatch:r,onClose:s,onImported:i,onPreview:o,prefillImport:a}){var j;const[l,c]=w.useState(o?"erwin":"sql"),[u,d]=w.useState("postgres"),[m,f]=w.useState(a??""),[x,y]=w.useState(!1),[v,p]=w.useState(null),[E,$]=w.useState([]);w.useEffect(()=>{a&&f(a)},[a]);const b=_=>{_&&_.text().then(f).catch(()=>p("could not read that file"))},A=async()=>{y(!0),p(null),$([]);try{if(o&&l==="erwin"){const I=await Am(l,m);if(!I.changes.length){p(I.warnings[0]??"nothing to import from that document"),y(!1);return}s(),o(I),y(!1);return}const _=await Mm(l,m,l==="sql"?u:void 0);if(!_.changes.length){p(_.warnings[0]??"nothing to import from that document"),y(!1);return}if(r)await r(_.changes);else for(const I of _.changes)await e(I.op,I.payload);$(_.warnings),i(_.tables)}catch(_){p(_ instanceof Error?_.message:String(_))}finally{y(!1)}};return h.jsxs("div",{className:"ie-menu ie-import",role:"dialog",children:[h.jsx("div",{className:"ie-tabs",children:di.map(_=>h.jsx("button",{className:"ie-tab"+(l===_.id?" active":""),onClick:()=>c(_.id),children:_.label},_.id))}),h.jsx("p",{className:"ie-hint",children:(j=di.find(_=>_.id===l))==null?void 0:j.hint}),l==="sql"&&h.jsxs("label",{className:"ie-dialect",children:[h.jsx("span",{children:"dialect"}),h.jsx("select",{value:u,onChange:_=>d(_.target.value),children:["postgres","snowflake","mysql","duckdb"].map(_=>h.jsx("option",{children:_},_))})]}),h.jsx("textarea",{className:"ie-textarea",placeholder:`Paste ${l} here, or choose a file…`,value:m,onChange:_=>f(_.target.value),rows:8}),h.jsx("input",{type:"file",className:"ie-file",accept:".sql,.mmd,.md,.json,.xml,.txt",onChange:_=>{var I;return b(((I=_.target.files)==null?void 0:I[0])??null)}}),l==="mermaid"&&h.jsx("p",{className:"ie-warn",children:"Mermaid is a diagram format: types and keys are approximate and relationships are inferred from the lines."}),E.map((_,I)=>h.jsx("p",{className:"ie-warn",children:_},I)),v&&h.jsx("p",{className:"ie-error",children:v}),h.jsxs("div",{className:"ie-foot",children:[h.jsx("button",{className:n,onClick:s,children:"Cancel"}),h.jsx("button",{className:n,disabled:x||!m.trim(),onClick:A,children:x?"Importing…":t})]})]})}export{Vo as A,xm as B,fm as C,Tm as E,Hx as I,fx as L,Px as M,ux as O,ci as P,To as R,zx as S,wx as a,Mx as b,jx as c,Ix as d,Rx as e,xx as f,Em as g,Tx as h,Cx as i,_x as j,Ex as k,Nx as l,kx as m,$x as n,Bo as o,Ax as p,Sx as q,bx as r,_m as s,vx as t,ri as u,Do as v,ox as w,im as x}; diff --git a/packages/server/src/mdl_server/static/assets/styles-BtrhR1xb.js b/packages/server/src/mdl_server/static/assets/styles-BtrhR1xb.js deleted file mode 100644 index f158962..0000000 --- a/packages/server/src/mdl_server/static/assets/styles-BtrhR1xb.js +++ /dev/null @@ -1,23 +0,0 @@ -var tl=Object.defineProperty;var nl=(e,t,n)=>t in e?tl(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ue=(e,t,n)=>nl(e,typeof t!="symbol"?t+"":t,n);import{r as w,g as fi,R as A,j as h}from"./client-DOmNpTln.js";function ye(e){if(typeof e=="string"||typeof e=="number")return""+e;let t="";if(Array.isArray(e))for(let n=0,r;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?ul:cl;gi.useSyncExternalStore=Et.useSyncExternalStore!==void 0?Et.useSyncExternalStore:dl;pi.exports=gi;var fl=pi.exports;/** - * @license React - * use-sync-external-store-shim/with-selector.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var kn=w,hl=fl;function ml(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var pl=typeof Object.is=="function"?Object.is:ml,gl=hl.useSyncExternalStore,yl=kn.useRef,xl=kn.useEffect,wl=kn.useMemo,bl=kn.useDebugValue;mi.useSyncExternalStoreWithSelector=function(e,t,n,r,s){var i=yl(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=wl(function(){function l(f){if(!c){if(c=!0,u=f,f=r(f),s!==void 0&&o.hasValue){var y=o.value;if(s(y,f))return d=y}return d=f}if(y=d,pl(u,f))return y;var p=r(f);return s!==void 0&&s(y,p)?(u=f,y):(u=f,d=p)}var c=!1,u,d,m=n===void 0?null:n;return[function(){return l(t())},m===null?void 0:function(){return l(m())}]},[t,n,r,s]);var a=gl(e,i[0],i[1]);return xl(function(){o.hasValue=!0,o.value=a},[a]),bl(a),a};hi.exports=mi;var vl=hi.exports;const _l=fi(vl),El={},Wr=e=>{let t;const n=new Set,r=(u,d)=>{const m=typeof u=="function"?u(t):u;if(!Object.is(m,t)){const f=t;t=d??(typeof m!="object"||m===null)?m:Object.assign({},t,m),n.forEach(y=>y(t,f))}},s=()=>t,l={setState:r,getState:s,getInitialState:()=>c,subscribe:u=>(n.add(u),()=>n.delete(u)),destroy:()=>{(El?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),n.clear()}},c=t=e(r,s,l);return l},Nl=e=>e?Wr(e):Wr,{useDebugValue:Sl}=A,{useSyncExternalStoreWithSelector:kl}=_l,jl=e=>e;function yi(e,t=jl,n){const r=kl(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return Sl(r),r}const Ur=(e,t)=>{const n=Nl(e),r=(s,i=t)=>yi(n,s,i);return Object.assign(r,n),r},Cl=(e,t)=>e?Ur(e,t):Ur;function ge(e,t){if(Object.is(e,t))return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return!1;if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(const[r,s]of e)if(!Object.is(s,t.get(r)))return!1;return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(const r of e)if(!t.has(r))return!1;return!0}const n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(const r of n)if(!Object.prototype.hasOwnProperty.call(t,r)||!Object.is(e[r],t[r]))return!1;return!0}var $l={value:()=>{}};function jn(){for(var e=0,t=arguments.length,n={},r;e=0&&(r=n.slice(s+1),n=n.slice(0,s)),n&&!t.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:r}})}cn.prototype=jn.prototype={constructor:cn,on:function(e,t){var n=this._,r=Ml(e+"",n),s,i=-1,o=r.length;if(arguments.length<2){for(;++i0)for(var n=new Array(s),r=0,s,i;r=0&&(t=e.slice(0,n))!=="xmlns"&&(e=e.slice(n+1)),qr.hasOwnProperty(t)?{space:qr[t],local:e}:e}function Il(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===nr&&t.documentElement.namespaceURI===nr?t.createElement(e):t.createElementNS(n,e)}}function Rl(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function xi(e){var t=Cn(e);return(t.local?Rl:Il)(t)}function Tl(){}function wr(e){return e==null?Tl:function(){return this.querySelector(e)}}function Ol(e){typeof e!="function"&&(e=wr(e));for(var t=this._groups,n=t.length,r=new Array(n),s=0;s=M&&(M=_+1);!(j=v[M])&&++M=0;)(o=r[s])&&(i&&o.compareDocumentPosition(i)^4&&i.parentNode.insertBefore(o,i),i=o);return this}function oc(e){e||(e=ac);function t(d,m){return d&&m?e(d.__data__,m.__data__):!d-!m}for(var n=this._groups,r=n.length,s=new Array(r),i=0;it?1:e>=t?0:NaN}function lc(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}function cc(){return Array.from(this)}function uc(){for(var e=this._groups,t=0,n=e.length;t1?this.each((t==null?vc:typeof t=="function"?Ec:_c)(e,t,n??"")):Nt(this.node(),e)}function Nt(e,t){return e.style.getPropertyValue(t)||Ei(e).getComputedStyle(e,null).getPropertyValue(t)}function Sc(e){return function(){delete this[e]}}function kc(e,t){return function(){this[e]=t}}function jc(e,t){return function(){var n=t.apply(this,arguments);n==null?delete this[e]:this[e]=n}}function Cc(e,t){return arguments.length>1?this.each((t==null?Sc:typeof t=="function"?jc:kc)(e,t)):this.node()[e]}function Ni(e){return e.trim().split(/^|\s+/)}function br(e){return e.classList||new Si(e)}function Si(e){this._node=e,this._names=Ni(e.getAttribute("class")||"")}Si.prototype={add:function(e){var t=this._names.indexOf(e);t<0&&(this._names.push(e),this._node.setAttribute("class",this._names.join(" ")))},remove:function(e){var t=this._names.indexOf(e);t>=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(e){return this._names.indexOf(e)>=0}};function ki(e,t){for(var n=br(e),r=-1,s=t.length;++r=0&&(n=t.slice(r+1),t=t.slice(0,r)),{type:t,name:n}})}function nu(e){return function(){var t=this.__on;if(t){for(var n=0,r=-1,s=t.length,i;n()=>e;function rr(e,{sourceEvent:t,subject:n,target:r,identifier:s,active:i,x:o,y:a,dx:l,dy:c,dispatch:u}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:s,enumerable:!0,configurable:!0},active:{value:i,enumerable:!0,configurable:!0},x:{value:o,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:c,enumerable:!0,configurable:!0},_:{value:u}})}rr.prototype.on=function(){var e=this._.on.apply(this._,arguments);return e===this._?this:e};function fu(e){return!e.ctrlKey&&!e.button}function hu(){return this.parentNode}function mu(e,t){return t??{x:e.x,y:e.y}}function pu(){return navigator.maxTouchPoints||"ontouchstart"in this}function gu(){var e=fu,t=hu,n=mu,r=pu,s={},i=jn("start","drag","end"),o=0,a,l,c,u,d=0;function m(b){b.on("mousedown.drag",f).filter(r).on("touchstart.drag",v).on("touchmove.drag",x,du).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(b,j){if(!(u||!e.call(this,b,j))){var C=M(this,t.call(this,b,j),b,j,"mouse");C&&(Ne(b.view).on("mousemove.drag",y,zt).on("mouseup.drag",p,zt),Mi(b.view),Hn(b),c=!1,a=b.clientX,l=b.clientY,C("start",b))}}function y(b){if(wt(b),!c){var j=b.clientX-a,C=b.clientY-l;c=j*j+C*C>d}s.mouse("drag",b)}function p(b){Ne(b.view).on("mousemove.drag mouseup.drag",null),Ai(b.view,c),wt(b),s.mouse("end",b)}function v(b,j){if(e.call(this,b,j)){var C=b.changedTouches,E=t.call(this,b,j),L=C.length,S,O;for(S=0;S>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?Jt(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?Jt(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=xu.exec(e))?new ve(t[1],t[2],t[3],1):(t=wu.exec(e))?new ve(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=bu.exec(e))?Jt(t[1],t[2],t[3],t[4]):(t=vu.exec(e))?Jt(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=_u.exec(e))?ns(t[1],t[2]/100,t[3]/100,1):(t=Eu.exec(e))?ns(t[1],t[2]/100,t[3]/100,t[4]):Kr.hasOwnProperty(e)?Jr(Kr[e]):e==="transparent"?new ve(NaN,NaN,NaN,0):null}function Jr(e){return new ve(e>>16&255,e>>8&255,e&255,1)}function Jt(e,t,n,r){return r<=0&&(e=t=n=NaN),new ve(e,t,n,r)}function ku(e){return e instanceof Xt||(e=Vt(e)),e?(e=e.rgb(),new ve(e.r,e.g,e.b,e.opacity)):new ve}function sr(e,t,n,r){return arguments.length===1?ku(e):new ve(e,t,n,r??1)}function ve(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}vr(ve,sr,Ii(Xt,{brighter(e){return e=e==null?mn:Math.pow(mn,e),new ve(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Ht:Math.pow(Ht,e),new ve(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new ve(at(this.r),at(this.g),at(this.b),pn(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:es,formatHex:es,formatHex8:ju,formatRgb:ts,toString:ts}));function es(){return`#${it(this.r)}${it(this.g)}${it(this.b)}`}function ju(){return`#${it(this.r)}${it(this.g)}${it(this.b)}${it((isNaN(this.opacity)?1:this.opacity)*255)}`}function ts(){const e=pn(this.opacity);return`${e===1?"rgb(":"rgba("}${at(this.r)}, ${at(this.g)}, ${at(this.b)}${e===1?")":`, ${e})`}`}function pn(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function at(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function it(e){return e=at(e),(e<16?"0":"")+e.toString(16)}function ns(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new Me(e,t,n,r)}function Ri(e){if(e instanceof Me)return new Me(e.h,e.s,e.l,e.opacity);if(e instanceof Xt||(e=Vt(e)),!e)return new Me;if(e instanceof Me)return e;e=e.rgb();var t=e.r/255,n=e.g/255,r=e.b/255,s=Math.min(t,n,r),i=Math.max(t,n,r),o=NaN,a=i-s,l=(i+s)/2;return a?(t===i?o=(n-r)/a+(n0&&l<1?0:o,new Me(o,a,l,e.opacity)}function Cu(e,t,n,r){return arguments.length===1?Ri(e):new Me(e,t,n,r??1)}function Me(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}vr(Me,Cu,Ii(Xt,{brighter(e){return e=e==null?mn:Math.pow(mn,e),new Me(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Ht:Math.pow(Ht,e),new Me(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*t,s=2*n-r;return new ve(Fn(e>=240?e-240:e+120,s,r),Fn(e,s,r),Fn(e<120?e+240:e-120,s,r),this.opacity)},clamp(){return new Me(rs(this.h),en(this.s),en(this.l),pn(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const e=pn(this.opacity);return`${e===1?"hsl(":"hsla("}${rs(this.h)}, ${en(this.s)*100}%, ${en(this.l)*100}%${e===1?")":`, ${e})`}`}}));function rs(e){return e=(e||0)%360,e<0?e+360:e}function en(e){return Math.max(0,Math.min(1,e||0))}function Fn(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}const Ti=e=>()=>e;function $u(e,t){return function(n){return e+n*t}}function Mu(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(r){return Math.pow(e+r*t,n)}}function Au(e){return(e=+e)==1?Oi:function(t,n){return n-t?Mu(t,n,e):Ti(isNaN(t)?n:t)}}function Oi(e,t){var n=t-e;return n?$u(e,n):Ti(isNaN(e)?t:e)}const ss=function e(t){var n=Au(t);function r(s,i){var o=n((s=sr(s)).r,(i=sr(i)).r),a=n(s.g,i.g),l=n(s.b,i.b),c=Oi(s.opacity,i.opacity);return function(u){return s.r=o(u),s.g=a(u),s.b=l(u),s.opacity=c(u),s+""}}return r.gamma=e,r}(1);function We(e,t){return e=+e,t=+t,function(n){return e*(1-n)+t*n}}var ir=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Vn=new RegExp(ir.source,"g");function Iu(e){return function(){return e}}function Ru(e){return function(t){return e(t)+""}}function Tu(e,t){var n=ir.lastIndex=Vn.lastIndex=0,r,s,i,o=-1,a=[],l=[];for(e=e+"",t=t+"";(r=ir.exec(e))&&(s=Vn.exec(t));)(i=s.index)>n&&(i=t.slice(n,i),a[o]?a[o]+=i:a[++o]=i),(r=r[0])===(s=s[0])?a[o]?a[o]+=s:a[++o]=s:(a[++o]=null,l.push({i:o,x:We(r,s)})),n=Vn.lastIndex;return n180?u+=360:u-c>180&&(c+=360),m.push({i:d.push(s(d)+"rotate(",null,r)-2,x:We(c,u)})):u&&d.push(s(d)+"rotate("+u+r)}function a(c,u,d,m){c!==u?m.push({i:d.push(s(d)+"skewX(",null,r)-2,x:We(c,u)}):u&&d.push(s(d)+"skewX("+u+r)}function l(c,u,d,m,f,y){if(c!==d||u!==m){var p=f.push(s(f)+"scale(",null,",",null,")");y.push({i:p-4,x:We(c,d)},{i:p-2,x:We(u,m)})}else(d!==1||m!==1)&&f.push(s(f)+"scale("+d+","+m+")")}return function(c,u){var d=[],m=[];return c=e(c),u=e(u),i(c.translateX,c.translateY,u.translateX,u.translateY,d,m),o(c.rotate,u.rotate,d,m),a(c.skewX,u.skewX,d,m),l(c.scaleX,c.scaleY,u.scaleX,u.scaleY,d,m),c=u=null,function(f){for(var y=-1,p=m.length,v;++y=0&&e._call.call(void 0,t),e=e._next;--St}function as(){ct=(yn=Bt.now())+$n,St=Ot=0;try{Gu()}finally{St=0,Wu(),ct=0}}function Yu(){var e=Bt.now(),t=e-yn;t>Pi&&($n-=t,yn=e)}function Wu(){for(var e,t=gn,n,r=1/0;t;)t._call?(r>t._time&&(r=t._time),e=t,t=t._next):(n=t._next,t._next=null,t=e?e._next=n:gn=n);Dt=e,ar(r)}function ar(e){if(!St){Ot&&(Ot=clearTimeout(Ot));var t=e-ct;t>24?(e<1/0&&(Ot=setTimeout(as,e-Bt.now()-$n)),Mt&&(Mt=clearInterval(Mt))):(Mt||(yn=Bt.now(),Mt=setInterval(Yu,Pi)),St=1,zi(as))}}function ls(e,t,n){var r=new xn;return t=t==null?0:+t,r.restart(s=>{r.stop(),e(s+t)},t,n),r}var Uu=jn("start","end","cancel","interrupt"),Xu=[],Fi=0,cs=1,lr=2,un=3,us=4,cr=5,dn=6;function Mn(e,t,n,r,s,i){var o=e.__transition;if(!o)e.__transition={};else if(n in o)return;qu(e,n,{name:t,index:r,group:s,on:Uu,tween:Xu,time:i.time,delay:i.delay,duration:i.duration,ease:i.ease,timer:null,state:Fi})}function Er(e,t){var n=Ae(e,t);if(n.state>Fi)throw new Error("too late; already scheduled");return n}function Oe(e,t){var n=Ae(e,t);if(n.state>un)throw new Error("too late; already running");return n}function Ae(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw new Error("transition not found");return n}function qu(e,t,n){var r=e.__transition,s;r[t]=n,n.timer=Hi(i,0,n.time);function i(c){n.state=cs,n.timer.restart(o,n.delay,n.time),n.delay<=c&&o(c-n.delay)}function o(c){var u,d,m,f;if(n.state!==cs)return l();for(u in r)if(f=r[u],f.name===n.name){if(f.state===un)return ls(o);f.state===us?(f.state=dn,f.timer.stop(),f.on.call("interrupt",e,e.__data__,f.index,f.group),delete r[u]):+ulr&&r.state=0&&(t=t.slice(0,n)),!t||t==="start"})}function Sd(e,t,n){var r,s,i=Nd(t)?Er:Oe;return function(){var o=i(this,e),a=o.on;a!==r&&(s=(r=a).copy()).on(t,n),o.on=s}}function kd(e,t){var n=this._id;return arguments.length<2?Ae(this.node(),n).on.on(e):this.each(Sd(n,e,t))}function jd(e){return function(){var t=this.parentNode;for(var n in this.__transition)if(+n!==e)return;t&&t.removeChild(this)}}function Cd(){return this.on("end.remove",jd(this._id))}function $d(e){var t=this._name,n=this._id;typeof e!="function"&&(e=wr(e));for(var r=this._groups,s=r.length,i=new Array(s),o=0;o()=>e;function ef(e,{sourceEvent:t,target:n,transform:r,dispatch:s}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:s}})}function He(e,t,n){this.k=e,this.x=t,this.y=n}He.prototype={constructor:He,scale:function(e){return e===1?this:new He(this.k*e,this.x,this.y)},translate:function(e,t){return e===0&t===0?this:new He(this.k,this.x+this.k*e,this.y+this.k*t)},apply:function(e){return[e[0]*this.k+this.x,e[1]*this.k+this.y]},applyX:function(e){return e*this.k+this.x},applyY:function(e){return e*this.k+this.y},invert:function(e){return[(e[0]-this.x)/this.k,(e[1]-this.y)/this.k]},invertX:function(e){return(e-this.x)/this.k},invertY:function(e){return(e-this.y)/this.k},rescaleX:function(e){return e.copy().domain(e.range().map(this.invertX,this).map(e.invert,e))},rescaleY:function(e){return e.copy().domain(e.range().map(this.invertY,this).map(e.invert,e))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Fe=new He(1,0,0);He.prototype;function Bn(e){e.stopImmediatePropagation()}function At(e){e.preventDefault(),e.stopImmediatePropagation()}function tf(e){return(!e.ctrlKey||e.type==="wheel")&&!e.button}function nf(){var e=this;return e instanceof SVGElement?(e=e.ownerSVGElement||e,e.hasAttribute("viewBox")?(e=e.viewBox.baseVal,[[e.x,e.y],[e.x+e.width,e.y+e.height]]):[[0,0],[e.width.baseVal.value,e.height.baseVal.value]]):[[0,0],[e.clientWidth,e.clientHeight]]}function ds(){return this.__zoom||Fe}function rf(e){return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*(e.ctrlKey?10:1)}function sf(){return navigator.maxTouchPoints||"ontouchstart"in this}function of(e,t,n){var r=e.invertX(t[0][0])-n[0][0],s=e.invertX(t[1][0])-n[1][0],i=e.invertY(t[0][1])-n[0][1],o=e.invertY(t[1][1])-n[1][1];return e.translate(s>r?(r+s)/2:Math.min(0,r)||Math.max(0,s),o>i?(i+o)/2:Math.min(0,i)||Math.max(0,o))}function Yi(){var e=tf,t=nf,n=of,r=rf,s=sf,i=[0,1/0],o=[[-1/0,-1/0],[1/0,1/0]],a=250,l=Vu,c=jn("start","zoom","end"),u,d,m,f=500,y=150,p=0,v=10;function x(g){g.property("__zoom",ds).on("wheel.zoom",L,{passive:!1}).on("mousedown.zoom",S).on("dblclick.zoom",O).filter(s).on("touchstart.zoom",P).on("touchmove.zoom",V).on("touchend.zoom touchcancel.zoom",F).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}x.transform=function(g,$,N,I){var H=g.selection?g.selection():g;H.property("__zoom",ds),g!==H?j(g,$,N,I):H.interrupt().each(function(){C(this,arguments).event(I).start().zoom(null,typeof $=="function"?$.apply(this,arguments):$).end()})},x.scaleBy=function(g,$,N,I){x.scaleTo(g,function(){var H=this.__zoom.k,k=typeof $=="function"?$.apply(this,arguments):$;return H*k},N,I)},x.scaleTo=function(g,$,N,I){x.transform(g,function(){var H=t.apply(this,arguments),k=this.__zoom,R=N==null?b(H):typeof N=="function"?N.apply(this,arguments):N,D=k.invert(R),z=typeof $=="function"?$.apply(this,arguments):$;return n(M(_(k,z),R,D),H,o)},N,I)},x.translateBy=function(g,$,N,I){x.transform(g,function(){return n(this.__zoom.translate(typeof $=="function"?$.apply(this,arguments):$,typeof N=="function"?N.apply(this,arguments):N),t.apply(this,arguments),o)},null,I)},x.translateTo=function(g,$,N,I,H){x.transform(g,function(){var k=t.apply(this,arguments),R=this.__zoom,D=I==null?b(k):typeof I=="function"?I.apply(this,arguments):I;return n(Fe.translate(D[0],D[1]).scale(R.k).translate(typeof $=="function"?-$.apply(this,arguments):-$,typeof N=="function"?-N.apply(this,arguments):-N),k,o)},I,H)};function _(g,$){return $=Math.max(i[0],Math.min(i[1],$)),$===g.k?g:new He($,g.x,g.y)}function M(g,$,N){var I=$[0]-N[0]*g.k,H=$[1]-N[1]*g.k;return I===g.x&&H===g.y?g:new He(g.k,I,H)}function b(g){return[(+g[0][0]+ +g[1][0])/2,(+g[0][1]+ +g[1][1])/2]}function j(g,$,N,I){g.on("start.zoom",function(){C(this,arguments).event(I).start()}).on("interrupt.zoom end.zoom",function(){C(this,arguments).event(I).end()}).tween("zoom",function(){var H=this,k=arguments,R=C(H,k).event(I),D=t.apply(H,k),z=N==null?b(D):typeof N=="function"?N.apply(H,k):N,Y=Math.max(D[1][0]-D[0][0],D[1][1]-D[0][1]),G=H.__zoom,K=typeof $=="function"?$.apply(H,k):$,J=l(G.invert(z).concat(Y/G.k),K.invert(z).concat(Y/K.k));return function(ee){if(ee===1)ee=K;else{var ie=J(ee),te=Y/ie[2];ee=new He(te,z[0]-ie[0]*te,z[1]-ie[1]*te)}R.zoom(null,ee)}})}function C(g,$,N){return!N&&g.__zooming||new E(g,$)}function E(g,$){this.that=g,this.args=$,this.active=0,this.sourceEvent=null,this.extent=t.apply(g,$),this.taps=0}E.prototype={event:function(g){return g&&(this.sourceEvent=g),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(g,$){return this.mouse&&g!=="mouse"&&(this.mouse[1]=$.invert(this.mouse[0])),this.touch0&&g!=="touch"&&(this.touch0[1]=$.invert(this.touch0[0])),this.touch1&&g!=="touch"&&(this.touch1[1]=$.invert(this.touch1[0])),this.that.__zoom=$,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(g){var $=Ne(this.that).datum();c.call(g,this.that,new ef(g,{sourceEvent:this.sourceEvent,target:x,transform:this.that.__zoom,dispatch:c}),$)}};function L(g,...$){if(!e.apply(this,arguments))return;var N=C(this,$).event(g),I=this.__zoom,H=Math.max(i[0],Math.min(i[1],I.k*Math.pow(2,r.apply(this,arguments)))),k=$e(g);if(N.wheel)(N.mouse[0][0]!==k[0]||N.mouse[0][1]!==k[1])&&(N.mouse[1]=I.invert(N.mouse[0]=k)),clearTimeout(N.wheel);else{if(I.k===H)return;N.mouse=[k,I.invert(k)],fn(this),N.start()}At(g),N.wheel=setTimeout(R,y),N.zoom("mouse",n(M(_(I,H),N.mouse[0],N.mouse[1]),N.extent,o));function R(){N.wheel=null,N.end()}}function S(g,...$){if(m||!e.apply(this,arguments))return;var N=g.currentTarget,I=C(this,$,!0).event(g),H=Ne(g.view).on("mousemove.zoom",z,!0).on("mouseup.zoom",Y,!0),k=$e(g,N),R=g.clientX,D=g.clientY;Mi(g.view),Bn(g),I.mouse=[k,this.__zoom.invert(k)],fn(this),I.start();function z(G){if(At(G),!I.moved){var K=G.clientX-R,J=G.clientY-D;I.moved=K*K+J*J>p}I.event(G).zoom("mouse",n(M(I.that.__zoom,I.mouse[0]=$e(G,N),I.mouse[1]),I.extent,o))}function Y(G){H.on("mousemove.zoom mouseup.zoom",null),Ai(G.view,I.moved),At(G),I.event(G).end()}}function O(g,...$){if(e.apply(this,arguments)){var N=this.__zoom,I=$e(g.changedTouches?g.changedTouches[0]:g,this),H=N.invert(I),k=N.k*(g.shiftKey?.5:2),R=n(M(_(N,k),I,H),t.apply(this,$),o);At(g),a>0?Ne(this).transition().duration(a).call(j,R,I,g):Ne(this).call(x.transform,R,I,g)}}function P(g,...$){if(e.apply(this,arguments)){var N=g.touches,I=N.length,H=C(this,$,g.changedTouches.length===I).event(g),k,R,D,z;for(Bn(g),R=0;R"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:e=>`Node type "${e}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:e=>`The old edge with id=${e} does not exist.`,error009:e=>`Marker type "${e}" doesn't exist.`,error008:(e,t)=>`Couldn't create edge for ${e?"target":"source"} handle id: "${e?t.targetHandle:t.sourceHandle}", edge id: ${t.id}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:e=>`Edge type "${e}" not found. Using fallback type "default".`,error012:e=>`Node with id "${e}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`},Wi=Be.error001();function ae(e,t){const n=w.useContext(An);if(n===null)throw new Error(Wi);return yi(n,e,t)}const pe=()=>{const e=w.useContext(An);if(e===null)throw new Error(Wi);return w.useMemo(()=>({getState:e.getState,setState:e.setState,subscribe:e.subscribe,destroy:e.destroy}),[e])},lf=e=>e.userSelectionActive?"none":"all";function Sr({position:e,children:t,className:n,style:r,...s}){const i=ae(lf),o=`${e}`.split("-");return A.createElement("div",{className:ye(["react-flow__panel",n,...o]),style:{...r,pointerEvents:i},...s},t)}function cf({proOptions:e,position:t="bottom-right"}){return e!=null&&e.hideAttribution?null:A.createElement(Sr,{position:t,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://reactflow.dev/pro"},A.createElement("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution"},"React Flow"))}const uf=({x:e,y:t,label:n,labelStyle:r={},labelShowBg:s=!0,labelBgStyle:i={},labelBgPadding:o=[2,4],labelBgBorderRadius:a=2,children:l,className:c,...u})=>{const d=w.useRef(null),[m,f]=w.useState({x:0,y:0,width:0,height:0}),y=ye(["react-flow__edge-textwrapper",c]);return w.useEffect(()=>{if(d.current){const p=d.current.getBBox();f({x:p.x,y:p.y,width:p.width,height:p.height})}},[n]),typeof n>"u"||!n?null:A.createElement("g",{transform:`translate(${e-m.width/2} ${t-m.height/2})`,className:y,visibility:m.width?"visible":"hidden",...u},s&&A.createElement("rect",{width:m.width+2*o[0],x:-o[0],y:-o[1],height:m.height+2*o[1],className:"react-flow__edge-textbg",style:i,rx:a,ry:a}),A.createElement("text",{className:"react-flow__edge-text",y:m.height/2,dy:"0.3em",ref:d,style:r},n),l)};var df=w.memo(uf);const kr=e=>({width:e.offsetWidth,height:e.offsetHeight}),kt=(e,t=0,n=1)=>Math.min(Math.max(e,t),n),jr=(e={x:0,y:0},t)=>({x:kt(e.x,t[0][0],t[1][0]),y:kt(e.y,t[0][1],t[1][1])}),fs=(e,t,n)=>en?-kt(Math.abs(e-n),1,50)/50:0,Ui=(e,t)=>{const n=fs(e.x,35,t.width-35)*20,r=fs(e.y,35,t.height-35)*20;return[n,r]},Xi=e=>{var t;return((t=e.getRootNode)==null?void 0:t.call(e))||(window==null?void 0:window.document)},qi=(e,t)=>({x:Math.min(e.x,t.x),y:Math.min(e.y,t.y),x2:Math.max(e.x2,t.x2),y2:Math.max(e.y2,t.y2)}),Gt=({x:e,y:t,width:n,height:r})=>({x:e,y:t,x2:e+n,y2:t+r}),Ki=({x:e,y:t,x2:n,y2:r})=>({x:e,y:t,width:n-e,height:r-t}),hs=e=>({...e.positionAbsolute||{x:0,y:0},width:e.width||0,height:e.height||0}),ff=(e,t)=>Ki(qi(Gt(e),Gt(t))),ur=(e,t)=>{const n=Math.max(0,Math.min(e.x+e.width,t.x+t.width)-Math.max(e.x,t.x)),r=Math.max(0,Math.min(e.y+e.height,t.y+t.height)-Math.max(e.y,t.y));return Math.ceil(n*r)},hf=e=>Se(e.width)&&Se(e.height)&&Se(e.x)&&Se(e.y),Se=e=>!isNaN(e)&&isFinite(e),le=Symbol.for("internals"),Zi=["Enter"," ","Escape"],mf=(e,t)=>{},pf=e=>"nativeEvent"in e;function dr(e){var s,i;const t=pf(e)?e.nativeEvent:e,n=((i=(s=t.composedPath)==null?void 0:s.call(t))==null?void 0:i[0])||e.target;return["INPUT","SELECT","TEXTAREA"].includes(n==null?void 0:n.nodeName)||(n==null?void 0:n.hasAttribute("contenteditable"))||!!(n!=null&&n.closest(".nokey"))}const Qi=e=>"clientX"in e,Ke=(e,t)=>{var i,o;const n=Qi(e),r=n?e.clientX:(i=e.touches)==null?void 0:i[0].clientX,s=n?e.clientY:(o=e.touches)==null?void 0:o[0].clientY;return{x:r-((t==null?void 0:t.left)??0),y:s-((t==null?void 0:t.top)??0)}},wn=()=>{var e;return typeof navigator<"u"&&((e=navigator==null?void 0:navigator.userAgent)==null?void 0:e.indexOf("Mac"))>=0},qt=({id:e,path:t,labelX:n,labelY:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f=20})=>A.createElement(A.Fragment,null,A.createElement("path",{id:e,style:u,d:t,fill:"none",className:"react-flow__edge-path",markerEnd:d,markerStart:m}),f&&A.createElement("path",{d:t,fill:"none",strokeOpacity:0,strokeWidth:f,className:"react-flow__edge-interaction"}),s&&Se(n)&&Se(r)?A.createElement(df,{x:n,y:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c}):null);qt.displayName="BaseEdge";function It(e,t,n){return n===void 0?n:r=>{const s=t().edges.find(i=>i.id===e);s&&n(r,{...s})}}function Ji({sourceX:e,sourceY:t,targetX:n,targetY:r}){const s=Math.abs(n-e)/2,i=n{const[v,x,_]=to({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:i});return A.createElement(qt,{path:v,labelX:x,labelY:_,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:y,interactionWidth:p})});Cr.displayName="SimpleBezierEdge";const ps={[Z.Left]:{x:-1,y:0},[Z.Right]:{x:1,y:0},[Z.Top]:{x:0,y:-1},[Z.Bottom]:{x:0,y:1}},gf=({source:e,sourcePosition:t=Z.Bottom,target:n})=>t===Z.Left||t===Z.Right?e.xMath.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2));function yf({source:e,sourcePosition:t=Z.Bottom,target:n,targetPosition:r=Z.Top,center:s,offset:i}){const o=ps[t],a=ps[r],l={x:e.x+o.x*i,y:e.y+o.y*i},c={x:n.x+a.x*i,y:n.y+a.y*i},u=gf({source:l,sourcePosition:t,target:c}),d=u.x!==0?"x":"y",m=u[d];let f=[],y,p;const v={x:0,y:0},x={x:0,y:0},[_,M,b,j]=Ji({sourceX:e.x,sourceY:e.y,targetX:n.x,targetY:n.y});if(o[d]*a[d]===-1){y=s.x??_,p=s.y??M;const E=[{x:y,y:l.y},{x:y,y:c.y}],L=[{x:l.x,y:p},{x:c.x,y:p}];o[d]===m?f=d==="x"?E:L:f=d==="x"?L:E}else{const E=[{x:l.x,y:c.y}],L=[{x:c.x,y:l.y}];if(d==="x"?f=o.x===m?L:E:f=o.y===m?E:L,t===r){const F=Math.abs(e[d]-n[d]);if(F<=i){const g=Math.min(i-1,i-F);o[d]===m?v[d]=(l[d]>e[d]?-1:1)*g:x[d]=(c[d]>n[d]?-1:1)*g}}if(t!==r){const F=d==="x"?"y":"x",g=o[d]===a[F],$=l[F]>c[F],N=l[F]=V?(y=(S.x+O.x)/2,p=f[0].y):(y=f[0].x,p=(S.y+O.y)/2)}return[[e,{x:l.x+v.x,y:l.y+v.y},...f,{x:c.x+x.x,y:c.y+x.y},n],y,p,b,j]}function xf(e,t,n,r){const s=Math.min(gs(e,t)/2,gs(t,n)/2,r),{x:i,y:o}=t;if(e.x===i&&i===n.x||e.y===o&&o===n.y)return`L${i} ${o}`;if(e.y===o){const c=e.x{let M="";return _>0&&_{const[x,_,M]=vn({sourceX:e,sourceY:t,sourcePosition:d,targetX:n,targetY:r,targetPosition:m,borderRadius:p==null?void 0:p.borderRadius,offset:p==null?void 0:p.offset});return A.createElement(qt,{path:x,labelX:_,labelY:M,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:f,markerStart:y,interactionWidth:v})});In.displayName="SmoothStepEdge";const $r=w.memo(e=>{var t;return A.createElement(In,{...e,pathOptions:w.useMemo(()=>{var n;return{borderRadius:0,offset:(n=e.pathOptions)==null?void 0:n.offset}},[(t=e.pathOptions)==null?void 0:t.offset])})});$r.displayName="StepEdge";function wf({sourceX:e,sourceY:t,targetX:n,targetY:r}){const[s,i,o,a]=Ji({sourceX:e,sourceY:t,targetX:n,targetY:r});return[`M ${e},${t}L ${n},${r}`,s,i,o,a]}const Mr=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f})=>{const[y,p,v]=wf({sourceX:e,sourceY:t,targetX:n,targetY:r});return A.createElement(qt,{path:y,labelX:p,labelY:v,label:s,labelStyle:i,labelShowBg:o,labelBgStyle:a,labelBgPadding:l,labelBgBorderRadius:c,style:u,markerEnd:d,markerStart:m,interactionWidth:f})});Mr.displayName="StraightEdge";function rn(e,t){return e>=0?.5*e:t*25*Math.sqrt(-e)}function ys({pos:e,x1:t,y1:n,x2:r,y2:s,c:i}){switch(e){case Z.Left:return[t-rn(t-r,i),n];case Z.Right:return[t+rn(r-t,i),n];case Z.Top:return[t,n-rn(n-s,i)];case Z.Bottom:return[t,n+rn(s-n,i)]}}function no({sourceX:e,sourceY:t,sourcePosition:n=Z.Bottom,targetX:r,targetY:s,targetPosition:i=Z.Top,curvature:o=.25}){const[a,l]=ys({pos:n,x1:e,y1:t,x2:r,y2:s,c:o}),[c,u]=ys({pos:i,x1:r,y1:s,x2:e,y2:t,c:o}),[d,m,f,y]=eo({sourceX:e,sourceY:t,targetX:r,targetY:s,sourceControlX:a,sourceControlY:l,targetControlX:c,targetControlY:u});return[`M${e},${t} C${a},${l} ${c},${u} ${r},${s}`,d,m,f,y]}const _n=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,sourcePosition:s=Z.Bottom,targetPosition:i=Z.Top,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:y,pathOptions:p,interactionWidth:v})=>{const[x,_,M]=no({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:i,curvature:p==null?void 0:p.curvature});return A.createElement(qt,{path:x,labelX:_,labelY:M,label:o,labelStyle:a,labelShowBg:l,labelBgStyle:c,labelBgPadding:u,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:y,interactionWidth:v})});_n.displayName="BezierEdge";const Ar=w.createContext(null),bf=Ar.Provider;Ar.Consumer;const vf=()=>w.useContext(Ar),_f=e=>"id"in e&&"source"in e&&"target"in e,Ef=({source:e,sourceHandle:t,target:n,targetHandle:r})=>`reactflow__edge-${e}${t||""}-${n}${r||""}`,fr=(e,t)=>typeof e>"u"?"":typeof e=="string"?e:`${t?`${t}__`:""}${Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("&")}`,Nf=(e,t)=>t.some(n=>n.source===e.source&&n.target===e.target&&(n.sourceHandle===e.sourceHandle||!n.sourceHandle&&!e.sourceHandle)&&(n.targetHandle===e.targetHandle||!n.targetHandle&&!e.targetHandle)),Sf=(e,t)=>{if(!e.source||!e.target)return t;let n;return _f(e)?n={...e}:n={...e,id:Ef(e)},Nf(n,t)?t:t.concat(n)},hr=({x:e,y:t},[n,r,s],i,[o,a])=>{const l={x:(e-n)/s,y:(t-r)/s};return i?{x:o*Math.round(l.x/o),y:a*Math.round(l.y/a)}:l},ro=({x:e,y:t},[n,r,s])=>({x:e*s+n,y:t*s+r}),lt=(e,t=[0,0])=>{if(!e)return{x:0,y:0,positionAbsolute:{x:0,y:0}};const n=(e.width??0)*t[0],r=(e.height??0)*t[1],s={x:e.position.x-n,y:e.position.y-r};return{...s,positionAbsolute:e.positionAbsolute?{x:e.positionAbsolute.x-n,y:e.positionAbsolute.y-r}:s}},Kt=(e,t=[0,0])=>{if(e.length===0)return{x:0,y:0,width:0,height:0};const n=e.reduce((r,s)=>{const{x:i,y:o}=lt(s,t).positionAbsolute;return qi(r,Gt({x:i,y:o,width:s.width||0,height:s.height||0}))},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Ki(n)},kf=(e,t=[0,0])=>(console.warn("[DEPRECATED] `getRectOfNodes` is deprecated. Instead use `getNodesBounds` https://reactflow.dev/api-reference/utils/get-nodes-bounds."),Kt(e,t)),so=(e,t,[n,r,s]=[0,0,1],i=!1,o=!1,a=[0,0])=>{const l={x:(t.x-n)/s,y:(t.y-r)/s,width:t.width/s,height:t.height/s},c=[];return e.forEach(u=>{const{width:d,height:m,selectable:f=!0,hidden:y=!1}=u;if(o&&!f||y)return!1;const{positionAbsolute:p}=lt(u,a),v={x:p.x,y:p.y,width:d||0,height:m||0},x=ur(l,v),_=typeof d>"u"||typeof m>"u"||d===null||m===null,M=i&&x>0,b=(d||0)*(m||0);(_||M||x>=b||u.dragging)&&c.push(u)}),c},io=(e,t)=>{const n=e.map(r=>r.id);return t.filter(r=>n.includes(r.source)||n.includes(r.target))},oo=(e,t,n,r,s,i=.1)=>{const o=t/(e.width*(1+i)),a=n/(e.height*(1+i)),l=Math.min(o,a),c=kt(l,r,s),u=e.x+e.width/2,d=e.y+e.height/2,m=t/2-u*c,f=n/2-d*c;return{x:m,y:f,zoom:c}},st=(e,t=0)=>e.transition().duration(t);function xs(e,t,n,r){return(t[n]||[]).reduce((s,i)=>{var o,a;return`${e.id}-${i.id}-${n}`!==r&&s.push({id:i.id||null,type:n,nodeId:e.id,x:(((o=e.positionAbsolute)==null?void 0:o.x)??0)+i.x+i.width/2,y:(((a=e.positionAbsolute)==null?void 0:a.y)??0)+i.y+i.height/2}),s},[])}function jf(e,t,n,r,s,i){const{x:o,y:a}=Ke(e),c=t.elementsFromPoint(o,a).find(y=>y.classList.contains("react-flow__handle"));if(c){const y=c.getAttribute("data-nodeid");if(y){const p=Ir(void 0,c),v=c.getAttribute("data-handleid"),x=i({nodeId:y,id:v,type:p});if(x){const _=s.find(M=>M.nodeId===y&&M.type===p&&M.id===v);return{handle:{id:v,type:p,nodeId:y,x:(_==null?void 0:_.x)||n.x,y:(_==null?void 0:_.y)||n.y},validHandleResult:x}}}}let u=[],d=1/0;if(s.forEach(y=>{const p=Math.sqrt((y.x-n.x)**2+(y.y-n.y)**2);if(p<=r){const v=i(y);p<=d&&(py.isValid),f=u.some(({handle:y})=>y.type==="target");return u.find(({handle:y,validHandleResult:p})=>f?y.type==="target":m?p.isValid:!0)||u[0]}const Cf={source:null,target:null,sourceHandle:null,targetHandle:null},ao=()=>({handleDomNode:null,isValid:!1,connection:Cf,endHandle:null});function lo(e,t,n,r,s,i,o){const a=s==="target",l=o.querySelector(`.react-flow__handle[data-id="${e==null?void 0:e.nodeId}-${e==null?void 0:e.id}-${e==null?void 0:e.type}"]`),c={...ao(),handleDomNode:l};if(l){const u=Ir(void 0,l),d=l.getAttribute("data-nodeid"),m=l.getAttribute("data-handleid"),f=l.classList.contains("connectable"),y=l.classList.contains("connectableend"),p={source:a?d:n,sourceHandle:a?m:r,target:a?n:d,targetHandle:a?r:m};c.connection=p,f&&y&&(t===ut.Strict?a&&u==="source"||!a&&u==="target":d!==n||m!==r)&&(c.endHandle={nodeId:d,handleId:m,type:u},c.isValid=i(p))}return c}function $f({nodes:e,nodeId:t,handleId:n,handleType:r}){return e.reduce((s,i)=>{if(i[le]){const{handleBounds:o}=i[le];let a=[],l=[];o&&(a=xs(i,o,"source",`${t}-${n}-${r}`),l=xs(i,o,"target",`${t}-${n}-${r}`)),s.push(...a,...l)}return s},[])}function Ir(e,t){return e||(t!=null&&t.classList.contains("target")?"target":t!=null&&t.classList.contains("source")?"source":null)}function Gn(e){e==null||e.classList.remove("valid","connecting","react-flow__handle-valid","react-flow__handle-connecting")}function Mf(e,t){let n=null;return t?n="valid":e&&!t&&(n="invalid"),n}function co({event:e,handleId:t,nodeId:n,onConnect:r,isTarget:s,getState:i,setState:o,isValidConnection:a,edgeUpdaterType:l,onReconnectEnd:c}){const u=Xi(e.target),{connectionMode:d,domNode:m,autoPanOnConnect:f,connectionRadius:y,onConnectStart:p,panBy:v,getNodes:x,cancelConnection:_}=i();let M=0,b;const{x:j,y:C}=Ke(e),E=u==null?void 0:u.elementFromPoint(j,C),L=Ir(l,E),S=m==null?void 0:m.getBoundingClientRect();if(!S||!L)return;let O,P=Ke(e,S),V=!1,F=null,g=!1,$=null;const N=$f({nodes:x(),nodeId:n,handleId:t,handleType:L}),I=()=>{if(!f)return;const[R,D]=Ui(P,S);v({x:R,y:D}),M=requestAnimationFrame(I)};o({connectionPosition:P,connectionStatus:null,connectionNodeId:n,connectionHandleId:t,connectionHandleType:L,connectionStartHandle:{nodeId:n,handleId:t,type:L},connectionEndHandle:null}),p==null||p(e,{nodeId:n,handleId:t,handleType:L});function H(R){const{transform:D}=i();P=Ke(R,S);const{handle:z,validHandleResult:Y}=jf(R,u,hr(P,D,!1,[1,1]),y,N,G=>lo(G,d,n,t,s?"target":"source",a,u));if(b=z,V||(I(),V=!0),$=Y.handleDomNode,F=Y.connection,g=Y.isValid,o({connectionPosition:b&&g?ro({x:b.x,y:b.y},D):P,connectionStatus:Mf(!!b,g),connectionEndHandle:Y.endHandle}),!b&&!g&&!$)return Gn(O);F.source!==F.target&&$&&(Gn(O),O=$,$.classList.add("connecting","react-flow__handle-connecting"),$.classList.toggle("valid",g),$.classList.toggle("react-flow__handle-valid",g))}function k(R){var D,z;(b||$)&&F&&g&&(r==null||r(F)),(z=(D=i()).onConnectEnd)==null||z.call(D,R),l&&(c==null||c(R)),Gn(O),_(),cancelAnimationFrame(M),V=!1,g=!1,F=null,$=null,u.removeEventListener("mousemove",H),u.removeEventListener("mouseup",k),u.removeEventListener("touchmove",H),u.removeEventListener("touchend",k)}u.addEventListener("mousemove",H),u.addEventListener("mouseup",k),u.addEventListener("touchmove",H),u.addEventListener("touchend",k)}const ws=()=>!0,Af=e=>({connectionStartHandle:e.connectionStartHandle,connectOnClick:e.connectOnClick,noPanClassName:e.noPanClassName}),If=(e,t,n)=>r=>{const{connectionStartHandle:s,connectionEndHandle:i,connectionClickStartHandle:o}=r;return{connecting:(s==null?void 0:s.nodeId)===e&&(s==null?void 0:s.handleId)===t&&(s==null?void 0:s.type)===n||(i==null?void 0:i.nodeId)===e&&(i==null?void 0:i.handleId)===t&&(i==null?void 0:i.type)===n,clickConnecting:(o==null?void 0:o.nodeId)===e&&(o==null?void 0:o.handleId)===t&&(o==null?void 0:o.type)===n}},uo=w.forwardRef(({type:e="source",position:t=Z.Top,isValidConnection:n,isConnectable:r=!0,isConnectableStart:s=!0,isConnectableEnd:i=!0,id:o,onConnect:a,children:l,className:c,onMouseDown:u,onTouchStart:d,...m},f)=>{var S,O;const y=o||null,p=e==="target",v=pe(),x=vf(),{connectOnClick:_,noPanClassName:M}=ae(Af,ge),{connecting:b,clickConnecting:j}=ae(If(x,y,e),ge);x||(O=(S=v.getState()).onError)==null||O.call(S,"010",Be.error010());const C=P=>{const{defaultEdgeOptions:V,onConnect:F,hasDefaultEdges:g}=v.getState(),$={...V,...P};if(g){const{edges:N,setEdges:I}=v.getState();I(Sf($,N))}F==null||F($),a==null||a($)},E=P=>{if(!x)return;const V=Qi(P);s&&(V&&P.button===0||!V)&&co({event:P,handleId:y,nodeId:x,onConnect:C,isTarget:p,getState:v.getState,setState:v.setState,isValidConnection:n||v.getState().isValidConnection||ws}),V?u==null||u(P):d==null||d(P)},L=P=>{const{onClickConnectStart:V,onClickConnectEnd:F,connectionClickStartHandle:g,connectionMode:$,isValidConnection:N}=v.getState();if(!x||!g&&!s)return;if(!g){V==null||V(P,{nodeId:x,handleId:y,handleType:e}),v.setState({connectionClickStartHandle:{nodeId:x,type:e,handleId:y}});return}const I=Xi(P.target),H=n||N||ws,{connection:k,isValid:R}=lo({nodeId:x,id:y,type:e},$,g.nodeId,g.handleId||null,g.type,H,I);R&&C(k),F==null||F(P),v.setState({connectionClickStartHandle:null})};return A.createElement("div",{"data-handleid":y,"data-nodeid":x,"data-handlepos":t,"data-id":`${x}-${y}-${e}`,className:ye(["react-flow__handle",`react-flow__handle-${t}`,"nodrag",M,c,{source:!p,target:p,connectable:r,connectablestart:s,connectableend:i,connecting:j,connectionindicator:r&&(s&&!b||i&&b)}]),onMouseDown:E,onTouchStart:E,onClick:_?L:void 0,ref:f,...m},l)});uo.displayName="Handle";var qe=w.memo(uo);const fo=({data:e,isConnectable:t,targetPosition:n=Z.Top,sourcePosition:r=Z.Bottom})=>A.createElement(A.Fragment,null,A.createElement(qe,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label,A.createElement(qe,{type:"source",position:r,isConnectable:t}));fo.displayName="DefaultNode";var mr=w.memo(fo);const ho=({data:e,isConnectable:t,sourcePosition:n=Z.Bottom})=>A.createElement(A.Fragment,null,e==null?void 0:e.label,A.createElement(qe,{type:"source",position:n,isConnectable:t}));ho.displayName="InputNode";var mo=w.memo(ho);const po=({data:e,isConnectable:t,targetPosition:n=Z.Top})=>A.createElement(A.Fragment,null,A.createElement(qe,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label);po.displayName="OutputNode";var go=w.memo(po);const Rr=()=>null;Rr.displayName="GroupNode";const Rf=e=>({selectedNodes:e.getNodes().filter(t=>t.selected),selectedEdges:e.edges.filter(t=>t.selected).map(t=>({...t}))}),sn=e=>e.id;function Tf(e,t){return ge(e.selectedNodes.map(sn),t.selectedNodes.map(sn))&&ge(e.selectedEdges.map(sn),t.selectedEdges.map(sn))}const yo=w.memo(({onSelectionChange:e})=>{const t=pe(),{selectedNodes:n,selectedEdges:r}=ae(Rf,Tf);return w.useEffect(()=>{const s={nodes:n,edges:r};e==null||e(s),t.getState().onSelectionChange.forEach(i=>i(s))},[n,r,e]),null});yo.displayName="SelectionListener";const Of=e=>!!e.onSelectionChange;function Df({onSelectionChange:e}){const t=ae(Of);return e||t?A.createElement(yo,{onSelectionChange:e}):null}const Lf=e=>({setNodes:e.setNodes,setEdges:e.setEdges,setDefaultNodesAndEdges:e.setDefaultNodesAndEdges,setMinZoom:e.setMinZoom,setMaxZoom:e.setMaxZoom,setTranslateExtent:e.setTranslateExtent,setNodeExtent:e.setNodeExtent,reset:e.reset});function mt(e,t){w.useEffect(()=>{typeof e<"u"&&t(e)},[e])}function ne(e,t,n){w.useEffect(()=>{typeof t<"u"&&n({[e]:t})},[t])}const Pf=({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:s,onConnectStart:i,onConnectEnd:o,onClickConnectStart:a,onClickConnectEnd:l,nodesDraggable:c,nodesConnectable:u,nodesFocusable:d,edgesFocusable:m,edgesUpdatable:f,elevateNodesOnSelect:y,minZoom:p,maxZoom:v,nodeExtent:x,onNodesChange:_,onEdgesChange:M,elementsSelectable:b,connectionMode:j,snapGrid:C,snapToGrid:E,translateExtent:L,connectOnClick:S,defaultEdgeOptions:O,fitView:P,fitViewOptions:V,onNodesDelete:F,onEdgesDelete:g,onNodeDrag:$,onNodeDragStart:N,onNodeDragStop:I,onSelectionDrag:H,onSelectionDragStart:k,onSelectionDragStop:R,noPanClassName:D,nodeOrigin:z,rfId:Y,autoPanOnConnect:G,autoPanOnNodeDrag:K,onError:J,connectionRadius:ee,isValidConnection:ie,nodeDragThreshold:te})=>{const{setNodes:re,setEdges:de,setDefaultNodesAndEdges:B,setMinZoom:q,setMaxZoom:W,setTranslateExtent:Q,setNodeExtent:U,reset:X}=ae(Lf,ge),T=pe();return w.useEffect(()=>{const se=r==null?void 0:r.map(ce=>({...ce,...O}));return B(n,se),()=>{X()}},[]),ne("defaultEdgeOptions",O,T.setState),ne("connectionMode",j,T.setState),ne("onConnect",s,T.setState),ne("onConnectStart",i,T.setState),ne("onConnectEnd",o,T.setState),ne("onClickConnectStart",a,T.setState),ne("onClickConnectEnd",l,T.setState),ne("nodesDraggable",c,T.setState),ne("nodesConnectable",u,T.setState),ne("nodesFocusable",d,T.setState),ne("edgesFocusable",m,T.setState),ne("edgesUpdatable",f,T.setState),ne("elementsSelectable",b,T.setState),ne("elevateNodesOnSelect",y,T.setState),ne("snapToGrid",E,T.setState),ne("snapGrid",C,T.setState),ne("onNodesChange",_,T.setState),ne("onEdgesChange",M,T.setState),ne("connectOnClick",S,T.setState),ne("fitViewOnInit",P,T.setState),ne("fitViewOnInitOptions",V,T.setState),ne("onNodesDelete",F,T.setState),ne("onEdgesDelete",g,T.setState),ne("onNodeDrag",$,T.setState),ne("onNodeDragStart",N,T.setState),ne("onNodeDragStop",I,T.setState),ne("onSelectionDrag",H,T.setState),ne("onSelectionDragStart",k,T.setState),ne("onSelectionDragStop",R,T.setState),ne("noPanClassName",D,T.setState),ne("nodeOrigin",z,T.setState),ne("rfId",Y,T.setState),ne("autoPanOnConnect",G,T.setState),ne("autoPanOnNodeDrag",K,T.setState),ne("onError",J,T.setState),ne("connectionRadius",ee,T.setState),ne("isValidConnection",ie,T.setState),ne("nodeDragThreshold",te,T.setState),mt(e,re),mt(t,de),mt(p,q),mt(v,W),mt(L,Q),mt(x,U),null},bs={display:"none"},zf={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},xo="react-flow__node-desc",wo="react-flow__edge-desc",Hf="react-flow__aria-live",Ff=e=>e.ariaLiveMessage;function Vf({rfId:e}){const t=ae(Ff);return A.createElement("div",{id:`${Hf}-${e}`,"aria-live":"assertive","aria-atomic":"true",style:zf},t)}function Bf({rfId:e,disableKeyboardA11y:t}){return A.createElement(A.Fragment,null,A.createElement("div",{id:`${xo}-${e}`,style:bs},"Press enter or space to select a node.",!t&&"You can then use the arrow keys to move the node around."," Press delete to remove it and escape to cancel."," "),A.createElement("div",{id:`${wo}-${e}`,style:bs},"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel."),!t&&A.createElement(Vf,{rfId:e}))}var Wt=(e=null,t={actInsideInputWithModifier:!0})=>{const[n,r]=w.useState(!1),s=w.useRef(!1),i=w.useRef(new Set([])),[o,a]=w.useMemo(()=>{if(e!==null){const c=(Array.isArray(e)?e:[e]).filter(d=>typeof d=="string").map(d=>d.split("+")),u=c.reduce((d,m)=>d.concat(...m),[]);return[c,u]}return[[],[]]},[e]);return w.useEffect(()=>{const l=typeof document<"u"?document:null,c=(t==null?void 0:t.target)||l;if(e!==null){const u=f=>{if(s.current=f.ctrlKey||f.metaKey||f.shiftKey,(!s.current||s.current&&!t.actInsideInputWithModifier)&&dr(f))return!1;const p=_s(f.code,a);i.current.add(f[p]),vs(o,i.current,!1)&&(f.preventDefault(),r(!0))},d=f=>{if((!s.current||s.current&&!t.actInsideInputWithModifier)&&dr(f))return!1;const p=_s(f.code,a);vs(o,i.current,!0)?(r(!1),i.current.clear()):i.current.delete(f[p]),f.key==="Meta"&&i.current.clear(),s.current=!1},m=()=>{i.current.clear(),r(!1)};return c==null||c.addEventListener("keydown",u),c==null||c.addEventListener("keyup",d),window.addEventListener("blur",m),()=>{c==null||c.removeEventListener("keydown",u),c==null||c.removeEventListener("keyup",d),window.removeEventListener("blur",m)}}},[e,r]),n};function vs(e,t,n){return e.filter(r=>n||r.length===t.size).some(r=>r.every(s=>t.has(s)))}function _s(e,t){return t.includes(e)?"code":"key"}function bo(e,t,n,r){var a,l;const s=e.parentNode||e.parentId;if(!s)return n;const i=t.get(s),o=lt(i,r);return bo(i,t,{x:(n.x??0)+o.x,y:(n.y??0)+o.y,z:(((a=i[le])==null?void 0:a.z)??0)>(n.z??0)?((l=i[le])==null?void 0:l.z)??0:n.z??0},r)}function vo(e,t,n){e.forEach(r=>{var i;const s=r.parentNode||r.parentId;if(s&&!e.has(s))throw new Error(`Parent node ${s} not found`);if(s||n!=null&&n[r.id]){const{x:o,y:a,z:l}=bo(r,e,{...r.position,z:((i=r[le])==null?void 0:i.z)??0},t);r.positionAbsolute={x:o,y:a},r[le].z=l,n!=null&&n[r.id]&&(r[le].isParent=!0)}})}function Yn(e,t,n,r){const s=new Map,i={},o=r?1e3:0;return e.forEach(a=>{var f;const l=(Se(a.zIndex)?a.zIndex:0)+(a.selected?o:0),c=t.get(a.id),u={...a,positionAbsolute:{x:a.position.x,y:a.position.y}},d=a.parentNode||a.parentId;d&&(i[d]=!0);const m=(c==null?void 0:c.type)&&(c==null?void 0:c.type)!==a.type;Object.defineProperty(u,le,{enumerable:!1,value:{handleBounds:m||(f=c==null?void 0:c[le])==null?void 0:f.handleBounds,z:l}}),s.set(a.id,u)}),vo(s,n,i),s}function _o(e,t={}){const{getNodes:n,width:r,height:s,minZoom:i,maxZoom:o,d3Zoom:a,d3Selection:l,fitViewOnInitDone:c,fitViewOnInit:u,nodeOrigin:d}=e(),m=t.initial&&!c&&u;if(a&&l&&(m||!t.initial)){const y=n().filter(v=>{var _;const x=t.includeHiddenNodes?v.width&&v.height:!v.hidden;return(_=t.nodes)!=null&&_.length?x&&t.nodes.some(M=>M.id===v.id):x}),p=y.every(v=>v.width&&v.height);if(y.length>0&&p){const v=Kt(y,d),{x,y:_,zoom:M}=oo(v,r,s,t.minZoom??i,t.maxZoom??o,t.padding??.1),b=Fe.translate(x,_).scale(M);return typeof t.duration=="number"&&t.duration>0?a.transform(st(l,t.duration),b):a.transform(l,b),!0}}return!1}function Gf(e,t){return e.forEach(n=>{const r=t.get(n.id);r&&t.set(r.id,{...r,[le]:r[le],selected:n.selected})}),new Map(t)}function Yf(e,t){return t.map(n=>{const r=e.find(s=>s.id===n.id);return r&&(n.selected=r.selected),n})}function on({changedNodes:e,changedEdges:t,get:n,set:r}){const{nodeInternals:s,edges:i,onNodesChange:o,onEdgesChange:a,hasDefaultNodes:l,hasDefaultEdges:c}=n();e!=null&&e.length&&(l&&r({nodeInternals:Gf(e,s)}),o==null||o(e)),t!=null&&t.length&&(c&&r({edges:Yf(t,i)}),a==null||a(t))}const pt=()=>{},Wf={zoomIn:pt,zoomOut:pt,zoomTo:pt,getZoom:()=>1,setViewport:pt,getViewport:()=>({x:0,y:0,zoom:1}),fitView:()=>!1,setCenter:pt,fitBounds:pt,project:e=>e,screenToFlowPosition:e=>e,flowToScreenPosition:e=>e,viewportInitialized:!1},Uf=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection}),Xf=()=>{const e=pe(),{d3Zoom:t,d3Selection:n}=ae(Uf,ge);return w.useMemo(()=>n&&t?{zoomIn:s=>t.scaleBy(st(n,s==null?void 0:s.duration),1.2),zoomOut:s=>t.scaleBy(st(n,s==null?void 0:s.duration),1/1.2),zoomTo:(s,i)=>t.scaleTo(st(n,i==null?void 0:i.duration),s),getZoom:()=>e.getState().transform[2],setViewport:(s,i)=>{const[o,a,l]=e.getState().transform,c=Fe.translate(s.x??o,s.y??a).scale(s.zoom??l);t.transform(st(n,i==null?void 0:i.duration),c)},getViewport:()=>{const[s,i,o]=e.getState().transform;return{x:s,y:i,zoom:o}},fitView:s=>_o(e.getState,s),setCenter:(s,i,o)=>{const{width:a,height:l,maxZoom:c}=e.getState(),u=typeof(o==null?void 0:o.zoom)<"u"?o.zoom:c,d=a/2-s*u,m=l/2-i*u,f=Fe.translate(d,m).scale(u);t.transform(st(n,o==null?void 0:o.duration),f)},fitBounds:(s,i)=>{const{width:o,height:a,minZoom:l,maxZoom:c}=e.getState(),{x:u,y:d,zoom:m}=oo(s,o,a,l,c,(i==null?void 0:i.padding)??.1),f=Fe.translate(u,d).scale(m);t.transform(st(n,i==null?void 0:i.duration),f)},project:s=>{const{transform:i,snapToGrid:o,snapGrid:a}=e.getState();return console.warn("[DEPRECATED] `project` is deprecated. Instead use `screenToFlowPosition`. There is no need to subtract the react flow bounds anymore! https://reactflow.dev/api-reference/types/react-flow-instance#screen-to-flow-position"),hr(s,i,o,a)},screenToFlowPosition:s=>{const{transform:i,snapToGrid:o,snapGrid:a,domNode:l}=e.getState();if(!l)return s;const{x:c,y:u}=l.getBoundingClientRect(),d={x:s.x-c,y:s.y-u};return hr(d,i,o,a)},flowToScreenPosition:s=>{const{transform:i,domNode:o}=e.getState();if(!o)return s;const{x:a,y:l}=o.getBoundingClientRect(),c=ro(s,i);return{x:c.x+a,y:c.y+l}},viewportInitialized:!0}:Wf,[t,n])};function Rn(){const e=Xf(),t=pe(),n=w.useCallback(()=>t.getState().getNodes().map(p=>({...p})),[]),r=w.useCallback(p=>t.getState().nodeInternals.get(p),[]),s=w.useCallback(()=>{const{edges:p=[]}=t.getState();return p.map(v=>({...v}))},[]),i=w.useCallback(p=>{const{edges:v=[]}=t.getState();return v.find(x=>x.id===p)},[]),o=w.useCallback(p=>{const{getNodes:v,setNodes:x,hasDefaultNodes:_,onNodesChange:M}=t.getState(),b=v(),j=typeof p=="function"?p(b):p;if(_)x(j);else if(M){const C=j.length===0?b.map(E=>({type:"remove",id:E.id})):j.map(E=>({item:E,type:"reset"}));M(C)}},[]),a=w.useCallback(p=>{const{edges:v=[],setEdges:x,hasDefaultEdges:_,onEdgesChange:M}=t.getState(),b=typeof p=="function"?p(v):p;if(_)x(b);else if(M){const j=b.length===0?v.map(C=>({type:"remove",id:C.id})):b.map(C=>({item:C,type:"reset"}));M(j)}},[]),l=w.useCallback(p=>{const v=Array.isArray(p)?p:[p],{getNodes:x,setNodes:_,hasDefaultNodes:M,onNodesChange:b}=t.getState();if(M){const C=[...x(),...v];_(C)}else if(b){const j=v.map(C=>({item:C,type:"add"}));b(j)}},[]),c=w.useCallback(p=>{const v=Array.isArray(p)?p:[p],{edges:x=[],setEdges:_,hasDefaultEdges:M,onEdgesChange:b}=t.getState();if(M)_([...x,...v]);else if(b){const j=v.map(C=>({item:C,type:"add"}));b(j)}},[]),u=w.useCallback(()=>{const{getNodes:p,edges:v=[],transform:x}=t.getState(),[_,M,b]=x;return{nodes:p().map(j=>({...j})),edges:v.map(j=>({...j})),viewport:{x:_,y:M,zoom:b}}},[]),d=w.useCallback(({nodes:p,edges:v})=>{const{nodeInternals:x,getNodes:_,edges:M,hasDefaultNodes:b,hasDefaultEdges:j,onNodesDelete:C,onEdgesDelete:E,onNodesChange:L,onEdgesChange:S}=t.getState(),O=(p||[]).map($=>$.id),P=(v||[]).map($=>$.id),V=_().reduce(($,N)=>{const I=N.parentNode||N.parentId,H=!O.includes(N.id)&&I&&$.find(R=>R.id===I);return(typeof N.deletable=="boolean"?N.deletable:!0)&&(O.includes(N.id)||H)&&$.push(N),$},[]),F=M.filter($=>typeof $.deletable=="boolean"?$.deletable:!0),g=F.filter($=>P.includes($.id));if(V||g){const $=io(V,F),N=[...g,...$],I=N.reduce((H,k)=>(H.includes(k.id)||H.push(k.id),H),[]);if((j||b)&&(j&&t.setState({edges:M.filter(H=>!I.includes(H.id))}),b&&(V.forEach(H=>{x.delete(H.id)}),t.setState({nodeInternals:new Map(x)}))),I.length>0&&(E==null||E(N),S&&S(I.map(H=>({id:H,type:"remove"})))),V.length>0&&(C==null||C(V),L)){const H=V.map(k=>({id:k.id,type:"remove"}));L(H)}}},[]),m=w.useCallback(p=>{const v=hf(p),x=v?null:t.getState().nodeInternals.get(p.id);return!v&&!x?[null,null,v]:[v?p:hs(x),x,v]},[]),f=w.useCallback((p,v=!0,x)=>{const[_,M,b]=m(p);return _?(x||t.getState().getNodes()).filter(j=>{if(!b&&(j.id===M.id||!j.positionAbsolute))return!1;const C=hs(j),E=ur(C,_);return v&&E>0||E>=_.width*_.height}):[]},[]),y=w.useCallback((p,v,x=!0)=>{const[_]=m(p);if(!_)return!1;const M=ur(_,v);return x&&M>0||M>=_.width*_.height},[]);return w.useMemo(()=>({...e,getNodes:n,getNode:r,getEdges:s,getEdge:i,setNodes:o,setEdges:a,addNodes:l,addEdges:c,toObject:u,deleteElements:d,getIntersectingNodes:f,isNodeIntersecting:y}),[e,n,r,s,i,o,a,l,c,u,d,f,y])}const qf={actInsideInputWithModifier:!1};var Kf=({deleteKeyCode:e,multiSelectionKeyCode:t})=>{const n=pe(),{deleteElements:r}=Rn(),s=Wt(e,qf),i=Wt(t);w.useEffect(()=>{if(s){const{edges:o,getNodes:a}=n.getState(),l=a().filter(u=>u.selected),c=o.filter(u=>u.selected);r({nodes:l,edges:c}),n.setState({nodesSelectionActive:!1})}},[s]),w.useEffect(()=>{n.setState({multiSelectionActive:i})},[i])};function Zf(e){const t=pe();w.useEffect(()=>{let n;const r=()=>{var i,o;if(!e.current)return;const s=kr(e.current);(s.height===0||s.width===0)&&((o=(i=t.getState()).onError)==null||o.call(i,"004",Be.error004())),t.setState({width:s.width||500,height:s.height||500})};return r(),window.addEventListener("resize",r),e.current&&(n=new ResizeObserver(()=>r()),n.observe(e.current)),()=>{window.removeEventListener("resize",r),n&&e.current&&n.unobserve(e.current)}},[])}const Tr={position:"absolute",width:"100%",height:"100%",top:0,left:0},Qf=(e,t)=>e.x!==t.x||e.y!==t.y||e.zoom!==t.k,an=e=>({x:e.x,y:e.y,zoom:e.k}),gt=(e,t)=>e.target.closest(`.${t}`),Es=(e,t)=>t===2&&Array.isArray(e)&&e.includes(2),Ns=e=>{const t=e.ctrlKey&&wn()?10:1;return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*t},Jf=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection,d3ZoomHandler:e.d3ZoomHandler,userSelectionActive:e.userSelectionActive}),eh=({onMove:e,onMoveStart:t,onMoveEnd:n,onPaneContextMenu:r,zoomOnScroll:s=!0,zoomOnPinch:i=!0,panOnScroll:o=!1,panOnScrollSpeed:a=.5,panOnScrollMode:l=ot.Free,zoomOnDoubleClick:c=!0,elementsSelectable:u,panOnDrag:d=!0,defaultViewport:m,translateExtent:f,minZoom:y,maxZoom:p,zoomActivationKeyCode:v,preventScrolling:x=!0,children:_,noWheelClassName:M,noPanClassName:b})=>{const j=w.useRef(),C=pe(),E=w.useRef(!1),L=w.useRef(!1),S=w.useRef(null),O=w.useRef({x:0,y:0,zoom:0}),{d3Zoom:P,d3Selection:V,d3ZoomHandler:F,userSelectionActive:g}=ae(Jf,ge),$=Wt(v),N=w.useRef(0),I=w.useRef(!1),H=w.useRef();return Zf(S),w.useEffect(()=>{if(S.current){const k=S.current.getBoundingClientRect(),R=Yi().scaleExtent([y,p]).translateExtent(f),D=Ne(S.current).call(R),z=Fe.translate(m.x,m.y).scale(kt(m.zoom,y,p)),Y=[[0,0],[k.width,k.height]],G=R.constrain()(z,Y,f);R.transform(D,G),R.wheelDelta(Ns),C.setState({d3Zoom:R,d3Selection:D,d3ZoomHandler:D.on("wheel.zoom"),transform:[G.x,G.y,G.k],domNode:S.current.closest(".react-flow")})}},[]),w.useEffect(()=>{V&&P&&(o&&!$&&!g?V.on("wheel.zoom",k=>{if(gt(k,M))return!1;k.preventDefault(),k.stopImmediatePropagation();const R=V.property("__zoom").k||1;if(k.ctrlKey&&i){const ie=$e(k),te=Ns(k),re=R*Math.pow(2,te);P.scaleTo(V,re,ie,k);return}const D=k.deltaMode===1?20:1;let z=l===ot.Vertical?0:k.deltaX*D,Y=l===ot.Horizontal?0:k.deltaY*D;!wn()&&k.shiftKey&&l!==ot.Vertical&&(z=k.deltaY*D,Y=0),P.translateBy(V,-(z/R)*a,-(Y/R)*a,{internal:!0});const G=an(V.property("__zoom")),{onViewportChangeStart:K,onViewportChange:J,onViewportChangeEnd:ee}=C.getState();clearTimeout(H.current),I.current||(I.current=!0,t==null||t(k,G),K==null||K(G)),I.current&&(e==null||e(k,G),J==null||J(G),H.current=setTimeout(()=>{n==null||n(k,G),ee==null||ee(G),I.current=!1},150))},{passive:!1}):typeof F<"u"&&V.on("wheel.zoom",function(k,R){if(!x&&k.type==="wheel"&&!k.ctrlKey||gt(k,M))return null;k.preventDefault(),F.call(this,k,R)},{passive:!1}))},[g,o,l,V,P,F,$,i,x,M,t,e,n]),w.useEffect(()=>{P&&P.on("start",k=>{var z,Y;if(!k.sourceEvent||k.sourceEvent.internal)return null;N.current=(z=k.sourceEvent)==null?void 0:z.button;const{onViewportChangeStart:R}=C.getState(),D=an(k.transform);E.current=!0,O.current=D,((Y=k.sourceEvent)==null?void 0:Y.type)==="mousedown"&&C.setState({paneDragging:!0}),R==null||R(D),t==null||t(k.sourceEvent,D)})},[P,t]),w.useEffect(()=>{P&&(g&&!E.current?P.on("zoom",null):g||P.on("zoom",k=>{var D;const{onViewportChange:R}=C.getState();if(C.setState({transform:[k.transform.x,k.transform.y,k.transform.k]}),L.current=!!(r&&Es(d,N.current??0)),(e||R)&&!((D=k.sourceEvent)!=null&&D.internal)){const z=an(k.transform);R==null||R(z),e==null||e(k.sourceEvent,z)}}))},[g,P,e,d,r]),w.useEffect(()=>{P&&P.on("end",k=>{if(!k.sourceEvent||k.sourceEvent.internal)return null;const{onViewportChangeEnd:R}=C.getState();if(E.current=!1,C.setState({paneDragging:!1}),r&&Es(d,N.current??0)&&!L.current&&r(k.sourceEvent),L.current=!1,(n||R)&&Qf(O.current,k.transform)){const D=an(k.transform);O.current=D,clearTimeout(j.current),j.current=setTimeout(()=>{R==null||R(D),n==null||n(k.sourceEvent,D)},o?150:0)}})},[P,o,d,n,r]),w.useEffect(()=>{P&&P.filter(k=>{const R=$||s,D=i&&k.ctrlKey;if((d===!0||Array.isArray(d)&&d.includes(1))&&k.button===1&&k.type==="mousedown"&&(gt(k,"react-flow__node")||gt(k,"react-flow__edge")))return!0;if(!d&&!R&&!o&&!c&&!i||g||!c&&k.type==="dblclick"||gt(k,M)&&k.type==="wheel"||gt(k,b)&&(k.type!=="wheel"||o&&k.type==="wheel"&&!$)||!i&&k.ctrlKey&&k.type==="wheel"||!R&&!o&&!D&&k.type==="wheel"||!d&&(k.type==="mousedown"||k.type==="touchstart")||Array.isArray(d)&&!d.includes(k.button)&&k.type==="mousedown")return!1;const z=Array.isArray(d)&&d.includes(k.button)||!k.button||k.button<=1;return(!k.ctrlKey||k.type==="wheel")&&z})},[g,P,s,i,o,c,d,u,$]),A.createElement("div",{className:"react-flow__renderer",ref:S,style:Tr},_)},th=e=>({userSelectionActive:e.userSelectionActive,userSelectionRect:e.userSelectionRect});function nh(){const{userSelectionActive:e,userSelectionRect:t}=ae(th,ge);return e&&t?A.createElement("div",{className:"react-flow__selection react-flow__container",style:{width:t.width,height:t.height,transform:`translate(${t.x}px, ${t.y}px)`}}):null}function Ss(e,t){const n=t.parentNode||t.parentId,r=e.find(s=>s.id===n);if(r){const s=t.position.x+t.width-r.width,i=t.position.y+t.height-r.height;if(s>0||i>0||t.position.x<0||t.position.y<0){if(r.style={...r.style},r.style.width=r.style.width??r.width,r.style.height=r.style.height??r.height,s>0&&(r.style.width+=s),i>0&&(r.style.height+=i),t.position.x<0){const o=Math.abs(t.position.x);r.position.x=r.position.x-o,r.style.width+=o,t.position.x=0}if(t.position.y<0){const o=Math.abs(t.position.y);r.position.y=r.position.y-o,r.style.height+=o,t.position.y=0}r.width=r.style.width,r.height=r.style.height}}}function rh(e,t){if(e.some(r=>r.type==="reset"))return e.filter(r=>r.type==="reset").map(r=>r.item);const n=e.filter(r=>r.type==="add").map(r=>r.item);return t.reduce((r,s)=>{const i=e.filter(a=>a.id===s.id);if(i.length===0)return r.push(s),r;const o={...s};for(const a of i)if(a)switch(a.type){case"select":{o.selected=a.selected;break}case"position":{typeof a.position<"u"&&(o.position=a.position),typeof a.positionAbsolute<"u"&&(o.positionAbsolute=a.positionAbsolute),typeof a.dragging<"u"&&(o.dragging=a.dragging),o.expandParent&&Ss(r,o);break}case"dimensions":{typeof a.dimensions<"u"&&(o.width=a.dimensions.width,o.height=a.dimensions.height),typeof a.updateStyle<"u"&&(o.style={...o.style||{},...a.dimensions}),typeof a.resizing=="boolean"&&(o.resizing=a.resizing),o.expandParent&&Ss(r,o);break}case"remove":return r}return r.push(o),r},n)}function sh(e,t){return rh(e,t)}const Ue=(e,t)=>({id:e,type:"select",selected:t});function xt(e,t){return e.reduce((n,r)=>{const s=t.includes(r.id);return!r.selected&&s?(r.selected=!0,n.push(Ue(r.id,!0))):r.selected&&!s&&(r.selected=!1,n.push(Ue(r.id,!1))),n},[])}const Wn=(e,t)=>n=>{n.target===t.current&&(e==null||e(n))},ih=e=>({userSelectionActive:e.userSelectionActive,elementsSelectable:e.elementsSelectable,dragging:e.paneDragging}),Eo=w.memo(({isSelecting:e,selectionMode:t=Yt.Full,panOnDrag:n,onSelectionStart:r,onSelectionEnd:s,onPaneClick:i,onPaneContextMenu:o,onPaneScroll:a,onPaneMouseEnter:l,onPaneMouseMove:c,onPaneMouseLeave:u,children:d})=>{const m=w.useRef(null),f=pe(),y=w.useRef(0),p=w.useRef(0),v=w.useRef(),{userSelectionActive:x,elementsSelectable:_,dragging:M}=ae(ih,ge),b=()=>{f.setState({userSelectionActive:!1,userSelectionRect:null}),y.current=0,p.current=0},j=F=>{i==null||i(F),f.getState().resetSelectedElements(),f.setState({nodesSelectionActive:!1})},C=F=>{if(Array.isArray(n)&&(n!=null&&n.includes(2))){F.preventDefault();return}o==null||o(F)},E=a?F=>a(F):void 0,L=F=>{const{resetSelectedElements:g,domNode:$}=f.getState();if(v.current=$==null?void 0:$.getBoundingClientRect(),!_||!e||F.button!==0||F.target!==m.current||!v.current)return;const{x:N,y:I}=Ke(F,v.current);g(),f.setState({userSelectionRect:{width:0,height:0,startX:N,startY:I,x:N,y:I}}),r==null||r(F)},S=F=>{const{userSelectionRect:g,nodeInternals:$,edges:N,transform:I,onNodesChange:H,onEdgesChange:k,nodeOrigin:R,getNodes:D}=f.getState();if(!e||!v.current||!g)return;f.setState({userSelectionActive:!0,nodesSelectionActive:!1});const z=Ke(F,v.current),Y=g.startX??0,G=g.startY??0,K={...g,x:z.xre.id),te=ee.map(re=>re.id);if(y.current!==te.length){y.current=te.length;const re=xt(J,te);re.length&&(H==null||H(re))}if(p.current!==ie.length){p.current=ie.length;const re=xt(N,ie);re.length&&(k==null||k(re))}f.setState({userSelectionRect:K})},O=F=>{if(F.button!==0)return;const{userSelectionRect:g}=f.getState();!x&&g&&F.target===m.current&&(j==null||j(F)),f.setState({nodesSelectionActive:y.current>0}),b(),s==null||s(F)},P=F=>{x&&(f.setState({nodesSelectionActive:y.current>0}),s==null||s(F)),b()},V=_&&(e||x);return A.createElement("div",{className:ye(["react-flow__pane",{dragging:M,selection:e}]),onClick:V?void 0:Wn(j,m),onContextMenu:Wn(C,m),onWheel:Wn(E,m),onMouseEnter:V?void 0:l,onMouseDown:V?L:void 0,onMouseMove:V?S:c,onMouseUp:V?O:void 0,onMouseLeave:V?P:u,ref:m,style:Tr},d,A.createElement(nh,null))});Eo.displayName="Pane";function No(e,t){const n=e.parentNode||e.parentId;if(!n)return!1;const r=t.get(n);return r?r.selected?!0:No(r,t):!1}function ks(e,t,n){let r=e;do{if(r!=null&&r.matches(t))return!0;if(r===n.current)return!1;r=r.parentElement}while(r);return!1}function oh(e,t,n,r){return Array.from(e.values()).filter(s=>(s.selected||s.id===r)&&(!s.parentNode||s.parentId||!No(s,e))&&(s.draggable||t&&typeof s.draggable>"u")).map(s=>{var i,o;return{id:s.id,position:s.position||{x:0,y:0},positionAbsolute:s.positionAbsolute||{x:0,y:0},distance:{x:n.x-(((i=s.positionAbsolute)==null?void 0:i.x)??0),y:n.y-(((o=s.positionAbsolute)==null?void 0:o.y)??0)},delta:{x:0,y:0},extent:s.extent,parentNode:s.parentNode||s.parentId,parentId:s.parentNode||s.parentId,width:s.width,height:s.height,expandParent:s.expandParent}})}function ah(e,t){return!t||t==="parent"?t:[t[0],[t[1][0]-(e.width||0),t[1][1]-(e.height||0)]]}function So(e,t,n,r,s=[0,0],i){const o=ah(e,e.extent||r);let a=o;const l=e.parentNode||e.parentId;if(e.extent==="parent"&&!e.expandParent)if(l&&e.width&&e.height){const d=n.get(l),{x:m,y:f}=lt(d,s).positionAbsolute;a=d&&Se(m)&&Se(f)&&Se(d.width)&&Se(d.height)?[[m+e.width*s[0],f+e.height*s[1]],[m+d.width-e.width+e.width*s[0],f+d.height-e.height+e.height*s[1]]]:a}else i==null||i("005",Be.error005()),a=o;else if(e.extent&&l&&e.extent!=="parent"){const d=n.get(l),{x:m,y:f}=lt(d,s).positionAbsolute;a=[[e.extent[0][0]+m,e.extent[0][1]+f],[e.extent[1][0]+m,e.extent[1][1]+f]]}let c={x:0,y:0};if(l){const d=n.get(l);c=lt(d,s).positionAbsolute}const u=a&&a!=="parent"?jr(t,a):t;return{position:{x:u.x-c.x,y:u.y-c.y},positionAbsolute:u}}function Un({nodeId:e,dragItems:t,nodeInternals:n}){const r=t.map(s=>({...n.get(s.id),position:s.position,positionAbsolute:s.positionAbsolute}));return[e?r.find(s=>s.id===e):r[0],r]}const js=(e,t,n,r)=>{const s=t.querySelectorAll(e);if(!s||!s.length)return null;const i=Array.from(s),o=t.getBoundingClientRect(),a={x:o.width*r[0],y:o.height*r[1]};return i.map(l=>{const c=l.getBoundingClientRect();return{id:l.getAttribute("data-handleid"),position:l.getAttribute("data-handlepos"),x:(c.left-o.left-a.x)/n,y:(c.top-o.top-a.y)/n,...kr(l)}})};function Rt(e,t,n){return n===void 0?n:r=>{const s=t().nodeInternals.get(e);s&&n(r,{...s})}}function pr({id:e,store:t,unselect:n=!1,nodeRef:r}){const{addSelectedNodes:s,unselectNodesAndEdges:i,multiSelectionActive:o,nodeInternals:a,onError:l}=t.getState(),c=a.get(e);if(!c){l==null||l("012",Be.error012(e));return}t.setState({nodesSelectionActive:!1}),c.selected?(n||c.selected&&o)&&(i({nodes:[c],edges:[]}),requestAnimationFrame(()=>{var u;return(u=r==null?void 0:r.current)==null?void 0:u.blur()})):s([e])}function lh(){const e=pe();return w.useCallback(({sourceEvent:n})=>{const{transform:r,snapGrid:s,snapToGrid:i}=e.getState(),o=n.touches?n.touches[0].clientX:n.clientX,a=n.touches?n.touches[0].clientY:n.clientY,l={x:(o-r[0])/r[2],y:(a-r[1])/r[2]};return{xSnapped:i?s[0]*Math.round(l.x/s[0]):l.x,ySnapped:i?s[1]*Math.round(l.y/s[1]):l.y,...l}},[])}function Xn(e){return(t,n,r)=>e==null?void 0:e(t,r)}function ko({nodeRef:e,disabled:t=!1,noDragClassName:n,handleSelector:r,nodeId:s,isSelectable:i,selectNodesOnDrag:o}){const a=pe(),[l,c]=w.useState(!1),u=w.useRef([]),d=w.useRef({x:null,y:null}),m=w.useRef(0),f=w.useRef(null),y=w.useRef({x:0,y:0}),p=w.useRef(null),v=w.useRef(!1),x=w.useRef(!1),_=w.useRef(!1),M=lh();return w.useEffect(()=>{if(e!=null&&e.current){const b=Ne(e.current),j=({x:L,y:S})=>{const{nodeInternals:O,onNodeDrag:P,onSelectionDrag:V,updateNodePositions:F,nodeExtent:g,snapGrid:$,snapToGrid:N,nodeOrigin:I,onError:H}=a.getState();d.current={x:L,y:S};let k=!1,R={x:0,y:0,x2:0,y2:0};if(u.current.length>1&&g){const z=Kt(u.current,I);R=Gt(z)}if(u.current=u.current.map(z=>{const Y={x:L-z.distance.x,y:S-z.distance.y};N&&(Y.x=$[0]*Math.round(Y.x/$[0]),Y.y=$[1]*Math.round(Y.y/$[1]));const G=[[g[0][0],g[0][1]],[g[1][0],g[1][1]]];u.current.length>1&&g&&!z.extent&&(G[0][0]=z.positionAbsolute.x-R.x+g[0][0],G[1][0]=z.positionAbsolute.x+(z.width??0)-R.x2+g[1][0],G[0][1]=z.positionAbsolute.y-R.y+g[0][1],G[1][1]=z.positionAbsolute.y+(z.height??0)-R.y2+g[1][1]);const K=So(z,Y,O,G,I,H);return k=k||z.position.x!==K.position.x||z.position.y!==K.position.y,z.position=K.position,z.positionAbsolute=K.positionAbsolute,z}),!k)return;F(u.current,!0,!0),c(!0);const D=s?P:Xn(V);if(D&&p.current){const[z,Y]=Un({nodeId:s,dragItems:u.current,nodeInternals:O});D(p.current,z,Y)}},C=()=>{if(!f.current)return;const[L,S]=Ui(y.current,f.current);if(L!==0||S!==0){const{transform:O,panBy:P}=a.getState();d.current.x=(d.current.x??0)-L/O[2],d.current.y=(d.current.y??0)-S/O[2],P({x:L,y:S})&&j(d.current)}m.current=requestAnimationFrame(C)},E=L=>{var I;const{nodeInternals:S,multiSelectionActive:O,nodesDraggable:P,unselectNodesAndEdges:V,onNodeDragStart:F,onSelectionDragStart:g}=a.getState();x.current=!0;const $=s?F:Xn(g);(!o||!i)&&!O&&s&&((I=S.get(s))!=null&&I.selected||V()),s&&i&&o&&pr({id:s,store:a,nodeRef:e});const N=M(L);if(d.current=N,u.current=oh(S,P,N,s),$&&u.current){const[H,k]=Un({nodeId:s,dragItems:u.current,nodeInternals:S});$(L.sourceEvent,H,k)}};if(t)b.on(".drag",null);else{const L=gu().on("start",S=>{const{domNode:O,nodeDragThreshold:P}=a.getState();P===0&&E(S),_.current=!1;const V=M(S);d.current=V,f.current=(O==null?void 0:O.getBoundingClientRect())||null,y.current=Ke(S.sourceEvent,f.current)}).on("drag",S=>{var F,g;const O=M(S),{autoPanOnNodeDrag:P,nodeDragThreshold:V}=a.getState();if(S.sourceEvent.type==="touchmove"&&S.sourceEvent.touches.length>1&&(_.current=!0),!_.current){if(!v.current&&x.current&&P&&(v.current=!0,C()),!x.current){const $=O.xSnapped-(((F=d==null?void 0:d.current)==null?void 0:F.x)??0),N=O.ySnapped-(((g=d==null?void 0:d.current)==null?void 0:g.y)??0);Math.sqrt($*$+N*N)>V&&E(S)}(d.current.x!==O.xSnapped||d.current.y!==O.ySnapped)&&u.current&&x.current&&(p.current=S.sourceEvent,y.current=Ke(S.sourceEvent,f.current),j(O))}}).on("end",S=>{if(!(!x.current||_.current)&&(c(!1),v.current=!1,x.current=!1,cancelAnimationFrame(m.current),u.current)){const{updateNodePositions:O,nodeInternals:P,onNodeDragStop:V,onSelectionDragStop:F}=a.getState(),g=s?V:Xn(F);if(O(u.current,!1,!1),g){const[$,N]=Un({nodeId:s,dragItems:u.current,nodeInternals:P});g(S.sourceEvent,$,N)}}}).filter(S=>{const O=S.target;return!S.button&&(!n||!ks(O,`.${n}`,e))&&(!r||ks(O,r,e))});return b.call(L),()=>{b.on(".drag",null)}}}},[e,t,n,r,i,a,s,o,M]),l}function jo(){const e=pe();return w.useCallback(n=>{const{nodeInternals:r,nodeExtent:s,updateNodePositions:i,getNodes:o,snapToGrid:a,snapGrid:l,onError:c,nodesDraggable:u}=e.getState(),d=o().filter(_=>_.selected&&(_.draggable||u&&typeof _.draggable>"u")),m=a?l[0]:5,f=a?l[1]:5,y=n.isShiftPressed?4:1,p=n.x*m*y,v=n.y*f*y,x=d.map(_=>{if(_.positionAbsolute){const M={x:_.positionAbsolute.x+p,y:_.positionAbsolute.y+v};a&&(M.x=l[0]*Math.round(M.x/l[0]),M.y=l[1]*Math.round(M.y/l[1]));const{positionAbsolute:b,position:j}=So(_,M,r,s,void 0,c);_.position=j,_.positionAbsolute=b}return _});i(x,!0,!1)},[])}const vt={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}};var Tt=e=>{const t=({id:n,type:r,data:s,xPos:i,yPos:o,xPosOrigin:a,yPosOrigin:l,selected:c,onClick:u,onMouseEnter:d,onMouseMove:m,onMouseLeave:f,onContextMenu:y,onDoubleClick:p,style:v,className:x,isDraggable:_,isSelectable:M,isConnectable:b,isFocusable:j,selectNodesOnDrag:C,sourcePosition:E,targetPosition:L,hidden:S,resizeObserver:O,dragHandle:P,zIndex:V,isParent:F,noDragClassName:g,noPanClassName:$,initialized:N,disableKeyboardA11y:I,ariaLabel:H,rfId:k,hasHandleBounds:R})=>{const D=pe(),z=w.useRef(null),Y=w.useRef(null),G=w.useRef(E),K=w.useRef(L),J=w.useRef(r),ee=M||_||u||d||m||f,ie=jo(),te=Rt(n,D.getState,d),re=Rt(n,D.getState,m),de=Rt(n,D.getState,f),B=Rt(n,D.getState,y),q=Rt(n,D.getState,p),W=X=>{const{nodeDragThreshold:T}=D.getState();if(M&&(!C||!_||T>0)&&pr({id:n,store:D,nodeRef:z}),u){const se=D.getState().nodeInternals.get(n);se&&u(X,{...se})}},Q=X=>{if(!dr(X)&&!I)if(Zi.includes(X.key)&&M){const T=X.key==="Escape";pr({id:n,store:D,unselect:T,nodeRef:z})}else _&&c&&Object.prototype.hasOwnProperty.call(vt,X.key)&&(D.setState({ariaLiveMessage:`Moved selected node ${X.key.replace("Arrow","").toLowerCase()}. New position, x: ${~~i}, y: ${~~o}`}),ie({x:vt[X.key].x,y:vt[X.key].y,isShiftPressed:X.shiftKey}))};w.useEffect(()=>()=>{Y.current&&(O==null||O.unobserve(Y.current),Y.current=null)},[]),w.useEffect(()=>{if(z.current&&!S){const X=z.current;(!N||!R||Y.current!==X)&&(Y.current&&(O==null||O.unobserve(Y.current)),O==null||O.observe(X),Y.current=X)}},[S,N,R]),w.useEffect(()=>{const X=J.current!==r,T=G.current!==E,se=K.current!==L;z.current&&(X||T||se)&&(X&&(J.current=r),T&&(G.current=E),se&&(K.current=L),D.getState().updateNodeDimensions([{id:n,nodeElement:z.current,forceUpdate:!0}]))},[n,r,E,L]);const U=ko({nodeRef:z,disabled:S||!_,noDragClassName:g,handleSelector:P,nodeId:n,isSelectable:M,selectNodesOnDrag:C});return S?null:A.createElement("div",{className:ye(["react-flow__node",`react-flow__node-${r}`,{[$]:_},x,{selected:c,selectable:M,parent:F,dragging:U}]),ref:z,style:{zIndex:V,transform:`translate(${a}px,${l}px)`,pointerEvents:ee?"all":"none",visibility:N?"visible":"hidden",...v},"data-id":n,"data-testid":`rf__node-${n}`,onMouseEnter:te,onMouseMove:re,onMouseLeave:de,onContextMenu:B,onClick:W,onDoubleClick:q,onKeyDown:j?Q:void 0,tabIndex:j?0:void 0,role:j?"button":void 0,"aria-describedby":I?void 0:`${xo}-${k}`,"aria-label":H},A.createElement(bf,{value:n},A.createElement(e,{id:n,data:s,type:r,xPos:i,yPos:o,selected:c,isConnectable:b,sourcePosition:E,targetPosition:L,dragging:U,dragHandle:P,zIndex:V})))};return t.displayName="NodeWrapper",w.memo(t)};const ch=e=>{const t=e.getNodes().filter(n=>n.selected);return{...Kt(t,e.nodeOrigin),transformString:`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`,userSelectionActive:e.userSelectionActive}};function uh({onSelectionContextMenu:e,noPanClassName:t,disableKeyboardA11y:n}){const r=pe(),{width:s,height:i,x:o,y:a,transformString:l,userSelectionActive:c}=ae(ch,ge),u=jo(),d=w.useRef(null);if(w.useEffect(()=>{var y;n||(y=d.current)==null||y.focus({preventScroll:!0})},[n]),ko({nodeRef:d}),c||!s||!i)return null;const m=e?y=>{const p=r.getState().getNodes().filter(v=>v.selected);e(y,p)}:void 0,f=y=>{Object.prototype.hasOwnProperty.call(vt,y.key)&&u({x:vt[y.key].x,y:vt[y.key].y,isShiftPressed:y.shiftKey})};return A.createElement("div",{className:ye(["react-flow__nodesselection","react-flow__container",t]),style:{transform:l}},A.createElement("div",{ref:d,className:"react-flow__nodesselection-rect",onContextMenu:m,tabIndex:n?void 0:-1,onKeyDown:n?void 0:f,style:{width:s,height:i,top:a,left:o}}))}var dh=w.memo(uh);const fh=e=>e.nodesSelectionActive,Co=({children:e,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:i,onPaneScroll:o,deleteKeyCode:a,onMove:l,onMoveStart:c,onMoveEnd:u,selectionKeyCode:d,selectionOnDrag:m,selectionMode:f,onSelectionStart:y,onSelectionEnd:p,multiSelectionKeyCode:v,panActivationKeyCode:x,zoomActivationKeyCode:_,elementsSelectable:M,zoomOnScroll:b,zoomOnPinch:j,panOnScroll:C,panOnScrollSpeed:E,panOnScrollMode:L,zoomOnDoubleClick:S,panOnDrag:O,defaultViewport:P,translateExtent:V,minZoom:F,maxZoom:g,preventScrolling:$,onSelectionContextMenu:N,noWheelClassName:I,noPanClassName:H,disableKeyboardA11y:k})=>{const R=ae(fh),D=Wt(d),z=Wt(x),Y=z||O,G=z||C,K=D||m&&Y!==!0;return Kf({deleteKeyCode:a,multiSelectionKeyCode:v}),A.createElement(eh,{onMove:l,onMoveStart:c,onMoveEnd:u,onPaneContextMenu:i,elementsSelectable:M,zoomOnScroll:b,zoomOnPinch:j,panOnScroll:G,panOnScrollSpeed:E,panOnScrollMode:L,zoomOnDoubleClick:S,panOnDrag:!D&&Y,defaultViewport:P,translateExtent:V,minZoom:F,maxZoom:g,zoomActivationKeyCode:_,preventScrolling:$,noWheelClassName:I,noPanClassName:H},A.createElement(Eo,{onSelectionStart:y,onSelectionEnd:p,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:i,onPaneScroll:o,panOnDrag:Y,isSelecting:!!K,selectionMode:f},e,R&&A.createElement(dh,{onSelectionContextMenu:N,noPanClassName:H,disableKeyboardA11y:k})))};Co.displayName="FlowRenderer";var hh=w.memo(Co);function mh(e){return ae(w.useCallback(n=>e?so(n.nodeInternals,{x:0,y:0,width:n.width,height:n.height},n.transform,!0):n.getNodes(),[e]))}function ph(e){const t={input:Tt(e.input||mo),default:Tt(e.default||mr),output:Tt(e.output||go),group:Tt(e.group||Rr)},n={},r=Object.keys(e).filter(s=>!["input","default","output","group"].includes(s)).reduce((s,i)=>(s[i]=Tt(e[i]||mr),s),n);return{...t,...r}}const gh=({x:e,y:t,width:n,height:r,origin:s})=>!n||!r?{x:e,y:t}:s[0]<0||s[1]<0||s[0]>1||s[1]>1?{x:e,y:t}:{x:e-n*s[0],y:t-r*s[1]},yh=e=>({nodesDraggable:e.nodesDraggable,nodesConnectable:e.nodesConnectable,nodesFocusable:e.nodesFocusable,elementsSelectable:e.elementsSelectable,updateNodeDimensions:e.updateNodeDimensions,onError:e.onError}),$o=e=>{const{nodesDraggable:t,nodesConnectable:n,nodesFocusable:r,elementsSelectable:s,updateNodeDimensions:i,onError:o}=ae(yh,ge),a=mh(e.onlyRenderVisibleElements),l=w.useRef(),c=w.useMemo(()=>{if(typeof ResizeObserver>"u")return null;const u=new ResizeObserver(d=>{const m=d.map(f=>({id:f.target.getAttribute("data-id"),nodeElement:f.target,forceUpdate:!0}));i(m)});return l.current=u,u},[]);return w.useEffect(()=>()=>{var u;(u=l==null?void 0:l.current)==null||u.disconnect()},[]),A.createElement("div",{className:"react-flow__nodes",style:Tr},a.map(u=>{var j,C,E;let d=u.type||"default";e.nodeTypes[d]||(o==null||o("003",Be.error003(d)),d="default");const m=e.nodeTypes[d]||e.nodeTypes.default,f=!!(u.draggable||t&&typeof u.draggable>"u"),y=!!(u.selectable||s&&typeof u.selectable>"u"),p=!!(u.connectable||n&&typeof u.connectable>"u"),v=!!(u.focusable||r&&typeof u.focusable>"u"),x=e.nodeExtent?jr(u.positionAbsolute,e.nodeExtent):u.positionAbsolute,_=(x==null?void 0:x.x)??0,M=(x==null?void 0:x.y)??0,b=gh({x:_,y:M,width:u.width??0,height:u.height??0,origin:e.nodeOrigin});return A.createElement(m,{key:u.id,id:u.id,className:u.className,style:u.style,type:d,data:u.data,sourcePosition:u.sourcePosition||Z.Bottom,targetPosition:u.targetPosition||Z.Top,hidden:u.hidden,xPos:_,yPos:M,xPosOrigin:b.x,yPosOrigin:b.y,selectNodesOnDrag:e.selectNodesOnDrag,onClick:e.onNodeClick,onMouseEnter:e.onNodeMouseEnter,onMouseMove:e.onNodeMouseMove,onMouseLeave:e.onNodeMouseLeave,onContextMenu:e.onNodeContextMenu,onDoubleClick:e.onNodeDoubleClick,selected:!!u.selected,isDraggable:f,isSelectable:y,isConnectable:p,isFocusable:v,resizeObserver:c,dragHandle:u.dragHandle,zIndex:((j=u[le])==null?void 0:j.z)??0,isParent:!!((C=u[le])!=null&&C.isParent),noDragClassName:e.noDragClassName,noPanClassName:e.noPanClassName,initialized:!!u.width&&!!u.height,rfId:e.rfId,disableKeyboardA11y:e.disableKeyboardA11y,ariaLabel:u.ariaLabel,hasHandleBounds:!!((E=u[le])!=null&&E.handleBounds)})}))};$o.displayName="NodeRenderer";var xh=w.memo($o);const wh=(e,t,n)=>n===Z.Left?e-t:n===Z.Right?e+t:e,bh=(e,t,n)=>n===Z.Top?e-t:n===Z.Bottom?e+t:e,Cs="react-flow__edgeupdater",$s=({position:e,centerX:t,centerY:n,radius:r=10,onMouseDown:s,onMouseEnter:i,onMouseOut:o,type:a})=>A.createElement("circle",{onMouseDown:s,onMouseEnter:i,onMouseOut:o,className:ye([Cs,`${Cs}-${a}`]),cx:wh(t,r,e),cy:bh(n,r,e),r,stroke:"transparent",fill:"transparent"}),vh=()=>!0;var yt=e=>{const t=({id:n,className:r,type:s,data:i,onClick:o,onEdgeDoubleClick:a,selected:l,animated:c,label:u,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:y,labelBgBorderRadius:p,style:v,source:x,target:_,sourceX:M,sourceY:b,targetX:j,targetY:C,sourcePosition:E,targetPosition:L,elementsSelectable:S,hidden:O,sourceHandleId:P,targetHandleId:V,onContextMenu:F,onMouseEnter:g,onMouseMove:$,onMouseLeave:N,reconnectRadius:I,onReconnect:H,onReconnectStart:k,onReconnectEnd:R,markerEnd:D,markerStart:z,rfId:Y,ariaLabel:G,isFocusable:K,isReconnectable:J,pathOptions:ee,interactionWidth:ie,disableKeyboardA11y:te})=>{const re=w.useRef(null),[de,B]=w.useState(!1),[q,W]=w.useState(!1),Q=pe(),U=w.useMemo(()=>`url('#${fr(z,Y)}')`,[z,Y]),X=w.useMemo(()=>`url('#${fr(D,Y)}')`,[D,Y]);if(O)return null;const T=xe=>{var Re;const{edges:Ce,addSelectedEdges:Je,unselectNodesAndEdges:et,multiSelectionActive:tt}=Q.getState(),De=Ce.find(Ct=>Ct.id===n);De&&(S&&(Q.setState({nodesSelectionActive:!1}),De.selected&&tt?(et({nodes:[],edges:[De]}),(Re=re.current)==null||Re.blur()):Je([n])),o&&o(xe,De))},se=It(n,Q.getState,a),ce=It(n,Q.getState,F),me=It(n,Q.getState,g),we=It(n,Q.getState,$),be=It(n,Q.getState,N),Ee=(xe,Ce)=>{if(xe.button!==0)return;const{edges:Je,isValidConnection:et}=Q.getState(),tt=Ce?_:x,De=(Ce?V:P)||null,Re=Ce?"target":"source",Ct=et||vh,Dn=Ce,$t=Je.find(nt=>nt.id===n);W(!0),k==null||k(xe,$t,Re);const Ln=nt=>{W(!1),R==null||R(nt,$t,Re)};co({event:xe,handleId:De,nodeId:tt,onConnect:nt=>H==null?void 0:H($t,nt),isTarget:Dn,getState:Q.getState,setState:Q.setState,isValidConnection:Ct,edgeUpdaterType:Re,onReconnectEnd:Ln})},Ge=xe=>Ee(xe,!0),oe=xe=>Ee(xe,!1),Qe=()=>B(!0),ft=()=>B(!1),ht=!S&&!o,jt=xe=>{var Ce;if(!te&&Zi.includes(xe.key)&&S){const{unselectNodesAndEdges:Je,addSelectedEdges:et,edges:tt}=Q.getState();xe.key==="Escape"?((Ce=re.current)==null||Ce.blur(),Je({edges:[tt.find(Re=>Re.id===n)]})):et([n])}};return A.createElement("g",{className:ye(["react-flow__edge",`react-flow__edge-${s}`,r,{selected:l,animated:c,inactive:ht,updating:de}]),onClick:T,onDoubleClick:se,onContextMenu:ce,onMouseEnter:me,onMouseMove:we,onMouseLeave:be,onKeyDown:K?jt:void 0,tabIndex:K?0:void 0,role:K?"button":"img","data-testid":`rf__edge-${n}`,"aria-label":G===null?void 0:G||`Edge from ${x} to ${_}`,"aria-describedby":K?`${wo}-${Y}`:void 0,ref:re},!q&&A.createElement(e,{id:n,source:x,target:_,selected:l,animated:c,label:u,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:y,labelBgBorderRadius:p,data:i,style:v,sourceX:M,sourceY:b,targetX:j,targetY:C,sourcePosition:E,targetPosition:L,sourceHandleId:P,targetHandleId:V,markerStart:U,markerEnd:X,pathOptions:ee,interactionWidth:ie}),J&&A.createElement(A.Fragment,null,(J==="source"||J===!0)&&A.createElement($s,{position:E,centerX:M,centerY:b,radius:I,onMouseDown:Ge,onMouseEnter:Qe,onMouseOut:ft,type:"source"}),(J==="target"||J===!0)&&A.createElement($s,{position:L,centerX:j,centerY:C,radius:I,onMouseDown:oe,onMouseEnter:Qe,onMouseOut:ft,type:"target"})))};return t.displayName="EdgeWrapper",w.memo(t)};function _h(e){const t={default:yt(e.default||_n),straight:yt(e.bezier||Mr),step:yt(e.step||$r),smoothstep:yt(e.step||In),simplebezier:yt(e.simplebezier||Cr)},n={},r=Object.keys(e).filter(s=>!["default","bezier"].includes(s)).reduce((s,i)=>(s[i]=yt(e[i]||_n),s),n);return{...t,...r}}function Ms(e,t,n=null){const r=((n==null?void 0:n.x)||0)+t.x,s=((n==null?void 0:n.y)||0)+t.y,i=(n==null?void 0:n.width)||t.width,o=(n==null?void 0:n.height)||t.height;switch(e){case Z.Top:return{x:r+i/2,y:s};case Z.Right:return{x:r+i,y:s+o/2};case Z.Bottom:return{x:r+i/2,y:s+o};case Z.Left:return{x:r,y:s+o/2}}}function As(e,t){return e?e.length===1||!t?e[0]:t&&e.find(n=>n.id===t)||null:null}const Eh=(e,t,n,r,s,i)=>{const o=Ms(n,e,t),a=Ms(i,r,s);return{sourceX:o.x,sourceY:o.y,targetX:a.x,targetY:a.y}};function Nh({sourcePos:e,targetPos:t,sourceWidth:n,sourceHeight:r,targetWidth:s,targetHeight:i,width:o,height:a,transform:l}){const c={x:Math.min(e.x,t.x),y:Math.min(e.y,t.y),x2:Math.max(e.x+n,t.x+s),y2:Math.max(e.y+r,t.y+i)};c.x===c.x2&&(c.x2+=1),c.y===c.y2&&(c.y2+=1);const u=Gt({x:(0-l[0])/l[2],y:(0-l[1])/l[2],width:o/l[2],height:a/l[2]}),d=Math.max(0,Math.min(u.x2,c.x2)-Math.max(u.x,c.x)),m=Math.max(0,Math.min(u.y2,c.y2)-Math.max(u.y,c.y));return Math.ceil(d*m)>0}function Is(e){var r,s,i,o,a;const t=((r=e==null?void 0:e[le])==null?void 0:r.handleBounds)||null,n=t&&(e==null?void 0:e.width)&&(e==null?void 0:e.height)&&typeof((s=e==null?void 0:e.positionAbsolute)==null?void 0:s.x)<"u"&&typeof((i=e==null?void 0:e.positionAbsolute)==null?void 0:i.y)<"u";return[{x:((o=e==null?void 0:e.positionAbsolute)==null?void 0:o.x)||0,y:((a=e==null?void 0:e.positionAbsolute)==null?void 0:a.y)||0,width:(e==null?void 0:e.width)||0,height:(e==null?void 0:e.height)||0},t,!!n]}const Sh=[{level:0,isMaxLevel:!0,edges:[]}];function kh(e,t,n=!1){let r=-1;const s=e.reduce((o,a)=>{var u,d;const l=Se(a.zIndex);let c=l?a.zIndex:0;if(n){const m=t.get(a.target),f=t.get(a.source),y=a.selected||(m==null?void 0:m.selected)||(f==null?void 0:f.selected),p=Math.max(((u=f==null?void 0:f[le])==null?void 0:u.z)||0,((d=m==null?void 0:m[le])==null?void 0:d.z)||0,1e3);c=(l?a.zIndex:0)+(y?p:0)}return o[c]?o[c].push(a):o[c]=[a],r=c>r?c:r,o},{}),i=Object.entries(s).map(([o,a])=>{const l=+o;return{edges:a,level:l,isMaxLevel:l===r}});return i.length===0?Sh:i}function jh(e,t,n){const r=ae(w.useCallback(s=>e?s.edges.filter(i=>{const o=t.get(i.source),a=t.get(i.target);return(o==null?void 0:o.width)&&(o==null?void 0:o.height)&&(a==null?void 0:a.width)&&(a==null?void 0:a.height)&&Nh({sourcePos:o.positionAbsolute||{x:0,y:0},targetPos:a.positionAbsolute||{x:0,y:0},sourceWidth:o.width,sourceHeight:o.height,targetWidth:a.width,targetHeight:a.height,width:s.width,height:s.height,transform:s.transform})}):s.edges,[e,t]));return kh(r,t,n)}const Ch=({color:e="none",strokeWidth:t=1})=>A.createElement("polyline",{style:{stroke:e,strokeWidth:t},strokeLinecap:"round",strokeLinejoin:"round",fill:"none",points:"-5,-4 0,0 -5,4"}),$h=({color:e="none",strokeWidth:t=1})=>A.createElement("polyline",{style:{stroke:e,fill:e,strokeWidth:t},strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"}),Rs={[bn.Arrow]:Ch,[bn.ArrowClosed]:$h};function Mh(e){const t=pe();return w.useMemo(()=>{var s,i;return Object.prototype.hasOwnProperty.call(Rs,e)?Rs[e]:((i=(s=t.getState()).onError)==null||i.call(s,"009",Be.error009(e)),null)},[e])}const Ah=({id:e,type:t,color:n,width:r=12.5,height:s=12.5,markerUnits:i="strokeWidth",strokeWidth:o,orient:a="auto-start-reverse"})=>{const l=Mh(t);return l?A.createElement("marker",{className:"react-flow__arrowhead",id:e,markerWidth:`${r}`,markerHeight:`${s}`,viewBox:"-10 -10 20 20",markerUnits:i,orient:a,refX:"0",refY:"0"},A.createElement(l,{color:n,strokeWidth:o})):null},Ih=({defaultColor:e,rfId:t})=>n=>{const r=[];return n.edges.reduce((s,i)=>([i.markerStart,i.markerEnd].forEach(o=>{if(o&&typeof o=="object"){const a=fr(o,t);r.includes(a)||(s.push({id:a,color:o.color||e,...o}),r.push(a))}}),s),[]).sort((s,i)=>s.id.localeCompare(i.id))},Mo=({defaultColor:e,rfId:t})=>{const n=ae(w.useCallback(Ih({defaultColor:e,rfId:t}),[e,t]),(r,s)=>!(r.length!==s.length||r.some((i,o)=>i.id!==s[o].id)));return A.createElement("defs",null,n.map(r=>A.createElement(Ah,{id:r.id,key:r.id,type:r.type,color:r.color,width:r.width,height:r.height,markerUnits:r.markerUnits,strokeWidth:r.strokeWidth,orient:r.orient})))};Mo.displayName="MarkerDefinitions";var Rh=w.memo(Mo);const Th=e=>({nodesConnectable:e.nodesConnectable,edgesFocusable:e.edgesFocusable,edgesUpdatable:e.edgesUpdatable,elementsSelectable:e.elementsSelectable,width:e.width,height:e.height,connectionMode:e.connectionMode,nodeInternals:e.nodeInternals,onError:e.onError}),Ao=({defaultMarkerColor:e,onlyRenderVisibleElements:t,elevateEdgesOnSelect:n,rfId:r,edgeTypes:s,noPanClassName:i,onEdgeContextMenu:o,onEdgeMouseEnter:a,onEdgeMouseMove:l,onEdgeMouseLeave:c,onEdgeClick:u,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:y,reconnectRadius:p,children:v,disableKeyboardA11y:x})=>{const{edgesFocusable:_,edgesUpdatable:M,elementsSelectable:b,width:j,height:C,connectionMode:E,nodeInternals:L,onError:S}=ae(Th,ge),O=jh(t,L,n);return j?A.createElement(A.Fragment,null,O.map(({level:P,edges:V,isMaxLevel:F})=>A.createElement("svg",{key:P,style:{zIndex:P},width:j,height:C,className:"react-flow__edges react-flow__container"},F&&A.createElement(Rh,{defaultColor:e,rfId:r}),A.createElement("g",null,V.map(g=>{const[$,N,I]=Is(L.get(g.source)),[H,k,R]=Is(L.get(g.target));if(!I||!R)return null;let D=g.type||"default";s[D]||(S==null||S("011",Be.error011(D)),D="default");const z=s[D]||s.default,Y=E===ut.Strict?k.target:(k.target??[]).concat(k.source??[]),G=As(N.source,g.sourceHandle),K=As(Y,g.targetHandle),J=(G==null?void 0:G.position)||Z.Bottom,ee=(K==null?void 0:K.position)||Z.Top,ie=!!(g.focusable||_&&typeof g.focusable>"u"),te=g.reconnectable||g.updatable,re=typeof m<"u"&&(te||M&&typeof te>"u");if(!G||!K)return S==null||S("008",Be.error008(G,g)),null;const{sourceX:de,sourceY:B,targetX:q,targetY:W}=Eh($,G,J,H,K,ee);return A.createElement(z,{key:g.id,id:g.id,className:ye([g.className,i]),type:D,data:g.data,selected:!!g.selected,animated:!!g.animated,hidden:!!g.hidden,label:g.label,labelStyle:g.labelStyle,labelShowBg:g.labelShowBg,labelBgStyle:g.labelBgStyle,labelBgPadding:g.labelBgPadding,labelBgBorderRadius:g.labelBgBorderRadius,style:g.style,source:g.source,target:g.target,sourceHandleId:g.sourceHandle,targetHandleId:g.targetHandle,markerEnd:g.markerEnd,markerStart:g.markerStart,sourceX:de,sourceY:B,targetX:q,targetY:W,sourcePosition:J,targetPosition:ee,elementsSelectable:b,onContextMenu:o,onMouseEnter:a,onMouseMove:l,onMouseLeave:c,onClick:u,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:y,reconnectRadius:p,rfId:r,ariaLabel:g.ariaLabel,isFocusable:ie,isReconnectable:re,pathOptions:"pathOptions"in g?g.pathOptions:void 0,interactionWidth:g.interactionWidth,disableKeyboardA11y:x})})))),v):null};Ao.displayName="EdgeRenderer";var Oh=w.memo(Ao);const Dh=e=>`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`;function Lh({children:e}){const t=ae(Dh);return A.createElement("div",{className:"react-flow__viewport react-flow__container",style:{transform:t}},e)}function Ph(e){const t=Rn(),n=w.useRef(!1);w.useEffect(()=>{!n.current&&t.viewportInitialized&&e&&(setTimeout(()=>e(t),1),n.current=!0)},[e,t.viewportInitialized])}const zh={[Z.Left]:Z.Right,[Z.Right]:Z.Left,[Z.Top]:Z.Bottom,[Z.Bottom]:Z.Top},Io=({nodeId:e,handleType:t,style:n,type:r=Xe.Bezier,CustomComponent:s,connectionStatus:i})=>{var C,E,L;const{fromNode:o,handleId:a,toX:l,toY:c,connectionMode:u}=ae(w.useCallback(S=>({fromNode:S.nodeInternals.get(e),handleId:S.connectionHandleId,toX:(S.connectionPosition.x-S.transform[0])/S.transform[2],toY:(S.connectionPosition.y-S.transform[1])/S.transform[2],connectionMode:S.connectionMode}),[e]),ge),d=(C=o==null?void 0:o[le])==null?void 0:C.handleBounds;let m=d==null?void 0:d[t];if(u===ut.Loose&&(m=m||(d==null?void 0:d[t==="source"?"target":"source"])),!o||!m)return null;const f=a?m.find(S=>S.id===a):m[0],y=f?f.x+f.width/2:(o.width??0)/2,p=f?f.y+f.height/2:o.height??0,v=(((E=o.positionAbsolute)==null?void 0:E.x)??0)+y,x=(((L=o.positionAbsolute)==null?void 0:L.y)??0)+p,_=f==null?void 0:f.position,M=_?zh[_]:null;if(!_||!M)return null;if(s)return A.createElement(s,{connectionLineType:r,connectionLineStyle:n,fromNode:o,fromHandle:f,fromX:v,fromY:x,toX:l,toY:c,fromPosition:_,toPosition:M,connectionStatus:i});let b="";const j={sourceX:v,sourceY:x,sourcePosition:_,targetX:l,targetY:c,targetPosition:M};return r===Xe.Bezier?[b]=no(j):r===Xe.Step?[b]=vn({...j,borderRadius:0}):r===Xe.SmoothStep?[b]=vn(j):r===Xe.SimpleBezier?[b]=to(j):b=`M${v},${x} ${l},${c}`,A.createElement("path",{d:b,fill:"none",className:"react-flow__connection-path",style:n})};Io.displayName="ConnectionLine";const Hh=e=>({nodeId:e.connectionNodeId,handleType:e.connectionHandleType,nodesConnectable:e.nodesConnectable,connectionStatus:e.connectionStatus,width:e.width,height:e.height});function Fh({containerStyle:e,style:t,type:n,component:r}){const{nodeId:s,handleType:i,nodesConnectable:o,width:a,height:l,connectionStatus:c}=ae(Hh,ge);return!(s&&i&&a&&o)?null:A.createElement("svg",{style:e,width:a,height:l,className:"react-flow__edges react-flow__connectionline react-flow__container"},A.createElement("g",{className:ye(["react-flow__connection",c])},A.createElement(Io,{nodeId:s,handleType:i,style:t,type:n,CustomComponent:r,connectionStatus:c})))}function Ts(e,t){return w.useRef(null),pe(),w.useMemo(()=>t(e),[e])}const Ro=({nodeTypes:e,edgeTypes:t,onMove:n,onMoveStart:r,onMoveEnd:s,onInit:i,onNodeClick:o,onEdgeClick:a,onNodeDoubleClick:l,onEdgeDoubleClick:c,onNodeMouseEnter:u,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,onSelectionContextMenu:y,onSelectionStart:p,onSelectionEnd:v,connectionLineType:x,connectionLineStyle:_,connectionLineComponent:M,connectionLineContainerStyle:b,selectionKeyCode:j,selectionOnDrag:C,selectionMode:E,multiSelectionKeyCode:L,panActivationKeyCode:S,zoomActivationKeyCode:O,deleteKeyCode:P,onlyRenderVisibleElements:V,elementsSelectable:F,selectNodesOnDrag:g,defaultViewport:$,translateExtent:N,minZoom:I,maxZoom:H,preventScrolling:k,defaultMarkerColor:R,zoomOnScroll:D,zoomOnPinch:z,panOnScroll:Y,panOnScrollSpeed:G,panOnScrollMode:K,zoomOnDoubleClick:J,panOnDrag:ee,onPaneClick:ie,onPaneMouseEnter:te,onPaneMouseMove:re,onPaneMouseLeave:de,onPaneScroll:B,onPaneContextMenu:q,onEdgeContextMenu:W,onEdgeMouseEnter:Q,onEdgeMouseMove:U,onEdgeMouseLeave:X,onReconnect:T,onReconnectStart:se,onReconnectEnd:ce,reconnectRadius:me,noDragClassName:we,noWheelClassName:be,noPanClassName:Ee,elevateEdgesOnSelect:Ge,disableKeyboardA11y:oe,nodeOrigin:Qe,nodeExtent:ft,rfId:ht})=>{const jt=Ts(e,ph),xe=Ts(t,_h);return Ph(i),A.createElement(hh,{onPaneClick:ie,onPaneMouseEnter:te,onPaneMouseMove:re,onPaneMouseLeave:de,onPaneContextMenu:q,onPaneScroll:B,deleteKeyCode:P,selectionKeyCode:j,selectionOnDrag:C,selectionMode:E,onSelectionStart:p,onSelectionEnd:v,multiSelectionKeyCode:L,panActivationKeyCode:S,zoomActivationKeyCode:O,elementsSelectable:F,onMove:n,onMoveStart:r,onMoveEnd:s,zoomOnScroll:D,zoomOnPinch:z,zoomOnDoubleClick:J,panOnScroll:Y,panOnScrollSpeed:G,panOnScrollMode:K,panOnDrag:ee,defaultViewport:$,translateExtent:N,minZoom:I,maxZoom:H,onSelectionContextMenu:y,preventScrolling:k,noDragClassName:we,noWheelClassName:be,noPanClassName:Ee,disableKeyboardA11y:oe},A.createElement(Lh,null,A.createElement(Oh,{edgeTypes:xe,onEdgeClick:a,onEdgeDoubleClick:c,onlyRenderVisibleElements:V,onEdgeContextMenu:W,onEdgeMouseEnter:Q,onEdgeMouseMove:U,onEdgeMouseLeave:X,onReconnect:T,onReconnectStart:se,onReconnectEnd:ce,reconnectRadius:me,defaultMarkerColor:R,noPanClassName:Ee,elevateEdgesOnSelect:!!Ge,disableKeyboardA11y:oe,rfId:ht},A.createElement(Fh,{style:_,type:x,component:M,containerStyle:b})),A.createElement("div",{className:"react-flow__edgelabel-renderer"}),A.createElement(xh,{nodeTypes:jt,onNodeClick:o,onNodeDoubleClick:l,onNodeMouseEnter:u,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,selectNodesOnDrag:g,onlyRenderVisibleElements:V,noPanClassName:Ee,noDragClassName:we,disableKeyboardA11y:oe,nodeOrigin:Qe,nodeExtent:ft,rfId:ht})))};Ro.displayName="GraphView";var Vh=w.memo(Ro);const gr=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Ye={rfId:"1",width:0,height:0,transform:[0,0,1],nodeInternals:new Map,edges:[],onNodesChange:null,onEdgesChange:null,hasDefaultNodes:!1,hasDefaultEdges:!1,d3Zoom:null,d3Selection:null,d3ZoomHandler:void 0,minZoom:.5,maxZoom:2,translateExtent:gr,nodeExtent:gr,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionNodeId:null,connectionHandleId:null,connectionHandleType:"source",connectionPosition:{x:0,y:0},connectionStatus:null,connectionMode:ut.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:[0,0],nodeDragThreshold:0,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesUpdatable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,fitViewOnInit:!1,fitViewOnInitDone:!1,fitViewOnInitOptions:void 0,onSelectionChange:[],multiSelectionActive:!1,connectionStartHandle:null,connectionEndHandle:null,connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,connectionRadius:20,onError:mf,isValidConnection:void 0},Bh=()=>Cl((e,t)=>({...Ye,setNodes:n=>{const{nodeInternals:r,nodeOrigin:s,elevateNodesOnSelect:i}=t();e({nodeInternals:Yn(n,r,s,i)})},getNodes:()=>Array.from(t().nodeInternals.values()),setEdges:n=>{const{defaultEdgeOptions:r={}}=t();e({edges:n.map(s=>({...r,...s}))})},setDefaultNodesAndEdges:(n,r)=>{const s=typeof n<"u",i=typeof r<"u",o=s?Yn(n,new Map,t().nodeOrigin,t().elevateNodesOnSelect):new Map;e({nodeInternals:o,edges:i?r:[],hasDefaultNodes:s,hasDefaultEdges:i})},updateNodeDimensions:n=>{const{onNodesChange:r,nodeInternals:s,fitViewOnInit:i,fitViewOnInitDone:o,fitViewOnInitOptions:a,domNode:l,nodeOrigin:c}=t(),u=l==null?void 0:l.querySelector(".react-flow__viewport");if(!u)return;const d=window.getComputedStyle(u),{m22:m}=new window.DOMMatrixReadOnly(d.transform),f=n.reduce((p,v)=>{const x=s.get(v.id);if(x!=null&&x.hidden)s.set(x.id,{...x,[le]:{...x[le],handleBounds:void 0}});else if(x){const _=kr(v.nodeElement);!!(_.width&&_.height&&(x.width!==_.width||x.height!==_.height||v.forceUpdate))&&(s.set(x.id,{...x,[le]:{...x[le],handleBounds:{source:js(".source",v.nodeElement,m,c),target:js(".target",v.nodeElement,m,c)}},..._}),p.push({id:x.id,type:"dimensions",dimensions:_}))}return p},[]);vo(s,c);const y=o||i&&!o&&_o(t,{initial:!0,...a});e({nodeInternals:new Map(s),fitViewOnInitDone:y}),(f==null?void 0:f.length)>0&&(r==null||r(f))},updateNodePositions:(n,r=!0,s=!1)=>{const{triggerNodeChanges:i}=t(),o=n.map(a=>{const l={id:a.id,type:"position",dragging:s};return r&&(l.positionAbsolute=a.positionAbsolute,l.position=a.position),l});i(o)},triggerNodeChanges:n=>{const{onNodesChange:r,nodeInternals:s,hasDefaultNodes:i,nodeOrigin:o,getNodes:a,elevateNodesOnSelect:l}=t();if(n!=null&&n.length){if(i){const c=sh(n,a()),u=Yn(c,s,o,l);e({nodeInternals:u})}r==null||r(n)}},addSelectedNodes:n=>{const{multiSelectionActive:r,edges:s,getNodes:i}=t();let o,a=null;r?o=n.map(l=>Ue(l,!0)):(o=xt(i(),n),a=xt(s,[])),on({changedNodes:o,changedEdges:a,get:t,set:e})},addSelectedEdges:n=>{const{multiSelectionActive:r,edges:s,getNodes:i}=t();let o,a=null;r?o=n.map(l=>Ue(l,!0)):(o=xt(s,n),a=xt(i(),[])),on({changedNodes:a,changedEdges:o,get:t,set:e})},unselectNodesAndEdges:({nodes:n,edges:r}={})=>{const{edges:s,getNodes:i}=t(),o=n||i(),a=r||s,l=o.map(u=>(u.selected=!1,Ue(u.id,!1))),c=a.map(u=>Ue(u.id,!1));on({changedNodes:l,changedEdges:c,get:t,set:e})},setMinZoom:n=>{const{d3Zoom:r,maxZoom:s}=t();r==null||r.scaleExtent([n,s]),e({minZoom:n})},setMaxZoom:n=>{const{d3Zoom:r,minZoom:s}=t();r==null||r.scaleExtent([s,n]),e({maxZoom:n})},setTranslateExtent:n=>{var r;(r=t().d3Zoom)==null||r.translateExtent(n),e({translateExtent:n})},resetSelectedElements:()=>{const{edges:n,getNodes:r}=t(),i=r().filter(a=>a.selected).map(a=>Ue(a.id,!1)),o=n.filter(a=>a.selected).map(a=>Ue(a.id,!1));on({changedNodes:i,changedEdges:o,get:t,set:e})},setNodeExtent:n=>{const{nodeInternals:r}=t();r.forEach(s=>{s.positionAbsolute=jr(s.position,n)}),e({nodeExtent:n,nodeInternals:new Map(r)})},panBy:n=>{const{transform:r,width:s,height:i,d3Zoom:o,d3Selection:a,translateExtent:l}=t();if(!o||!a||!n.x&&!n.y)return!1;const c=Fe.translate(r[0]+n.x,r[1]+n.y).scale(r[2]),u=[[0,0],[s,i]],d=o==null?void 0:o.constrain()(c,u,l);return o.transform(a,d),r[0]!==d.x||r[1]!==d.y||r[2]!==d.k},cancelConnection:()=>e({connectionNodeId:Ye.connectionNodeId,connectionHandleId:Ye.connectionHandleId,connectionHandleType:Ye.connectionHandleType,connectionStatus:Ye.connectionStatus,connectionStartHandle:Ye.connectionStartHandle,connectionEndHandle:Ye.connectionEndHandle}),reset:()=>e({...Ye})}),Object.is),To=({children:e})=>{const t=w.useRef(null);return t.current||(t.current=Bh()),A.createElement(af,{value:t.current},e)};To.displayName="ReactFlowProvider";const Oo=({children:e})=>w.useContext(An)?A.createElement(A.Fragment,null,e):A.createElement(To,null,e);Oo.displayName="ReactFlowWrapper";const Gh={input:mo,default:mr,output:go,group:Rr},Yh={default:_n,straight:Mr,step:$r,smoothstep:In,simplebezier:Cr},Wh=[0,0],Uh=[15,15],Xh={x:0,y:0,zoom:1},qh={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0},Do=w.forwardRef(({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,className:s,nodeTypes:i=Gh,edgeTypes:o=Yh,onNodeClick:a,onEdgeClick:l,onInit:c,onMove:u,onMoveStart:d,onMoveEnd:m,onConnect:f,onConnectStart:y,onConnectEnd:p,onClickConnectStart:v,onClickConnectEnd:x,onNodeMouseEnter:_,onNodeMouseMove:M,onNodeMouseLeave:b,onNodeContextMenu:j,onNodeDoubleClick:C,onNodeDragStart:E,onNodeDrag:L,onNodeDragStop:S,onNodesDelete:O,onEdgesDelete:P,onSelectionChange:V,onSelectionDragStart:F,onSelectionDrag:g,onSelectionDragStop:$,onSelectionContextMenu:N,onSelectionStart:I,onSelectionEnd:H,connectionMode:k=ut.Strict,connectionLineType:R=Xe.Bezier,connectionLineStyle:D,connectionLineComponent:z,connectionLineContainerStyle:Y,deleteKeyCode:G="Backspace",selectionKeyCode:K="Shift",selectionOnDrag:J=!1,selectionMode:ee=Yt.Full,panActivationKeyCode:ie="Space",multiSelectionKeyCode:te=wn()?"Meta":"Control",zoomActivationKeyCode:re=wn()?"Meta":"Control",snapToGrid:de=!1,snapGrid:B=Uh,onlyRenderVisibleElements:q=!1,selectNodesOnDrag:W=!0,nodesDraggable:Q,nodesConnectable:U,nodesFocusable:X,nodeOrigin:T=Wh,edgesFocusable:se,edgesUpdatable:ce,elementsSelectable:me,defaultViewport:we=Xh,minZoom:be=.5,maxZoom:Ee=2,translateExtent:Ge=gr,preventScrolling:oe=!0,nodeExtent:Qe,defaultMarkerColor:ft="#b1b1b7",zoomOnScroll:ht=!0,zoomOnPinch:jt=!0,panOnScroll:xe=!1,panOnScrollSpeed:Ce=.5,panOnScrollMode:Je=ot.Free,zoomOnDoubleClick:et=!0,panOnDrag:tt=!0,onPaneClick:De,onPaneMouseEnter:Re,onPaneMouseMove:Ct,onPaneMouseLeave:Dn,onPaneScroll:$t,onPaneContextMenu:Ln,children:Vr,onEdgeContextMenu:nt,onEdgeDoubleClick:_a,onEdgeMouseEnter:Ea,onEdgeMouseMove:Na,onEdgeMouseLeave:Sa,onEdgeUpdate:ka,onEdgeUpdateStart:ja,onEdgeUpdateEnd:Ca,onReconnect:$a,onReconnectStart:Ma,onReconnectEnd:Aa,reconnectRadius:Ia=10,edgeUpdaterRadius:Ra=10,onNodesChange:Ta,onEdgesChange:Oa,noDragClassName:Da="nodrag",noWheelClassName:La="nowheel",noPanClassName:Br="nopan",fitView:Pa=!1,fitViewOptions:za,connectOnClick:Ha=!0,attributionPosition:Fa,proOptions:Va,defaultEdgeOptions:Ba,elevateNodesOnSelect:Ga=!0,elevateEdgesOnSelect:Ya=!1,disableKeyboardA11y:Gr=!1,autoPanOnConnect:Wa=!0,autoPanOnNodeDrag:Ua=!0,connectionRadius:Xa=20,isValidConnection:qa,onError:Ka,style:Za,id:Yr,nodeDragThreshold:Qa,...Ja},el)=>{const Pn=Yr||"1";return A.createElement("div",{...Ja,style:{...Za,...qh},ref:el,className:ye(["react-flow",s]),"data-testid":"rf__wrapper",id:Yr},A.createElement(Oo,null,A.createElement(Vh,{onInit:c,onMove:u,onMoveStart:d,onMoveEnd:m,onNodeClick:a,onEdgeClick:l,onNodeMouseEnter:_,onNodeMouseMove:M,onNodeMouseLeave:b,onNodeContextMenu:j,onNodeDoubleClick:C,nodeTypes:i,edgeTypes:o,connectionLineType:R,connectionLineStyle:D,connectionLineComponent:z,connectionLineContainerStyle:Y,selectionKeyCode:K,selectionOnDrag:J,selectionMode:ee,deleteKeyCode:G,multiSelectionKeyCode:te,panActivationKeyCode:ie,zoomActivationKeyCode:re,onlyRenderVisibleElements:q,selectNodesOnDrag:W,defaultViewport:we,translateExtent:Ge,minZoom:be,maxZoom:Ee,preventScrolling:oe,zoomOnScroll:ht,zoomOnPinch:jt,zoomOnDoubleClick:et,panOnScroll:xe,panOnScrollSpeed:Ce,panOnScrollMode:Je,panOnDrag:tt,onPaneClick:De,onPaneMouseEnter:Re,onPaneMouseMove:Ct,onPaneMouseLeave:Dn,onPaneScroll:$t,onPaneContextMenu:Ln,onSelectionContextMenu:N,onSelectionStart:I,onSelectionEnd:H,onEdgeContextMenu:nt,onEdgeDoubleClick:_a,onEdgeMouseEnter:Ea,onEdgeMouseMove:Na,onEdgeMouseLeave:Sa,onReconnect:$a??ka,onReconnectStart:Ma??ja,onReconnectEnd:Aa??Ca,reconnectRadius:Ia??Ra,defaultMarkerColor:ft,noDragClassName:Da,noWheelClassName:La,noPanClassName:Br,elevateEdgesOnSelect:Ya,rfId:Pn,disableKeyboardA11y:Gr,nodeOrigin:T,nodeExtent:Qe}),A.createElement(Pf,{nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:f,onConnectStart:y,onConnectEnd:p,onClickConnectStart:v,onClickConnectEnd:x,nodesDraggable:Q,nodesConnectable:U,nodesFocusable:X,edgesFocusable:se,edgesUpdatable:ce,elementsSelectable:me,elevateNodesOnSelect:Ga,minZoom:be,maxZoom:Ee,nodeExtent:Qe,onNodesChange:Ta,onEdgesChange:Oa,snapToGrid:de,snapGrid:B,connectionMode:k,translateExtent:Ge,connectOnClick:Ha,defaultEdgeOptions:Ba,fitView:Pa,fitViewOptions:za,onNodesDelete:O,onEdgesDelete:P,onNodeDragStart:E,onNodeDrag:L,onNodeDragStop:S,onSelectionDrag:g,onSelectionDragStart:F,onSelectionDragStop:$,noPanClassName:Br,nodeOrigin:T,rfId:Pn,autoPanOnConnect:Wa,autoPanOnNodeDrag:Ua,onError:Ka,connectionRadius:Xa,isValidConnection:qa,nodeDragThreshold:Qa}),A.createElement(Df,{onSelectionChange:V}),Vr,A.createElement(cf,{proOptions:Va,position:Fa}),A.createElement(Bf,{rfId:Pn,disableKeyboardA11y:Gr})))});Do.displayName="ReactFlow";const Lo=({id:e,x:t,y:n,width:r,height:s,style:i,color:o,strokeColor:a,strokeWidth:l,className:c,borderRadius:u,shapeRendering:d,onClick:m,selected:f})=>{const{background:y,backgroundColor:p}=i||{},v=o||y||p;return A.createElement("rect",{className:ye(["react-flow__minimap-node",{selected:f},c]),x:t,y:n,rx:u,ry:u,width:r,height:s,fill:v,stroke:a,strokeWidth:l,shapeRendering:d,onClick:m?x=>m(x,e):void 0})};Lo.displayName="MiniMapNode";var Kh=w.memo(Lo);const Zh=e=>e.nodeOrigin,Qh=e=>e.getNodes().filter(t=>!t.hidden&&t.width&&t.height),qn=e=>e instanceof Function?e:()=>e;function Jh({nodeStrokeColor:e="transparent",nodeColor:t="#e2e2e2",nodeClassName:n="",nodeBorderRadius:r=5,nodeStrokeWidth:s=2,nodeComponent:i=Kh,onClick:o}){const a=ae(Qh,ge),l=ae(Zh),c=qn(t),u=qn(e),d=qn(n),m=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return A.createElement(A.Fragment,null,a.map(f=>{const{x:y,y:p}=lt(f,l).positionAbsolute;return A.createElement(i,{key:f.id,x:y,y:p,width:f.width,height:f.height,style:f.style,selected:f.selected,className:d(f),color:c(f),borderRadius:r,strokeColor:u(f),strokeWidth:s,shapeRendering:m,onClick:o,id:f.id})}))}var em=w.memo(Jh);const tm=200,nm=150,rm=e=>{const t=e.getNodes(),n={x:-e.transform[0]/e.transform[2],y:-e.transform[1]/e.transform[2],width:e.width/e.transform[2],height:e.height/e.transform[2]};return{viewBB:n,boundingRect:t.length>0?ff(Kt(t,e.nodeOrigin),n):n,rfId:e.rfId}},sm="react-flow__minimap-desc";function Po({style:e,className:t,nodeStrokeColor:n="transparent",nodeColor:r="#e2e2e2",nodeClassName:s="",nodeBorderRadius:i=5,nodeStrokeWidth:o=2,nodeComponent:a,maskColor:l="rgb(240, 240, 240, 0.6)",maskStrokeColor:c="none",maskStrokeWidth:u=1,position:d="bottom-right",onClick:m,onNodeClick:f,pannable:y=!1,zoomable:p=!1,ariaLabel:v="React Flow mini map",inversePan:x=!1,zoomStep:_=10,offsetScale:M=5}){const b=pe(),j=w.useRef(null),{boundingRect:C,viewBB:E,rfId:L}=ae(rm,ge),S=(e==null?void 0:e.width)??tm,O=(e==null?void 0:e.height)??nm,P=C.width/S,V=C.height/O,F=Math.max(P,V),g=F*S,$=F*O,N=M*F,I=C.x-(g-C.width)/2-N,H=C.y-($-C.height)/2-N,k=g+N*2,R=$+N*2,D=`${sm}-${L}`,z=w.useRef(0);z.current=F,w.useEffect(()=>{if(j.current){const K=Ne(j.current),J=te=>{const{transform:re,d3Selection:de,d3Zoom:B}=b.getState();if(te.sourceEvent.type!=="wheel"||!de||!B)return;const q=-te.sourceEvent.deltaY*(te.sourceEvent.deltaMode===1?.05:te.sourceEvent.deltaMode?1:.002)*_,W=re[2]*Math.pow(2,q);B.scaleTo(de,W)},ee=te=>{const{transform:re,d3Selection:de,d3Zoom:B,translateExtent:q,width:W,height:Q}=b.getState();if(te.sourceEvent.type!=="mousemove"||!de||!B)return;const U=z.current*Math.max(1,re[2])*(x?-1:1),X={x:re[0]-te.sourceEvent.movementX*U,y:re[1]-te.sourceEvent.movementY*U},T=[[0,0],[W,Q]],se=Fe.translate(X.x,X.y).scale(re[2]),ce=B.constrain()(se,T,q);B.transform(de,ce)},ie=Yi().on("zoom",y?ee:null).on("zoom.wheel",p?J:null);return K.call(ie),()=>{K.on("zoom",null)}}},[y,p,x,_]);const Y=m?K=>{const J=$e(K);m(K,{x:J[0],y:J[1]})}:void 0,G=f?(K,J)=>{const ee=b.getState().nodeInternals.get(J);f(K,ee)}:void 0;return A.createElement(Sr,{position:d,style:e,className:ye(["react-flow__minimap",t]),"data-testid":"rf__minimap"},A.createElement("svg",{width:S,height:O,viewBox:`${I} ${H} ${k} ${R}`,role:"img","aria-labelledby":D,ref:j,onClick:Y},v&&A.createElement("title",{id:D},v),A.createElement(em,{onClick:G,nodeColor:r,nodeStrokeColor:n,nodeBorderRadius:i,nodeClassName:s,nodeStrokeWidth:o,nodeComponent:a}),A.createElement("path",{className:"react-flow__minimap-mask",d:`M${I-N},${H-N}h${k+N*2}v${R+N*2}h${-k-N*2}z - M${E.x},${E.y}h${E.width}v${E.height}h${-E.width}z`,fill:l,fillRule:"evenodd",stroke:c,strokeWidth:u,pointerEvents:"none"})))}Po.displayName="MiniMap";var im=w.memo(Po);function om(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32"},A.createElement("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"}))}function am(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5"},A.createElement("path",{d:"M0 0h32v4.2H0z"}))}function lm(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30"},A.createElement("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"}))}function cm(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32"},A.createElement("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"}))}function um(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32"},A.createElement("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"}))}const Lt=({children:e,className:t,...n})=>A.createElement("button",{type:"button",className:ye(["react-flow__controls-button",t]),...n},e);Lt.displayName="ControlButton";const dm=e=>({isInteractive:e.nodesDraggable||e.nodesConnectable||e.elementsSelectable,minZoomReached:e.transform[2]<=e.minZoom,maxZoomReached:e.transform[2]>=e.maxZoom}),zo=({style:e,showZoom:t=!0,showFitView:n=!0,showInteractive:r=!0,fitViewOptions:s,onZoomIn:i,onZoomOut:o,onFitView:a,onInteractiveChange:l,className:c,children:u,position:d="bottom-left"})=>{const m=pe(),[f,y]=w.useState(!1),{isInteractive:p,minZoomReached:v,maxZoomReached:x}=ae(dm,ge),{zoomIn:_,zoomOut:M,fitView:b}=Rn();if(w.useEffect(()=>{y(!0)},[]),!f)return null;const j=()=>{_(),i==null||i()},C=()=>{M(),o==null||o()},E=()=>{b(s),a==null||a()},L=()=>{m.setState({nodesDraggable:!p,nodesConnectable:!p,elementsSelectable:!p}),l==null||l(!p)};return A.createElement(Sr,{className:ye(["react-flow__controls",c]),position:d,style:e,"data-testid":"rf__controls"},t&&A.createElement(A.Fragment,null,A.createElement(Lt,{onClick:j,className:"react-flow__controls-zoomin",title:"zoom in","aria-label":"zoom in",disabled:x},A.createElement(om,null)),A.createElement(Lt,{onClick:C,className:"react-flow__controls-zoomout",title:"zoom out","aria-label":"zoom out",disabled:v},A.createElement(am,null))),n&&A.createElement(Lt,{className:"react-flow__controls-fitview",onClick:E,title:"fit view","aria-label":"fit view"},A.createElement(lm,null)),r&&A.createElement(Lt,{className:"react-flow__controls-interactive",onClick:L,title:"toggle interactivity","aria-label":"toggle interactivity"},p?A.createElement(um,null):A.createElement(cm,null)),u)};zo.displayName="Controls";var fm=w.memo(zo),ke;(function(e){e.Lines="lines",e.Dots="dots",e.Cross="cross"})(ke||(ke={}));function hm({color:e,dimensions:t,lineWidth:n}){return A.createElement("path",{stroke:e,strokeWidth:n,d:`M${t[0]/2} 0 V${t[1]} M0 ${t[1]/2} H${t[0]}`})}function mm({color:e,radius:t}){return A.createElement("circle",{cx:t,cy:t,r:t,fill:e})}const pm={[ke.Dots]:"#91919a",[ke.Lines]:"#eee",[ke.Cross]:"#e2e2e2"},gm={[ke.Dots]:1,[ke.Lines]:1,[ke.Cross]:6},ym=e=>({transform:e.transform,patternId:`pattern-${e.rfId}`});function Ho({id:e,variant:t=ke.Dots,gap:n=20,size:r,lineWidth:s=1,offset:i=2,color:o,style:a,className:l}){const c=w.useRef(null),{transform:u,patternId:d}=ae(ym,ge),m=o||pm[t],f=r||gm[t],y=t===ke.Dots,p=t===ke.Cross,v=Array.isArray(n)?n:[n,n],x=[v[0]*u[2]||1,v[1]*u[2]||1],_=f*u[2],M=p?[_,_]:x,b=y?[_/i,_/i]:[M[0]/i,M[1]/i];return A.createElement("svg",{className:ye(["react-flow__background",l]),style:{...a,position:"absolute",width:"100%",height:"100%",top:0,left:0},ref:c,"data-testid":"rf__background"},A.createElement("pattern",{id:d+e,x:u[0]%x[0],y:u[1]%x[1],width:x[0],height:x[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${b[0]},-${b[1]})`},y?A.createElement(mm,{color:m,radius:_/i}):A.createElement(hm,{dimensions:M,color:m,lineWidth:s})),A.createElement("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${d+e})`}))}Ho.displayName="Background";var xm=w.memo(Ho);const Or=(()=>{const e=window.location.pathname.match(/^(\/view\/[^/]+)(?:\/|$)/);return e?e[1]:""})();function Fo(e){return Or+e}async function he(e){const t=await fetch(Fo(e));if(!t.ok)throw new Error(`${e}: ${t.status} ${await t.text()}`);return await t.json()}async function Ze(e,t){const n=await fetch(Fo(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json();if(!n.ok)throw new Vo(r.error??`${e}: ${n.status}`,n.status);return r}class Vo extends Error{constructor(n,r){super(n);ue(this,"status");this.status=r}}const yx=e=>he(e?`/api/model?subject_area=${encodeURIComponent(e)}`:"/api/model"),xx=()=>he("/api/diagnostics"),Bo=(e,t)=>he(`/api/ontology/search?q=${encodeURIComponent(e)}`+(t?`&within=${encodeURIComponent(t)}`:"")),Go=()=>he("/api/ontology/ontologies"),wm=e=>he(`/api/ontology/term?ref=${encodeURIComponent(e)}`),bm=()=>he("/api/ontology/stack"),vm=()=>he("/api/ontology/coverage"),_m=(e,t,n)=>Ze("/api/command",{op:e,payload:t,fingerprint:n??""}),Em=()=>he("/api/git/status"),Nm=()=>he("/api/git/diff"),Sm=e=>Ze("/api/git/commit",{message:e}),km=()=>Ze("/api/git/discard",{}),jm=()=>he("/api/decisions"),Os=(e,t)=>Ze(`/api/decisions/${e}/verdict`,{verdict:t}),wx=(e={})=>{const t=new URLSearchParams;return e.subject_area&&t.set("subject_area",e.subject_area),e.q&&t.set("q",e.q),he(`/api/glossary/terms?${t.toString()}`)},bx=()=>he("/api/glossary/config"),vx=e=>Ze("/api/git/propose",e),_x=(e="HEAD")=>he(`/api/git/diff/model?base=${encodeURIComponent(e)}`),Ex=(e="HEAD")=>he(`/api/git/classify?base=${encodeURIComponent(e)}`),Nx=e=>Ze("/api/git/classify",{changes:e}),Sx=e=>he(`/api/git/conflicts?base=${encodeURIComponent(e)}`),kx=(e="")=>he(`/api/git/context?user=${encodeURIComponent(e)}`),jx=(e="")=>he(`/api/git/proposals?user=${encodeURIComponent(e)}`),Cm=()=>he("/api/export"),$m=(e,t)=>{const n=t?`?dialect=${encodeURIComponent(t)}`:"";return`${Or}/api/export/${encodeURIComponent(e)}${n}`},Mm=(e,t,n)=>Ze("/api/import",{format:e,content:t,dialect:n}),Cx=async e=>{for(const t of e)await _m(t.op,t.payload)},$x=(e,t,n)=>fetch(`${Or}/api/preview`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({changes:e,subject_area:t??""}),signal:n}).then(async r=>{if(!r.ok)throw new Vo(await r.text(),r.status);return await r.json()}),Mx=()=>he("/api/glossary/subject-areas"),Ax=e=>he(`/api/glossary/subject-area/${e}`),Ix=(e,t)=>Ze(`/api/glossary/subject-area/${e}/expand`,t),Am={business_key:"🔑",surrogate_key:"#",measure:"Σ",attribute:""};function Im(e){return e??""}const Dr="__entity_source",Lr="__entity_target",Rm=w.memo(function({data:t,selected:n}){var C;const{entity:r,color:s,dimmed:i,highlighted:o,showTypes:a,collapseDetail:l,expanded:c,onToggleExpand:u,endpointAttrs:d,highlightAttrs:m,dimAttrs:f,onHoverAttr:y}=t,p=E=>E.role==="business_key"?0:d!=null&&d.has(E.id)?1:2,v=[...r.attributes].map((E,L)=>({a:E,i:L})).sort((E,L)=>p(E.a)-p(L.a)||E.i-L.i).map(E=>E.a),x=v.filter(E=>E.role==="business_key");let _=v.filter(E=>E.role!=="business_key");const M=!!l&&!c;let b=0;if(M){const E=_.filter(L=>d==null?void 0:d.has(L.id));b=_.length-E.length,_=E}const j=(E,L)=>{const S="attr-row"+(m!=null&&m.has(E.id)?" rel-hit":"")+(f!=null&&f.has(E.id)?" rel-dim":"")+(d!=null&&d.has(E.id)?" endpoint":"");return h.jsxs("div",{className:S,onMouseEnter:y?()=>y(E.id):void 0,onMouseLeave:y?()=>y(null):void 0,children:[h.jsx(qe,{id:E.id,type:"target",position:Z.Left,className:"port attr-port"}),h.jsx(qe,{id:E.id,type:"source",position:Z.Right,className:"port attr-port"}),h.jsx("span",{className:"attr-icon",children:Am[E.role]}),h.jsx("span",{className:"attr-name"+(L?" pk":""),children:E.name}),a&&h.jsx("span",{className:"attr-type",children:Im(E.domain)}),!E.nullable&&h.jsx("span",{className:"attr-notnull",title:"not null",children:"●"})]},E.id)};return h.jsxs("div",{className:"entity-node"+(n?" selected":"")+(i?" dimmed":"")+(o?" highlighted":""),style:{borderTopColor:s},children:[h.jsx(qe,{id:Lr,type:"target",position:Z.Left,className:"port"}),h.jsx(qe,{id:Dr,type:"source",position:Z.Right,className:"port"}),h.jsxs("div",{className:"entity-header",children:[h.jsx("span",{className:"entity-name",title:((C=r.conceptual)==null?void 0:C.definition)??void 0,children:r.name}),h.jsxs("span",{className:"entity-badges",children:[r.pattern&&h.jsx("span",{className:"badge pattern",children:r.pattern}),(()=>{var S;const E=(((S=r.conceptual)==null?void 0:S.ontology_refs)??[]).filter(O=>O.uri);if(E.length===0)return null;const L=E.map(O=>O.uri).join(` -`);return h.jsxs("span",{className:"badge ontology",title:L,children:["⚙",E.length>1?` ${E.length}`:""]})})()]})]}),x.length>0&&h.jsx("div",{className:"attr-section keys",children:x.map(E=>j(E,!0))}),h.jsxs("div",{className:"attr-section",children:[_.map(E=>j(E,!1)),r.attributes.length===0&&h.jsx("div",{className:"attr-row empty",children:"no attributes"}),b>0&&h.jsx("div",{className:"attr-row more",title:"Show all columns",onClick:u?()=>u(r.id):void 0,children:`+${b} more`}),c&&!!l&&h.jsx("div",{className:"attr-row more",title:"Collapse to keys",onClick:u?()=>u(r.id):void 0,children:"− collapse"})]})]})}),Tm=["string","integer","bigint","decimal","boolean","date","timestamp"];function Om({entity:e,doc:t,caps:n,exec:r,onClose:s,onFocusEntity:i,onAlign:o,onEditMapping:a}){var m,f,y,p,v,x,_,M,b,j,C,E,L;const l=!n.canEdit,c=e.conceptual,u=t.relationships.filter(S=>S.from.entity===e.id||S.to.entity===e.id),d=S=>{var O;return((O=t.entities.find(P=>P.id===S))==null?void 0:O.name)??S};return h.jsxs("aside",{className:"detail-panel",children:[h.jsxs("div",{className:"detail-header",children:[h.jsxs("div",{style:{flex:1},children:[h.jsx(Pe,{value:e.name,className:"entity-title-input",readOnly:l,onCommit:S=>r("rename_entity",{id:e.id,name:S})}),(e.physical_name||!l)&&h.jsxs("div",{className:"physical-name-row",title:"Physical table name (erwin Physical_Name)",children:[h.jsx("span",{className:"physical-label",children:"physical"}),h.jsx(Pe,{value:e.physical_name??"",className:"physical-name-input",placeholder:l?"—":"physical table name…",readOnly:l,onCommit:S=>r("rename_entity",{id:e.id,physical_name:S})})]}),h.jsxs("div",{children:[h.jsx(Lm,{entity:e,doc:t,readOnly:l,exec:r}),h.jsx(zm,{entity:e,readOnly:l,exec:r}),e.category&&h.jsx("span",{className:"cat-badge "+e.category.role,title:e.category.role==="supertype"?`Supertype of category "${e.category.category}"`:`Subtype in "${e.category.category}" (${e.category.materialization})`,children:e.category.role==="supertype"?"◆ supertype":"◇ subtype"})]})]}),h.jsx("button",{className:"icon-btn",onClick:s,title:"Close",children:"✕"})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Definition"}),h.jsx(Pe,{value:((m=c==null?void 0:c.definition)==null?void 0:m.trim())??"",multiline:!0,placeholder:l?"—":"add a business definition…",className:"definition-input",readOnly:l,onCommit:S=>r("set_definition",{id:e.id,definition:S})})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Ontology",!l&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>o(e),children:(((f=c==null?void 0:c.ontology_refs)==null?void 0:f.length)??0)>0?"Add / re-align…":"Align…"}),(((y=c==null?void 0:c.ontology_refs)==null?void 0:y.length)??0)>0&&h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_alignment",{id:e.id}),title:"Clear every alignment on this object",children:"Clear all"})]})]}),(c==null?void 0:c.ontology_layer)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"layer"}),h.jsx("span",{className:`chip layer-${c.ontology_layer}`,children:c.ontology_layer}),c.no_industry_equivalent&&h.jsx("span",{className:"chip",title:"reviewed: no industry equivalent",children:"no industry equivalent"})]}),h.jsx(Dm,{entityId:e.id,refs:(c==null?void 0:c.ontology_refs)??[],caps:n,exec:r})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Knowledge Graph mapping",!l&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>a(e),children:(p=e.term_map)!=null&&p.subject_template||(v=e.term_map)!=null&&v.class_iri?"Edit mapping…":"Map…"}),(((x=e.term_map)==null?void 0:x.subject_template)||((_=e.term_map)==null?void 0:_.class_iri))&&h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_term_map",{id:e.id}),children:"Clear"})]})]}),(M=e.term_map)!=null&&M.subject_template||(b=e.term_map)!=null&&b.class_iri?h.jsxs(h.Fragment,{children:[((j=e.term_map)==null?void 0:j.class_iri)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"class IRI"}),h.jsx("code",{className:"v",children:e.term_map.class_iri})]}),((C=e.term_map)==null?void 0:C.subject_template)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"subject"}),h.jsx("code",{className:"v",children:e.term_map.subject_template})]})]}):h.jsxs("p",{className:"empty-hint",children:["uses the default R2RML term-map (base IRI",t.project.kg_base_iri?` ${t.project.kg_base_iri}`:""," + primary key)"]})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Stewardship"}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"owner"}),h.jsx(Pe,{value:((E=c==null?void 0:c.stewardship)==null?void 0:E.owner)??"",placeholder:"—",readOnly:l,onCommit:S=>{var O;return r("set_stewardship",{id:e.id,owner:S,steward:((O=c==null?void 0:c.stewardship)==null?void 0:O.steward)??null})}})]}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"steward"}),h.jsx(Pe,{value:((L=c==null?void 0:c.stewardship)==null?void 0:L.steward)??"",placeholder:"—",readOnly:l,onCommit:S=>{var O;return r("set_stewardship",{id:e.id,owner:((O=c==null?void 0:c.stewardship)==null?void 0:O.owner)??null,steward:S})}})]})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Attributes (",e.attributes.length,")"]}),h.jsx("table",{className:"attr-table",children:h.jsx("tbody",{children:e.attributes.map(S=>h.jsx(Fm,{a:S,entity:e,doc:t,readOnly:l,exec:r},S.id))})}),!l&&h.jsx(Vm,{entity:e,exec:r})]}),e.key_groups&&e.key_groups.length>0&&h.jsxs("section",{children:[h.jsxs("h3",{children:["Keys (",e.key_groups.length,")"]}),e.key_groups.map(S=>{const O=S.members.map(P=>{var V;return((V=e.attributes.find(F=>F.id===P))==null?void 0:V.name)??"?"}).join(", ");return h.jsxs("div",{className:"key-row",children:[h.jsx("span",{className:"key-badge "+S.type,children:S.type.toUpperCase()}),h.jsx("span",{className:"key-name",children:S.name}),h.jsx("span",{className:"key-cols",children:O})]},S.id)})]}),e.udp&&Object.keys(e.udp).length>0&&h.jsxs("section",{children:[h.jsx("h3",{children:"Properties (UDP)"}),h.jsx("table",{className:"attr-table",children:h.jsx("tbody",{children:Object.entries(e.udp).map(([S,O])=>h.jsxs("tr",{className:"udp-row",children:[h.jsx("td",{className:"udp-key",children:S}),h.jsx("td",{className:"udp-val",children:String(O)})]},S))})})]}),u.length>0&&h.jsxs("section",{children:[h.jsxs("h3",{children:["Relationships (",u.length,")"]}),u.map(S=>{const O=S.from.entity===e.id?S.to.entity:S.from.entity;return h.jsxs("div",{className:"rel-row",children:[h.jsxs("div",{className:"rel-main",children:[h.jsxs("button",{className:"rel-link",onClick:()=>i(O),children:[h.jsx("span",{className:"rel-card",children:Bm(S,e.id)}),d(O)]}),(S.verb_phrase||S.inverse_verb_phrase||!l)&&h.jsxs("div",{className:"verb-phrases",title:"erwin verb phrases (parent → child, and inverse)",children:[h.jsx(Pe,{value:S.verb_phrase??"",className:"verb-input",placeholder:l?"":"verb phrase…",readOnly:l,onCommit:P=>r("update_relationship",{id:S.id,verb_phrase:P})}),h.jsx("span",{className:"verb-sep",children:"/"}),h.jsx(Pe,{value:S.inverse_verb_phrase??"",className:"verb-input",placeholder:l?"":"inverse…",readOnly:l,onCommit:P=>r("update_relationship",{id:S.id,inverse_verb_phrase:P})})]})]}),!l&&h.jsx("button",{className:"mini-btn danger",title:`delete ${S.name}`,onClick:()=>r("delete_relationship",{id:S.id}),children:"✕"})]},S.id)})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Identity"}),h.jsx("code",{className:"ulid",title:"Immutable ULID — click to copy",onClick:()=>{var S;return(S=navigator.clipboard)==null?void 0:S.writeText(e.id)},children:e.id}),!l&&h.jsx("div",{style:{marginTop:12},children:h.jsx("button",{className:"mini-btn danger",onClick:()=>{confirm(`Delete entity '${e.name}' (and its relationships)?`)&&r("delete_entity",{id:e.id,cascade:!0}).then(s)},children:"Delete entity…"})})]})]})}function Dm({entityId:e,refs:t,caps:n,exec:r}){const s=!n.canEdit,i=t.filter(o=>o.uri);return i.length===0?h.jsx("p",{className:"empty-hint",children:"not aligned to any ontology term"}):h.jsx("div",{className:"ref-list",children:i.map((o,a)=>{const l=o.status==="proposed";return h.jsxs("div",{className:"ref-card"+(l?" proposed":""),children:[h.jsxs("div",{className:"ref-head",children:[h.jsx("code",{className:"ref-uri",title:o.uri??void 0,children:o.uri}),h.jsx("span",{className:"ref-status "+(l?"proposed":"accepted"),children:l?"proposed":"accepted"})]}),h.jsxs("div",{className:"ref-meta",children:[o.predicate&&h.jsx("span",{className:"ref-pill",children:o.predicate}),o.layer&&h.jsx("span",{className:`chip layer-${o.layer}`,children:o.layer}),o.resolved_via&&h.jsxs("span",{className:"ref-via",title:"resolver that found this term",children:["via ",o.resolved_via]}),typeof o.confidence=="number"&&h.jsxs("span",{className:"ref-via",children:["conf ",Math.round(o.confidence*100),"%"]})]}),!s&&h.jsxs("div",{className:"ref-actions",children:[l&&n.canArbitrate&&h.jsx("button",{className:"mini-btn",onClick:()=>r("promote_alignment",{id:e,uri:o.uri}),title:"Accept this proposed alignment (architect action)",children:"Promote"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_alignment",{id:e,uri:o.uri}),title:"Remove this alignment",children:"Remove"})]})]},`${o.uri}-${a}`)})})}function Pe({value:e,onCommit:t,readOnly:n,multiline:r=!1,placeholder:s="",className:i=""}){const[o,a]=w.useState(e);w.useEffect(()=>a(e),[e]);const l=()=>{o.trim()!==e.trim()&&t(o.trim())};return n?h.jsx("span",{className:i,children:e||s}):r?h.jsx("textarea",{className:`edit-input ${i}`,value:o,placeholder:s,rows:3,onChange:c=>a(c.target.value),onBlur:l}):h.jsx("input",{className:`edit-input ${i}`,value:o,placeholder:s,onChange:c=>a(c.target.value),onBlur:l,onKeyDown:c=>c.key==="Enter"&&c.target.blur()})}function Lm({entity:e,doc:t,readOnly:n,exec:r}){var i,o,a;const s=((o=(i=e.conceptual)==null?void 0:i.subject_area)==null?void 0:o.id)??"";return n?(a=e.conceptual)!=null&&a.subject_area?h.jsx("span",{className:"chip",children:e.conceptual.subject_area.name}):null:h.jsxs("select",{className:"chip-select",value:s,onChange:l=>r("set_subject_area",{id:e.id,subject_area:l.target.value||null}),children:[h.jsx("option",{value:"",children:"no subject area"}),t.subject_areas.map(l=>h.jsx("option",{value:l.id,children:l.name},l.id))]})}const Pm=["","scd2","hub","link","satellite","bridge"];function zm({entity:e,readOnly:t,exec:n}){return t?e.pattern?h.jsx("span",{className:"chip pattern",children:e.pattern}):null:h.jsx("select",{className:"chip-select",value:e.pattern??"",onChange:r=>n("set_pattern",{id:e.id,pattern:r.target.value||null}),children:Pm.map(r=>h.jsx("option",{value:r,children:r||"no pattern"},r))})}const Hm=["attribute","business_key","measure","surrogate_key"];function Fm({a:e,entity:t,doc:n,readOnly:r,exec:s}){if(r)return h.jsxs("tr",{children:[h.jsxs("td",{className:e.role==="business_key"?"pk":"",children:[e.name,e.physical_name&&e.physical_name!==e.name&&h.jsx("span",{className:"physical-col",title:"Physical column name",children:e.physical_name}),e.enum_values&&e.enum_values.length>0&&h.jsx("span",{className:"enum-badge",title:`Allowed: ${e.enum_values.join(", ")}`,children:"enum"})]}),h.jsx("td",{className:"type",children:e.domain??""}),h.jsx("td",{className:"null",children:e.nullable?"":"NN"})]});const i=o=>s("update_attribute",{entity_id:t.id,attribute_id:e.id,...o});return h.jsxs("tr",{children:[h.jsxs("td",{className:e.role==="business_key"?"pk":"",children:[h.jsx(Pe,{value:e.name,readOnly:!1,onCommit:o=>i({name:o})}),(e.physical_name||!1)&&h.jsx(Pe,{value:e.physical_name??"",className:"physical-col-input",placeholder:"physical…",readOnly:!1,onCommit:o=>i({physical_name:o})}),e.enum_values&&e.enum_values.length>0&&h.jsx("span",{className:"enum-badge",title:`Allowed: ${e.enum_values.join(", ")}`,children:"enum"})]}),h.jsx("td",{className:"type",children:h.jsx("select",{className:"cell-select",value:e.domain??"string",onChange:o=>i({domain:o.target.value}),children:[...new Set([...Tm,...n.domains??[],e.domain??"string"])].sort().map(o=>h.jsx("option",{value:o,children:o},o))})}),h.jsx("td",{children:h.jsx("select",{className:"cell-select",value:e.role,onChange:o=>i({role:o.target.value}),title:"role",children:Hm.map(o=>h.jsx("option",{value:o,children:o==="business_key"?"🔑 key":o==="measure"?"Σ measure":o},o))})}),h.jsx("td",{className:"null",children:h.jsx("input",{type:"checkbox",checked:!e.nullable,title:"not null",onChange:o=>i({nullable:!o.target.checked})})}),h.jsx("td",{children:h.jsx("button",{className:"mini-btn danger",title:"delete attribute",onClick:()=>s("delete_attribute",{entity_id:t.id,attribute_id:e.id}),children:"✕"})})]})}function Vm({entity:e,exec:t}){const[n,r]=w.useState(""),s=()=>{n.trim()&&t("add_attribute",{entity_id:e.id,name:n,domain:"string"}).then(()=>r(""))};return h.jsxs("div",{className:"add-attr",children:[h.jsx("input",{className:"edit-input",placeholder:"+ add attribute…",value:n,onChange:i=>r(i.target.value),onKeyDown:i=>i.key==="Enter"&&s()}),n&&h.jsx("button",{className:"mini-btn",onClick:s,children:"Add"})]})}function Bm(e,t){const n=e.from.entity===t;switch(e.cardinality){case"many_to_one":return n?"N:1 →":"1:N ←";case"one_to_many":return n?"1:N →":"N:1 ←";case"one_to_one":return"1:1";default:return"N:M"}}var Gm="\0",rt="\0",Ds="";let Ym=class{constructor(t){ue(this,"_isDirected",!0);ue(this,"_isMultigraph",!1);ue(this,"_isCompound",!1);ue(this,"_label");ue(this,"_defaultNodeLabelFn",()=>{});ue(this,"_defaultEdgeLabelFn",()=>{});ue(this,"_nodes",{});ue(this,"_in",{});ue(this,"_preds",{});ue(this,"_out",{});ue(this,"_sucs",{});ue(this,"_edgeObjs",{});ue(this,"_edgeLabels",{});ue(this,"_nodeCount",0);ue(this,"_edgeCount",0);ue(this,"_parent");ue(this,"_children");t&&(this._isDirected=Object.hasOwn(t,"directed")?t.directed:!0,this._isMultigraph=Object.hasOwn(t,"multigraph")?t.multigraph:!1,this._isCompound=Object.hasOwn(t,"compound")?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children[rt]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return this._defaultNodeLabelFn=t,typeof t!="function"&&(this._defaultNodeLabelFn=()=>t),this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){var t=this;return this.nodes().filter(n=>Object.keys(t._in[n]).length===0)}sinks(){var t=this;return this.nodes().filter(n=>Object.keys(t._out[n]).length===0)}setNodes(t,n){var r=arguments,s=this;return t.forEach(function(i){r.length>1?s.setNode(i,n):s.setNode(i)}),this}setNode(t,n){return Object.hasOwn(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=n),this):(this._nodes[t]=arguments.length>1?n:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]=rt,this._children[t]={},this._children[rt][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return Object.hasOwn(this._nodes,t)}removeNode(t){var n=this;if(Object.hasOwn(this._nodes,t)){var r=s=>n.removeEdge(n._edgeObjs[s]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(function(s){n.setParent(s)}),delete this._children[t]),Object.keys(this._in[t]).forEach(r),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(r),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,n){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(n===void 0)n=rt;else{n+="";for(var r=n;r!==void 0;r=this.parent(r))if(r===t)throw new Error("Setting "+n+" as parent of "+t+" would create a cycle");this.setNode(n)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=n,this._children[n][t]=!0,this}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}parent(t){if(this._isCompound){var n=this._parent[t];if(n!==rt)return n}}children(t=rt){if(this._isCompound){var n=this._children[t];if(n)return Object.keys(n)}else{if(t===rt)return this.nodes();if(this.hasNode(t))return[]}}predecessors(t){var n=this._preds[t];if(n)return Object.keys(n)}successors(t){var n=this._sucs[t];if(n)return Object.keys(n)}neighbors(t){var n=this.predecessors(t);if(n){const s=new Set(n);for(var r of this.successors(t))s.add(r);return Array.from(s.values())}}isLeaf(t){var n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),n.length===0}filterNodes(t){var n=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});n.setGraph(this.graph());var r=this;Object.entries(this._nodes).forEach(function([o,a]){t(o)&&n.setNode(o,a)}),Object.values(this._edgeObjs).forEach(function(o){n.hasNode(o.v)&&n.hasNode(o.w)&&n.setEdge(o,r.edge(o))});var s={};function i(o){var a=r.parent(o);return a===void 0||n.hasNode(a)?(s[o]=a,a):a in s?s[a]:i(a)}return this._isCompound&&n.nodes().forEach(o=>n.setParent(o,i(o))),n}setDefaultEdgeLabel(t){return this._defaultEdgeLabelFn=t,typeof t!="function"&&(this._defaultEdgeLabelFn=()=>t),this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,n){var r=this,s=arguments;return t.reduce(function(i,o){return s.length>1?r.setEdge(i,o,n):r.setEdge(i,o),o}),this}setEdge(){var t,n,r,s,i=!1,o=arguments[0];typeof o=="object"&&o!==null&&"v"in o?(t=o.v,n=o.w,r=o.name,arguments.length===2&&(s=arguments[1],i=!0)):(t=o,n=arguments[1],r=arguments[3],arguments.length>2&&(s=arguments[2],i=!0)),t=""+t,n=""+n,r!==void 0&&(r=""+r);var a=Pt(this._isDirected,t,n,r);if(Object.hasOwn(this._edgeLabels,a))return i&&(this._edgeLabels[a]=s),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(n),this._edgeLabels[a]=i?s:this._defaultEdgeLabelFn(t,n,r);var l=Wm(this._isDirected,t,n,r);return t=l.v,n=l.w,Object.freeze(l),this._edgeObjs[a]=l,Ls(this._preds[n],t),Ls(this._sucs[t],n),this._in[n][a]=l,this._out[t][a]=l,this._edgeCount++,this}edge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r);return this._edgeLabels[s]}edgeAsObj(){const t=this.edge(...arguments);return typeof t!="object"?{label:t}:t}hasEdge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r);return Object.hasOwn(this._edgeLabels,s)}removeEdge(t,n,r){var s=arguments.length===1?Kn(this._isDirected,arguments[0]):Pt(this._isDirected,t,n,r),i=this._edgeObjs[s];return i&&(t=i.v,n=i.w,delete this._edgeLabels[s],delete this._edgeObjs[s],Ps(this._preds[n],t),Ps(this._sucs[t],n),delete this._in[n][s],delete this._out[t][s],this._edgeCount--),this}inEdges(t,n){var r=this._in[t];if(r){var s=Object.values(r);return n?s.filter(i=>i.v===n):s}}outEdges(t,n){var r=this._out[t];if(r){var s=Object.values(r);return n?s.filter(i=>i.w===n):s}}nodeEdges(t,n){var r=this.inEdges(t,n);if(r)return r.concat(this.outEdges(t,n))}};function Ls(e,t){e[t]?e[t]++:e[t]=1}function Ps(e,t){--e[t]||delete e[t]}function Pt(e,t,n,r){var s=""+t,i=""+n;if(!e&&s>i){var o=s;s=i,i=o}return s+Ds+i+Ds+(r===void 0?Gm:r)}function Wm(e,t,n,r){var s=""+t,i=""+n;if(!e&&s>i){var o=s;s=i,i=o}var a={v:s,w:i};return r&&(a.name=r),a}function Kn(e,t){return Pt(e,t.v,t.w,t.name)}var Pr=Ym,Um="2.2.4",Xm={Graph:Pr,version:Um},qm=Pr,Km={write:Zm,read:ep};function Zm(e){var t={options:{directed:e.isDirected(),multigraph:e.isMultigraph(),compound:e.isCompound()},nodes:Qm(e),edges:Jm(e)};return e.graph()!==void 0&&(t.value=structuredClone(e.graph())),t}function Qm(e){return e.nodes().map(function(t){var n=e.node(t),r=e.parent(t),s={v:t};return n!==void 0&&(s.value=n),r!==void 0&&(s.parent=r),s})}function Jm(e){return e.edges().map(function(t){var n=e.edge(t),r={v:t.v,w:t.w};return t.name!==void 0&&(r.name=t.name),n!==void 0&&(r.value=n),r})}function ep(e){var t=new qm(e.options).setGraph(e.value);return e.nodes.forEach(function(n){t.setNode(n.v,n.value),n.parent&&t.setParent(n.v,n.parent)}),e.edges.forEach(function(n){t.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),t}var tp=np;function np(e){var t={},n=[],r;function s(i){Object.hasOwn(t,i)||(t[i]=!0,r.push(i),e.successors(i).forEach(s),e.predecessors(i).forEach(s))}return e.nodes().forEach(function(i){r=[],s(i),r.length&&n.push(r)}),n}let rp=class{constructor(){ue(this,"_arr",[]);ue(this,"_keyIndices",{})}size(){return this._arr.length}keys(){return this._arr.map(function(t){return t.key})}has(t){return Object.hasOwn(this._keyIndices,t)}priority(t){var n=this._keyIndices[t];if(n!==void 0)return this._arr[n].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,n){var r=this._keyIndices;if(t=String(t),!Object.hasOwn(r,t)){var s=this._arr,i=s.length;return r[t]=i,s.push({key:t,priority:n}),this._decrease(i),!0}return!1}removeMin(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,n){var r=this._keyIndices[t];if(n>this._arr[r].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[r].priority+" New: "+n);this._arr[r].priority=n,this._decrease(r)}_heapify(t){var n=this._arr,r=2*t,s=r+1,i=t;r>1,!(n[s].priority1;function op(e,t,n,r){return ap(e,String(t),n||ip,r||function(s){return e.outEdges(s)})}function ap(e,t,n,r){var s={},i=new sp,o,a,l=function(c){var u=c.v!==o?c.v:c.w,d=s[u],m=n(c),f=a.distance+m;if(m<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+c+" Weight: "+m);f0&&(o=i.removeMin(),a=s[o],a.distance!==Number.POSITIVE_INFINITY);)r(o).forEach(l);return s}var lp=Wo,cp=up;function up(e,t,n){return e.nodes().reduce(function(r,s){return r[s]=lp(e,s,t,n),r},{})}var Uo=dp;function dp(e){var t=0,n=[],r={},s=[];function i(o){var a=r[o]={onStack:!0,lowlink:t,index:t++};if(n.push(o),e.successors(o).forEach(function(u){Object.hasOwn(r,u)?r[u].onStack&&(a.lowlink=Math.min(a.lowlink,r[u].index)):(i(u),a.lowlink=Math.min(a.lowlink,r[u].lowlink))}),a.lowlink===a.index){var l=[],c;do c=n.pop(),r[c].onStack=!1,l.push(c);while(o!==c);s.push(l)}}return e.nodes().forEach(function(o){Object.hasOwn(r,o)||i(o)}),s}var fp=Uo,hp=mp;function mp(e){return fp(e).filter(function(t){return t.length>1||t.length===1&&e.hasEdge(t[0],t[0])})}var pp=yp,gp=()=>1;function yp(e,t,n){return xp(e,t||gp,n||function(r){return e.outEdges(r)})}function xp(e,t,n){var r={},s=e.nodes();return s.forEach(function(i){r[i]={},r[i][i]={distance:0},s.forEach(function(o){i!==o&&(r[i][o]={distance:Number.POSITIVE_INFINITY})}),n(i).forEach(function(o){var a=o.v===i?o.w:o.v,l=t(o);r[i][a]={distance:l,predecessor:i}})}),s.forEach(function(i){var o=r[i];s.forEach(function(a){var l=r[a];s.forEach(function(c){var u=l[i],d=o[c],m=l[c],f=u.distance+d.distance;fe.successors(a):a=>e.neighbors(a),s=n==="post"?_p:Ep,i=[],o={};return t.forEach(a=>{if(!e.hasNode(a))throw new Error("Graph does not have node: "+a);s(a,r,o,i)}),i}function _p(e,t,n,r){for(var s=[[e,!1]];s.length>0;){var i=s.pop();i[1]?r.push(i[0]):Object.hasOwn(n,i[0])||(n[i[0]]=!0,s.push([i[0],!0]),Zo(t(i[0]),o=>s.push([o,!1])))}}function Ep(e,t,n,r){for(var s=[e];s.length>0;){var i=s.pop();Object.hasOwn(n,i)||(n[i]=!0,r.push(i),Zo(t(i),o=>s.push(o)))}}function Zo(e,t){for(var n=e.length;n--;)t(e[n],n,e);return e}var Np=Ko,Sp=kp;function kp(e,t){return Np(e,t,"post")}var jp=Ko,Cp=$p;function $p(e,t){return jp(e,t,"pre")}var Mp=Pr,Ap=Yo,Ip=Rp;function Rp(e,t){var n=new Mp,r={},s=new Ap,i;function o(l){var c=l.v===i?l.w:l.v,u=s.priority(c);if(u!==void 0){var d=t(l);d0;){if(i=s.removeMin(),Object.hasOwn(r,i))n.setEdge(i,r[i]);else{if(a)throw new Error("Input graph is not connected: "+e);a=!0}e.nodeEdges(i).forEach(o)}return n}var Tp={components:tp,dijkstra:Wo,dijkstraAll:cp,findCycles:hp,floydWarshall:pp,isAcyclic:wp,postorder:Sp,preorder:Cp,prim:Ip,tarjan:Uo,topsort:qo},Hs=Xm,Ie={Graph:Hs.Graph,json:Km,alg:Tp,version:Hs.version};let Op=class{constructor(){let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,n=t._prev;if(n!==t)return Fs(n),n}enqueue(t){let n=this._sentinel;t._prev&&t._next&&Fs(t),t._next=n._next,n._next._prev=t,n._next=t,t._prev=n}toString(){let t=[],n=this._sentinel,r=n._prev;for(;r!==n;)t.push(JSON.stringify(r,Dp)),r=r._prev;return"["+t.join(", ")+"]"}};function Fs(e){e._prev._next=e._next,e._next._prev=e._prev,delete e._next,delete e._prev}function Dp(e,t){if(e!=="_next"&&e!=="_prev")return t}var Lp=Op;let Pp=Ie.Graph,zp=Lp;var Hp=Vp;let Fp=()=>1;function Vp(e,t){if(e.nodeCount()<=1)return[];let n=Gp(e,t||Fp);return Bp(n.graph,n.buckets,n.zeroIdx).flatMap(s=>e.outEdges(s.v,s.w))}function Bp(e,t,n){let r=[],s=t[t.length-1],i=t[0],o;for(;e.nodeCount();){for(;o=i.dequeue();)Zn(e,t,n,o);for(;o=s.dequeue();)Zn(e,t,n,o);if(e.nodeCount()){for(let a=t.length-2;a>0;--a)if(o=t[a].dequeue(),o){r=r.concat(Zn(e,t,n,o,!0));break}}}return r}function Zn(e,t,n,r,s){let i=s?[]:void 0;return e.inEdges(r.v).forEach(o=>{let a=e.edge(o),l=e.node(o.v);s&&i.push({v:o.v,w:o.w}),l.out-=a,xr(t,n,l)}),e.outEdges(r.v).forEach(o=>{let a=e.edge(o),l=o.w,c=e.node(l);c.in-=a,xr(t,n,c)}),e.removeNode(r.v),i}function Gp(e,t){let n=new Pp,r=0,s=0;e.nodes().forEach(a=>{n.setNode(a,{v:a,in:0,out:0})}),e.edges().forEach(a=>{let l=n.edge(a.v,a.w)||0,c=t(a),u=l+c;n.setEdge(a.v,a.w,u),s=Math.max(s,n.node(a.v).out+=c),r=Math.max(r,n.node(a.w).in+=c)});let i=Yp(s+r+3).map(()=>new zp),o=r+1;return n.nodes().forEach(a=>{xr(i,o,n.node(a))}),{graph:n,buckets:i,zeroIdx:o}}function xr(e,t,n){n.out?n.in?e[n.out-n.in+t].enqueue(n):e[e.length-1].enqueue(n):e[0].enqueue(n)}function Yp(e){const t=[];for(let n=0;nt.setNode(n,e.node(n))),e.edges().forEach(n=>{let r=t.edge(n.v,n.w)||{weight:0,minlen:1},s=e.edge(n);t.setEdge(n.v,n.w,{weight:r.weight+s.weight,minlen:Math.max(r.minlen,s.minlen)})}),t}function Up(e){let t=new Qo({multigraph:e.isMultigraph()}).setGraph(e.graph());return e.nodes().forEach(n=>{e.children(n).length||t.setNode(n,e.node(n))}),e.edges().forEach(n=>{t.setEdge(n,e.edge(n))}),t}function Xp(e){let t=e.nodes().map(n=>{let r={};return e.outEdges(n).forEach(s=>{r[s.w]=(r[s.w]||0)+e.edge(s).weight}),r});return zr(e.nodes(),t)}function qp(e){let t=e.nodes().map(n=>{let r={};return e.inEdges(n).forEach(s=>{r[s.v]=(r[s.v]||0)+e.edge(s).weight}),r});return zr(e.nodes(),t)}function Kp(e,t){let n=e.x,r=e.y,s=t.x-n,i=t.y-r,o=e.width/2,a=e.height/2;if(!s&&!i)throw new Error("Not possible to find intersection inside of the rectangle");let l,c;return Math.abs(i)*o>Math.abs(s)*a?(i<0&&(a=-a),l=a*s/i,c=a):(s<0&&(o=-o),l=o,c=o*i/s),{x:n+l,y:r+c}}function Zp(e){let t=ra(ta(e)+1).map(()=>[]);return e.nodes().forEach(n=>{let r=e.node(n),s=r.rank;s!==void 0&&(t[s][r.order]=n)}),t}function Qp(e){let t=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MAX_VALUE:s}),n=Tn(Math.min,t);e.nodes().forEach(r=>{let s=e.node(r);Object.hasOwn(s,"rank")&&(s.rank-=n)})}function Jp(e){let t=e.nodes().map(o=>e.node(o).rank),n=Tn(Math.min,t),r=[];e.nodes().forEach(o=>{let a=e.node(o).rank-n;r[a]||(r[a]=[]),r[a].push(o)});let s=0,i=e.graph().nodeRankFactor;Array.from(r).forEach((o,a)=>{o===void 0&&a%i!==0?--s:o!==void 0&&s&&o.forEach(l=>e.node(l).rank+=s)})}function eg(e,t,n,r){let s={width:0,height:0};return arguments.length>=4&&(s.rank=n,s.order=r),Jo(e,"border",s,t)}function tg(e,t=ea){const n=[];for(let r=0;rea){const n=tg(t);return e.apply(null,n.map(r=>e.apply(null,r)))}else return e.apply(null,t)}function ta(e){const n=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MIN_VALUE:s});return Tn(Math.max,n)}function ng(e,t){let n={lhs:[],rhs:[]};return e.forEach(r=>{t(r)?n.lhs.push(r):n.rhs.push(r)}),n}function rg(e,t){let n=Date.now();try{return t()}finally{console.log(e+" time: "+(Date.now()-n)+"ms")}}function sg(e,t){return t()}let ig=0;function na(e){var t=++ig;return e+(""+t)}function ra(e,t,n=1){t==null&&(t=e,e=0);let r=i=>itr[t]),Object.entries(e).reduce((r,[s,i])=>(r[s]=n(i,s),r),{})}function zr(e,t){return e.reduce((n,r,s)=>(n[r]=t[s],n),{})}let lg=Hp,cg=fe.uniqueId;var ug={run:dg,undo:hg};function dg(e){(e.graph().acyclicer==="greedy"?lg(e,n(e)):fg(e)).forEach(r=>{let s=e.edge(r);e.removeEdge(r),s.forwardName=r.name,s.reversed=!0,e.setEdge(r.w,r.v,s,cg("rev"))});function n(r){return s=>r.edge(s).weight}}function fg(e){let t=[],n={},r={};function s(i){Object.hasOwn(r,i)||(r[i]=!0,n[i]=!0,e.outEdges(i).forEach(o=>{Object.hasOwn(n,o.w)?t.push(o):s(o.w)}),delete n[i])}return e.nodes().forEach(s),t}function hg(e){e.edges().forEach(t=>{let n=e.edge(t);if(n.reversed){e.removeEdge(t);let r=n.forwardName;delete n.reversed,delete n.forwardName,e.setEdge(t.w,t.v,n,r)}})}let mg=fe;var pg={run:gg,undo:xg};function gg(e){e.graph().dummyChains=[],e.edges().forEach(t=>yg(e,t))}function yg(e,t){let n=t.v,r=e.node(n).rank,s=t.w,i=e.node(s).rank,o=t.name,a=e.edge(t),l=a.labelRank;if(i===r+1)return;e.removeEdge(t);let c,u,d;for(d=0,++r;r{let n=e.node(t),r=n.edgeLabel,s;for(e.setEdge(n.edgeObj,r);n.dummy;)s=e.successors(t)[0],e.removeNode(t),r.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(r.x=n.x,r.y=n.y,r.width=n.width,r.height=n.height),t=s,n=e.node(t)})}const{applyWithChunking:wg}=fe;var On={longestPath:bg,slack:vg};function bg(e){var t={};function n(r){var s=e.node(r);if(Object.hasOwn(t,r))return s.rank;t[r]=!0;let i=e.outEdges(r).map(a=>a==null?Number.POSITIVE_INFINITY:n(a.w)-e.edge(a).minlen);var o=wg(Math.min,i);return o===Number.POSITIVE_INFINITY&&(o=0),s.rank=o}e.sources().forEach(n)}function vg(e,t){return e.node(t.w).rank-e.node(t.v).rank-e.edge(t).minlen}var _g=Ie.Graph,En=On.slack,sa=Eg;function Eg(e){var t=new _g({directed:!1}),n=e.nodes()[0],r=e.nodeCount();t.setNode(n,{});for(var s,i;Ng(t,e){var i=s.v,o=r===i?s.w:i;!e.hasNode(o)&&!En(t,s)&&(e.setNode(o,{}),e.setEdge(r,o,{}),n(o))})}return e.nodes().forEach(n),e.nodeCount()}function Sg(e,t){return t.edges().reduce((r,s)=>{let i=Number.POSITIVE_INFINITY;return e.hasNode(s.v)!==e.hasNode(s.w)&&(i=En(t,s)),it.node(r).rank+=n)}var jg=sa,Vs=On.slack,Cg=On.longestPath,$g=Ie.alg.preorder,Mg=Ie.alg.postorder,Ag=fe.simplify,Ig=dt;dt.initLowLimValues=Fr;dt.initCutValues=Hr;dt.calcCutValue=ia;dt.leaveEdge=aa;dt.enterEdge=la;dt.exchangeEdges=ca;function dt(e){e=Ag(e),Cg(e);var t=jg(e);Fr(t),Hr(t,e);for(var n,r;n=aa(t);)r=la(t,e,n),ca(t,e,n,r)}function Hr(e,t){var n=Mg(e,e.nodes());n=n.slice(0,n.length-1),n.forEach(r=>Rg(e,t,r))}function Rg(e,t,n){var r=e.node(n),s=r.parent;e.edge(n,s).cutvalue=ia(e,t,n)}function ia(e,t,n){var r=e.node(n),s=r.parent,i=!0,o=t.edge(n,s),a=0;return o||(i=!1,o=t.edge(s,n)),a=o.weight,t.nodeEdges(n).forEach(l=>{var c=l.v===n,u=c?l.w:l.v;if(u!==s){var d=c===i,m=t.edge(l).weight;if(a+=d?m:-m,Og(e,n,u)){var f=e.edge(n,u).cutvalue;a+=d?-f:f}}}),a}function Fr(e,t){arguments.length<2&&(t=e.nodes()[0]),oa(e,{},1,t)}function oa(e,t,n,r,s){var i=n,o=e.node(r);return t[r]=!0,e.neighbors(r).forEach(a=>{Object.hasOwn(t,a)||(n=oa(e,t,n,a,r))}),o.low=i,o.lim=n++,s?o.parent=s:delete o.parent,n}function aa(e){return e.edges().find(t=>e.edge(t).cutvalue<0)}function la(e,t,n){var r=n.v,s=n.w;t.hasEdge(r,s)||(r=n.w,s=n.v);var i=e.node(r),o=e.node(s),a=i,l=!1;i.lim>o.lim&&(a=o,l=!0);var c=t.edges().filter(u=>l===Bs(e,e.node(u.v),a)&&l!==Bs(e,e.node(u.w),a));return c.reduce((u,d)=>Vs(t,d)!t.node(s).parent),r=$g(e,n);r=r.slice(1),r.forEach(s=>{var i=e.node(s).parent,o=t.edge(s,i),a=!1;o||(o=t.edge(i,s),a=!0),t.node(s).rank=t.node(i).rank+(a?o.minlen:-o.minlen)})}function Og(e,t,n){return e.hasEdge(t,n)}function Bs(e,t,n){return n.low<=t.lim&&t.lim<=n.lim}var Dg=On,ua=Dg.longestPath,Lg=sa,Pg=Ig,zg=Hg;function Hg(e){var t=e.graph().ranker;if(t instanceof Function)return t(e);switch(e.graph().ranker){case"network-simplex":Gs(e);break;case"tight-tree":Vg(e);break;case"longest-path":Fg(e);break;case"none":break;default:Gs(e)}}var Fg=ua;function Vg(e){ua(e),Lg(e)}function Gs(e){Pg(e)}var Bg=Gg;function Gg(e){let t=Wg(e);e.graph().dummyChains.forEach(n=>{let r=e.node(n),s=r.edgeObj,i=Yg(e,t,s.v,s.w),o=i.path,a=i.lca,l=0,c=o[l],u=!0;for(;n!==s.w;){if(r=e.node(n),u){for(;(c=o[l])!==a&&e.node(c).maxRanko||a>t[l].lim));for(c=l,l=r;(l=e.parent(l))!==c;)i.push(l);return{path:s.concat(i.reverse()),lca:c}}function Wg(e){let t={},n=0;function r(s){let i=n;e.children(s).forEach(r),t[s]={low:i,lim:n++}}return e.children().forEach(r),t}let Nn=fe;var Ug={run:Xg,cleanup:Zg};function Xg(e){let t=Nn.addDummyNode(e,"root",{},"_root"),n=qg(e),r=Object.values(n),s=Nn.applyWithChunking(Math.max,r)-1,i=2*s+1;e.graph().nestingRoot=t,e.edges().forEach(a=>e.edge(a).minlen*=i);let o=Kg(e)+1;e.children().forEach(a=>da(e,t,i,o,s,n,a)),e.graph().nodeRankFactor=i}function da(e,t,n,r,s,i,o){let a=e.children(o);if(!a.length){o!==t&&e.setEdge(t,o,{weight:0,minlen:n});return}let l=Nn.addBorderNode(e,"_bt"),c=Nn.addBorderNode(e,"_bb"),u=e.node(o);e.setParent(l,o),u.borderTop=l,e.setParent(c,o),u.borderBottom=c,a.forEach(d=>{da(e,t,n,r,s,i,d);let m=e.node(d),f=m.borderTop?m.borderTop:d,y=m.borderBottom?m.borderBottom:d,p=m.borderTop?r:2*r,v=f!==y?1:s-i[o]+1;e.setEdge(l,f,{weight:p,minlen:v,nestingEdge:!0}),e.setEdge(y,c,{weight:p,minlen:v,nestingEdge:!0})}),e.parent(o)||e.setEdge(t,l,{weight:0,minlen:s+i[o]})}function qg(e){var t={};function n(r,s){var i=e.children(r);i&&i.length&&i.forEach(o=>n(o,s+1)),t[r]=s}return e.children().forEach(r=>n(r,1)),t}function Kg(e){return e.edges().reduce((t,n)=>t+e.edge(n).weight,0)}function Zg(e){var t=e.graph();e.removeNode(t.nestingRoot),delete t.nestingRoot,e.edges().forEach(n=>{var r=e.edge(n);r.nestingEdge&&e.removeEdge(n)})}let Qg=fe;var Jg=e0;function e0(e){function t(n){let r=e.children(n),s=e.node(n);if(r.length&&r.forEach(t),Object.hasOwn(s,"minRank")){s.borderLeft=[],s.borderRight=[];for(let i=s.minRank,o=s.maxRank+1;iWs(e.node(t))),e.edges().forEach(t=>Ws(e.edge(t)))}function Ws(e){let t=e.width;e.width=e.height,e.height=t}function s0(e){e.nodes().forEach(t=>Qn(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(Qn),Object.hasOwn(n,"y")&&Qn(n)})}function Qn(e){e.y=-e.y}function i0(e){e.nodes().forEach(t=>Jn(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(Jn),Object.hasOwn(n,"x")&&Jn(n)})}function Jn(e){let t=e.x;e.x=e.y,e.y=t}let Us=fe;var o0=a0;function a0(e){let t={},n=e.nodes().filter(l=>!e.children(l).length),r=n.map(l=>e.node(l).rank),s=Us.applyWithChunking(Math.max,r),i=Us.range(s+1).map(()=>[]);function o(l){if(t[l])return;t[l]=!0;let c=e.node(l);i[c.rank].push(l),e.successors(l).forEach(o)}return n.sort((l,c)=>e.node(l).rank-e.node(c).rank).forEach(o),i}let l0=fe.zipObject;var c0=u0;function u0(e,t){let n=0;for(let r=1;ru)),s=t.flatMap(c=>e.outEdges(c).map(u=>({pos:r[u.w],weight:e.edge(u).weight})).sort((u,d)=>u.pos-d.pos)),i=1;for(;i{let u=c.pos+i;a[u]+=c.weight;let d=0;for(;u>0;)u%2&&(d+=a[u+1]),u=u-1>>1,a[u]+=c.weight;l+=c.weight*d}),l}var f0=h0;function h0(e,t=[]){return t.map(n=>{let r=e.inEdges(n);if(r.length){let s=r.reduce((i,o)=>{let a=e.edge(o),l=e.node(o.v);return{sum:i.sum+a.weight*l.order,weight:i.weight+a.weight}},{sum:0,weight:0});return{v:n,barycenter:s.sum/s.weight,weight:s.weight}}else return{v:n}})}let m0=fe;var p0=g0;function g0(e,t){let n={};e.forEach((s,i)=>{let o=n[s.v]={indegree:0,in:[],out:[],vs:[s.v],i};s.barycenter!==void 0&&(o.barycenter=s.barycenter,o.weight=s.weight)}),t.edges().forEach(s=>{let i=n[s.v],o=n[s.w];i!==void 0&&o!==void 0&&(o.indegree++,i.out.push(n[s.w]))});let r=Object.values(n).filter(s=>!s.indegree);return y0(r)}function y0(e){let t=[];function n(s){return i=>{i.merged||(i.barycenter===void 0||s.barycenter===void 0||i.barycenter>=s.barycenter)&&x0(s,i)}}function r(s){return i=>{i.in.push(s),--i.indegree===0&&e.push(i)}}for(;e.length;){let s=e.pop();t.push(s),s.in.reverse().forEach(n(s)),s.out.forEach(r(s))}return t.filter(s=>!s.merged).map(s=>m0.pick(s,["vs","i","barycenter","weight"]))}function x0(e,t){let n=0,r=0;e.weight&&(n+=e.barycenter*e.weight,r+=e.weight),t.weight&&(n+=t.barycenter*t.weight,r+=t.weight),e.vs=t.vs.concat(e.vs),e.barycenter=n/r,e.weight=r,e.i=Math.min(t.i,e.i),t.merged=!0}let w0=fe;var b0=v0;function v0(e,t){let n=w0.partition(e,u=>Object.hasOwn(u,"barycenter")),r=n.lhs,s=n.rhs.sort((u,d)=>d.i-u.i),i=[],o=0,a=0,l=0;r.sort(_0(!!t)),l=Xs(i,s,l),r.forEach(u=>{l+=u.vs.length,i.push(u.vs),o+=u.barycenter*u.weight,a+=u.weight,l=Xs(i,s,l)});let c={vs:i.flat(!0)};return a&&(c.barycenter=o/a,c.weight=a),c}function Xs(e,t,n){let r;for(;t.length&&(r=t[t.length-1]).i<=n;)t.pop(),e.push(r.vs),n++;return n}function _0(e){return(t,n)=>t.barycentern.barycenter?1:e?n.i-t.i:t.i-n.i}let E0=f0,N0=p0,S0=b0;var k0=ha;function ha(e,t,n,r){let s=e.children(t),i=e.node(t),o=i?i.borderLeft:void 0,a=i?i.borderRight:void 0,l={};o&&(s=s.filter(m=>m!==o&&m!==a));let c=E0(e,s);c.forEach(m=>{if(e.children(m.v).length){let f=ha(e,m.v,n,r);l[m.v]=f,Object.hasOwn(f,"barycenter")&&C0(m,f)}});let u=N0(c,n);j0(u,l);let d=S0(u,r);if(o&&(d.vs=[o,d.vs,a].flat(!0),e.predecessors(o).length)){let m=e.node(e.predecessors(o)[0]),f=e.node(e.predecessors(a)[0]);Object.hasOwn(d,"barycenter")||(d.barycenter=0,d.weight=0),d.barycenter=(d.barycenter*d.weight+m.order+f.order)/(d.weight+2),d.weight+=2}return d}function j0(e,t){e.forEach(n=>{n.vs=n.vs.flatMap(r=>t[r]?t[r].vs:r)})}function C0(e,t){e.barycenter!==void 0?(e.barycenter=(e.barycenter*e.weight+t.barycenter*t.weight)/(e.weight+t.weight),e.weight+=t.weight):(e.barycenter=t.barycenter,e.weight=t.weight)}let $0=Ie.Graph,M0=fe;var A0=I0;function I0(e,t,n,r){r||(r=e.nodes());let s=R0(e),i=new $0({compound:!0}).setGraph({root:s}).setDefaultNodeLabel(o=>e.node(o));return r.forEach(o=>{let a=e.node(o),l=e.parent(o);(a.rank===t||a.minRank<=t&&t<=a.maxRank)&&(i.setNode(o),i.setParent(o,l||s),e[n](o).forEach(c=>{let u=c.v===o?c.w:c.v,d=i.edge(u,o),m=d!==void 0?d.weight:0;i.setEdge(u,o,{weight:e.edge(c).weight+m})}),Object.hasOwn(a,"minRank")&&i.setNode(o,{borderLeft:a.borderLeft[t],borderRight:a.borderRight[t]}))}),i}function R0(e){for(var t;e.hasNode(t=M0.uniqueId("_root")););return t}var T0=O0;function O0(e,t,n){let r={},s;n.forEach(i=>{let o=e.parent(i),a,l;for(;o;){if(a=e.parent(o),a?(l=r[a],r[a]=o):(l=s,s=o),l&&l!==o){t.setEdge(l,o);return}o=a}})}let D0=o0,L0=c0,P0=k0,z0=A0,H0=T0,F0=Ie.Graph,ln=fe;var V0=ma;function ma(e,t){if(t&&typeof t.customOrder=="function"){t.customOrder(e,ma);return}let n=ln.maxRank(e),r=qs(e,ln.range(1,n+1),"inEdges"),s=qs(e,ln.range(n-1,-1,-1),"outEdges"),i=D0(e);if(Ks(e,i),t&&t.disableOptimalOrderHeuristic)return;let o=Number.POSITIVE_INFINITY,a;for(let l=0,c=0;c<4;++l,++c){B0(l%2?r:s,l%4>=2),i=ln.buildLayerMatrix(e);let u=L0(e,i);u{r.has(i)||r.set(i,[]),r.get(i).push(o)};for(const i of e.nodes()){const o=e.node(i);if(typeof o.rank=="number"&&s(o.rank,i),typeof o.minRank=="number"&&typeof o.maxRank=="number")for(let a=o.minRank;a<=o.maxRank;a++)a!==o.rank&&s(a,i)}return t.map(function(i){return z0(e,i,n,r.get(i)||[])})}function B0(e,t){let n=new F0;e.forEach(function(r){let s=r.graph().root,i=P0(r,s,n,t);i.vs.forEach((o,a)=>r.node(o).order=a),H0(r,n,i.vs)})}function Ks(e,t){Object.values(t).forEach(n=>n.forEach((r,s)=>e.node(r).order=s))}let G0=Ie.Graph,ze=fe;var Y0={positionX:ny};function W0(e,t){let n={};function r(s,i){let o=0,a=0,l=s.length,c=i[i.length-1];return i.forEach((u,d)=>{let m=X0(e,u),f=m?e.node(m).order:l;(m||u===c)&&(i.slice(a,d+1).forEach(y=>{e.predecessors(y).forEach(p=>{let v=e.node(p),x=v.order;(x{u=i[d],e.node(u).dummy&&e.predecessors(u).forEach(m=>{let f=e.node(m);f.dummy&&(f.orderc)&&pa(n,m,u)})})}function s(i,o){let a=-1,l,c=0;return o.forEach((u,d)=>{if(e.node(u).dummy==="border"){let m=e.predecessors(u);m.length&&(l=e.node(m[0]).order,r(o,c,d,a,l),c=d,a=l)}r(o,c,o.length,l,i.length)}),o}return t.length&&t.reduce(s),n}function X0(e,t){if(e.node(t).dummy)return e.predecessors(t).find(n=>e.node(n).dummy)}function pa(e,t,n){if(t>n){let s=t;t=n,n=s}let r=e[t];r||(e[t]=r={}),r[n]=!0}function q0(e,t,n){if(t>n){let r=t;t=n,n=r}return!!e[t]&&Object.hasOwn(e[t],n)}function K0(e,t,n,r){let s={},i={},o={};return t.forEach(a=>{a.forEach((l,c)=>{s[l]=l,i[l]=l,o[l]=c})}),t.forEach(a=>{let l=-1;a.forEach(c=>{let u=r(c);if(u.length){u=u.sort((m,f)=>o[m]-o[f]);let d=(u.length-1)/2;for(let m=Math.floor(d),f=Math.ceil(d);m<=f;++m){let y=u[m];i[c]===c&&lMath.max(m,i[f.v]+o.edge(f)),0)}function u(d){let m=o.outEdges(d).reduce((y,p)=>Math.min(y,i[p.w]-o.edge(p)),Number.POSITIVE_INFINITY),f=e.node(d);m!==Number.POSITIVE_INFINITY&&f.borderType!==a&&(i[d]=Math.max(i[d],m))}return l(c,o.predecessors.bind(o)),l(u,o.successors.bind(o)),Object.keys(r).forEach(d=>i[d]=i[n[d]]),i}function Q0(e,t,n,r){let s=new G0,i=e.graph(),o=ry(i.nodesep,i.edgesep,r);return t.forEach(a=>{let l;a.forEach(c=>{let u=n[c];if(s.setNode(u),l){var d=n[l],m=s.edge(d,u);s.setEdge(d,u,Math.max(o(e,c,l),m||0))}l=c})}),s}function J0(e,t){return Object.values(t).reduce((n,r)=>{let s=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;Object.entries(r).forEach(([a,l])=>{let c=sy(e,a)/2;s=Math.max(l+c,s),i=Math.min(l-c,i)});const o=s-i;return o{["l","r"].forEach(o=>{let a=i+o,l=e[a];if(l===t)return;let c=Object.values(l),u=r-ze.applyWithChunking(Math.min,c);o!=="l"&&(u=s-ze.applyWithChunking(Math.max,c)),u&&(e[a]=ze.mapValues(l,d=>d+u))})})}function ty(e,t){return ze.mapValues(e.ul,(n,r)=>{if(t)return e[t.toLowerCase()][r];{let s=Object.values(e).map(i=>i[r]).sort((i,o)=>i-o);return(s[1]+s[2])/2}})}function ny(e){let t=ze.buildLayerMatrix(e),n=Object.assign(W0(e,t),U0(e,t)),r={},s;["u","d"].forEach(o=>{s=o==="u"?t:Object.values(t).reverse(),["l","r"].forEach(a=>{a==="r"&&(s=s.map(d=>Object.values(d).reverse()));let l=(o==="u"?e.predecessors:e.successors).bind(e),c=K0(e,s,n,l),u=Z0(e,s,c.root,c.align,a==="r");a==="r"&&(u=ze.mapValues(u,d=>-d)),r[o+a]=u})});let i=J0(e,r);return ey(r,i),ty(r,e.graph().align)}function ry(e,t,n){return(r,s,i)=>{let o=r.node(s),a=r.node(i),l=0,c;if(l+=o.width/2,Object.hasOwn(o,"labelpos"))switch(o.labelpos.toLowerCase()){case"l":c=-o.width/2;break;case"r":c=o.width/2;break}if(c&&(l+=n?c:-c),c=0,l+=(o.dummy?t:e)/2,l+=(a.dummy?t:e)/2,l+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":c=a.width/2;break;case"r":c=-a.width/2;break}return c&&(l+=n?c:-c),c=0,l}}function sy(e,t){return e.node(t).width}let ga=fe,iy=Y0.positionX;var oy=ay;function ay(e){e=ga.asNonCompoundGraph(e),ly(e),Object.entries(iy(e)).forEach(([t,n])=>e.node(t).x=n)}function ly(e){let t=ga.buildLayerMatrix(e),n=e.graph().ranksep,r=0;t.forEach(s=>{const i=s.reduce((o,a)=>{const l=e.node(a).height;return o>l?o:l},0);s.forEach(o=>e.node(o).y=r+i/2),r+=i+n})}let Zs=ug,Qs=pg,cy=zg,uy=fe.normalizeRanks,dy=Bg,fy=fe.removeEmptyRanks,Js=Ug,hy=Jg,ei=t0,my=V0,py=oy,je=fe,gy=Ie.Graph;var yy=xy;function xy(e,t){let n=t&&t.debugTiming?je.time:je.notime;n("layout",()=>{let r=n(" buildLayoutGraph",()=>Cy(e));n(" runLayout",()=>wy(r,n,t)),n(" updateInputGraph",()=>by(e,r))})}function wy(e,t,n){t(" makeSpaceForEdgeLabels",()=>$y(e)),t(" removeSelfEdges",()=>Py(e)),t(" acyclic",()=>Zs.run(e)),t(" nestingGraph.run",()=>Js.run(e)),t(" rank",()=>cy(je.asNonCompoundGraph(e))),t(" injectEdgeLabelProxies",()=>My(e)),t(" removeEmptyRanks",()=>fy(e)),t(" nestingGraph.cleanup",()=>Js.cleanup(e)),t(" normalizeRanks",()=>uy(e)),t(" assignRankMinMax",()=>Ay(e)),t(" removeEdgeLabelProxies",()=>Iy(e)),t(" normalize.run",()=>Qs.run(e)),t(" parentDummyChains",()=>dy(e)),t(" addBorderSegments",()=>hy(e)),t(" order",()=>my(e,n)),t(" insertSelfEdges",()=>zy(e)),t(" adjustCoordinateSystem",()=>ei.adjust(e)),t(" position",()=>py(e)),t(" positionSelfEdges",()=>Hy(e)),t(" removeBorderNodes",()=>Ly(e)),t(" normalize.undo",()=>Qs.undo(e)),t(" fixupEdgeLabelCoords",()=>Oy(e)),t(" undoCoordinateSystem",()=>ei.undo(e)),t(" translateGraph",()=>Ry(e)),t(" assignNodeIntersects",()=>Ty(e)),t(" reversePoints",()=>Dy(e)),t(" acyclic.undo",()=>Zs.undo(e))}function by(e,t){e.nodes().forEach(n=>{let r=e.node(n),s=t.node(n);r&&(r.x=s.x,r.y=s.y,r.rank=s.rank,t.children(n).length&&(r.width=s.width,r.height=s.height))}),e.edges().forEach(n=>{let r=e.edge(n),s=t.edge(n);r.points=s.points,Object.hasOwn(s,"x")&&(r.x=s.x,r.y=s.y)}),e.graph().width=t.graph().width,e.graph().height=t.graph().height}let vy=["nodesep","edgesep","ranksep","marginx","marginy"],_y={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},Ey=["acyclicer","ranker","rankdir","align"],Ny=["width","height","rank"],ti={width:0,height:0},Sy=["minlen","weight","width","height","labeloffset"],ky={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},jy=["labelpos"];function Cy(e){let t=new gy({multigraph:!0,compound:!0}),n=tr(e.graph());return t.setGraph(Object.assign({},_y,er(n,vy),je.pick(n,Ey))),e.nodes().forEach(r=>{let s=tr(e.node(r));const i=er(s,Ny);Object.keys(ti).forEach(o=>{i[o]===void 0&&(i[o]=ti[o])}),t.setNode(r,i),t.setParent(r,e.parent(r))}),e.edges().forEach(r=>{let s=tr(e.edge(r));t.setEdge(r,Object.assign({},ky,er(s,Sy),je.pick(s,jy)))}),t}function $y(e){let t=e.graph();t.ranksep/=2,e.edges().forEach(n=>{let r=e.edge(n);r.minlen*=2,r.labelpos.toLowerCase()!=="c"&&(t.rankdir==="TB"||t.rankdir==="BT"?r.width+=r.labeloffset:r.height+=r.labeloffset)})}function My(e){e.edges().forEach(t=>{let n=e.edge(t);if(n.width&&n.height){let r=e.node(t.v),i={rank:(e.node(t.w).rank-r.rank)/2+r.rank,e:t};je.addDummyNode(e,"edge-proxy",i,"_ep")}})}function Ay(e){let t=0;e.nodes().forEach(n=>{let r=e.node(n);r.borderTop&&(r.minRank=e.node(r.borderTop).rank,r.maxRank=e.node(r.borderBottom).rank,t=Math.max(t,r.maxRank))}),e.graph().maxRank=t}function Iy(e){e.nodes().forEach(t=>{let n=e.node(t);n.dummy==="edge-proxy"&&(e.edge(n.e).labelRank=n.rank,e.removeNode(t))})}function Ry(e){let t=Number.POSITIVE_INFINITY,n=0,r=Number.POSITIVE_INFINITY,s=0,i=e.graph(),o=i.marginx||0,a=i.marginy||0;function l(c){let u=c.x,d=c.y,m=c.width,f=c.height;t=Math.min(t,u-m/2),n=Math.max(n,u+m/2),r=Math.min(r,d-f/2),s=Math.max(s,d+f/2)}e.nodes().forEach(c=>l(e.node(c))),e.edges().forEach(c=>{let u=e.edge(c);Object.hasOwn(u,"x")&&l(u)}),t-=o,r-=a,e.nodes().forEach(c=>{let u=e.node(c);u.x-=t,u.y-=r}),e.edges().forEach(c=>{let u=e.edge(c);u.points.forEach(d=>{d.x-=t,d.y-=r}),Object.hasOwn(u,"x")&&(u.x-=t),Object.hasOwn(u,"y")&&(u.y-=r)}),i.width=n-t+o,i.height=s-r+a}function Ty(e){e.edges().forEach(t=>{let n=e.edge(t),r=e.node(t.v),s=e.node(t.w),i,o;n.points?(i=n.points[0],o=n.points[n.points.length-1]):(n.points=[],i=s,o=r),n.points.unshift(je.intersectRect(r,i)),n.points.push(je.intersectRect(s,o))})}function Oy(e){e.edges().forEach(t=>{let n=e.edge(t);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function Dy(e){e.edges().forEach(t=>{let n=e.edge(t);n.reversed&&n.points.reverse()})}function Ly(e){e.nodes().forEach(t=>{if(e.children(t).length){let n=e.node(t),r=e.node(n.borderTop),s=e.node(n.borderBottom),i=e.node(n.borderLeft[n.borderLeft.length-1]),o=e.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(o.x-i.x),n.height=Math.abs(s.y-r.y),n.x=i.x+n.width/2,n.y=r.y+n.height/2}}),e.nodes().forEach(t=>{e.node(t).dummy==="border"&&e.removeNode(t)})}function Py(e){e.edges().forEach(t=>{if(t.v===t.w){var n=e.node(t.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e:t,label:e.edge(t)}),e.removeEdge(t)}})}function zy(e){var t=je.buildLayerMatrix(e);t.forEach(n=>{var r=0;n.forEach((s,i)=>{var o=e.node(s);o.order=i+r,(o.selfEdges||[]).forEach(a=>{je.addDummyNode(e,"selfedge",{width:a.label.width,height:a.label.height,rank:o.rank,order:i+ ++r,e:a.e,label:a.label},"_se")}),delete o.selfEdges})})}function Hy(e){e.nodes().forEach(t=>{var n=e.node(t);if(n.dummy==="selfedge"){var r=e.node(n.e.v),s=r.x+r.width/2,i=r.y,o=n.x-s,a=r.height/2;e.setEdge(n.e,n.label),e.removeNode(t),n.label.points=[{x:s+2*o/3,y:i-a},{x:s+5*o/6,y:i-a},{x:s+o,y:i},{x:s+5*o/6,y:i+a},{x:s+2*o/3,y:i+a}],n.label.x=n.x,n.label.y=n.y}})}function er(e,t){return je.mapValues(je.pick(e,t),Number)}function tr(e){var t={};return e&&Object.entries(e).forEach(([n,r])=>{typeof n=="string"&&(n=n.toLowerCase()),t[n]=r}),t}let Fy=fe,Vy=Ie.Graph;var By={debugOrdering:Gy};function Gy(e){let t=Fy.buildLayerMatrix(e),n=new Vy({compound:!0,multigraph:!0}).setGraph({});return e.nodes().forEach(r=>{n.setNode(r,{label:r}),n.setParent(r,"layer"+e.node(r).rank)}),e.edges().forEach(r=>n.setEdge(r.v,r.w,{},r.name)),t.forEach((r,s)=>{let i="layer"+s;n.setNode(i,{rank:"same"}),r.reduce((o,a)=>(n.setEdge(o,a,{style:"invis"}),a))}),n}var Yy="1.1.8",Wy={graphlib:Ie,layout:yy,debug:By,util:{time:fe.time,notime:fe.notime},version:Yy};const ni=fi(Wy),_t=264,Uy=44,Xy=26,qy=14;function Sn(e){const t=Math.max(e.attributes.length,1);return Uy+t*Xy+qy}const Ky=40;function ri(e,t,n,r="auto",s,i="LR"){const o=Zy(n,s);return r==="grid"?ai(e,n,o):r==="clustered"?oi(e,t,n,o,i):r==="hierarchical"||e.length<=Ky?si(e,t,n,i):new Set(e.map(l=>o(l.id)).filter(Boolean)).size>1?oi(e,t,n,o,i):ai(e,n,o)}function Zy(e,t){return n=>{var r,s,i;if(t){const o=t.get(n);if(o)return o.name}return((i=(s=(r=e.get(n))==null?void 0:r.conceptual)==null?void 0:s.subject_area)==null?void 0:i.name)??""}}function ya(e,t,n,r=!1,s="LR"){const i=new ni.graphlib.Graph;i.setGraph({rankdir:s,nodesep:r?45:70,ranksep:r?110:150,edgesep:r?20:30,marginx:24,marginy:24,ranker:"network-simplex"}),i.setDefaultEdgeLabel(()=>({}));const o=new Set(e.map(u=>u.id));for(const u of e){const d=n.get(u.id);i.setNode(u.id,{width:_t,height:d?Sn(d):120})}for(const u of t)o.has(u.source)&&o.has(u.target)&&i.setEdge(u.source,u.target,{minlen:1,weight:2});ni.layout(i);let a=0,l=0;return{nodes:e.map(u=>{const d=i.node(u.id),m=n.get(u.id),f=m?Sn(m):120,y=d.x-_t/2,p=d.y-f/2;return a=Math.max(a,y+_t),l=Math.max(l,p+f),{...u,position:{x:y,y:p}}}),width:a,height:l}}function si(e,t,n,r="LR"){return ya(e,t,n,!1,r).nodes}function Qy(e,t){const n=a=>{const l=t.get(a);return l?Sn(l):120},r=Math.max(1,Math.round(Math.sqrt(e.length))),s=[];let i=0,o=0;for(let a=0;a{const d=u*(_t+xa);s.push({...c,position:{x:d,y:i}}),o=Math.max(o,d+_t)}),i+=Math.max(...l.map(c=>n(c.id)))+wa}return{nodes:s,width:o,height:i}}const ii=120;function oi(e,t,n,r,s="LR"){const i=new Map;for(const m of e){const f=r(m.id);(i.get(f)??i.set(f,[]).get(f)).push(m)}const a=[...i.keys()].sort((m,f)=>m===""?1:f===""?-1:m.localeCompare(f)).map(m=>{const f=i.get(m),y=new Set(f.map(_=>_.id));let p=0;for(const _ of t)y.has(_.source)&&y.has(_.target)&&p++;const x=f.length<=8&&p>=f.length-1?ya(f,t,n,!0,s):Qy(f,n);return{name:m,...x}}),l=Math.max(1,Math.ceil(Math.sqrt(a.length*1.6))),c=[];let u=0;for(let m=0;m[m.id,m.position]));return e.map(m=>({...m,position:d.get(m.id)??m.position}))}const xa=40,wa=40;function ai(e,t,n){const r=c=>{const u=t.get(c);return u?Sn(u):120},s=[...e].sort((c,u)=>{var p,v;const d=n(c.id),m=n(u.id);if(d!==m)return d.localeCompare(m);const f=((p=t.get(c.id))==null?void 0:p.name)??c.id,y=((v=t.get(u.id))==null?void 0:v.name)??u.id;return f.localeCompare(y)}),i=Math.max(1,Math.round(Math.sqrt(s.length))),o=Array.from({length:i},(c,u)=>u*(_t+xa)),a=new Map;let l=0;for(let c=0;ca.set(m.id,{x:o[f],y:l}));const d=Math.max(...u.map(m=>r(m.id)));l+=d+wa}return e.map(c=>({...c,position:a.get(c.id)??c.position}))}function Zt({title:e,onClose:t,children:n,wide:r}){return h.jsx("div",{className:"modal-backdrop",onClick:t,children:h.jsxs("div",{className:"modal"+(r?" wide":""),onClick:s=>s.stopPropagation(),children:[h.jsxs("div",{className:"modal-header",children:[h.jsx("h2",{children:e}),h.jsx("button",{className:"icon-btn",onClick:t,children:"✕"})]}),n]})})}const Jy=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch","skos:relatedMatch"],ba=["industry","core","domain","specialised"];function ex({entity:e,exec:t,onClose:n}){var b,j;const[r,s]=w.useState(((b=e.conceptual)==null?void 0:b.name)??e.name),[i,o]=w.useState([]),[a,l]=w.useState(null),[c,u]=w.useState(null),[d,m]=w.useState("skos:exactMatch"),[f,y]=w.useState(((j=e.conceptual)==null?void 0:j.ontology_layer)??"core"),[p,v]=w.useState([]),[x,_]=w.useState("");w.useEffect(()=>{Go().then(C=>v(C.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const C=setTimeout(()=>{if(!r.trim())return o([]);Bo(r,x||void 0).then(E=>{o(E.results),l(E.loaded_terms)}).catch(()=>o([]))},200);return()=>clearTimeout(C)},[r,x]);const M=()=>{c&&t("set_alignment",{id:e.id,aligns_to:c.prefixed,alignment:d,layer:f,resolved_via:c.source}).then(n)};return h.jsxs(Zt,{title:`Align '${e.name}' to ontology`,onClose:n,wide:!0,children:[p.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(x===""?" active":""),onClick:()=>_(""),children:"all sources"}),p.map(C=>h.jsx("button",{className:"src-chip"+(x===C.id?" active":""),title:C.count!=null?`${C.count} terms`:"live resolver",onClick:()=>_(C.id),children:C.name},C.id))]}),h.jsx("input",{autoFocus:!0,className:"edit-input modal-search",placeholder:x?`Search within ${x}…`:"Search vocabulary terms…",value:r,onChange:C=>s(C.target.value)}),a===0&&p.length===0&&h.jsx("p",{className:"empty-hint",children:"No vocabulary loaded. Declare one in mdl-project.yaml's ontology_stack (or run `mdl ontology vendor fibo`)."}),h.jsxs("div",{className:"term-results",children:[i.map(C=>h.jsxs("button",{className:"term-card"+((c==null?void 0:c.iri)===C.iri?" picked":""),onClick:()=>u(C),children:[h.jsxs("div",{className:"term-head",children:[h.jsx("span",{className:"term-label",children:C.label}),h.jsxs("span",{className:"term-source",children:[C.source_kind==="glossary-term"&&h.jsx("span",{className:"kind-badge",children:"glossary"}),C.source]})]}),h.jsx("code",{className:"term-iri",children:C.prefixed}),C.definition&&h.jsx("p",{className:"term-def",children:C.definition})]},C.iri)),r&&i.length===0&&a!==0&&h.jsxs("p",{className:"empty-hint",children:["no matches for “",r,"”"]})]}),h.jsxs("div",{className:"modal-footer",children:[h.jsxs("label",{children:["predicate",h.jsx("select",{value:d,onChange:C=>m(C.target.value),children:Jy.map(C=>h.jsx("option",{children:C},C))})]}),h.jsxs("label",{children:["layer",h.jsx("select",{value:f,onChange:C=>y(C.target.value),children:ba.map(C=>h.jsx("option",{children:C},C))})]}),h.jsxs("button",{className:"primary-btn",disabled:!c,onClick:M,children:["Align",c?` to ${c.label}`:""]})]})]})}function tx({entity:e,doc:t,exec:n,onClose:r}){var b,j,C;const[s,i]=w.useState(((b=e.term_map)==null?void 0:b.subject_template)??""),[o,a]=w.useState(((j=e.term_map)==null?void 0:j.class_iri)??""),l=new Set(e.attributes.map(E=>E.name)),u=Array.from(s.matchAll(/\{([^}]+)\}/g)).map(E=>E[1]).filter(E=>!l.has(E)),d=E=>!E||E.startsWith("http://")||E.startsWith("https://")||/^[^\s:]+:[^\s:]+/.test(E),m=u.length===0&&d(o),f=()=>{n("set_term_map",{id:e.id,subject_template:s.trim()||null,class_iri:o.trim()||null}).then(r)},y=(t.project.name||"").replace(/[^0-9a-zA-Z]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase()||"modelith-model",p=t.project.kg_base_iri||`urn:${y}:`,v=(C=(e.key_groups??[]).find(E=>E.type==="pk"))==null?void 0:C.members.map(E=>{var L;return(L=e.attributes.find(S=>S.id===E))==null?void 0:L.name}).filter(Boolean),x=v&&v.length?`${p}${e.id}/${v.map(E=>`{${E}}`).join("_")}`:"(blank-node: entity has no primary key)",_=o.trim()||`${p}${e.id}`,M=s.trim()||x;return h.jsxs(Zt,{title:`Knowledge Graph mapping — ${e.name}`,onClose:r,wide:!0,children:[h.jsx("p",{className:"empty-hint",style:{marginBottom:14},children:"Override the R2RML term-map for this entity. Leave a field blank to use the default (project base IRI + primary key)."}),h.jsxs("div",{className:"form-row",children:[h.jsx("label",{htmlFor:"tm-class",children:"Class IRI"}),h.jsx("input",{id:"tm-class",className:"edit-input",value:o,placeholder:"https://acme.com/Customer or fibo-fnd:Party",onChange:E=>a(E.target.value)}),d(o)?h.jsxs("code",{className:"form-preview",children:["rr:class ",_]}):h.jsx("span",{className:"form-error",children:"not a valid IRI or prefix:local CURIE"})]}),h.jsxs("div",{className:"form-row",children:[h.jsx("label",{htmlFor:"tm-subject",children:"Subject template"}),h.jsx("input",{id:"tm-subject",className:"edit-input",value:s,placeholder:"https://acme.com/id/customer/{customer_id}",onChange:E=>i(E.target.value)}),u.length>0?h.jsxs("span",{className:"form-error",children:["unknown column",u.length>1?"s":"",": ",u.join(", ")]}):h.jsxs("code",{className:"form-preview",children:["rr:template ",M]}),h.jsxs("span",{className:"form-hint",children:["Wrap a column in ","{ }"," to fill it from each row, e.g."," ","{"+((v==null?void 0:v[0])??"id")+"}",". Columns:"," ",e.attributes.map(E=>E.name).join(", ")]})]}),h.jsxs("div",{className:"modal-footer",children:[h.jsx("button",{className:"mini-btn",onClick:r,children:"Cancel"}),h.jsx("button",{className:"primary-btn",disabled:!m,onClick:f,children:"Save mapping"})]})]})}function nx({doc:e,exec:t,onClose:n,onCreated:r}){const[s,i]=w.useState(""),[o,a]=w.useState(""),[l,c]=w.useState(""),[u,d]=w.useState(""),m=()=>{s.trim()&&t("create_entity",{name:s,definition:o||null,subject_area:l||null,layer:u||null}).then(f=>{const y=f==null?void 0:f.created_id;n(),y&&r(y)})};return h.jsxs(Zt,{title:"New entity",onClose:n,children:[h.jsxs("label",{className:"form-row",children:["name",h.jsx("input",{autoFocus:!0,className:"edit-input",placeholder:"e.g. custody_account",value:s,onChange:f=>i(f.target.value),onKeyDown:f=>f.key==="Enter"&&m()})]}),h.jsxs("label",{className:"form-row",children:["definition",h.jsx("textarea",{className:"edit-input",rows:2,placeholder:"business definition…",value:o,onChange:f=>a(f.target.value)})]}),h.jsxs("label",{className:"form-row",children:["subject area",h.jsxs("select",{value:l,onChange:f=>c(f.target.value),children:[h.jsx("option",{value:"",children:"none"}),e.subject_areas.map(f=>h.jsx("option",{value:f.id,children:f.name},f.id))]})]}),h.jsxs("label",{className:"form-row",children:["ontology layer",h.jsxs("select",{value:u,onChange:f=>d(f.target.value),children:[h.jsx("option",{value:"",children:"none"}),ba.map(f=>h.jsx("option",{children:f},f))]})]}),h.jsx("div",{className:"modal-footer",children:h.jsx("button",{className:"primary-btn",disabled:!s.trim(),onClick:m,children:"Create"})})]})}const va=["many_to_one","one_to_many","one_to_one","many_to_many"];function rx({doc:e,fromId:t,toId:n,initialFromAttr:r,initialToAttr:s,exec:i,onClose:o}){const a=e.entities.find(b=>b.id===t),l=e.entities.find(b=>b.id===n),c=w.useMemo(()=>l==null?void 0:l.attributes.find(b=>b.role==="business_key"),[l]),[u,d]=w.useState(r??""),[m,f]=w.useState(s??(c==null?void 0:c.id)??""),[y,p]=w.useState("many_to_one"),[v,x]=w.useState("mandatory");if(!a||!l)return null;const _=u==="__new__",M=()=>i("create_relationship",{from_entity:a.id,to_entity:l.id,from_attribute:_?null:u||null,to_attribute:m||null,create_from_fk:_,cardinality:y,optionality:v}).then(o);return h.jsxs(Zt,{title:`${a.name} → ${l.name}`,onClose:o,children:[h.jsxs("label",{className:"form-row",children:["cardinality (",a.name," : ",l.name,")",h.jsx("select",{value:y,onChange:b=>p(b.target.value),children:va.map(b=>h.jsx("option",{children:b},b))})]}),h.jsxs("label",{className:"form-row",children:["foreign key on ",a.name,h.jsxs("select",{value:u,onChange:b=>d(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),h.jsxs("option",{value:"__new__",children:["➕ create column",c?` "${c.name}"`:""]}),a.attributes.map(b=>h.jsx("option",{value:b.id,children:b.name},b.id))]})]}),h.jsxs("label",{className:"form-row",children:["references on ",l.name,h.jsxs("select",{value:m,onChange:b=>f(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),l.attributes.map(b=>h.jsxs("option",{value:b.id,children:[b.name,b.role==="business_key"?" 🔑":""]},b.id))]})]}),h.jsxs("label",{className:"form-row",children:["optionality",h.jsxs("select",{value:v,onChange:b=>x(b.target.value),children:[h.jsx("option",{children:"mandatory"}),h.jsx("option",{children:"optional"})]})]}),h.jsx("div",{className:"modal-footer",children:h.jsx("button",{className:"primary-btn",onClick:M,children:"Create relationship"})})]})}function sx({rel:e,doc:t,exec:n,onClose:r}){const s=x=>{var _;return((_=t.entities.find(M=>M.id===x))==null?void 0:_.name)??"?"},[i,o]=w.useState(e.name),[a,l]=w.useState(e.cardinality),[c,u]=w.useState(e.optionality),[d,m]=w.useState(e.identifying),[f,y]=w.useState(!1),p=async()=>{i.trim()&&i.trim()!==e.name&&await n("rename_relationship",{id:e.id,name:i.trim()}),(a!==e.cardinality||c!==e.optionality||d!==e.identifying)&&await n("update_relationship",{id:e.id,cardinality:a,optionality:c,identifying:d}),r()},v=()=>n("delete_relationship",{id:e.id}).then(r);return h.jsxs(Zt,{title:`${s(e.from.entity)} → ${s(e.to.entity)}`,onClose:r,children:[h.jsxs("label",{className:"form-row",children:["relationship name",h.jsx("input",{value:i,onChange:x=>o(x.target.value),autoFocus:!0})]}),h.jsxs("label",{className:"form-row",children:["cardinality",h.jsx("select",{value:a,onChange:x=>l(x.target.value),children:va.map(x=>h.jsx("option",{children:x},x))})]}),h.jsxs("label",{className:"form-row",children:["optionality",h.jsxs("select",{value:c,onChange:x=>u(x.target.value),children:[h.jsx("option",{value:"mandatory",children:"mandatory"}),h.jsx("option",{value:"optional",children:"optional"})]})]}),h.jsxs("label",{className:"form-row",children:["identifying",h.jsxs("select",{value:d?"yes":"no",onChange:x=>m(x.target.value==="yes"),children:[h.jsx("option",{value:"no",children:"no — parent key is a plain reference"}),h.jsx("option",{value:"yes",children:"yes — parent key is part of the child's identity"})]})]}),h.jsxs("div",{className:"modal-footer",style:{justifyContent:"space-between"},children:[f?h.jsx("button",{className:"danger-btn",onClick:v,children:"Confirm delete"}):h.jsx("button",{className:"mini-btn danger",onClick:()=>y(!0),children:"Delete relationship"}),h.jsx("button",{className:"primary-btn",onClick:p,children:"Save"})]})]})}const ix=w.memo(function({id:t,sourceX:n,sourceY:r,targetX:s,targetY:i,sourcePosition:o,targetPosition:a,data:l,selected:c}){const[u]=vn({sourceX:n,sourceY:r,sourcePosition:o,targetX:s,targetY:i,targetPosition:a,borderRadius:10}),d=l==null?void 0:l.relationship,m=(l==null?void 0:l.dimmed)??!1,f=(d==null?void 0:d.cardinality)??"many_to_one",y=(d==null?void 0:d.optionality)==="optional",p=(d==null?void 0:d.identifying)??!1,v=f==="many_to_one"||f==="many_to_many",x=f==="one_to_many"||f==="many_to_many",_="rel-edge"+(c?" selected":"")+(m?" dimmed":""),M=((l==null?void 0:l.memberCount)??0)>1,b=l==null?void 0:l.onHoverEdge;return h.jsxs("g",{className:_,children:[h.jsx("path",{id:t,className:"rel-path",d:u,fill:"none",strokeDasharray:p?void 0:"6 4"}),h.jsx("path",{d:u,fill:"none",strokeWidth:14,stroke:"transparent",onMouseEnter:b?()=>b(d.id):void 0,onMouseLeave:b?()=>b(null):void 0}),h.jsx(li,{x:n,y:r,side:o,many:v,optional:y}),h.jsx(li,{x:s,y:i,side:a,many:x,optional:!1}),(c||M)&&d&&h.jsxs("text",{className:"rel-label",x:(n+s)/2,y:(r+i)/2-8,children:[c?d.verb_phrase||d.name:"",M?`${c?" ":""}(${l.memberCount})`:""]})]})});function li({x:e,y:t,side:n,many:r,optional:s}){const i=n===Z.Left?-1:1,o=7,a=12,l=[];return r?l.push(h.jsx("path",{className:"rel-glyph",d:`M ${e+i*a} ${t} L ${e} ${t-o} M ${e+i*a} ${t} L ${e} ${t} M ${e+i*a} ${t} L ${e} ${t+o}`,fill:"none"},"foot")):l.push(h.jsx("line",{className:"rel-glyph",x1:e+i*(a-3),y1:t-o,x2:e+i*(a-3),y2:t+o},"one")),s?l.push(h.jsx("circle",{className:"rel-glyph ring",cx:e+i*(a+6),cy:t,r:4},"opt")):l.push(h.jsx("line",{className:"rel-glyph",x1:e+i*(a+5),y1:t-o,x2:e+i*(a+5),y2:t+o},"mand")),h.jsx(h.Fragment,{children:l})}const ox={entity:Rm},ax={relationship:ix},ci=["#e0a63a","#e8825a","#d98ba8","#c4a3e0","#7fb0d8","#7fc9a0","#e6c14a"],ui="#8a7d6a";function lx(e,t){const n=(i,o)=>{var a;return!!o&&!!((a=t.get(i))!=null&&a.attributes.some(l=>l.id===o))},r=e.from.attributes[0],s=e.to.attributes[0];return{sourceHandle:n(e.from.entity,r)?r:Dr,targetHandle:n(e.to.entity,s)?s:Lr}}function Dx({doc:e,caps:t,exec:n,selectedId:r,onSelect:s,query:i="",showTypes:o=!0,collapseDetail:a=!1,expandedEntities:l,onToggleExpand:c,activeAreas:u,onError:d,handleRef:m}){const{fitView:f,fitBounds:y,getNodes:p}=Rn(),[v,x]=w.useState([]),[_,M]=w.useState([]),b=w.useRef(!1),[j,C]=w.useState(null),[E,L]=w.useState(null),[S,O]=w.useState(!1),[P,V]=w.useState(null),[F,g]=w.useState(null),[$,N]=w.useState(null),[I,H]=w.useState(null),k=!t.canEdit,R=w.useMemo(()=>{const B=new Map;return e.subject_areas.forEach((q,W)=>B.set(q.id,ci[W%ci.length])),B},[e]),D=w.useMemo(()=>{const B=new Map;return e.entities.forEach(q=>B.set(q.id,q)),B},[e]),z=w.useMemo(()=>{const B=new Map,q=new Map;return e.subject_areas.forEach(W=>{(W.members??[]).forEach(Q=>q.set(Q,{id:W.id,name:W.name}))}),e.entities.forEach(W=>{var T,se;const Q=(T=W.conceptual)!=null&&T.id?q.get(W.conceptual.id):void 0,U=(se=W.conceptual)==null?void 0:se.subject_area,X=Q??(U?{id:U.id,name:U.name??U.id}:void 0);X&&B.set(W.id,X)}),B},[e]),Y=w.useCallback(B=>{var W;if(!u||u.size===0)return!1;const q=(W=z.get(B))==null?void 0:W.id;return!(q&&u.has(q))},[u,z]),G=w.useMemo(()=>{const B=new Map,q=(W,Q)=>{if(!Q.length)return;const U=B.get(W)??new Set;Q.forEach(X=>U.add(X)),B.set(W,U)};for(const W of e.relationships)q(W.from.entity,W.from.attributes),q(W.to.entity,W.to.attributes);return B},[e]);w.useEffect(()=>{var X;const B=i.trim().toLowerCase(),q=new Set;if(B)for(const T of e.entities)(T.name.toLowerCase()+" "+(((X=T.conceptual)==null?void 0:X.name.toLowerCase())??"")+" "+T.attributes.map(ce=>ce.name.toLowerCase()).join(" ")).includes(B)&&q.add(T.id);const W=new Set;if(r){W.add(r);for(const T of e.relationships)T.from.entity===r&&W.add(T.to.entity),T.to.entity===r&&W.add(T.from.entity)}const Q=e.entities.map(T=>{const se=B!==""&&!q.has(T.id),ce=r!==null&&!W.has(T.id);return{id:T.id,type:"entity",position:{x:0,y:0},hidden:Y(T.id),data:{entity:T,color:(()=>{const me=z.get(T.id);return me?R.get(me.id)??ui:ui})(),dimmed:se||ce,highlighted:B!==""&&q.has(T.id),showTypes:o,collapseDetail:a,expanded:(l==null?void 0:l.has(T.id))??!1,onToggleExpand:c,endpointAttrs:G.get(T.id),highlightAttrs:void 0,onHoverAttr:N}}}),U=e.relationships.filter(T=>D.has(T.from.entity)&&D.has(T.to.entity)).map(T=>{const{sourceHandle:se,targetHandle:ce}=lx(T,D),me=r!==null&&T.from.entity!==r&&T.to.entity!==r;return{id:T.id,source:T.from.entity,target:T.to.entity,sourceHandle:se,targetHandle:ce,type:"relationship",data:{relationship:T,dimmed:me,memberCount:Math.max(T.from.attributes.length,T.to.attributes.length),onHoverEdge:H}}});x(T=>{const se=new Map(T.map(oe=>[oe.id,oe.position])),ce=Q.every(oe=>se.has(oe.id)),me=((u==null?void 0:u.size)??0)>0;if(b.current&&ce&&T.length>=Q.length&&!me)return Q.map(oe=>({...oe,position:se.get(oe.id)??oe.position}));const we=me?Q.filter(oe=>!oe.hidden):Q,be=ri(we,U,D,"auto",z),Ee=new Map(be.map(oe=>[oe.id,oe.position])),Ge=Q.map(oe=>Ee.has(oe.id)?{...oe,position:Ee.get(oe.id)}:se.has(oe.id)&&b.current?{...oe,position:se.get(oe.id)}:oe);return b.current=!0,requestAnimationFrame(()=>f({padding:.15,duration:300})),Ge}),M(U)},[e,i,r,o,a,l,c,R,D,z,Y,u,G,f]);const K=w.useMemo(()=>{const B=new Set,q=new Set,W=new Map,Q=(U,X)=>{if(!X.length)return;const T=W.get(U)??new Set;X.forEach(se=>T.add(se)),W.set(U,T)};if($)for(const U of e.relationships)(U.from.attributes.includes($)||U.to.attributes.includes($))&&(B.add(U.id),q.add(U.from.entity),q.add(U.to.entity),Q(U.from.entity,U.from.attributes),Q(U.to.entity,U.to.attributes));if(I){const U=e.relationships.find(X=>X.id===I);U&&(Q(U.from.entity,U.from.attributes),Q(U.to.entity,U.to.attributes))}return{active:$!==null,relsForAttr:B,entsForAttr:q,hitAttrsByEntity:W}},[e,$,I]);w.useEffect(()=>{var Q;const B=i.trim().toLowerCase(),q=new Set;if(B)for(const U of e.entities)(U.name.toLowerCase()+" "+(((Q=U.conceptual)==null?void 0:Q.name.toLowerCase())??"")+" "+U.attributes.map(T=>T.name.toLowerCase()).join(" ")).includes(B)&&q.add(U.id);const W=new Set;if(r){W.add(r);for(const U of e.relationships)U.from.entity===r&&W.add(U.to.entity),U.to.entity===r&&W.add(U.from.entity)}x(U=>U.map(X=>{const T=B!==""&&!q.has(X.id),se=r!==null&&!W.has(X.id),ce=K.active&&!K.entsForAttr.has(X.id),me=T||se||ce,we=B!==""&&q.has(X.id)||K.active&&K.entsForAttr.has(X.id),be=K.hitAttrsByEntity.get(X.id);return X.data.dimmed===me&&X.data.highlighted===we&&X.data.highlightAttrs===be?X:{...X,data:{...X.data,dimmed:me,highlighted:we,highlightAttrs:be}}})),M(U=>U.map(X=>{var we,be;const T=(we=X.data)==null?void 0:we.relationship,se=r!==null&&T!==void 0&&T.from.entity!==r&&T.to.entity!==r,ce=K.active&&!K.relsForAttr.has(X.id),me=se||ce;return((be=X.data)==null?void 0:be.dimmed)===me?X:{...X,data:{...X.data,dimmed:me}}}))},[K,i,r,e]),w.useEffect(()=>{var Q;const B=i.trim().toLowerCase();if(!B)return;const q=new Set;for(const U of e.entities)(U.name.toLowerCase()+" "+(((Q=U.conceptual)==null?void 0:Q.name.toLowerCase())??"")+" "+U.attributes.map(T=>T.name.toLowerCase()).join(" ")).includes(B)&&q.add(U.id);if(!q.size)return;const W=requestAnimationFrame(()=>{const U=p().filter(T=>q.has(T.id));if(!U.length)return;const X=kf(U);y(X,{padding:.35,duration:400})});return()=>cancelAnimationFrame(W)},[i,e,p,y]);const J=w.useCallback((B="auto",q="LR")=>{x(W=>ri(W,_,D,B,z,q)),requestAnimationFrame(()=>f({padding:.15,duration:300}))},[_,D,z,f]),ee=w.useCallback(B=>{s(B),x(q=>(q.find(Q=>Q.id===B)&&requestAnimationFrame(()=>f({nodes:[{id:B}],padding:.4,duration:400})),q))},[f,s]);w.useImperativeHandle(m,()=>({relayout:J,fitView:()=>f({padding:.15,duration:300}),focusEntity:ee,newEntity:()=>O(!0)}),[J,f,ee]);const ie=w.useCallback(B=>{if(k||!B.source||!B.target||B.source===B.target)return;const q=B.sourceHandle&&B.sourceHandle!==Dr?B.sourceHandle:void 0,W=B.targetHandle&&B.targetHandle!==Lr?B.targetHandle:void 0;V({from:B.source,to:B.target,fromAttr:q,toAttr:W})},[k]),te=w.useCallback(async(B,q)=>{try{return await n(B,q)}catch(W){throw d==null||d(W instanceof Error?W.message:String(W)),W}},[n,d]),re=r?D.get(r)??null:null,de=e.relationships.find(B=>B.id===F)??null;return h.jsxs(h.Fragment,{children:[h.jsxs(Do,{nodes:v,edges:_,nodeTypes:ox,edgeTypes:ax,onNodeClick:(B,q)=>s(q.id),onEdgeClick:(B,q)=>!k&&g(q.id),onPaneClick:()=>{s(null),g(null)},onNodeDragStop:(B,q)=>x(W=>W.map(Q=>Q.id===q.id?{...Q,position:q.position}:Q)),onConnect:ie,nodesConnectable:!k,minZoom:.05,onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},fitView:!0,children:[h.jsx(xm,{variant:ke.Dots,gap:22,size:1.5,color:"#33291f"}),h.jsx(im,{pannable:!0,zoomable:!0,nodeColor:B=>{var q;return((q=B.data)==null?void 0:q.color)??"#5a4f42"},maskColor:"rgba(20, 17, 15, 0.75)"}),h.jsx(fm,{showInteractive:!1})]}),re&&h.jsx(Om,{entity:re,doc:e,caps:t,exec:te,onClose:()=>s(null),onFocusEntity:ee,onAlign:C,onEditMapping:L}),j&&h.jsx(ex,{entity:j,exec:te,onClose:()=>C(null)}),E&&h.jsx(tx,{entity:E,doc:e,exec:te,onClose:()=>L(null)}),S&&h.jsx(nx,{doc:e,exec:te,onClose:()=>O(!1),onCreated:ee}),P&&h.jsx(rx,{doc:e,fromId:P.from,toId:P.to,initialFromAttr:P.fromAttr,initialToAttr:P.toAttr,exec:te,onClose:()=>V(null)}),de&&h.jsx(sx,{rel:de,doc:e,exec:te,onClose:()=>g(null)})]})}function Lx({tab:e,onClose:t,readOnly:n,refreshKey:r,onModelChanged:s,onFocusEntity:i}){return h.jsxs("aside",{className:"side-panel",children:[h.jsxs("div",{className:"detail-header",children:[h.jsxs("h2",{children:[e==="ontology"&&"Ontology",e==="layers"&&"Ontology layers",e==="changes"&&"Uncommitted changes",e==="decisions"&&"Decision ledger"]}),h.jsx("button",{className:"icon-btn",onClick:t,children:"✕"})]}),e==="ontology"&&h.jsx(cx,{}),e==="layers"&&h.jsx(dx,{refreshKey:r,onFocusEntity:i}),e==="changes"&&h.jsx(fx,{readOnly:n,refreshKey:r,onModelChanged:s}),e==="decisions"&&h.jsx(hx,{readOnly:n})]})}function cx(){const[e,t]=w.useState(""),[n,r]=w.useState([]),[s,i]=w.useState({loaded:0,vocabs:[]}),[o,a]=w.useState(null),[l,c]=w.useState([]),[u,d]=w.useState("");w.useEffect(()=>{Go().then(f=>c(f.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const f=setTimeout(()=>{Bo(e||"a",u||void 0).then(y=>{r(e?y.results:[]),i({loaded:y.loaded_terms,vocabs:y.vocabularies})}).catch(()=>{})},200);return()=>clearTimeout(f)},[e,u]);const m=w.useCallback(f=>{wm(f).then(a).catch(()=>{})},[]);return o?h.jsxs("div",{className:"panel-body",children:[h.jsx("button",{className:"mini-btn",onClick:()=>a(null),children:"← back"}),h.jsx("h3",{style:{marginTop:12},children:o.label}),h.jsx("code",{className:"term-iri",children:o.prefixed}),o.definition&&h.jsx("p",{className:"term-def",children:o.definition}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"source"}),h.jsx("span",{className:"v",children:o.source})]}),o.broader.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"broader"}),o.broader.map(f=>h.jsxs("button",{className:"rel-link",onClick:()=>m(f.iri),children:["↑ ",f.label]},f.iri))]}),o.narrower.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"narrower"}),o.narrower.map(f=>h.jsxs("button",{className:"rel-link",onClick:()=>m(f.iri),children:["↓ ",f.label]},f.iri))]})]}):h.jsxs("div",{className:"panel-body",children:[l.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(u===""?" active":""),onClick:()=>d(""),children:"all sources"}),l.map(f=>h.jsxs("button",{className:"src-chip"+(u===f.id?" active":""),title:(f.description??"")+(f.count!=null?` · ${f.count} terms`:" · live"),onClick:()=>d(f.id),children:[f.name,f.layer&&h.jsx("span",{className:`src-layer layer-${f.layer}`,children:f.layer})]},f.id))]}),h.jsx("input",{className:"edit-input",placeholder:u?`Search within ${u}…`:"Search vocabulary terms…",value:e,onChange:f=>t(f.target.value)}),h.jsx("p",{className:"meta-line",children:l.length>0?`${l.length} source${l.length>1?"s":""}: ${s.vocabs.join(", ")}`+(s.loaded>0?` · ${s.loaded} local terms indexed`:""):"no vocabulary loaded — declare ontology_stack in mdl-project.yaml or run `mdl ontology vendor fibo`"}),h.jsx("div",{className:"term-results",children:n.map(f=>h.jsxs("button",{className:"term-card",onClick:()=>m(f.prefixed),children:[h.jsxs("div",{className:"term-head",children:[h.jsx("span",{className:"term-label",children:f.label}),h.jsxs("span",{className:"term-source",children:[f.source_kind==="glossary-term"&&h.jsx("span",{className:"kind-badge",title:"glossary term",children:"glossary"}),f.source]})]}),h.jsx("code",{className:"term-iri",children:f.prefixed}),f.definition&&h.jsx("p",{className:"term-def",children:f.definition})]},f.iri))})]})}const ux=["industry","core","domain","specialised"];function dx({refreshKey:e,onFocusEntity:t}){const[n,r]=w.useState(null),[s,i]=w.useState(null);if(w.useEffect(()=>{bm().then(r).catch(()=>{}),vm().then(i).catch(()=>{})},[e]),!n)return h.jsx("div",{className:"panel-body",children:"loading…"});const o=new Set((s==null?void 0:s.core_uncovered)??[]);return h.jsxs("div",{className:"panel-body",children:[s&&h.jsxs("div",{className:"coverage-banner"+(s.coverage_pct<100?" warn":""),children:["industry alignment coverage: ",h.jsxs("b",{children:[s.coverage_pct,"%"]})," (",s.core_with_industry,"+",s.core_exempt,"/",s.total_core," core terms)"]}),h.jsxs("div",{className:"layer-band external",children:[h.jsx("h4",{children:"industry (external vocabularies)"}),n.external_terms.length===0&&h.jsx("p",{className:"empty-hint",children:"no aligned terms yet"}),n.external_terms.map(a=>h.jsxs("div",{className:"stack-term external",title:a.definition??void 0,children:[a.label,h.jsx("span",{className:"term-source",children:a.source})]},a.iri))]}),ux.slice(1).map(a=>h.jsxs("div",{className:"layer-band",children:[h.jsx("h4",{children:a}),(n.layers[a]??[]).length===0&&h.jsx("p",{className:"empty-hint",children:"—"}),(n.layers[a]??[]).map(l=>{var c;return h.jsxs("button",{className:"stack-term"+(o.has(l.name)?" uncovered":""),title:l.definition??void 0,onClick:()=>t(l.id),children:[l.name,(l.aligned_refs??(l.aligned_to?[l.aligned_to]:[])).map((u,d)=>h.jsxs("span",{className:"align-arrow"+(u.resolved?"":" unresolved")+(u.status==="proposed"?" proposed":""),title:u.ref+(u.resolved_via?` (via ${u.resolved_via})`:""),children:["↑ ",u.label,u.status==="proposed"?" ·prop":""]},`${u.ref}-${d}`)),!l.aligned_to&&(((c=l.aligned_refs)==null?void 0:c.length)??0)===0&&!l.no_industry_equivalent&&h.jsx("span",{className:"align-arrow missing",children:"unaligned"})]},l.id)})]},a))]})}function fx({readOnly:e,refreshKey:t,onModelChanged:n}){const[r,s]=w.useState(null),[i,o]=w.useState(""),[a,l]=w.useState(""),[c,u]=w.useState(!1),d=w.useCallback(()=>{Em().then(s).catch(()=>s(null)),Nm().then(m=>o([m.diff,...m.untracked].filter(Boolean).join(` - -`))).catch(()=>o(""))},[]);return w.useEffect(d,[d,t]),r!=null&&r.git?r.clean?h.jsx("div",{className:"panel-body ok",children:"✓ working tree clean"}):h.jsxs("div",{className:"panel-body",children:[h.jsx("ul",{className:"dirty-list",children:r.dirty.map(m=>h.jsxs("li",{children:[h.jsx("code",{className:"dirty-state",children:m.state})," ",m.path]},m.path))}),h.jsx("pre",{className:"diff-view",children:i.split(` -`).map((m,f)=>h.jsx("span",{className:m.startsWith("+")&&!m.startsWith("+++")?"diff-add":m.startsWith("-")&&!m.startsWith("---")?"diff-del":m.startsWith("@@")?"diff-hunk":"",children:m+` -`},f))}),!e&&h.jsxs(h.Fragment,{children:[h.jsx("input",{className:"edit-input",placeholder:"commit message…",value:a,onChange:m=>l(m.target.value)}),h.jsxs("div",{className:"modal-footer",children:[h.jsx("button",{className:"mini-btn danger",disabled:c,onClick:()=>{confirm("Discard ALL uncommitted model changes?")&&(u(!0),km().then(()=>{u(!1),d(),n()}))},children:"Discard all"}),h.jsx("button",{className:"primary-btn",disabled:c||!a.trim(),onClick:()=>{u(!0),Sm(a).then(()=>{u(!1),l(""),d()})},children:"Commit"})]})]})]}):h.jsx("div",{className:"panel-body",children:"model directory is not in a git repo"})}function hx({readOnly:e}){const[t,n]=w.useState([]),r=w.useCallback(()=>{jm().then(i=>n(i.decisions)).catch(()=>n([]))},[]);if(w.useEffect(r,[r]),t.length===0)return h.jsx("div",{className:"panel-body",children:"no recorded decisions (run `mdl reverse` to propose)"});const s={accepted:"✓",rejected:"✗",proposed:"?"};return h.jsx("div",{className:"panel-body",children:t.map(i=>h.jsxs("div",{className:"decision "+i.verdict,children:[h.jsxs("div",{className:"decision-head",children:[h.jsx("span",{className:"decision-mark",children:s[i.verdict]}),h.jsx("span",{className:"chip",children:i.confidence}),h.jsx("span",{className:"chip",children:i.signal})]}),h.jsx("p",{className:"decision-subject",children:i.subject}),!e&&i.verdict==="proposed"&&h.jsxs("div",{className:"decision-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>Os(i.signal_key,"accepted").then(r),children:"Accept"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>Os(i.signal_key,"rejected").then(r),children:"Reject"})]})]},i.signal_key))})}function Px({exec:e,canEdit:t,onImported:n,submitLabel:r="Import & review",buttonClass:s="erd-action ghost",applyBatch:i,openImport:o=!1}){const[a,l]=w.useState(o&&t?"import":null),[c,u]=w.useState([]),[d,m]=w.useState("postgres"),f=w.useRef(null);w.useEffect(()=>{Cm().then(p=>u(p.formats)).catch(()=>u([]))},[]),w.useEffect(()=>{if(!a)return;const p=v=>{f.current&&!f.current.contains(v.target)&&l(null)};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[a]);const y=c.find(p=>p.id==="sql");return h.jsxs("div",{className:"ie-wrap",ref:f,children:[h.jsxs("button",{className:s+(a==="export"?" on":""),onClick:()=>l(a==="export"?null:"export"),children:[h.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"↧"}),"Export ",h.jsx("span",{className:"erd-caret",children:"▾"})]}),t&&h.jsxs("button",{className:s+(a==="import"?" on":""),onClick:()=>l(a==="import"?null:"import"),children:[h.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"↥"}),"Import ",h.jsx("span",{className:"erd-caret",children:"▾"})]}),a==="export"&&h.jsxs("div",{className:"ie-menu",role:"menu",children:[(y==null?void 0:y.dialects)&&h.jsxs("label",{className:"ie-dialect",children:[h.jsx("span",{children:"SQL dialect"}),h.jsx("select",{value:d,onChange:p=>m(p.target.value),children:y.dialects.map(p=>h.jsx("option",{value:p,children:p},p))})]}),c.map(p=>h.jsx("a",{className:"ie-item",href:$m(p.id,p.id==="sql"?d:void 0),download:!0,onClick:()=>l(null),children:p.label},p.id))]}),a==="import"&&h.jsx(mx,{exec:e,submitLabel:r,buttonClass:s,applyBatch:i,onClose:()=>l(null),onImported:p=>{l(null),n==null||n(p)}})]})}const di=[{id:"sql",label:"SQL DDL",hint:"CREATE TABLE … PRIMARY KEY / FOREIGN KEY"},{id:"mermaid",label:"Mermaid erDiagram",hint:"structural: entities, attributes, relationships"},{id:"json-schema",label:"JSON Schema",hint:"each object definition becomes an entity"},{id:"erwin",label:"erwin XML",hint:"entities, keys, relationships, subject areas, subtypes"}];function mx({exec:e,submitLabel:t,buttonClass:n,applyBatch:r,onClose:s,onImported:i}){var b;const[o,a]=w.useState("sql"),[l,c]=w.useState("postgres"),[u,d]=w.useState(""),[m,f]=w.useState(!1),[y,p]=w.useState(null),[v,x]=w.useState([]),_=j=>{j&&j.text().then(d).catch(()=>p("could not read that file"))},M=async()=>{f(!0),p(null),x([]);try{const j=await Mm(o,u,o==="sql"?l:void 0);if(!j.changes.length){p(j.warnings[0]??"nothing to import from that document"),f(!1);return}if(r)await r(j.changes);else for(const C of j.changes)await e(C.op,C.payload);x(j.warnings),i(j.tables)}catch(j){p(j instanceof Error?j.message:String(j))}finally{f(!1)}};return h.jsxs("div",{className:"ie-menu ie-import",role:"dialog",children:[h.jsx("div",{className:"ie-tabs",children:di.map(j=>h.jsx("button",{className:"ie-tab"+(o===j.id?" active":""),onClick:()=>a(j.id),children:j.label},j.id))}),h.jsx("p",{className:"ie-hint",children:(b=di.find(j=>j.id===o))==null?void 0:b.hint}),o==="sql"&&h.jsxs("label",{className:"ie-dialect",children:[h.jsx("span",{children:"dialect"}),h.jsx("select",{value:l,onChange:j=>c(j.target.value),children:["postgres","snowflake","mysql","duckdb"].map(j=>h.jsx("option",{children:j},j))})]}),h.jsx("textarea",{className:"ie-textarea",placeholder:`Paste ${o} here, or choose a file…`,value:u,onChange:j=>d(j.target.value),rows:8}),h.jsx("input",{type:"file",className:"ie-file",accept:".sql,.mmd,.md,.json,.xml,.txt",onChange:j=>{var C;return _(((C=j.target.files)==null?void 0:C[0])??null)}}),o==="mermaid"&&h.jsx("p",{className:"ie-warn",children:"Mermaid is a diagram format: types and keys are approximate and relationships are inferred from the lines."}),v.map((j,C)=>h.jsx("p",{className:"ie-warn",children:j},C)),y&&h.jsx("p",{className:"ie-error",children:y}),h.jsxs("div",{className:"ie-foot",children:[h.jsx("button",{className:n,onClick:s,children:"Cancel"}),h.jsx("button",{className:n,disabled:m||!u.trim(),onClick:M,children:m?"Importing…":t})]})]})}export{Vo as A,xm as B,fm as C,Rm as E,Px as I,dx as L,Dx as M,cx as O,ci as P,To as R,Lx as S,xx as a,Cx as b,kx as c,Mx as d,Ax as e,yx as f,Em as g,Ix as h,jx as i,vx as j,_x as k,Ex as l,Sx as m,Nx as n,Bo as o,$x as p,wx as q,bx as r,_m as s,ri as t,Do as u,ix as v,im as w}; diff --git a/packages/server/src/mdl_server/static/index.html b/packages/server/src/mdl_server/static/index.html index 47f766d..3ef1a9a 100644 --- a/packages/server/src/mdl_server/static/index.html +++ b/packages/server/src/mdl_server/static/index.html @@ -4,9 +4,9 @@ Modelith - + - + diff --git a/packages/server/src/mdl_server/static/sme.html b/packages/server/src/mdl_server/static/sme.html index e66a046..ccd2daa 100644 --- a/packages/server/src/mdl_server/static/sme.html +++ b/packages/server/src/mdl_server/static/sme.html @@ -4,9 +4,9 @@ Modelith Glossary - + - + diff --git a/packages/server/tests/test_import_erwin_api.py b/packages/server/tests/test_import_erwin_api.py index d5f554b..42f0cff 100644 --- a/packages/server/tests/test_import_erwin_api.py +++ b/packages/server/tests/test_import_erwin_api.py @@ -31,3 +31,51 @@ def test_api_import_empty_content_422(tmp_path): client = TestClient(create_app(tmp_path)) r = client.post("/api/import", json={"format": "erwin", "content": ""}) assert r.status_code == 422 + + +def test_import_preview_returns_diff_doc_and_changes(tmp_path): + """Pre-write review: /api/import/preview returns a ModelDiffDoc (the review view's shape) + PLUS the import's changes + warnings, and writes nothing.""" + (tmp_path / "mdl-project.yaml").write_text("name: t\ndbt_target: duckdb\n") + client = TestClient(create_app(tmp_path)) + r = client.post("/api/import/preview", json={"format": "erwin", "content": _erwin_xml()}) + assert r.status_code == 200, r.text + doc = r.json() + # the ModelDiffDoc contract the canvas review view consumes + assert doc["ok"] is True + assert "objects" in doc and isinstance(doc["objects"], list) + assert "max_severity" in doc and "has_breaking" in doc + assert doc["base"]["label"] == "current model" + assert doc["head"]["label"] == "erwin import" + # into an empty model, the 3 erwin entities are all ADDED + names = {o.get("name_after") for o in doc["objects"]} + assert {"counterparty", "trade", "institution"} <= names + assert all(o["change"] == "added" for o in doc["objects"] if o["object_kind"] == "logical_entity") + # the write payload rides along for the apply action + ops = {c["op"] for c in doc["changes"]} + assert "create_entity" in ops + assert isinstance(doc["warnings"], list) + # it wrote nothing — the model dir still has only the project file + assert not (tmp_path / "logical").exists() + + +def test_import_preview_non_erwin_422(tmp_path): + (tmp_path / "mdl-project.yaml").write_text("name: t\ndbt_target: duckdb\n") + client = TestClient(create_app(tmp_path)) + r = client.post("/api/import/preview", json={"format": "sql", "content": "CREATE TABLE t(x int);"}) + assert r.status_code == 422 + + +def test_import_stash_roundtrip_is_one_shot(tmp_path): + """A host (VS Code) stashes .xml content; the canvas fetches it once by token.""" + (tmp_path / "mdl-project.yaml").write_text("name: t\ndbt_target: duckdb\n") + client = TestClient(create_app(tmp_path)) + put = client.post("/api/import/stash", json={"content": ""}) + assert put.status_code == 200 + token = put.json()["token"] + got = client.get(f"/api/import/stash/{token}") + assert got.status_code == 200 and got.json()["content"] == "" + # consumed -> a second read is 404 + assert client.get(f"/api/import/stash/{token}").status_code == 404 + # empty content is rejected + assert client.post("/api/import/stash", json={"content": ""}).status_code == 422 diff --git a/specs/modelith-semantic-compare-merge-spec.md b/specs/modelith-semantic-compare-merge-spec.md index 2372086..93cf9f6 100644 --- a/specs/modelith-semantic-compare-merge-spec.md +++ b/specs/modelith-semantic-compare-merge-spec.md @@ -145,11 +145,15 @@ text diff of `mdl model render` to the webview review over `diff_models_by_name` ## Phasing (each phase shippable) -1. **Per-field selection + filters + pluggable actions** on the existing 2-pane `ReviewView`, wired to - flows that ALREADY produce `ModelDiffDoc` (preview, propose, git-diff/refs). Immediate UX win, no - adapters. Modernizes the SME review on its own. -2. **Import pre-write review** (import adapter + canvas route + VS Code command) — highest-value new - flow; erwin import becomes "review before it lands," reusing phase 1. +1. **[SHIPPED] Per-field selection + filters + pluggable actions** on the existing 2-pane `ReviewView`, + wired to flows that ALREADY produce `ModelDiffDoc` (preview, propose, git-diff/refs). Immediate UX + win, no adapters. Modernizes the SME review on its own. +2. **[SHIPPED] Import pre-write review** (import adapter + canvas route + VS Code command) — highest-value + new flow; erwin import becomes "review before it lands," reusing phase 1. Delivered as + `POST /api/import/preview` (parse-without-write → enriched `ModelDiffDoc`), the `ReviewScreen` + `action` prop + "Apply import" (writes only selected new objects), and the `modelith.reviewImport` + command framed in the Reverse Review view, with a one-shot `/api/import/stash` channel so a VS Code + `.xml` pre-fills the review. 3. **Drift review** (drift adapter + `/api/drift` + canvas route + VS Code command + tree "Open in review"). 4. **Decisions review** (decisions adapter + route + VS Code). diff --git a/vscode/package.json b/vscode/package.json index 0b85737..b2fec66 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -305,6 +305,11 @@ "title": "Modelith: Import Erwin XML", "icon": "$(cloud-download)" }, + { + "command": "modelith.reviewImport", + "title": "Modelith: Review Erwin Import", + "icon": "$(diff)" + }, { "command": "modelith.stopServer", "title": "Modelith: Stop Canvas Server" @@ -509,6 +514,11 @@ "when": "resourceExtname == .xml", "group": "modelith@1" }, + { + "command": "modelith.reviewImport", + "when": "resourceExtname == .xml", + "group": "modelith@2" + }, { "command": "modelith.liftModel", "when": "resourceExtname == .sql", @@ -588,6 +598,11 @@ "when": "view == modelithReverse", "group": "navigation@3" }, + { + "command": "modelith.reviewImport", + "when": "view == modelithReverse", + "group": "navigation@4" + }, { "command": "modelith.compareReversed", "when": "view == modelithReverse", diff --git a/vscode/src/canvasPanel.ts b/vscode/src/canvasPanel.ts index 18a0e2d..098a58d 100644 --- a/vscode/src/canvasPanel.ts +++ b/vscode/src/canvasPanel.ts @@ -56,12 +56,15 @@ export class CanvasManager { * wizard the toolbar button opens is already up when the panel appears. When a * query is given we rewrite the iframe even on an already-open panel, so the * wizard opens whether or not the canvas was already showing. */ - async open(modelDir: string, query?: string): Promise { + async open(modelDir: string, query?: string, path = ""): Promise { const cfg = vscode.workspace.getConfiguration("modelith"); await this.ensureServer(modelDir); const local = vscode.Uri.parse(`http://127.0.0.1:${this.port}/`); const external = await vscode.env.asExternalUri(local); - const url = external.toString().replace(/\/$/, "") + "/" + (query ? `?${query}` : ""); + // `path` selects which SPA (default "" = architect canvas; "sme" = the SME review app + // served at /sme). `query` (e.g. import=1) is appended as-is. + const base = external.toString().replace(/\/$/, "") + "/" + path; + const url = base + (query ? `?${query}` : ""); if (cfg.get("canvas.display") === "external") { await vscode.env.openExternal(vscode.Uri.parse(url)); @@ -87,6 +90,29 @@ export class CanvasManager { this.panel.onDidDispose(() => (this.panel = undefined)); } + /** Open the SME review app with the erwin import panel. If `content` is given (a .xml the + * host read from disk — the webview cannot read local files), stash it on the server first + * and open /sme?import= so the panel is pre-filled; otherwise open /sme?import=1 for + * a paste-or-pick. */ + async openImportReview(modelDir: string, content?: string): Promise { + await this.ensureServer(modelDir); + let query = "import=1"; + if (content) { + try { + const res = await fetch(`http://127.0.0.1:${this.port}/api/import/stash`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ content }), + }); + const data = (await res.json()) as { ok?: boolean; token?: string }; + if (data.ok && data.token) query = `import=${encodeURIComponent(data.token)}`; + } catch { + // stash failed -> fall back to the paste-or-pick panel + } + } + await this.open(modelDir, query, "sme"); + } + /** Re-hydrate a webview panel VS Code restored after a window reload. The * `mdl serve` child process died with the old extension host, so the restored * iframe points at a dead port (blank canvas). Adopt the panel, restart the diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 80749d4..266c97e 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -640,6 +640,43 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { }), ); + cmd("modelith.reviewImport", (arg: unknown) => + withModelDir(async (dir) => { + if (vscode.workspace.getConfiguration("modelith").get("canvas.readOnly")) { + void vscode.window.showWarningMessage( + "Modelith: the canvas is in read-only mode — turn off modelith.canvas.readOnly to import.", + ); + return; + } + try { + // Resolve an erwin XML: the clicked file (explorer right-click), else a picker. If the + // user cancels the picker we still open the panel empty (they can paste). + let fileUri = arg instanceof vscode.Uri ? arg : undefined; + if (!fileUri) { + const picked = await vscode.window.showOpenDialog({ + title: "Review Erwin XML before import", + canSelectMany: false, + filters: { "erwin XML": ["xml"] }, + openLabel: "Review", + }); + fileUri = picked?.[0]; + } + // Read the file here (the webview can't) and hand the content to the SME review app via + // a one-shot server stash. An erwin import there is shown as a SEMANTIC diff you review + // (filters, per-field selection, break impact) BEFORE anything is written; "Apply + // import" writes only the entities you keep. + let content: string | undefined; + if (fileUri) { + content = Buffer.from(await vscode.workspace.fs.readFile(fileUri)).toString("utf8"); + } + await canvas.openImportReview(dir, content); + } catch (e) { + if (isMdlNotFound(e)) throw e; // let cmd() offer the one-click installer + void vscode.window.showErrorMessage(`Modelith canvas: ${e}`); + } + }), + ); + // Import an erwin XML export into a fresh Modelith model. An erwin export is a whole // model (like a reverse), so this WRITES a model dir via the CLI (10MB-safe, server- // free) and opens it — not a canvas paste. Reachable from the palette, the Reverse