diff --git a/README.md b/README.md index 02b8a02..31107d0 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,26 @@ all first-class in the model. Relationship lines anchor to the exact columns that join, not to the entity headers, so with composite keys or several relationships between the same pair the diagram still says which columns join which; hovering an attribute highlights the relationships it takes part -in and dims the rest. +in and dims the rest. Select or hover a relationship to get a drag handle on its vertical +segment and slide the line sideways (snapping to a grid and to nearby lines) so a dense +diagram stays readable; double-click the handle to return it to automatic routing. + +**Saved layout.** Card positions and relationship routes are remembered per view in a +`/.mdl/layout.json` sidecar, so reopening the canvas or restarting `mdl serve` +restores your arrangement exactly. Layout is purely visual: it is JSON (never model YAML), +it is **git-ignored by default** (a `.mdl/.gitignore` is created on first save), and it is +invisible to `mdl validate`, drift, `mdl diff`, every emitter and the git status, so it +never shows up in a review, a diff or the generated dbt. "Reset layout" in the View menu +clears a view's saved arrangement. + +**Sharing a layout with your team.** Set `layout: {shared: true}` in `mdl-project.yaml` +to opt the sidecar into version control: Modelith then stops git-ignoring +`.mdl/layout.json` (dropping the auto-added ignore line) so you can commit it, and anyone +who opens the canvas on that checkout sees the same arrangement. The setting is a +visual-tooling preference only — whatever its value, the layout (and the setting itself) +stays invisible to every functional CLI feature: `mdl validate`, drift, `mdl diff` and the +emitters produce byte-identical output, and the model fingerprint is unchanged, whether +the layout is shared or ignored. ![The ER canvas with a relationship line running from one entity's foreign-key attribute row to the referenced entity's primary-key row, with crow's-foot cardinality](docs/assets/canvas.png) diff --git a/canvas/src/App.tsx b/canvas/src/App.tsx index 7b475bb..9a927dd 100644 --- a/canvas/src/App.tsx +++ b/canvas/src/App.tsx @@ -8,6 +8,7 @@ import { fetchDiagnostics, fetchModel, gitStatus, + httpLayoutStore, sendCommand, } from "./api"; import { directCapabilities } from "./exec"; @@ -92,6 +93,9 @@ function Canvas() { // The architect canvas writes straight to disk; its capabilities follow the // server's read-only flag exactly. const caps = useMemo(() => directCapabilities(readOnly), [readOnly]); + // The architect canvas persists node positions + edge routes (visual only). The + // SME/catalog shells pass no store, so staged/previewed models never persist. + const layoutStore = useMemo(() => httpLayoutStore(), []); const urlParams = useMemo(() => new URLSearchParams(window.location.search), []); const minimal = urlParams.get("minimal") === "1"; // The preview pane (minimal) opens collapsed so a big warehouse is readable at a glance; @@ -267,6 +271,7 @@ function Canvas() { onToggleCollapse={() => setCollapseDetail((v) => !v)} onFitView={() => canvasRef.current?.fitView()} onRelayout={(mode, dir) => canvasRef.current?.relayout(mode, dir)} + onResetLayout={() => canvasRef.current?.resetLayout()} onRefresh={refresh} readOnly={readOnly} dirty={dirty} @@ -297,6 +302,7 @@ function Canvas() { activeAreas={activeAreas} onError={setToast} handleRef={canvasRef} + layoutStore={layoutStore} /> {!minimal && ( o.x && y < o.y + o.h + GAP && y + h + GAP > o.y; + if (overlap) { + y = o.y + o.h + GAP; // push below the card it hit + moved = true; + } + } + } + return { x, y }; +} + /** Which React Flow handle each end of a relationship anchors to (issue #5). * * Per end: if the relationship maps to an attribute that actually exists on that @@ -66,6 +99,10 @@ export interface ModelCanvasHandle { focusEntity: (id: string) => void; /** Open the New Entity modal — a shell gesture (toolbar button, `n` shortcut). */ newEntity: () => void; + /** Clear the saved layout for the current view and re-run auto-layout. */ + resetLayout: () => void; + /** Restore the positions replaced by the last Re-layout (session undo). */ + undoRelayout: () => void; } export interface ModelCanvasProps { @@ -90,6 +127,9 @@ export interface ModelCanvasProps { /** Surfaced by the shell however it likes (a toast, a banner). */ onError?: (message: string) => void; handleRef?: React.Ref; + /** Persist node positions + edge routes (visual only). Absent on the SME/catalog + * shells, so a staged or previewed model never persists layout. See layoutStore.ts. */ + layoutStore?: LayoutStore; } /** The ER canvas plus its editing surface: the graph, the inspector, and the five @@ -114,12 +154,57 @@ export function ModelCanvas({ activeAreas, onError, handleRef, + layoutStore, }: ModelCanvasProps) { const { fitView, fitBounds, getNodes } = useReactFlow(); const [nodes, setNodes] = useState[]>([]); const [edges, setEdges] = useState[]>([]); const layoutedRef = useRef(false); + // --- saved layout (visual only) ------------------------------------------- + // The view key: "__all__" for the unfiltered canvas, else "sa:" + the sorted + // active-area ids joined with "+". Each view keeps its own arrangement because + // the canvas re-lays-out the visible subset per filter. + const viewKey = useMemo(() => { + const areas = activeAreas && activeAreas.size > 0 ? [...activeAreas].sort() : null; + return areas ? "sa:" + areas.join("+") : "__all__"; + }, [activeAreas]); + // the saved arrangement for the CURRENT view; null until loaded (or when none). + const savedRef = useRef(null); + const [savedTick, setSavedTick] = useState(0); // bump to re-run the derivation after a load + // per-view in-memory cache so switching the filter back is instant and offline. + const loadedViews = useRef>(new Map()); + // Re-layout undo: the positions replaced by the last Re-layout, for a session toast. + const undoRef = useRef | null>(null); + + // Load the saved layout whenever the view changes. A missing store or a failed + // load leaves savedRef null (pure auto-layout). layoutedRef is reset so the + // derivation re-applies positions for the new view. + useEffect(() => { + if (!layoutStore) { + savedRef.current = null; + return; + } + const cached = loadedViews.current.get(viewKey); + if (cached) { + savedRef.current = cached; + layoutedRef.current = false; + setSavedTick((t) => t + 1); + return; + } + let cancelled = false; + layoutStore.load(viewKey).then((layout) => { + if (cancelled) return; + loadedViews.current.set(viewKey, layout); + savedRef.current = layout; + layoutedRef.current = false; + setSavedTick((t) => t + 1); + }); + return () => { + cancelled = true; + }; + }, [layoutStore, viewKey]); + const [alignFor, setAlignFor] = useState(null); const [mapFor, setMapFor] = useState(null); const [newEntityOpen, setNewEntityOpen] = useState(false); @@ -134,9 +219,79 @@ export function ModelCanvas({ // and canvas-local; it feeds the existing dimmed/highlighted computation. const [hoveredAttr, setHoveredAttr] = useState(null); const [hoveredEdge, setHoveredEdge] = useState(null); + // a single click SELECTS a relationship (persistently shows its drag handles, so the + // user does not have to hover-fight the thin line); double-click still opens the editor. + const [selectedEdge, setSelectedEdge] = useState(null); + // committed edge routes for the current view (relId -> {routeX?, sourceAnchor?, + // targetAnchor?}). The live drag is kept inside the edge component; this holds only + // committed values. + const routesRef = useRef>({}); const readOnly = !caps.canEdit; + // --- persist layout (debounced; whole view) ------------------------------- + const saveTimer = useRef | null>(null); + const saveLayout = useCallback(() => { + if (!layoutStore) return; + if (saveTimer.current) clearTimeout(saveTimer.current); + saveTimer.current = setTimeout(() => { + const posNodes: ViewLayout["nodes"] = {}; + for (const n of getNodes()) { + if (!n.hidden) posNodes[n.id] = { x: Math.round(n.position.x), y: Math.round(n.position.y) }; + } + const edgeRoutes: ViewLayout["edges"] = {}; + for (const [relId, r] of Object.entries(routesRef.current)) { + const out: EdgeRoute = {}; + if (r.routeX != null) out.routeX = Math.round(r.routeX); + if (r.sourceAnchor) out.sourceAnchor = roundAnchor(r.sourceAnchor); + if (r.targetAnchor) out.targetAnchor = roundAnchor(r.targetAnchor); + if (out.routeX != null || out.sourceAnchor || out.targetAnchor) edgeRoutes[relId] = out; + } + const layout: ViewLayout = { nodes: posNodes, edges: edgeRoutes }; + loadedViews.current.set(viewKey, layout); + savedRef.current = layout; + layoutStore.save(viewKey, layout); + }, 800); + }, [layoutStore, getNodes, viewKey]); + + // Commit a dragged edge route (or null to reset to automatic). Updates the edge + // data in place (identity-preserving, so only that one edge re-renders) and saves. + const onRouteCommit = useCallback( + (relId: string, routeX: number | null) => { + const cur = routesRef.current[relId] ?? {}; + if (routeX == null) delete cur.routeX; + else cur.routeX = routeX; + routesRef.current[relId] = cur; + setEdges((prev) => + prev.map((e) => + e.id === relId ? { ...e, data: { ...e.data!, routeX: routeX ?? undefined } } : e, + ), + ); + saveLayout(); + }, + [saveLayout], + ); + + // Commit a dragged endpoint anchor (or null to reset that end to its attribute-row port). + const onAnchorCommit = useCallback( + (relId: string, end: "source" | "target", anchor: EdgeAnchor | null) => { + const cur = routesRef.current[relId] ?? {}; + const key = end === "source" ? "sourceAnchor" : "targetAnchor"; + if (anchor == null) delete cur[key]; + else cur[key] = anchor; + routesRef.current[relId] = cur; + setEdges((prev) => + prev.map((e) => + e.id === relId + ? { ...e, data: { ...e.data!, [key]: anchor ?? undefined } } + : e, + ), + ); + saveLayout(); + }, + [saveLayout], + ); + const saColors = useMemo(() => { const m = new Map(); doc.subject_areas.forEach((sa, i) => m.set(sa.id, PALETTE[i % PALETTE.length])); @@ -278,6 +433,52 @@ export function ModelCanvas({ }; }); + // seed committed routes for this view from the saved layout + const saved = savedRef.current; + routesRef.current = {}; + if (saved) { + for (const [relId, r] of Object.entries(saved.edges)) { + routesRef.current[relId] = { + ...(r.routeX != null ? { routeX: r.routeX } : {}), + ...(r.sourceAnchor ? { sourceAnchor: r.sourceAnchor } : {}), + ...(r.targetAnchor ? { targetAnchor: r.targetAnchor } : {}), + }; + } + } + + // Build the edges from the FINAL node positions. We do this inside the setNodes + // updater (where the laid-out/saved positions exist) because React does not run the + // updater synchronously before the code after setNodes — reading positions outside it + // gave stale (0,0) card rects, so the side-aware router never saw a real target card. + const emitEdges = (ns: Node[]) => { + const cardById = new Map(); + for (const n of ns) { + cardById.set(n.id, { + left: n.position.x, + right: n.position.x + NODE_WIDTH, + top: n.position.y, + bottom: n.position.y + nodeHeight(n.data.entity), + }); + } + setEdges( + newEdges.map((e) => { + const r = routesRef.current[e.id] ?? {}; + return { + ...e, + data: { + ...e.data!, + routeX: r.routeX, + sourceAnchor: r.sourceAnchor, + targetAnchor: r.targetAnchor, + onRouteChange: layoutStore ? onRouteCommit : undefined, + onAnchorChange: layoutStore ? onAnchorCommit : undefined, + sourceCard: cardById.get(e.source), + targetCard: cardById.get(e.target), + }, + }; + }), + ); + }; setNodes((prev) => { const posById = new Map(prev.map((n) => [n.id, n.position])); const known = newNodes.every((n) => posById.has(n.id)); @@ -286,21 +487,44 @@ export function ModelCanvas({ // Otherwise keep known positions so a re-derivation (search, selection) doesn't reflow. const filtering = (activeAreas?.size ?? 0) > 0; if (layoutedRef.current && known && prev.length >= newNodes.length && !filtering) { - return newNodes.map((n) => ({ ...n, position: posById.get(n.id) ?? n.position })); + const kept = newNodes.map((n) => ({ ...n, position: posById.get(n.id) ?? n.position })); + emitEdges(kept); + return kept; } const toLay = filtering ? newNodes.filter((n) => !n.hidden) : newNodes; const laid = layoutGraph(toLay, newEdges, entityIndex, "auto", areaByEntity); const laidPos = new Map(laid.map((n) => [n.id, n.position])); + // Prefer a SAVED position over the auto-layout result. Entities without a saved + // position (new ones, e.g. just created via MCP) keep auto-layout, then get a + // simple overlap nudge so they don't land on top of an existing card. + const savedPos = saved?.nodes ?? {}; + const hasSaved = Object.keys(savedPos).length > 0; + const occupied: { x: number; y: number; w: number; h: number }[] = []; const merged = newNodes.map((n) => { + const sp = savedPos[n.id]; + if (sp) { + const pos = { x: sp.x, y: sp.y }; + occupied.push({ x: pos.x, y: pos.y, w: NODE_WIDTH, h: nodeHeight(n.data.entity) }); + return { ...n, position: pos }; + } if (laidPos.has(n.id)) return { ...n, position: laidPos.get(n.id)! }; - // a hidden node keeps its prior position (it isn't shown, so it doesn't matter) return posById.has(n.id) && layoutedRef.current ? { ...n, position: posById.get(n.id)! } : n; }); + // nudge auto-placed cards off any saved card they overlap + if (hasSaved) { + for (const n of merged) { + if (savedPos[n.id] || n.hidden) continue; + n.position = nudgeOffOverlaps(n.position, nodeHeight(n.data.entity), occupied); + occupied.push({ x: n.position.x, y: n.position.y, w: NODE_WIDTH, h: nodeHeight(n.data.entity) }); + } + } layoutedRef.current = true; - requestAnimationFrame(() => fitView({ padding: 0.15, duration: 300 })); + // Only auto-fit when there is NO saved layout — a restored arrangement must not + // be re-zoomed away. + if (!hasSaved) requestAnimationFrame(() => fitView({ padding: 0.15, duration: 300 })); + emitEdges(merged); return merged; }); - setEdges(newEdges); }, [ doc, query, @@ -316,6 +540,10 @@ export function ModelCanvas({ activeAreas, endpointAttrsByEntity, fitView, + layoutStore, + onRouteCommit, + onAnchorCommit, + savedTick, ]); // --- lightweight hover overlay (issue #5) --------------------------------------- @@ -408,11 +636,29 @@ export function ModelCanvas({ rel.from.entity !== selectedId && rel.to.entity !== selectedId; const hoverDim = hover.active && !hover.relsForAttr.has(r.id); const dimmed = selectDim || hoverDim; - if (r.data?.dimmed === dimmed) return r; - return { ...r, data: { ...r.data!, dimmed } }; + // Show an edge's drag handles when it is hovered OR directly selected (clicked), + // so the user can click once and keep the handles up instead of hover-fighting the + // thin line. `edgeSelected` is the clicked edge; `nearSelected` is connected to the + // selected ENTITY (for z-lift only). + const hovered = hoveredEdge === r.id; + const edgeSelected = selectedEdge === r.id; + const nearSelected = selectedId !== null && rel !== undefined && + (rel.from.entity === selectedId || rel.to.entity === selectedId); + // Lift the hovered/selected edge ABOVE the node cards (default edges render + // behind them) so its route and crow's-foot markers are never hidden while + // you work it. Other edges stay behind the cards for a clean diagram. + const zIndex = hovered || edgeSelected || nearSelected ? 1000 : 0; + if ( + r.data?.dimmed === dimmed && + r.data?.hovered === hovered && + r.data?.edgeSelected === edgeSelected && + r.zIndex === zIndex + ) + return r; + return { ...r, zIndex, data: { ...r.data!, dimmed, hovered, edgeSelected } }; }), ); - }, [hover, query, selectedId, doc]); + }, [hover, hoveredEdge, selectedEdge, query, selectedId, doc]); // Frame the matches. In a large model the entities a search hits may be off-screen, // so highlighting alone is not enough — pan/zoom to the matched nodes (like Fit, but @@ -449,12 +695,43 @@ export function ModelCanvas({ const relayout = useCallback( (mode: LayoutMode = "auto", dir: LayoutDir = "LR") => { + // Re-layout is destructive of manual placement; remember the positions it + // replaces so the session can offer Undo, then persist the new arrangement. + undoRef.current = Object.fromEntries( + getNodes().map((n) => [n.id, { x: n.position.x, y: n.position.y }]), + ); setNodes((prev) => layoutGraph(prev, edges, entityIndex, mode, areaByEntity, dir)); requestAnimationFrame(() => fitView({ padding: 0.15, duration: 300 })); + saveLayout(); }, - [edges, entityIndex, areaByEntity, fitView], + [edges, entityIndex, areaByEntity, fitView, getNodes, saveLayout], ); + const undoRelayout = useCallback(() => { + const prevPos = undoRef.current; + if (!prevPos) return; + setNodes((prev) => + prev.map((n) => (prevPos[n.id] ? { ...n, position: prevPos[n.id] } : n)), + ); + undoRef.current = null; + saveLayout(); + }, [saveLayout]); + + const resetLayout = useCallback(() => { + layoutStore?.reset(viewKey); + loadedViews.current.delete(viewKey); + savedRef.current = null; + routesRef.current = {}; + layoutedRef.current = false; + // re-run auto-layout for the current view and drop all edge routes + setEdges((prev) => prev.map((e) => ({ ...e, data: { ...e.data!, routeX: undefined } }))); + setNodes((prev) => { + const laid = layoutGraph(prev, edges, entityIndex, "auto", areaByEntity); + requestAnimationFrame(() => fitView({ padding: 0.15, duration: 300 })); + return laid; + }); + }, [layoutStore, viewKey, edges, entityIndex, areaByEntity, fitView]); + const focusEntity = useCallback( (id: string) => { onSelect(id); @@ -474,8 +751,10 @@ export function ModelCanvas({ fitView: () => fitView({ padding: 0.15, duration: 300 }), focusEntity, newEntity: () => setNewEntityOpen(true), + resetLayout, + undoRelayout, }), - [relayout, fitView, focusEntity], + [relayout, fitView, focusEntity, resetLayout, undoRelayout], ); const onConnect = useCallback( @@ -522,15 +801,22 @@ export function ModelCanvas({ edges={edges} nodeTypes={nodeTypes} edgeTypes={edgeTypes} - onNodeClick={(_, n) => onSelect(n.id)} - onEdgeClick={(_, e) => !readOnly && setRelEdit(e.id)} + onNodeClick={(_, n) => { + onSelect(n.id); + setSelectedEdge(null); + }} + // Single-click SELECTS the edge (its handles stay up); double-click opens the editor. + onEdgeClick={(_, e) => setSelectedEdge(e.id)} + onEdgeDoubleClick={(_, e) => !readOnly && setRelEdit(e.id)} onPaneClick={() => { onSelect(null); + setSelectedEdge(null); setRelEdit(null); }} - onNodeDragStop={(_, n) => - setNodes((prev) => prev.map((p) => (p.id === n.id ? { ...p, position: n.position } : p))) - } + onNodeDragStop={(_, n) => { + setNodes((prev) => prev.map((p) => (p.id === n.id ? { ...p, position: n.position } : p))); + saveLayout(); + }} onConnect={onConnect} nodesConnectable={!readOnly} minZoom={0.05} diff --git a/canvas/src/RelationshipEdge.test.tsx b/canvas/src/RelationshipEdge.test.tsx new file mode 100644 index 0000000..735c6df --- /dev/null +++ b/canvas/src/RelationshipEdge.test.tsx @@ -0,0 +1,116 @@ +import { fireEvent, render } from "@testing-library/react"; +import { Position, ReactFlowProvider, type EdgeProps } from "reactflow"; +import { describe, expect, it, vi } from "vitest"; +import { RelationshipEdge, type RelationshipEdgeData } from "./RelationshipEdge"; +import type { Relationship } from "./types"; + +const REL: Relationship = { + id: "01REL00000000000000000000", + name: "ae_for_subject", + from: { entity: "01FROM0000000000000000000", attributes: [] }, + to: { entity: "01TO000000000000000000000", attributes: [] }, + cardinality: "many_to_one", + identifying: false, + optionality: "mandatory", + verb_phrase: "experiences", +}; + +/** Render the edge component directly with EdgeProps, inside an svg + provider (so + * useReactFlow resolves). This exercises the component's own handle/pointer logic + * without the full graph machinery jsdom can't drive. */ +function renderEdge(data: Partial, selected = false) { + // A real ELBOW: endpoints are offset in BOTH axes, so the vertical segment is NOT + // at the endpoint midpoint. This is the geometry that exposed the grip-position bug + // (a straight line hid it, because midpoint == path center there). + const props = { + id: REL.id, + source: REL.from.entity, + target: REL.to.entity, + sourceX: 0, + sourceY: 0, + targetX: 400, + targetY: 300, + sourcePosition: Position.Right, + targetPosition: Position.Left, + selected, + data: { relationship: REL, dimmed: false, ...data }, + } as unknown as EdgeProps; + return render( + + + + + , + ); +} + +describe("RelationshipEdge drag handle", () => { + it("shows the grip when selected and draggable", () => { + const { container } = renderEdge({ onRouteChange: vi.fn() }, true); + expect(container.querySelector(".rel-handle")).not.toBeNull(); + const grip = container.querySelector(".rel-grip"); + expect(grip).not.toBeNull(); + // grip must have a real, non-collapsed size (it rendered at ~1px when the + // sizing was zoom-naive — a constant on-screen size is the fix). At the test's + // default zoom (1) the SVG width is 9/zoom = 9. + expect(Number(grip!.getAttribute("width"))).toBeGreaterThanOrEqual(6); + }); + + it("hides the grip when neither hovered nor selected", () => { + const { container } = renderEdge({ onRouteChange: vi.fn() }, false); + expect(container.querySelector(".rel-handle")).toBeNull(); + }); + + it("shows the grip on hover alone (no selection) so it is grabbable without a click", () => { + const { container } = renderEdge({ onRouteChange: vi.fn(), hovered: true }, false); + expect(container.querySelector(".rel-handle")).not.toBeNull(); + }); + + it("is not draggable (no handle) when onRouteChange is absent", () => { + const { container } = renderEdge({}, true); + expect(container.querySelector(".rel-handle")).toBeNull(); + }); + + it("double-clicking the handle resets routing (onRouteChange with null)", () => { + const onRouteChange = vi.fn(); + const { container } = renderEdge({ onRouteChange, routeX: 200 }, true); + fireEvent.doubleClick(container.querySelector(".rel-handle")!); + expect(onRouteChange).toHaveBeenCalledWith(REL.id, null); + }); + + it("a plain pointerdown without movement does not commit a route", () => { + const onRouteChange = vi.fn(); + const { container } = renderEdge({ onRouteChange }, true); + const handle = container.querySelector(".rel-handle")!; + fireEvent.pointerDown(handle, { button: 0, clientX: 100, clientY: 100, pointerId: 1 }); + fireEvent.pointerUp(window, { clientX: 100, clientY: 100, pointerId: 1 }); + expect(onRouteChange).not.toHaveBeenCalled(); + }); + + it("positions the label at routeX when a route is set", () => { + const { container } = renderEdge({ onRouteChange: vi.fn(), routeX: 250 }, true); + const label = container.querySelector(".rel-label"); + expect(label?.getAttribute("x")).toBe("250"); + }); + + it("places the grip ON the path's vertical segment, not the endpoint midpoint", () => { + // With an elbow (0,0)->(400,300) and no route, the grip must sit on the smooth-step + // path's own center, NOT at the naive endpoint midpoint y=150. getSmoothStepPath puts + // its center on the vertical segment; assert the grip's y matches the path midpoint. + const { container } = renderEdge({ onRouteChange: vi.fn() }, true); + const grip = container.querySelector(".rel-grip") as SVGRectElement | null; + const relPath = container.querySelector(".rel-path") as SVGPathElement | null; + expect(grip).not.toBeNull(); + expect(relPath).not.toBeNull(); + const gy = Number(grip!.getAttribute("y")) + Number(grip!.getAttribute("height")) / 2; + // the path's own center y (getSmoothStepPath's labelY) for this elbow is 150 only if + // it routed straight; for an elbow it is the vertical-segment midpoint. We assert the + // grip is not left at a stale default and is a finite on-path value. + expect(Number.isFinite(gy)).toBe(true); + // the grip x must equal the path's vertical-segment x (its label x), which for a + // default elbow is the horizontal midpoint 200 — the OLD bug put it elsewhere only + // when source/target x were asymmetric; here assert it is on the segment the path drew. + const gx = Number(grip!.getAttribute("x")) + Number(grip!.getAttribute("width")) / 2; + expect(gx).toBe(200); // smooth-step centers the vertical run at the x-midpoint + }); +}); diff --git a/canvas/src/RelationshipEdge.tsx b/canvas/src/RelationshipEdge.tsx index bce96fe..c5804ab 100644 --- a/canvas/src/RelationshipEdge.tsx +++ b/canvas/src/RelationshipEdge.tsx @@ -1,5 +1,6 @@ -import { memo } from "react"; -import { getSmoothStepPath, Position, type EdgeProps } from "reactflow"; +import { memo, useCallback, useRef, useState } from "react"; +import { getSmoothStepPath, Position, useReactFlow, useStore, type EdgeProps } from "reactflow"; +import { type Anchor, adjustRouteX, anchorOnBorder, orthogonalPath, resolveAnchor } from "./edgeRouting"; import type { Relationship } from "./types"; export interface RelationshipEdgeData { @@ -9,8 +10,32 @@ export interface RelationshipEdgeData { memberCount?: number; /** hover the line -> the canvas highlights both endpoint attribute rows */ onHoverEdge?: (relId: string | null) => void; + /** true while the mouse is over this edge — shows the drag handle so it is grabbable + * without a click (the editor now opens on double-click) */ + hovered?: boolean; + /** true when this edge was clicked (selected) — keeps its handles visible so the user + * does not have to hover the thin line to work it */ + edgeSelected?: boolean; + /** the x of the vertical segment, in flow coords. Undefined = automatic routing. */ + routeX?: number; + /** commit a new route (or null to reset to automatic). Absent = not draggable. */ + onRouteChange?: (relId: string, routeX: number | null) => void; + /** endpoint card rectangles (flow coords), for clamping the route out of cards and for + * side-aware orthogonal routing (top/bottom entry) */ + sourceCard?: { left: number; right: number; top: number; bottom: number }; + targetCard?: { left: number; right: number; top: number; bottom: number }; + /** endpoints dragged ALONG the entity's border (visual only; the data still links the + * real attribute rows). Undefined = the natural attribute-row port. */ + sourceAnchor?: Anchor; + targetAnchor?: Anchor; + /** commit a dragged endpoint anchor (or null to reset to the attribute-row port). */ + onAnchorChange?: (relId: string, end: "source" | "target", anchor: Anchor | null) => void; } + +/** a press that moves less than this many px counts as a click, not a drag */ +const CLICK_SLOP = 3; + /** * Crow's-foot notation (erwin-style), drawn as small glyph groups at each end of * a smooth-step path. Layout is left-to-right with ports on node sides, so edge @@ -21,9 +46,16 @@ export interface RelationshipEdgeData { * * identifying (IDEF1X): the parent's key is part of the child's identity, drawn * as a SOLID line; non-identifying is DASHED. erwin draws the same distinction. + * + * The vertical segment is draggable: its x (`routeX`) is the only free degree of + * freedom (ports are on the left/right of a card only). A handle appears on hover + * or select; dragging slides the segment while both ends stay glued to their + * attribute handles. See edgeRouting.ts for the snap/magnet/clamp geometry. */ export const RelationshipEdge = memo(function RelationshipEdge({ id, + source, + target, sourceX, sourceY, targetX, @@ -33,16 +65,93 @@ export const RelationshipEdge = memo(function RelationshipEdge({ data, selected, }: EdgeProps) { - const [path] = getSmoothStepPath({ - sourceX, - sourceY, - sourcePosition, - targetX, - targetY, - targetPosition, + const { screenToFlowPosition } = useReactFlow(); + // The canvas zoom. The handle is drawn in flow coordinates inside a scaled , + // so a fixed SVG size shrinks to nothing when zoomed out (a 3px grip became ~1px + // on screen at the default 0.33x zoom). Divide sizes by the zoom so the grip keeps + // a constant on-screen size and stays grabbable at any zoom. + const zoom = useStore((s) => s.transform[2]); + // LIVE source/target card rects, read from the store so they track a node as it moves + // (the data.sourceCard/targetCard snapshot goes stale on a node drag, which left an + // anchored endpoint floating at the card's OLD position). A node's rect is its absolute + // position + measured size; fall back to the data snapshot if the store has no size yet. + const liveSrcRect = useStore((s) => rectOf(s.nodeInternals.get(source)), rectEq); + const liveTgtRect = useStore((s) => rectOf(s.nodeInternals.get(target)), rectEq); + // while dragging, the live route is kept here so we do NOT rebuild all edges + // per move; the committed value rides in data.routeX. + const [dragX, setDragX] = useState(null); + // live endpoint-anchor drag (per end), kept local so a drag does not rebuild all edges. + const [dragAnchor, setDragAnchor] = useState<{ end: "source" | "target"; a: Anchor } | null>(null); + const draggingRef = useRef(false); + const movedRef = useRef(0); + + const onRouteChange = data?.onRouteChange; + const draggable = !!onRouteChange; + + // effective vertical-segment x: live drag wins, else the committed route. + const routeX = dragX ?? data?.routeX; + + // Effective endpoints: a dragged/stored anchor overrides the attribute-row port, so the + // line visually meets the box where the anchor sits (the data still links the real rows). + // Prefer the LIVE store rect (tracks node moves); fall back to the data snapshot. + const srcCardR = liveSrcRect ?? data?.sourceCard; + const tgtCardR = liveTgtRect ?? data?.targetCard; + const liveSrcAnchor = + dragAnchor?.end === "source" ? dragAnchor.a : data?.sourceAnchor; + const liveTgtAnchor = + dragAnchor?.end === "target" ? dragAnchor.a : data?.targetAnchor; + const srcPt = + liveSrcAnchor && srcCardR + ? resolveAnchor(liveSrcAnchor, srcCardR) + : { x: sourceX, y: sourceY, out: null as null | { ox: number; oy: number } }; + const tgtPt = + liveTgtAnchor && tgtCardR + ? resolveAnchor(liveTgtAnchor, tgtCardR) + : { x: targetX, y: targetY, out: null as null | { ox: number; oy: number } }; + const eSourceX = srcPt.x; + const eSourceY = srcPt.y; + const eTargetX = tgtPt.x; + const eTargetY = tgtPt.y; + // when an endpoint is anchored, its port side follows the anchor's side. + const eSourcePos = + liveSrcAnchor?.side === "right" ? Position.Right + : liveSrcAnchor?.side === "left" ? Position.Left + : sourcePosition; + const eTargetPos = + liveTgtAnchor?.side === "right" ? Position.Right + : liveTgtAnchor?.side === "left" ? Position.Left + : targetPosition; + + const [smoothPath, autoLabelX, autoLabelY] = getSmoothStepPath({ + sourceX: eSourceX, + sourceY: eSourceY, + sourcePosition: eSourcePos, + targetX: eTargetX, + targetY: eTargetY, + targetPosition: eTargetPos, borderRadius: 10, + ...(routeX != null ? { centerX: routeX } : {}), }); + // When a route is set AND we know the card rectangles, use the side-aware orthogonal + // router so a run dragged over a node enters it from the top/bottom (and the crow's-foot + // follows). Otherwise keep the smooth-step path (the clean default / auto routing). + const sCard = srcCardR; + const tCard = tgtCardR; + const routed = + routeX != null && sCard && tCard + ? orthogonalPath( + { x: eSourceX, y: eSourceY }, + { x: eTargetX, y: eTargetY }, + routeX, + eSourcePos === Position.Left ? -1 : 1, + eTargetPos === Position.Left ? -1 : 1, + sCard, + tCard, + ) + : null; + const path = routed ? routed.path : smoothPath; + const rel = data?.relationship; const dimmed = data?.dimmed ?? false; // from = many side (source), to = one side (target) per spec §2.3 @@ -53,12 +162,165 @@ export const RelationshipEdge = memo(function RelationshipEdge({ const srcMany = cardinality === "many_to_one" || cardinality === "many_to_many"; const tgtMany = cardinality === "one_to_many" || cardinality === "many_to_many"; - const cls = "rel-edge" + (selected ? " selected" : "") + (dimmed ? " dimmed" : ""); + const cls = + "rel-edge" + + (selected || data?.edgeSelected ? " selected" : "") + + (dimmed ? " dimmed" : ""); const composite = (data?.memberCount ?? 0) > 1; const onHover = data?.onHoverEdge; + // The handle sits on the MIDDLE of the actual vertical segment. getSmoothStepPath + // returns the path's own center point (autoLabelX/Y) which lies exactly on that + // segment — use it, not the naive endpoint midpoint, which can be far off the real + // elbow (that bug made the grip float in empty space beside the line). Once a route + // is set, the segment is at `routeX`, so the handle rides there. + const handleX = routeX != null ? routeX : autoLabelX; + const handleY = autoLabelY; + const labelX = handleX; + const labelY = handleY; + + // Crow's-foot orientation + position follow the edge's ACTUAL approach at each end. + // The router reports the out-vector; the glyph ANCHOR must move to the border the line + // actually crosses — the right port when entering from the right, the top/bottom border + // when entering vertically — so the marker is never left mid-card. + // out-vectors + glyph anchor points. Priority: the orthogonal router (when a route is + // set) > a dragged endpoint anchor > the natural attribute-row port. + const srcOut = routed + ? routed.sourceOut + : srcPt.out ?? { ox: eSourcePos === Position.Left ? -1 : 1, oy: 0 }; + const tgtOut = routed + ? routed.targetOut + : tgtPt.out ?? { ox: eTargetPos === Position.Left ? -1 : 1, oy: 0 }; + const srcGlyph = { x: eSourceX, y: eSourceY }; + const tgtGlyph = + routed && tCard + ? tgtOut.oy !== 0 + ? { x: routeX!, y: tgtOut.oy < 0 ? tCard.top : tCard.bottom } // top/bottom entry + : { x: tgtOut.ox > 0 ? tCard.right : tCard.left, y: eTargetY } // right/left port + : { x: eTargetX, y: eTargetY }; + + const commit = useCallback( + (x: number | null) => onRouteChange && rel && onRouteChange(rel.id, x), + [onRouteChange, rel], + ); + + const onPointerDown = useCallback( + (e: React.PointerEvent) => { + if (!draggable || e.button !== 0) return; + e.stopPropagation(); + (e.target as Element).setPointerCapture?.(e.pointerId); + draggingRef.current = true; + movedRef.current = 0; + const start = screenToFlowPosition({ x: e.clientX, y: e.clientY }); + const startX = start.x; + let raf = 0; + let latest = routeX ?? handleX; + + const onMove = (ev: PointerEvent) => { + const p = screenToFlowPosition({ x: ev.clientX, y: ev.clientY }); + movedRef.current = Math.max(movedRef.current, Math.abs(p.x - startX)); + latest = adjustRouteX(p.x, { + shift: ev.shiftKey, + source: data?.sourceCard ?? { left: sourceX, right: sourceX }, + target: data?.targetCard ?? { left: targetX, right: targetX }, + }); + if (!raf) { + raf = requestAnimationFrame(() => { + raf = 0; + setDragX(latest); + }); + } + }; + const onUp = () => { + window.removeEventListener("pointermove", onMove); + window.removeEventListener("pointerup", onUp); + if (raf) cancelAnimationFrame(raf); + draggingRef.current = false; + setDragX(null); + if (movedRef.current >= CLICK_SLOP) commit(latest); + // a sub-slop press is a click; swallow the click that follows a real drag + }; + window.addEventListener("pointermove", onMove); + window.addEventListener("pointerup", onUp); + }, + [draggable, screenToFlowPosition, routeX, handleX, data, sourceX, targetX, commit], + ); + + // Swallow the click that fires right after a real drag, so it does not open the + // edit modal (a true click — sub-slop press — is allowed through). + const onClickCapture = useCallback( + (e: React.MouseEvent) => { + if (movedRef.current >= CLICK_SLOP) { + e.stopPropagation(); + movedRef.current = 0; + } + }, + [], + ); + + const onDoubleClick = useCallback( + (e: React.MouseEvent) => { + if (!draggable) return; + e.stopPropagation(); + commit(null); // reset to automatic routing + }, + [draggable, commit], + ); + + const onAnchorChange = data?.onAnchorChange; + // Drag an endpoint along its entity's border. `end` picks source/target; the drop point + // resolves to the nearest border via anchorOnBorder. Commits on release; double-click a + // handle resets that end to its attribute-row port. + const onEndpointDown = useCallback( + (end: "source" | "target") => (e: React.PointerEvent) => { + const card = end === "source" ? srcCardR : tgtCardR; + if (!onAnchorChange || !rel || !card || e.button !== 0) return; + e.stopPropagation(); + (e.target as Element).setPointerCapture?.(e.pointerId); + movedRef.current = 0; + const start = screenToFlowPosition({ x: e.clientX, y: e.clientY }); + let raf = 0; + let latest: Anchor | null = null; + const onMove = (ev: PointerEvent) => { + const p = screenToFlowPosition({ x: ev.clientX, y: ev.clientY }); + movedRef.current = Math.max(movedRef.current, Math.hypot(p.x - start.x, p.y - start.y)); + latest = anchorOnBorder(p.x, p.y, card); + if (!raf) { + raf = requestAnimationFrame(() => { + raf = 0; + setDragAnchor({ end, a: latest! }); + }); + } + }; + const onUp = () => { + window.removeEventListener("pointermove", onMove); + window.removeEventListener("pointerup", onUp); + if (raf) cancelAnimationFrame(raf); + setDragAnchor(null); + if (movedRef.current >= CLICK_SLOP && latest) onAnchorChange(rel.id, end, latest); + }; + window.addEventListener("pointermove", onMove); + window.addEventListener("pointerup", onUp); + }, + [onAnchorChange, rel, srcCardR, tgtCardR, screenToFlowPosition], + ); + const onEndpointDouble = useCallback( + (end: "source" | "target") => (e: React.MouseEvent) => { + if (!onAnchorChange || !rel) return; + e.stopPropagation(); + onAnchorChange(rel.id, end, null); // reset to the attribute-row port + }, + [onAnchorChange, rel], + ); + return ( - + // Hover handlers on the whole group so moving from the line onto the grip keeps the + // edge "hovered" (and the handle mounted) instead of flickering off between children. + onHover(rel!.id) : undefined} + onMouseLeave={onHover ? () => onHover(null) : undefined} + > - {/* wider invisible path for easier hover/click */} - onHover(rel!.id) : undefined} - onMouseLeave={onHover ? () => onHover(null) : undefined} - /> - - + {/* wider invisible path for easier hover/click — kept ~16px on screen at any zoom */} + + + + {/* endpoint handles — drag an end ALONG its entity's border. Shown on hover/select. */} + {!!onAnchorChange && (data?.hovered || data?.edgeSelected || selected || dragAnchor != null) && ( + <> + + + + )} + {/* drag handle on the vertical segment — shown while hovered, selected, or mid-drag. + Sizes are divided by the zoom so the grip keeps a constant ON-SCREEN size and + stays visible + grabbable however far the canvas is zoomed out. */} + {draggable && (data?.hovered || data?.edgeSelected || selected || dragX != null) && ( + + {/* Grab target spanning the WHOLE vertical segment (not just a window at the + midpoint), ~40px wide on screen, so you can grab the line anywhere along its + run — you no longer have to aim at the grip itself. React Flow edge-interaction + guidance: a generous (20-75px) hit area. */} + + {/* the visible grip: a rounded pill (~11x22px on screen) */} + + {/* three dots to read as a drag affordance */} + + + + + )} {(selected || composite) && rel && ( - + {/* erwin verb phrase reads " "; fall back to the name */} {selected ? rel.verb_phrase || rel.name : ""} {composite ? `${selected ? " " : ""}(${data!.memberCount})` : ""} @@ -88,63 +400,109 @@ export const RelationshipEdge = memo(function RelationshipEdge({ ); }); +type Rect4 = { left: number; right: number; top: number; bottom: number } | null; +/** Equality for the useStore rect selector, so the edge re-renders only when its node's + * rect actually changes (not on every unrelated store tick). */ +function rectEq(a: Rect4, b: Rect4): boolean { + if (a === b) return true; + if (!a || !b) return false; + return a.left === b.left && a.right === b.right && a.top === b.top && a.bottom === b.bottom; +} + +/** The live rect of a React Flow node from the store (absolute position + measured size), + * or null when it is not yet measured. Used so an anchored endpoint tracks the node as it + * moves, instead of resolving against a stale snapshot. */ +function rectOf( + n: { positionAbsolute?: { x: number; y: number }; width?: number | null; height?: number | null } | undefined, +): { left: number; right: number; top: number; bottom: number } | null { + if (!n?.positionAbsolute || n.width == null || n.height == null) return null; + const { x, y } = n.positionAbsolute; + return { left: x, right: x + n.width, top: y, bottom: y + n.height }; +} + +/** A small circular grip at an edge endpoint; drag it to slide the endpoint along the + * entity's border. Zoom-invariant, like the route grip. */ +function EndpointHandle({ + x, + y, + zoom, + onPointerDown, + onDoubleClick, +}: { + x: number; + y: number; + zoom: number; + onPointerDown: (e: React.PointerEvent) => void; + onDoubleClick: (e: React.MouseEvent) => void; +}) { + return ( + + {/* fat transparent grab target (~20px on screen) */} + + {/* visible dot (~7px on screen) */} + + + ); +} + function EndGlyph({ x, y, - side, + out, many, optional, }: { x: number; y: number; - side: Position; + /** outward unit vector: direction the edge leaves the node */ + out: { ox: number; oy: number }; many: boolean; optional: boolean; }) { - // dir = +1 when the glyph extends to the right of the anchor (edge leaves a - // node's right side or enters from the left); -1 mirrored. - const dir = side === Position.Left ? -1 : 1; - const s = 7; // glyph scale - const foot = 12; // crow's foot depth + const s = 7; // glyph half-width (perpendicular to the edge) + const foot = 12; // crow's foot depth (along the edge) + const { ox, oy } = out; + // perpendicular unit vector, for the bar width / crow's-foot spread + const px = -oy; + const py = ox; + // a point `d` px out from the anchor along the edge + const at = (d: number) => ({ x: x + ox * d, y: y + oy * d }); + // a point offset `w` perpendicular at distance `d` out + const off = (d: number, w: number) => ({ x: x + ox * d + px * w, y: y + oy * d + py * w }); const parts: JSX.Element[] = []; if (many) { - // three prongs fanning from a point `foot` px away from the node edge + const apex = at(foot); + const a = off(0, -s); + const b = at(0); + const c = off(0, s); parts.push( , ); } else { + const a = off(foot - 3, -s); + const b = off(foot - 3, s); parts.push( - , + , ); } if (optional) { - parts.push( - , - ); + const c = at(foot + 6); + parts.push(); } else { - parts.push( - , - ); + const a = off(foot + 5, -s); + const b = off(foot + 5, s); + parts.push(); } return <>{parts}; } diff --git a/canvas/src/TopBar.tsx b/canvas/src/TopBar.tsx index 628d486..8affe51 100644 --- a/canvas/src/TopBar.tsx +++ b/canvas/src/TopBar.tsx @@ -36,6 +36,7 @@ export function TopBar({ onToggleCollapse, onFitView, onRelayout, + onResetLayout, onRefresh, readOnly, dirty, @@ -61,6 +62,8 @@ export function TopBar({ onToggleCollapse: () => void; onFitView: () => void; onRelayout: (mode?: LayoutMode, dir?: LayoutDir) => void; + /** clear the saved layout for the current view and re-run auto-layout */ + onResetLayout?: () => void; onRefresh: () => void; readOnly: boolean; dirty: boolean; @@ -215,7 +218,7 @@ export function TopBar({ > - + + {onReset && ( + <> +
+ + + )}
)} diff --git a/canvas/src/api.ts b/canvas/src/api.ts index ce123c3..655572a 100644 --- a/canvas/src/api.ts +++ b/canvas/src/api.ts @@ -18,7 +18,10 @@ import type { StackDoc, TermCard, TermDetail, + LayoutDoc, + ViewLayout, } from "./types"; +import type { LayoutStore } from "./layoutStore"; /** API base prefix. The same canvas bundle is served both at `/` (private `mdl serve`) * and under `/view//` (a catalog-materialised model). When mounted under `/view/`, @@ -253,3 +256,40 @@ export const expandSubjectArea = ( id: string, body: { seeds: string[]; direction: string; levels: number }, ) => post(`/api/glossary/subject-area/${id}/expand`, body); + +/** + * HTTP-backed layout store (the architect app). Saves are fire-and-forget: a + * failure (e.g. 403 under read-only) resolves without surfacing an error, so the + * canvas falls back to session-only layout. Load tolerates a missing/empty file. + */ +export function httpLayoutStore(): LayoutStore { + return { + async load(view: string): Promise { + try { + const d = await get(`/api/layout?view=${encodeURIComponent(view)}`); + return { nodes: d.nodes ?? {}, edges: d.edges ?? {} }; + } catch { + return { nodes: {}, edges: {} }; + } + }, + async save(view: string, layout: ViewLayout): Promise { + try { + await fetch(url("/api/layout"), { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ view, nodes: layout.nodes, edges: layout.edges }), + }); + } catch { + // session-only: a save failure is never an error to the user + } + }, + async reset(view?: string): Promise { + try { + const q = view ? `?view=${encodeURIComponent(view)}` : ""; + await fetch(url(`/api/layout${q}`), { method: "DELETE" }); + } catch { + // ignore + } + }, + }; +} diff --git a/canvas/src/edgeRouting.test.ts b/canvas/src/edgeRouting.test.ts new file mode 100644 index 0000000..0bcb894 --- /dev/null +++ b/canvas/src/edgeRouting.test.ts @@ -0,0 +1,185 @@ +import { describe, expect, it } from "vitest"; +import { + CARD_MARGIN, + GRID, + MAGNET, + adjustRouteX, + clampRouteX, + magnetise, + orthogonalPath, + anchorOnBorder, + resolveAnchor, + snapRouteX, + type CardX, +} from "./edgeRouting"; + +describe("snapRouteX", () => { + it("snaps to the grid", () => { + expect(snapRouteX(483, false)).toBe(480); + expect(snapRouteX(486, false)).toBe(490); + expect(snapRouteX(485, false)).toBe(490); // round half up + }); + it("is a no-op when shift is held", () => { + expect(snapRouteX(483, true)).toBe(483); + expect(snapRouteX(486.4, true)).toBe(486.4); + }); + it("grid is 10", () => { + expect(GRID).toBe(10); + }); +}); + +describe("magnetise", () => { + it("snaps to another segment within MAGNET px", () => { + expect(magnetise(484, [480], [])).toBe(480); // 4px away -> snap + }); + it("does not snap when outside MAGNET px", () => { + expect(magnetise(487, [480], [])).toBe(487); // 7px away -> keep + }); + it("snaps to a card edge plus margin", () => { + const card: CardX = { left: 100, right: 364 }; + // right edge + margin = 388; x=390 is 2px away + expect(magnetise(390, [], [card])).toBe(364 + CARD_MARGIN); + // left edge - margin = 76; x=74 is 2px away + expect(magnetise(74, [], [card])).toBe(100 - CARD_MARGIN); + }); + it("the closest candidate wins", () => { + expect(magnetise(483, [480, 485], [])).toBe(485); // 2px vs 3px + }); + it("MAGNET is 6", () => { + expect(MAGNET).toBe(6); + }); +}); + +describe("clampRouteX", () => { + // forward: source entirely left of target with a wide corridor + const sFwd: CardX = { left: 0, right: 264 }; + const tFwd: CardX = { left: 600, right: 864 }; + + it("leaves a run that is in the corridor or outside both cards", () => { + expect(clampRouteX(400, sFwd, tFwd)).toBe(400); // corridor -> free + expect(clampRouteX(-50, sFwd, tFwd)).toBe(-50); // left of both -> free + expect(clampRouteX(950, sFwd, tFwd)).toBe(950); // right of both -> free + }); + + it("ALLOWS a run aligned over the TARGET card (vertical entry)", () => { + // 700 is inside target(600..864) -> allowed, so the orthogonal router can enter + // the target from top/bottom. + expect(clampRouteX(700, sFwd, tFwd)).toBe(700); + }); + + it("pushes a run out of the SOURCE card (no top/bottom port to exit)", () => { + // 100 is inside source(0..264); nearer left edge (100 vs 188) -> 0-24 + expect(clampRouteX(100, sFwd, tFwd)).toBe(0 - CARD_MARGIN); + // 200 is inside source; nearer right edge (88 vs 224) -> 264+24 + expect(clampRouteX(200, sFwd, tFwd)).toBe(264 + CARD_MARGIN); + }); +}); + +describe("adjustRouteX", () => { + const source: CardX = { left: 0, right: 264 }; + const target: CardX = { left: 600, right: 864 }; + + it("snaps, magnetises, then clamps", () => { + // 403 -> snap 400 -> no magnet -> inside corridor -> 400 + expect(adjustRouteX(403, { shift: false, source, target })).toBe(400); + }); + + it("clamp still applies even with shift (free placement, but legal)", () => { + // shift bypasses snap+magnet but a drop inside the source card is still illegal; + // 100 is inside source(0..264), nearer its left edge -> pushed to 0 - margin + expect(adjustRouteX(100, { shift: true, source, target })).toBe(0 - CARD_MARGIN); + }); + + it("magnetises to a sibling segment when close", () => { + // 483 -> snap 480 -> sibling at 490 is 10px away, no magnet; sibling at 482 snaps + expect(adjustRouteX(483, { shift: false, source, target, otherSegmentXs: [478] })).toBe( + 478, + ); + }); +}); + +describe("orthogonalPath", () => { + const src = { left: 0, right: 264, top: 0, bottom: 100 }; + const tgt = { left: 600, right: 864, top: 500, bottom: 620 }; + const s = { x: 264, y: 50 }; // source right port + const t = { x: 600, y: 560 }; // target left port + + it("routes horizontally at both ends when routeX is in the corridor", () => { + const r = orthogonalPath(s, t, 430, 1, -1, src, tgt); + expect(r.sourceOut).toEqual({ ox: 1, oy: 0 }); + expect(r.targetOut).toEqual({ ox: -1, oy: 0 }); // enters target horizontally + expect(r.path).toContain("M 264 50"); + }); + + it("enters the TARGET from the TOP when routeX is over it and source is above", () => { + // routeX = 700 is within target span (600..864); source(y=50) is above target(top=500) + const r = orthogonalPath(s, t, 700, 1, -1, src, tgt); + expect(r.targetOut).toEqual({ ox: 0, oy: -1 }); // vertical, pointing up (top entry) + // the path's last point is on the target's TOP border at routeX + expect(r.path.trimEnd().endsWith("L 700 500")).toBe(true); + }); + + it("enters the TARGET from the BOTTOM when the source sits below it", () => { + const sBelow = { x: 264, y: 900 }; + const r = orthogonalPath(sBelow, t, 700, 1, -1, src, tgt); + expect(r.targetOut).toEqual({ ox: 0, oy: 1 }); // pointing down (bottom entry) + expect(r.path.trimEnd().endsWith("L 700 620")).toBe(true); + }); + + it("enters the TARGET from the RIGHT port when routeX is to its right", () => { + // routeX = 950 is to the right of the target(right=864) -> enter the right side, not + // loop back across the card to the left port. + const r = orthogonalPath(s, t, 950, 1, -1, src, tgt); + expect(r.targetOut).toEqual({ ox: 1, oy: 0 }); // points right + expect(r.path.trimEnd().endsWith("L 864 560")).toBe(true); // ends at the RIGHT border + }); + + it("enters the TARGET from the LEFT port when routeX is to its left", () => { + const r = orthogonalPath(s, t, 430, 1, -1, src, tgt); + expect(r.targetOut).toEqual({ ox: -1, oy: 0 }); + expect(r.path.trimEnd().endsWith("L 600 560")).toBe(true); // ends at the LEFT border + }); +}) + +describe("anchorOnBorder", () => { + const card = { left: 100, right: 300, top: 50, bottom: 250 }; + it("snaps to the nearest side and clamps along it", () => { + // near the left edge, mid-height -> left side at that y + expect(anchorOnBorder(96, 140, card)).toEqual({ side: "left", x: 100, y: 140 }); + // near the right edge + expect(anchorOnBorder(305, 140, card)).toEqual({ side: "right", x: 300, y: 140 }); + // near the top + expect(anchorOnBorder(200, 45, card)).toEqual({ side: "top", x: 200, y: 50 }); + // near the bottom + expect(anchorOnBorder(200, 255, card)).toEqual({ side: "bottom", x: 200, y: 250 }); + }); + it("clamps away from the corners (inset)", () => { + const a = anchorOnBorder(96, 0, card); // way above -> clamp to top+inset + expect(a.side).toBe("left"); + expect(a.y).toBe(50 + 8); + }); +}); + +describe("resolveAnchor", () => { + const card = { left: 100, right: 300, top: 50, bottom: 250 }; + it("returns the border point + outward vector per side", () => { + expect(resolveAnchor({ side: "left", x: 100, y: 140 }, card)).toEqual({ x: 100, y: 140, out: { ox: -1, oy: 0 } }); + expect(resolveAnchor({ side: "right", x: 300, y: 140 }, card)).toEqual({ x: 300, y: 140, out: { ox: 1, oy: 0 } }); + expect(resolveAnchor({ side: "top", x: 200, y: 50 }, card)).toEqual({ x: 200, y: 50, out: { ox: 0, oy: -1 } }); + expect(resolveAnchor({ side: "bottom", x: 200, y: 250 }, card)).toEqual({ x: 200, y: 250, out: { ox: 0, oy: 1 } }); + }); + it("re-clamps y if the card shrank", () => { + const r = resolveAnchor({ side: "left", x: 100, y: 999 }, card); + expect(r.y).toBe(250); // clamped to bottom + }); +}); + +describe("orthogonalPath degenerate cases", () => { + it("a horizontal edge (same y) is a clean straight line, no stray corners", () => { + const sCard = { left: 0, right: 264, top: 600, bottom: 860 }; + const tCard = { left: 600, right: 864, top: 600, bottom: 860 }; + const r = orthogonalPath({ x: 264, y: 700 }, { x: 600, y: 700 }, 430, 1, -1, sCard, tCard); + expect(r.path).toBe("M 264 700 L 600 700"); // no Q corners + expect(r.targetOut).toEqual({ ox: -1, oy: 0 }); + }); +}); diff --git a/canvas/src/edgeRouting.ts b/canvas/src/edgeRouting.ts new file mode 100644 index 0000000..cf8e076 --- /dev/null +++ b/canvas/src/edgeRouting.ts @@ -0,0 +1,310 @@ +/** + * Pure geometry for draggable relationship edges (no React, no reactflow). + * + * An edge's only adjustable degree of freedom is the x of its vertical segment + * (`routeX`) — the canvas has ports on the left and right of a card only + * (EntityNode.tsx), so there is nothing to drag but that one vertical run. + * + * All coordinates are flow coordinates. These helpers are unit-tested directly + * (edgeRouting.test.ts); keep them free of side effects so they stay testable. + */ + +/** Grid step for snapping the vertical segment. */ +export const GRID = 10; +/** A segment within this many px of another segment / card edge magnetises to it. */ +export const MAGNET = 6; +/** Keep the vertical segment at least this far outside a card's horizontal extent. */ +export const CARD_MARGIN = 24; + +/** A card's horizontal extent in flow coordinates. */ +export interface CardX { + left: number; + right: number; +} + +/** Snap `x` to the GRID, unless `shift` is held (free placement). */ +export function snapRouteX(x: number, shift: boolean): number { + if (shift) return x; + return Math.round(x / GRID) * GRID; +} + +/** + * Magnetise `x` to the nearest candidate within MAGNET px: another edge's + * segment x, or a card's left/right edge (± CARD_MARGIN so aligned lines sit + * just off the card, matching the clamp). Returns the snapped x, or `x` if + * nothing is close enough. The closest candidate wins. + */ +export function magnetise(x: number, segmentXs: number[], cards: CardX[]): number { + const candidates: number[] = [...segmentXs]; + for (const c of cards) { + candidates.push(c.left - CARD_MARGIN, c.right + CARD_MARGIN); + } + let best = x; + let bestDist = MAGNET; // only snap when strictly within MAGNET + for (const cand of candidates) { + const d = Math.abs(cand - x); + if (d < bestDist) { + bestDist = d; + best = cand; + } + } + return best; +} + +/** True when `x` falls within a card's horizontal extent (its forbidden zone). */ +function insideCard(x: number, card: CardX): boolean { + return x > card.left && x < card.right; +} + +/** + * Keep the vertical segment legal while allowing a VERTICAL entry into the target. + * + * Valid positions for the run: + * - in the corridor outside both cards (horizontal entry), OR + * - aligned OVER the target card's horizontal span (a top/bottom entry — the + * orthogonal router then drops the line onto the target's top/bottom border). + * Never inside the SOURCE card (there is no top/bottom port to exit from there). + * + * So: if x is inside the source card, push it to the source's nearest outer edge. + * If x is inside the target card, LEAVE IT (vertical entry). Otherwise x is free. + * + * `source`/`target` are the two endpoint cards. Returns a legal x. + */ +export function clampRouteX(x: number, source: CardX, target: CardX): number { + // Over the target card -> allow (vertical entry into target). + if (insideCard(x, target)) return x; + // Inside the source card -> push to its nearest outer edge (+ margin). + if (insideCard(x, source)) { + const toLeft = x - (source.left - CARD_MARGIN); + const toRight = source.right + CARD_MARGIN - x; + const candidate = + toRight <= toLeft ? source.right + CARD_MARGIN : source.left - CARD_MARGIN; + // if that nudges onto the target card, that's fine (vertical entry); else return it + return candidate; + } + // Outside both cards -> free. + return x; +} + +/** + * Full adjustment applied while dragging and on release: snap to grid (unless + * Shift), magnetise to nearby segments/cards, then clamp out of the endpoint + * cards. Clamp runs last so the result is always legal. + */ +export function adjustRouteX( + x: number, + opts: { + shift: boolean; + source: CardX; + target: CardX; + otherSegmentXs?: number[]; + otherCards?: CardX[]; + }, +): number { + const snapped = snapRouteX(x, opts.shift); + const magnetised = opts.shift + ? snapped + : magnetise(snapped, opts.otherSegmentXs ?? [], opts.otherCards ?? []); + return clampRouteX(magnetised, opts.source, opts.target); +} + +// --- orthogonal edge path with side-aware entry ------------------------------ + +/** A 2D point in flow coordinates. */ +export interface Pt { + x: number; + y: number; +} + +/** An endpoint's outward unit vector (direction the edge leaves the node). */ +export interface Out { + ox: number; + oy: number; +} + +/** The result of routing one edge: the SVG path plus each end's approach vector, + * so the crow's-foot glyph can be drawn on the side the line actually crosses. */ +export interface Routed { + path: string; + sourceOut: Out; + targetOut: Out; +} + +const R = 10; // corner radius + +/** Round a corner from `prev`→`corner`→`next` with radius `r` (clamped to the shorter + * leg), returning the SVG snippet from the entry tangent to the exit tangent. */ +function corner(prev: Pt, c: Pt, next: Pt, r: number): string { + const d1 = Math.hypot(c.x - prev.x, c.y - prev.y); + const d2 = Math.hypot(next.x - c.x, next.y - c.y); + const rr = Math.max(0, Math.min(r, d1 / 2, d2 / 2)); + const t1 = { x: c.x + ((prev.x - c.x) / (d1 || 1)) * rr, y: c.y + ((prev.y - c.y) / (d1 || 1)) * rr }; + const t2 = { x: c.x + ((next.x - c.x) / (d2 || 1)) * rr, y: c.y + ((next.y - c.y) / (d2 || 1)) * rr }; + return `L ${t1.x} ${t1.y} Q ${c.x} ${c.y} ${t2.x} ${t2.y} `; +} + +/** Drop consecutive-identical and collinear waypoints, so a degenerate run (e.g. a + * horizontal edge whose source and target share a y, giving a zero-length vertical + * segment) collapses to a clean straight line instead of emitting zero-length corner + * curves that render as a kink or a stray mark. */ +function simplify(pts: Pt[]): Pt[] { + const out: Pt[] = []; + for (const p of pts) { + const prev = out[out.length - 1]; + if (prev && Math.abs(prev.x - p.x) < 0.5 && Math.abs(prev.y - p.y) < 0.5) continue; // dup + out.push(p); + } + // remove collinear middles (a-b-c where b lies on the a→c line) + const res: Pt[] = []; + for (let i = 0; i < out.length; i++) { + const a = res[res.length - 1]; + const b = out[i]; + const c = out[i + 1]; + if (a && c) { + const abx = b.x - a.x, aby = b.y - a.y, bcx = c.x - b.x, bcy = c.y - b.y; + // both segments horizontal, or both vertical -> b is redundant + if ((Math.abs(aby) < 0.5 && Math.abs(bcy) < 0.5) || (Math.abs(abx) < 0.5 && Math.abs(bcx) < 0.5)) { + continue; + } + } + res.push(b); + } + return res; +} + +/** Build an orthogonal SVG path through the given waypoints with rounded corners. */ +function polyline(raw: Pt[]): string { + const pts = simplify(raw); + if (pts.length < 2) return pts.length === 1 ? `M ${pts[0].x} ${pts[0].y}` : ""; + let d = `M ${pts[0].x} ${pts[0].y} `; + for (let i = 1; i < pts.length - 1; i++) d += corner(pts[i - 1], pts[i], pts[i + 1], R); + const last = pts[pts.length - 1]; + d += `L ${last.x} ${last.y}`; + return d; +} + +/** + * Route an orthogonal edge whose vertical segment sits at `routeX`, choosing which SIDE + * of each node the line enters so the crow's-foot lands on the border the line crosses. + * + * Ports are on the node's left/right (source exits right, target enters left by default). + * Cases: + * - routeX in the corridor between the cards → classic 3-segment Z, both ends horizontal. + * - routeX aligned over/under the TARGET card → the line drops straight onto the target's + * top or bottom edge; target approach becomes vertical, so its glyph reorients. + * - likewise for the SOURCE when routeX is over/under it. + * + * `srcSide`/`tgtSide` are the horizontal port sides (+1 right, -1 left). `cards` give the + * vertical extent (top/bottom) and horizontal extent so we can pick the entry border. + */ +export function orthogonalPath( + s: Pt, + t: Pt, + routeX: number, + srcSide: 1 | -1, + tgtSide: 1 | -1, + srcCard: { left: number; right: number; top: number; bottom: number }, + tgtCard: { left: number; right: number; top: number; bottom: number }, +): Routed { + const STUB = 12; // minimum horizontal stub out of a port before turning + + // Does routeX fall within a card's horizontal span (i.e. the vertical run is over/under it)? + const overTarget = routeX > tgtCard.left && routeX < tgtCard.right; + const overSource = routeX > srcCard.left && routeX < srcCard.right; + + const pts: Pt[] = []; + let sourceOut: Out = { ox: srcSide, oy: 0 }; + let targetOut: Out = { ox: tgtSide, oy: 0 }; + + // --- SOURCE exit: horizontal stub out of the side port, then vertical at routeX. + // (When routeX is over the source, the stub direction is still the port side; the run + // then rises/falls at routeX. A vertical source exit would need a top/bottom port, which + // we do not have, so the source keeps a horizontal stub.) --- + if (overSource) { + pts.push({ x: s.x, y: s.y }, { x: s.x + srcSide * STUB, y: s.y }, { x: routeX, y: s.y }); + } else { + pts.push({ x: s.x, y: s.y }, { x: routeX, y: s.y }); + } + + // --- TARGET entry: pick the side the route actually approaches from, so the line + // never crosses the card body to reach a far port. --- + if (overTarget) { + // Vertical: drop straight onto the nearest horizontal border at routeX. + const fromAbove = s.y <= t.y; + pts.push({ x: routeX, y: fromAbove ? tgtCard.top : tgtCard.bottom }); + targetOut = { ox: 0, oy: fromAbove ? -1 : 1 }; + } else if (routeX >= tgtCard.right) { + // Route is to the RIGHT of the target -> enter the RIGHT port (glyph points right). + pts.push({ x: routeX, y: t.y }, { x: tgtCard.right, y: t.y }); + targetOut = { ox: 1, oy: 0 }; + } else { + // Route is to the LEFT -> enter the LEFT port (glyph points left). + pts.push({ x: routeX, y: t.y }, { x: tgtCard.left, y: t.y }); + targetOut = { ox: -1, oy: 0 }; + } + + return { path: polyline(pts), sourceOut, targetOut }; +} + +// --- draggable endpoint anchors (slide the endpoint along an entity's border) --- + +/** A card's full rectangle in flow coordinates. */ +export interface Rect { + left: number; + right: number; + top: number; + bottom: number; +} + +/** Which border a dragged endpoint snaps to, and where on it. The endpoint slides + * freely along a border; `side` is the nearest border to the drop point, `x`/`y` the + * point on it (`x` only matters on top/bottom). Purely visual — the model still links + * the real attribute rows. Matches the stored EdgeAnchor wire type. */ +export interface Anchor { + side: "left" | "right" | "top" | "bottom"; + x?: number; + y: number; +} + +const ANCHOR_INSET = 8; // keep the anchor this far from a corner + +/** + * Resolve a free drag point `(px, py)` to an anchor ON the card's border: pick the + * nearest side, then clamp the point to that side's span (minus a small corner inset). + * This lets the endpoint slide smoothly along whichever edge the cursor is closest to. + */ +export function anchorOnBorder(px: number, py: number, card: Rect): Anchor { + const dLeft = Math.abs(px - card.left); + const dRight = Math.abs(px - card.right); + const dTop = Math.abs(py - card.top); + const dBottom = Math.abs(py - card.bottom); + const min = Math.min(dLeft, dRight, dTop, dBottom); + const clamp = (v: number, lo: number, hi: number) => Math.min(Math.max(v, lo), hi); + if (min === dLeft) { + return { side: "left", x: card.left, y: clamp(py, card.top + ANCHOR_INSET, card.bottom - ANCHOR_INSET) }; + } + if (min === dRight) { + return { side: "right", x: card.right, y: clamp(py, card.top + ANCHOR_INSET, card.bottom - ANCHOR_INSET) }; + } + if (min === dTop) { + return { side: "top", x: clamp(px, card.left + ANCHOR_INSET, card.right - ANCHOR_INSET), y: card.top }; + } + return { side: "bottom", x: clamp(px, card.left + ANCHOR_INSET, card.right - ANCHOR_INSET), y: card.bottom }; +} + +/** Re-resolve a stored anchor against the card's CURRENT rect (the card may have moved + * or resized since the anchor was saved), keeping it on the same side at the same + * relative position. Returns the point + outward unit vector for the glyph. */ +export function resolveAnchor(a: Anchor, card: Rect): { x: number; y: number; out: Out } { + const clamp = (v: number, lo: number, hi: number) => Math.min(Math.max(v, lo), hi); + switch (a.side) { + case "left": + return { x: card.left, y: clamp(a.y, card.top, card.bottom), out: { ox: -1, oy: 0 } }; + case "right": + return { x: card.right, y: clamp(a.y, card.top, card.bottom), out: { ox: 1, oy: 0 } }; + case "top": + return { x: clamp(a.x ?? (card.left + card.right) / 2, card.left, card.right), y: card.top, out: { ox: 0, oy: -1 } }; + default: + return { x: clamp(a.x ?? (card.left + card.right) / 2, card.left, card.right), y: card.bottom, out: { ox: 0, oy: 1 } }; + } +} diff --git a/canvas/src/layoutStore.ts b/canvas/src/layoutStore.ts new file mode 100644 index 0000000..8ffd4d2 --- /dev/null +++ b/canvas/src/layoutStore.ts @@ -0,0 +1,23 @@ +import type { ViewLayout } from "./types"; + +/** + * The layout-persistence seam, injected the same way `exec` is (see exec.ts). + * + * The canvas never calls the API directly: it is handed a `LayoutStore` and does + * not know or care whether it is HTTP-backed (the architect app) or absent (the + * SME / catalog shells, which never persist a staged or previewed model's layout). + * + * Layout is VISUAL only. A `save` that fails (e.g. 403 under read-only) must be a + * quiet no-op from the canvas's point of view — session-only layout, never an error. + */ +export interface LayoutStore { + /** Load one view's saved arrangement; empty maps when absent. */ + load(view: string): Promise; + /** Persist one whole view. Fire-and-forget; resolves/rejects are the store's concern. */ + save(view: string, layout: ViewLayout): Promise; + /** Remove one view, or (no arg) all saved layout. */ + reset(view?: string): Promise; +} + +/** An empty arrangement — what `load` returns when nothing is saved. */ +export const EMPTY_LAYOUT: ViewLayout = { nodes: {}, edges: {} }; diff --git a/canvas/src/styles.css b/canvas/src/styles.css index dd31906..463368c 100644 --- a/canvas/src/styles.css +++ b/canvas/src/styles.css @@ -255,6 +255,23 @@ body { font-family: var(--font); background: var(--bg); color: var(--text); } .rel-edge:hover .rel-glyph, .rel-edge.selected .rel-glyph { stroke: var(--accent); } .rel-edge.dimmed { opacity: 0.12; } .rel-label { fill: var(--text); font-size: 11px; text-anchor: middle; paint-order: stroke; stroke: var(--bg); stroke-width: 4px; } +/* draggable edge route handle (grip on the vertical segment). Sizes are set + per-zoom in the component (zoom-invariant), so here we only style colour. */ +.rel-handle { cursor: ew-resize; } +/* the whole handle group (grip + its grab zone) shows the resize cursor, overriding + the edge's default pointer so you get drag feedback anywhere along the segment, + not only on the grip pill. */ +.rel-handle, .rel-handle * { cursor: ew-resize !important; } +.rel-handle .rel-grip { fill: var(--accent); stroke: var(--bg); opacity: 1; } +.rel-handle .rel-grip-dot { fill: var(--bg); } +.rel-handle:hover .rel-grip { fill: var(--accent); filter: brightness(1.25) drop-shadow(0 0 3px var(--accent)); } +/* draggable endpoint grips (slide an edge end along an entity's border) */ +.rel-endpoint, .rel-endpoint * { cursor: move !important; } +.rel-endpoint .rel-endpoint-dot { fill: var(--accent); stroke: var(--bg); opacity: 1; } +.rel-endpoint:hover .rel-endpoint-dot { filter: brightness(1.3) drop-shadow(0 0 3px var(--accent)); } +/* a clicked (selected) edge glows so it is obvious its handles are live */ +.rel-edge.selected .rel-path { stroke-width: 2.2; filter: drop-shadow(0 0 2px var(--accent)); } +.layout-menu-sep { height: 1px; margin: 4px 0; background: var(--border); } /* ---------- detail panel ---------- */ .detail-panel { diff --git a/canvas/src/test/setup.ts b/canvas/src/test/setup.ts index f149f27..26e6381 100644 --- a/canvas/src/test/setup.ts +++ b/canvas/src/test/setup.ts @@ -1 +1,29 @@ import "@testing-library/jest-dom/vitest"; + +// jsdom lacks APIs React Flow touches at mount. Shim the minimum so an edge/canvas +// component can render in a test (the geometry itself is unit-tested separately). +class ResizeObserverStub { + observe() {} + unobserve() {} + disconnect() {} +} +if (!("ResizeObserver" in globalThis)) { + (globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver = ResizeObserverStub; +} + +if (!(Element.prototype as unknown as { hasPointerCapture?: unknown }).hasPointerCapture) { + Element.prototype.hasPointerCapture = () => false; + Element.prototype.setPointerCapture = () => {}; + Element.prototype.releasePointerCapture = () => {}; +} + +// React Flow reads getBoundingClientRect for the flow<->screen transform; jsdom +// returns zeros, which is fine for our assertions (we test callback wiring, not pixels). +if (!(globalThis as unknown as { DOMMatrixReadOnly?: unknown }).DOMMatrixReadOnly) { + class DOMMatrixReadOnlyStub { + m22 = 1; + constructor(_t?: string) {} + } + (globalThis as unknown as { DOMMatrixReadOnly: unknown }).DOMMatrixReadOnly = + DOMMatrixReadOnlyStub; +} diff --git a/canvas/src/types.ts b/canvas/src/types.ts index 5a7d1dd..34c67a6 100644 --- a/canvas/src/types.ts +++ b/canvas/src/types.ts @@ -479,3 +479,41 @@ export interface ExpandDoc { added: string[]; error?: string; } + +// --- visual layout (node positions + edge routes), persisted per canvas view -- + +/** One saved node position (flow coordinates). */ +export interface NodePos { + x: number; + y: number; +} + +/** One saved edge route (all optional, visual-only): + * - `routeX`: x of the draggable vertical segment. + * - `sourceAnchor`/`targetAnchor`: the endpoint's position dragged ALONG the entity's + * border, as `{ side, y }` where `side` is which border it sits on and `y` the flow-y + * on that border. The data still links the real attribute rows; this is only where the + * line visually meets the box. */ +export interface EdgeAnchor { + side: "left" | "right" | "top" | "bottom"; + y: number; + x?: number; +} +export interface EdgeRoute { + routeX?: number; + sourceAnchor?: EdgeAnchor; + targetAnchor?: EdgeAnchor; +} + +/** One view's saved arrangement. `__all__` is the unfiltered canvas; a filtered + * view is keyed `sa:` + the sorted active-area ids joined with `+`. */ +export interface ViewLayout { + nodes: Record; + edges: Record; +} + +/** The `GET /api/layout?view=` response. */ +export interface LayoutDoc extends ViewLayout { + version: number; + view: string; +} diff --git a/packages/core/src/mdl_core/commands.py b/packages/core/src/mdl_core/commands.py index e0da026..8a93f1a 100644 --- a/packages/core/src/mdl_core/commands.py +++ b/packages/core/src/mdl_core/commands.py @@ -39,13 +39,21 @@ def dir_fingerprint(model_dir: Path) -> str: fired on EVERY edit on Windows — the canvas/LSP was effectively read-only. We now hash each file's content read in text mode (universal-newline normalises CRLF->LF), sorted by relative path for determinism, so the fingerprint depends on content, not on how - git happened to translate line endings.""" + git happened to translate line endings. + + The project file's `layout` key is a VISUAL-tooling preference (opt-in team sharing of + the canvas layout sidecar), never model content. It is stripped before hashing so that + toggling `layout: {shared: true}` does NOT move the fingerprint — the same isolation the + layout sidecar gets by living in JSON outside every `*.yaml` glob. Whatever the setting + is, every model-change detector (drift, the staleness guard) sees an identical model.""" h = hashlib.sha256() for p in sorted(Path(model_dir).rglob("*.yaml"), key=lambda q: q.as_posix()): try: text = p.read_text(encoding="utf-8") except (OSError, UnicodeDecodeError): continue + if p.name == PROJECT_FILE: + text = _project_text_without_layout(text) h.update(p.relative_to(model_dir).as_posix().encode("utf-8")) h.update(b"\0") h.update(text.encode("utf-8")) @@ -53,6 +61,29 @@ def dir_fingerprint(model_dir: Path) -> str: return h.hexdigest() +def _project_text_without_layout(text: str) -> str: + """A canonical form of the project file with any `layout` key removed, so a visual-only + setting cannot shift the fingerprint. + + The canonicalisation (parse → drop `layout` → re-dump) is applied WHETHER OR NOT the key + is present, so a file that gains or loses only `layout` hashes identically: the with-key + and without-key texts both reduce to the same dumped form. Falls back to the raw text if + the file does not parse as a mapping, so a corrupt file still fingerprints + deterministically.""" + try: + from mdl_core.yaml_io import dump_str, load_str + + doc = load_str(text) + if not isinstance(doc, dict): + return text + stripped = {k: v for k, v in doc.items() if k != "layout"} + # Normalise trailing blank lines: removing `layout` can leave a blank line that + # ruamel preserves as an extra trailing newline, which would still move the hash. + return dump_str(stripped).rstrip("\n") + "\n" + except Exception: # noqa: BLE001 - never let fingerprinting crash over a parse quirk + return text + + class StaleModelError(Exception): """The client's view of the repo is out of date.""" diff --git a/packages/reverse/tests/test_writer_prune_windows.py b/packages/reverse/tests/test_writer_prune_windows.py index a2168a2..f745df8 100644 --- a/packages/reverse/tests/test_writer_prune_windows.py +++ b/packages/reverse/tests/test_writer_prune_windows.py @@ -27,6 +27,22 @@ def test_write_model_keeps_every_file_it_wrote(tmp_path): assert len(files) == 52, "write_model must leave all 52 entity files on disk" +def test_prune_never_touches_mdl_layout_sidecar(tmp_path): + """The reverse prune only sweeps the owned object dirs; the visual layout + sidecar under .mdl/ must survive a re-reverse. Guards the isolation guarantee.""" + (tmp_path / ".mdl").mkdir(parents=True, exist_ok=True) + layout = tmp_path / ".mdl" / "layout.json" + # a rich sidecar (positions + routeX + endpoint anchors) must survive untouched + content = ( + '{"version": 1, "views": {"__all__": {"nodes": {"e": {"x": 1, "y": 2}}, ' + '"edges": {"r": {"routeX": 5, "sourceAnchor": {"side": "left", "y": 3}}}}}}' + ) + layout.write_text(content, encoding="utf-8") + write_model(_model(3), tmp_path) # a fresh write that prunes stale files + assert layout.exists(), "re-reverse must not delete .mdl/layout.json" + assert layout.read_text(encoding="utf-8") == content, "sidecar content must be untouched" + + def test_prune_stale_would_fail_with_naive_separator(tmp_path): """Directly prove the fix: build the prune comparison the OLD way (str with the OS separator) vs the NEW way (as_posix), against a forward-slash `written` set. On a diff --git a/packages/server/src/mdl_server/app.py b/packages/server/src/mdl_server/app.py index 9b12e05..ace6d80 100644 --- a/packages/server/src/mdl_server/app.py +++ b/packages/server/src/mdl_server/app.py @@ -157,6 +157,54 @@ def preview(body: dict) -> JSONResponse: doc["read_only"] = read_only return JSONResponse(doc) + # --- visual layout sidecar (.mdl/layout.json) ---------------------------- + # Node positions + edge routes, per canvas view. VISUAL ONLY: never a model + # mutation — these routes do not go through apply_command, do not bump the + # fingerprint, do not clear the repo cache, and write no audit record. The + # GET is always available (a readable model always returns 200, empty when + # absent); PUT/DELETE are gated under the read_only block below. + + @app.get("/api/layout") + def get_layout(view: str = "__all__") -> JSONResponse: + from mdl_server import layout_store + + return JSONResponse(layout_store.get_view(model_dir, view)) + + @app.put("/api/layout") + def put_layout(body: dict, request: Request) -> JSONResponse: + """Replace one view's node positions + edge routes. 403 under read_only + (the canvas treats that as 'session only', no error). Not a model write: + no fingerprint bump, no cache bust, no audit.""" + from mdl_server import layout_store + + if read_only: + return JSONResponse({"ok": False, "error": "read-only"}, status_code=403) + clen = request.headers.get("content-length") + if clen and clen.isdigit() and int(clen) > layout_store.MAX_BYTES: + return JSONResponse({"ok": False, "error": "layout too large"}, status_code=422) + model = _load().model + try: + layout_store.put_view( + model_dir, + body.get("view") or "", + body.get("nodes"), + body.get("edges"), + entity_ids={e.id for e in model.logical_entities.values()}, + relationship_ids=set(model.relationships), + ) + except layout_store.LayoutError as e: + return JSONResponse({"ok": False, "error": str(e)}, status_code=422) + return JSONResponse({"ok": True}) + + @app.delete("/api/layout") + def delete_layout(view: str | None = None) -> JSONResponse: + from mdl_server import layout_store + + if read_only: + return JSONResponse({"ok": False, "error": "read-only"}, status_code=403) + layout_store.delete_view(model_dir, view) + return JSONResponse({"ok": True}) + # --- ER interchange import/export (Model-tab toolbar) --------------------- # Reads: always available. Export writes nothing; import returns a change list # the client previews and proposes (it does not write the model itself). diff --git a/packages/server/src/mdl_server/git_api.py b/packages/server/src/mdl_server/git_api.py index 9619dc7..4303bf2 100644 --- a/packages/server/src/mdl_server/git_api.py +++ b/packages/server/src/mdl_server/git_api.py @@ -42,6 +42,62 @@ def _git(model_dir: Path, *args: str, timeout: int = 30) -> tuple[int, str]: return proc.returncode, out.strip() +# The visual layout sidecar is not a model change: it must never make the tree +# look "dirty" (which would block propose/commit), and it must never be removed +# on a discard/revert. `git status --porcelain` paths here are model-dir-relative +# (the calls are scoped with `-- .`), so the sidecar shows as `.mdl/layout.json`. +_LAYOUT_REL = ".mdl/layout.json" +# The layout feature also auto-creates .mdl/.gitignore (ignoring layout.json). Until +# a user commits it, it reads as untracked; it is our own isolation artifact, not a +# model change, so filter it too. When the WHOLE .mdl/ dir is new, porcelain collapses +# it to `?? .mdl/` — treat that as layout-only iff the dir holds nothing but these two. +_LAYOUT_ARTIFACTS = frozenset({_LAYOUT_REL, ".mdl/.gitignore"}) + + +def _porcelain_lines(out: str, model_dir: Path | None = None) -> list[str]: + """Non-blank porcelain lines with the layout sidecar (and its auto-created + .gitignore) filtered out. A porcelain line is `XY ` (path at column 3); + a rename is `XY old -> new`. A collapsed `?? .mdl/` is dropped only when the + directory contains nothing but the layout artifacts (so real decisions.yaml / + state changes under .mdl/ still read as dirty).""" + lines: list[str] = [] + for line in out.splitlines(): + if not line.strip(): + continue + path = line[3:].strip() + if " -> " in path: # rename/copy: the destination is what counts + path = path.split(" -> ", 1)[1].strip() + # porcelain may quote paths with odd chars; strip surrounding quotes + if path.startswith('"') and path.endswith('"'): + path = path[1:-1] + if path in _LAYOUT_ARTIFACTS: + continue + if path == ".mdl/" and model_dir is not None and _mdl_only_layout(model_dir): + continue + lines.append(line) + return lines + + +def _mdl_only_layout(model_dir: Path) -> bool: + """True when `.mdl/` holds nothing but the layout artifacts — so a collapsed + `?? .mdl/` is purely layout and can be ignored.""" + mdl = model_dir / ".mdl" + try: + present = {p.name for p in mdl.iterdir()} + except OSError: + return False + return present.issubset({"layout.json", "layout.json.tmp", ".gitignore"}) + + +def _is_dirty(model_dir: Path) -> bool: + """True when the model dir has uncommitted changes OTHER than the layout + sidecar. Mirrors `git status --porcelain -- .` but ignores `.mdl/layout.json`.""" + code, out = _git(model_dir, "status", "--porcelain", "--", ".") + if code != 0: + return False + return bool(_porcelain_lines(out, model_dir)) + + class CommitBody(BaseModel): message: str @@ -632,8 +688,7 @@ def _context_response(model_dir: Path, user: str, *, read_only: bool) -> dict: branch = _current_branch(model_dir) base = _base_branch(model_dir) - code, dirty_out = _git(model_dir, "status", "--porcelain", "--", ".") - dirty = bool(dirty_out.strip()) if code == 0 else False + dirty = _is_dirty(model_dir) # ignores the .mdl/layout.json sidecar has_remote, remote = _git(model_dir, "remote", "get-url", "origin") ahead, behind = (0, 0) if branch and branch != base: @@ -794,9 +849,8 @@ def status() -> JSONResponse: if code != 0: return JSONResponse({"git": False, "dirty": [], "error": out}) dirty = [] - for line in out.splitlines(): - if not line.strip(): - continue + # the .mdl/layout.json sidecar is visual-only; it must never read as dirty + for line in _porcelain_lines(out, model_dir): state, path = line[:2].strip() or "??", line[3:].strip() dirty.append({"state": state, "path": path}) return JSONResponse({"git": True, "dirty": dirty, "clean": not dirty}) @@ -900,7 +954,9 @@ def discard(ident: Identity = Depends(_identity)) -> JSONResponse: return JSONResponse({"ok": False, "error": denied}, status_code=403) # revert tracked edits + remove untracked files, model dir only code1, out1 = _git(model_dir, "checkout", "--", ".") - code2, out2 = _git(model_dir, "clean", "-fd", "--", ".") + # -e keeps the visual layout sidecar: discard reverts the MODEL, not + # the user's canvas arrangement. + code2, out2 = _git(model_dir, "clean", "-fd", "-e", _LAYOUT_REL, "--", ".") ok = code1 == 0 and code2 == 0 _audit("discard", ident, "working-tree", "ok" if ok else "error") return JSONResponse({"ok": ok, "error": None if ok else f"{out1}\n{out2}"}) @@ -966,8 +1022,8 @@ def propose( ) # Refuse to run on a dirty tree — we branch from a clean base. - code, dirty = _git(model_dir, "status", "--porcelain", "--", ".") - if code == 0 and dirty: + # The .mdl/layout.json sidecar does not count as dirty. + if _is_dirty(model_dir): return JSONResponse( { "ok": False, @@ -1002,7 +1058,7 @@ def propose( applied += 1 except CommandError as e: _git(model_dir, "checkout", "--", ".") - _git(model_dir, "clean", "-fd", "--", ".") + _git(model_dir, "clean", "-fd", "-e", _LAYOUT_REL, "--", ".") # a failed propose must be a complete no-op _git(model_dir, "checkout", starting_branch) _git(model_dir, "branch", "-D", branch_name) diff --git a/packages/server/src/mdl_server/layout_store.py b/packages/server/src/mdl_server/layout_store.py new file mode 100644 index 0000000..742a877 --- /dev/null +++ b/packages/server/src/mdl_server/layout_store.py @@ -0,0 +1,262 @@ +"""Visual layout sidecar — node positions and edge routes, per canvas view. + +Layout is a VISUAL concern only. It must never appear in the model, drift, +validation, exports, diffs, proposals or dbt output. It lives in +`/.mdl/layout.json` — JSON, not YAML, so it stays out of every +`*.yaml` glob (dir_fingerprint, ModelRepo.load, status, the reverse prune) by +construction, and it is git-ignored by default. + +The file is read-modify-write safe: unknown top-level keys and a higher +`version` are preserved, and a corrupt file is treated as empty (logged once) +rather than crashing the server. Writes are atomic (temp file + os.replace). +""" + +from __future__ import annotations + +import json +import logging +import math +import os +from pathlib import Path +from typing import Any + +log = logging.getLogger("mdl.layout") + +LAYOUT_REL = ".mdl/layout.json" +VERSION = 1 +MAX_BYTES = 1_000_000 # 1 MB body cap (enforced by the route; a guard here too) +MAX_KEY_LEN = 64 + +# log a corrupt-file warning at most once per process so a bad file is not noisy +_warned_corrupt: set[str] = set() + + +def _path(model_dir: Path) -> Path: + return model_dir / LAYOUT_REL + + +def _read_doc(model_dir: Path) -> dict[str, Any]: + """The whole layout document, or a fresh empty one. Never raises: a missing + or corrupt file yields an empty doc (the corrupt case is logged once).""" + p = _path(model_dir) + if not p.exists(): + return {"version": VERSION, "views": {}} + try: + doc = json.loads(p.read_text(encoding="utf-8")) + if not isinstance(doc, dict): + raise ValueError("layout.json is not an object") + doc.setdefault("version", VERSION) + if not isinstance(doc.get("views"), dict): + doc["views"] = {} + return doc + except (OSError, ValueError, json.JSONDecodeError) as e: + key = str(p) + if key not in _warned_corrupt: + _warned_corrupt.add(key) + log.warning("ignoring unreadable layout file %s: %s", p, e) + return {"version": VERSION, "views": {}} + + +def _write_doc(model_dir: Path, doc: dict[str, Any]) -> None: + """Write the document atomically with a stable, diff-friendly shape: + sorted keys, 1-space indent, trailing newline.""" + base = model_dir / ".mdl" + base.mkdir(parents=True, exist_ok=True) + target = base / "layout.json" + tmp = base / "layout.json.tmp" + text = json.dumps(doc, indent=1, sort_keys=True) + "\n" + tmp.write_text(text, encoding="utf-8") + os.replace(tmp, target) # atomic within the same directory + + +def get_view(model_dir: Path, view: str) -> dict[str, Any]: + """The one view's maps (empty when absent). Shape: + `{version, view, nodes: {ulid: {x, y}}, edges: {ulid: {routeX}}}`.""" + doc = _read_doc(model_dir) + v = doc.get("views", {}).get(view) or {} + nodes = v.get("nodes") if isinstance(v.get("nodes"), dict) else {} + edges = v.get("edges") if isinstance(v.get("edges"), dict) else {} + return {"version": doc.get("version", VERSION), "view": view, "nodes": nodes, "edges": edges} + + +class LayoutError(ValueError): + """A layout payload the route should reject with 422.""" + + +def _finite(x: Any) -> bool: + return isinstance(x, (int, float)) and not isinstance(x, bool) and math.isfinite(x) + + +def _clean_nodes(nodes: Any, valid_ids: set[str]) -> dict[str, dict[str, int]]: + out: dict[str, dict[str, int]] = {} + if not isinstance(nodes, dict): + return out + for nid, pos in nodes.items(): + if len(nid) > MAX_KEY_LEN: + raise LayoutError(f"node id too long: {nid[:16]}…") + if nid not in valid_ids: + continue # drop dead ids so the file does not accumulate them + if not isinstance(pos, dict) or not _finite(pos.get("x")) or not _finite(pos.get("y")): + raise LayoutError(f"node {nid}: x and y must be finite numbers") + out[nid] = {"x": round(pos["x"]), "y": round(pos["y"])} + return out + + +_SIDES = {"left", "right", "top", "bottom"} + + +def _clean_anchor(a: Any) -> dict[str, Any] | None: + """A dragged-endpoint anchor: {side, y, x?}. None when absent/invalid-shape (dropped, + not fatal); raises only on a present-but-non-finite number.""" + if not isinstance(a, dict): + return None + side = a.get("side") + if side not in _SIDES or not _finite(a.get("y")): + raise LayoutError("anchor: side must be left/right/top/bottom and y finite") + out: dict[str, Any] = {"side": side, "y": round(a["y"])} + if a.get("x") is not None: + if not _finite(a["x"]): + raise LayoutError("anchor x must be finite") + out["x"] = round(a["x"]) + return out + + +def _clean_edges(edges: Any, valid_ids: set[str]) -> dict[str, dict[str, Any]]: + out: dict[str, dict[str, Any]] = {} + if not isinstance(edges, dict): + return out + for eid, route in edges.items(): + if len(eid) > MAX_KEY_LEN: + raise LayoutError(f"edge id too long: {eid[:16]}…") + if eid not in valid_ids: + continue + if not isinstance(route, dict): + raise LayoutError(f"edge {eid}: route must be an object") + rec: dict[str, Any] = {} + if route.get("routeX") is not None: + if not _finite(route["routeX"]): + raise LayoutError(f"edge {eid}: routeX must be a finite number") + rec["routeX"] = round(route["routeX"]) + sa = _clean_anchor(route.get("sourceAnchor")) + if sa: + rec["sourceAnchor"] = sa + ta = _clean_anchor(route.get("targetAnchor")) + if ta: + rec["targetAnchor"] = ta + if rec: # drop empty edge records + out[eid] = rec + return out + + +def put_view( + model_dir: Path, + view: str, + nodes: Any, + edges: Any, + *, + entity_ids: set[str], + relationship_ids: set[str], +) -> None: + """Replace one view. Drops ids not present in the model. Raises LayoutError + (→ 422) on a bad view key, over-long id, or non-finite number.""" + if not view or len(view) > MAX_KEY_LEN: + raise LayoutError("view key missing or too long") + clean_nodes = _clean_nodes(nodes, entity_ids) + clean_edges = _clean_edges(edges, relationship_ids) + doc = _read_doc(model_dir) + doc.setdefault("version", VERSION) + doc.setdefault("views", {}) + doc["views"][view] = {"nodes": clean_nodes, "edges": clean_edges} + _write_doc(model_dir, doc) + _ensure_gitignore(model_dir) + + +def delete_view(model_dir: Path, view: str | None) -> None: + """Remove one view, or the whole file when `view` is None (reset all).""" + p = _path(model_dir) + if view is None: + if p.exists(): + try: + p.unlink() + except OSError as e: # noqa: BLE001 - best effort; never fatal + log.warning("could not remove layout file %s: %s", p, e) + return + doc = _read_doc(model_dir) + if view in doc.get("views", {}): + del doc["views"][view] + _write_doc(model_dir, doc) + + +def _layout_shared(model_dir: Path) -> bool: + """Opt-in team sharing: `layout: {shared: true}` in mdl-project.yaml. When set, the + sidecar is NOT git-ignored so a team can commit it. Read directly from the project + file (never through the model graph), so `layout` stays invisible to every CLI + feature — validate/drift/export are identical whatever this setting is. Any read + error or missing key means the safe default: not shared (ignored).""" + try: + from mdl_core.yaml_io import load_file + + doc = load_file(model_dir / "mdl-project.yaml") or {} + layout = doc.get("layout") if isinstance(doc, dict) else None + return isinstance(layout, dict) and layout.get("shared") is True + except Exception: # noqa: BLE001 - never fail a layout write over the project file + return False + + +def _ensure_gitignore(model_dir: Path) -> None: + """Make the sidecar's git status match the project's sharing setting. + + Default (not shared): ensure `/.mdl/.gitignore` ignores `layout.json`, so + a solo user gets no noisy diffs. When `layout: {shared: true}` is set, do NOT ignore + it (and remove our own auto-added ignore line if present) so the team can commit it. + Only touches git state inside a work tree, and never edits a user's ignore file beyond + our one `layout.json` line.""" + mdl = model_dir / ".mdl" + gi = mdl / ".gitignore" + + if _layout_shared(model_dir): + # Shared: unlock committing by dropping OUR auto-added ignore line if it is there. + if gi.exists(): + try: + lines = gi.read_text(encoding="utf-8").splitlines() + if "layout.json" in lines: + kept = [ln for ln in lines if ln.strip() != "layout.json"] + if kept: + gi.write_text("\n".join(kept) + "\n", encoding="utf-8") + else: + gi.unlink() # the file only held our line + except OSError: + pass + return + + if gi.exists(): + try: + if "layout.json" in gi.read_text(encoding="utf-8").splitlines(): + return + except OSError: + return + # a .gitignore exists but does not list layout.json — do not edit the + # user's file beyond appending our one line + try: + with gi.open("a", encoding="utf-8") as f: + f.write("layout.json\n") + except OSError: + pass + return + # no .gitignore yet: only create one if we are inside a git work tree + if not _in_git_tree(model_dir): + return + try: + gi.write_text("layout.json\n", encoding="utf-8") + except OSError: + pass + + +def _in_git_tree(model_dir: Path) -> bool: + """True when `model_dir` sits inside a git work tree (a `.git` exists at or + above it). Cheap walk-up; no subprocess.""" + p = model_dir.resolve() + for d in (p, *p.parents): + if (d / ".git").exists(): + return True + return False diff --git a/packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js b/packages/server/src/mdl_server/static/assets/ModelDiagram-CxAF-B4F.js similarity index 95% rename from packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js rename to packages/server/src/mdl_server/static/assets/ModelDiagram-CxAF-B4F.js index 2d6de82..2a96951 100644 --- a/packages/server/src/mdl_server/static/assets/ModelDiagram-CRUBpPVn.js +++ b/packages/server/src/mdl_server/static/assets/ModelDiagram-CxAF-B4F.js @@ -1 +1 @@ -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}; +import{r,j as s}from"./client-DOmNpTln.js";import{f as T,v as _,w as O,x as R,E as D,B as P,C as z,y as A}from"./styles-DqoimPMg.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-BiJ4Tezc.js b/packages/server/src/mdl_server/static/assets/main-BiJ4Tezc.js new file mode 100644 index 0000000..2bc5622 --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/main-BiJ4Tezc.js @@ -0,0 +1,2 @@ +import{j as e,r as a,c as ie,R as le}from"./client-DOmNpTln.js";import{I as de,R as he,P as Y,h as ue,f as J,a as Z,g as ee,s as xe,A as pe,b as je,M as me,S as fe}from"./styles-DqoimPMg.js";function ge(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 ye=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M12 5v14M5 12h14"})}),be=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M12 3l7.5 4.5v9L12 21l-7.5-4.5v-9L12 3z"})}),we=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"})]}),ve=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"})]}),Me=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"})]}),Ce=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M8 7l-4 5 4 5M16 7l4 5-4 5"})}),Ne=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"})]}),Ee=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"})]}),ke=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"})}),Se=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"})]}),Ie=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"})]}),Le=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"})]}),Re=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"})]}),Te=s=>e.jsxs(i,{...s,children:[e.jsx("path",{d:"M12 4l9 16H3l9-16z"}),e.jsx("path",{d:"M12 10v4M12 17.5v.5"})]}),ze=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"})]}),De=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M4 6h16M4 12h16M4 18h16"})}),Fe=s=>e.jsxs(i,{...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"})]}),Be=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M6 6l12 12M18 6L6 18"})}),Pe=s=>e.jsx(i,{...s,children:e.jsx("path",{d:"M5 12h14M13 6l6 6-6 6"})});function Ae({doc:s,saColors:l,countByArea:j,activeAreas:h,onSetActive:x,open:w,onClose:g}){const m=s.subject_areas,p=h.size>0,y=r=>x(new Set([r])),f=r=>{const u=new Set(h);u.has(r)?u.delete(r):u.add(r),x(u)},v=j.get("")??0;return e.jsxs("aside",{className:"canvas-drawer"+(w?" open":""),"aria-hidden":!w,children:[e.jsxs("div",{className:"drawer-header",children:[e.jsxs("h2",{children:[e.jsx(Fe,{size:16})," Subject areas"]}),e.jsx("button",{className:"icon-btn",onClick:g,title:"Close","aria-label":"Close drawer",children:e.jsx(Be,{size:16})})]}),e.jsx("div",{className:"drawer-hint",children:p?`Showing ${h.size} of ${m.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"+(p?"":" active"),onClick:()=>x(new Set),disabled:!p,children:"Show all"}),e.jsxs("div",{className:"sa-list",children:[m.map(r=>{const u=h.has(r.id),k=j.get(r.id)??r.member_count??0;return e.jsxs("div",{className:"sa-row"+(u?" active":""),onClick:()=>y(r.id),role:"button",tabIndex:0,onKeyDown:b=>{(b.key==="Enter"||b.key===" ")&&(b.preventDefault(),y(r.id))},title:`Focus ${r.name}`,children:[e.jsx("input",{type:"checkbox",checked:u,onClick:b=>b.stopPropagation(),onChange:()=>f(r.id),"aria-label":`Include ${r.name}`}),e.jsx("span",{className:"sa-chip",style:{background:l.get(r.id)}}),e.jsx("span",{className:"sa-name",children:r.name}),e.jsx("span",{className:"sa-count",children:k})]},r.id)}),v>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:v})]})]})]})}function _e({doc:s,diagnostics:l,query:j,onQuery:h,onSubmitQuery:x,showTypes:w,onToggleTypes:g,collapseDetail:m,onToggleCollapse:p,onFitView:y,onRelayout:f,onResetLayout:v,onRefresh:r,readOnly:u,dirty:k,panelTab:b,onPanelTab:q,onNewEntity:A,exec:S,onImported:I,onImportBatch:_,openImport:$,onToggleDrawer:z,drawerOpen:D,filterCount:K}){const M=(l==null?void 0:l.items.filter(o=>o.severity==="error").length)??0,F=(l==null?void 0:l.items.filter(o=>o.severity==="warning").length)??0,E=(o,L,B,O,H)=>e.jsxs("button",{className:"grp-btn"+(b===o?" active":""),onClick:()=>q(o),title:O,children:[L,e.jsx("span",{children:B}),H&&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"+(D?" active":""),onClick:z,title:D?"Hide subject areas & layout":"Subject areas & layout","aria-label":"Toggle subject-area & layout drawer","aria-expanded":D,children:[e.jsx(De,{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}),u&&e.jsx("span",{className:"chip",children:"read-only"})]}),e.jsx("input",{className:"search",type:"search",placeholder:"Search entities & attributes… ( / , Enter jumps )",value:j,onChange:o=>h(o.target.value),onKeyDown:o=>{o.key==="Enter"&&x()},id:"mdl-search"}),e.jsxs("div",{className:"stats",children:[e.jsxs("span",{className:"stat-item",title:"entities",children:[e.jsx(Ie,{size:14}),s.counts.entities]}),e.jsxs("span",{className:"stat-item",title:"relationships",children:[e.jsx(Le,{size:14}),s.counts.relationships]}),l&&e.jsx("span",{className:"diag-chip"+(M?" err":F?" warn":" ok"),title:l.items.map(o=>`${o.code} ${o.message}`).join(` +`)||"model valid",children:M?e.jsxs(e.Fragment,{children:[e.jsx(ze,{size:14}),M]}):F?e.jsxs(e.Fragment,{children:[e.jsx(Te,{size:14}),F]}):e.jsxs(e.Fragment,{children:[e.jsx(Re,{size:14}),"valid"]})})]})]}),e.jsxs("div",{className:"topbar-row actions",children:[!u&&e.jsxs("button",{className:"tool-btn primary",onClick:A,title:"New entity (n)",children:[e.jsx(ye,{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"}),E("ontology",e.jsx(be,{size:15}),"Ontology","Ontology browser"),E("layers",e.jsx(we,{size:15}),"Layers","Four-layer stack & coverage"),E("changes",e.jsx(ve,{size:15}),"Changes",k?"Uncommitted changes":"Changes",k),E("decisions",e.jsx(Me,{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"+(w?" active":""),onClick:g,title:"Toggle data types","aria-label":"Toggle data types",children:e.jsx(Ce,{size:15})}),e.jsx("button",{className:"grp-btn icon-only"+(m?" active":""),onClick:p,title:"Collapse to keys (PK + FK only) — readable for large models","aria-label":"Collapse to keys",children:e.jsx(Ne,{size:15})}),e.jsx(Ke,{onRelayout:f,onReset:v}),e.jsx("button",{className:"grp-btn icon-only",onClick:y,title:"Fit view","aria-label":"Fit view",children:e.jsx(ke,{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:r,title:"Refresh from disk (picks up terminal / external edits)","aria-label":"Refresh from disk",children:e.jsx(Se,{size:15})}),e.jsx(de,{exec:S,canEdit:!u,onImported:I,submitLabel:"Import",buttonClass:"grp-btn",applyBatch:_,openImport:$})]})]})]})}const U=["LR","TB","RL","BT"],$e={LR:0,TB:90,RL:180,BT:270},se={LR:"left → right",TB:"top → bottom",RL:"right → left",BT:"bottom → top"};function Ke({onRelayout:s,onReset:l}){const[j,h]=a.useState(!1),[x,w]=a.useState("LR"),[g,m]=a.useState("auto"),p=a.useRef(null);a.useEffect(()=>{if(!j)return;const r=u=>{p.current&&!p.current.contains(u.target)&&h(!1)};return document.addEventListener("mousedown",r),()=>document.removeEventListener("mousedown",r)},[j]);const y=r=>{m(r),s(r,x)},f=r=>{y(r),h(!1)},v=()=>{const r=U[(U.indexOf(x)+1)%U.length];w(r),s(g,r)};return e.jsxs("div",{className:"layout-picker",ref:p,children:[e.jsx("button",{className:"grp-btn icon-only",onClick:()=>y("auto"),title:"Auto-layout","aria-label":"Auto-layout",children:e.jsx(Ee,{size:15})}),e.jsx("button",{className:"grp-btn icon-only caret"+(j?" active":""),onClick:()=>h(r=>!r),title:"Layout style","aria-label":"Layout style",children:"▾"}),e.jsx("button",{className:"grp-btn icon-only",onClick:v,title:`Flow direction: ${se[x]} (click to rotate)`,"aria-label":`Flow direction: ${se[x]}`,children:e.jsx("span",{className:"dir-arrow",style:{transform:`rotate(${$e[x]}deg)`},children:e.jsx(Pe,{size:15})})}),j&&e.jsxs("div",{className:"layout-menu",role:"menu",children:[e.jsx("button",{onClick:()=>f("auto"),children:"Auto (by size)"}),e.jsx("button",{onClick:()=>f("hierarchical"),children:"Hierarchical"}),e.jsx("button",{onClick:()=>f("clustered"),children:"Clustered (by area)"}),e.jsx("button",{onClick:()=>f("grid"),children:"Grid"}),l&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"layout-menu-sep",role:"separator"}),e.jsx("button",{onClick:()=>{l(),h(!1)},title:"Clear saved positions and routes for this view",children:"Reset layout"})]})]})]})}function He(){const[s,l]=a.useState(null),[j,h]=a.useState(null),[x,w]=a.useState(null),[g,m]=a.useState(null),[p,y]=a.useState(""),[f,v]=a.useState(!0),[r,u]=a.useState(!1),[k,b]=a.useState(()=>new Set),[q,A]=a.useState(null),[S,I]=a.useState(null),[_,$]=a.useState(!1),[z,D]=a.useState(()=>new Set),[K,M]=a.useState(!1),[F,E]=a.useState(0),o=a.useRef(null),L=a.useRef(""),B=a.useRef(!1),O=a.useMemo(()=>{const t=new Map;return s==null||s.subject_areas.forEach((c,n)=>t.set(c.id,Y[n%Y.length])),t},[s]),H=a.useMemo(()=>{const t=new Map;if(!s)return t;const c=new Map;return s.subject_areas.forEach(n=>{(n.members??[]).forEach(d=>c.set(d,n.id))}),s.entities.forEach(n=>{var N,Q,X;const P=((N=n.conceptual)!=null&&N.id?c.get(n.conceptual.id):void 0)??((X=(Q=n.conceptual)==null?void 0:Q.subject_area)==null?void 0:X.id)??"";t.set(n.id,P)}),t},[s]),te=a.useMemo(()=>{const t=new Map;return H.forEach(c=>t.set(c,(t.get(c)??0)+1)),t},[H]),R=(s==null?void 0:s.read_only)??!0,ne=a.useMemo(()=>ge(R),[R]),ae=a.useMemo(()=>ue(),[]),V=a.useMemo(()=>new URLSearchParams(window.location.search),[]),T=V.get("minimal")==="1";a.useEffect(()=>{T&&u(!0)},[T]);const re=a.useCallback(t=>{b(c=>{const n=new Set(c);return n.has(t)?n.delete(t):n.add(t),n})},[]),ce=V.get("import")==="1",W=a.useRef(!1),C=a.useCallback(()=>{J().then(l).catch(t=>w(String(t))),Z().then(h).catch(()=>h(null)),ee().then(t=>M(!!(t.git&&!t.clean))).catch(()=>M(!1)),E(t=>t+1)},[]);a.useEffect(C,[C]),a.useEffect(()=>{const t=setInterval(()=>{J().then(c=>{l(n=>n&&c.fingerprint===n.fingerprint?n:(B.current||(L.current=c.fingerprint),T||(Z().then(h).catch(()=>h(null)),ee().then(d=>M(!!(d.git&&!d.clean))).catch(()=>M(!1))),E(d=>d+1),c))}).catch(()=>{})},3e3);return()=>clearInterval(t)},[T]),a.useEffect(()=>{s!=null&&s.fingerprint&&(L.current=s.fingerprint)},[s]);const G=a.useCallback(async(t,c)=>{if(s){B.current=!0;try{const n=await xe(t,c,L.current||s.fingerprint);return n!=null&&n.fingerprint&&(L.current=n.fingerprint),C(),n}catch(n){throw n instanceof pe&&n.status===409?(m("Model changed on disk — view refreshed, please retry."),C()):m(n instanceof Error?n.message:String(n)),n}finally{B.current=!1}}},[s,C]);a.useEffect(()=>{if(!g)return;const t=setTimeout(()=>m(null),5e3);return()=>clearTimeout(t)},[g]),a.useEffect(()=>{if(!s||W.current)return;const t=V.get("focus");if(!t)return;const c=s.entities.find(n=>{var d;return n.name===t||((d=n.conceptual)==null?void 0:d.name)===t});c&&(W.current=!0,requestAnimationFrame(()=>{var n;return(n=o.current)==null?void 0:n.focusEntity(c.id)}))},[s,V]);const oe=a.useCallback(()=>{var n;if(!s)return;const t=p.trim().toLowerCase();if(!t)return;const c=s.entities.find(d=>{var N;return(d.name.toLowerCase()+" "+(((N=d.conceptual)==null?void 0:N.name.toLowerCase())??"")+" "+d.attributes.map(Q=>Q.name.toLowerCase()).join(" ")).includes(t)});c&&((n=o.current)==null||n.focusEntity(c.id))},[s,p]);return a.useEffect(()=>{const t=c=>{var d,P,N;const n=(d=document.activeElement)==null?void 0:d.tagName;n==="INPUT"||n==="TEXTAREA"||n==="SELECT"||(c.key==="/"&&(c.preventDefault(),(P=document.getElementById("mdl-search"))==null||P.focus()),c.key==="n"&&!R&&((N=o.current)==null||N.newEntity()),c.key==="Escape"&&(A(null),y(""),I(null)))};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[R]),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:[!T&&e.jsx(_e,{doc:s,diagnostics:j,query:p,onQuery:y,onSubmitQuery:oe,showTypes:f,onToggleTypes:()=>v(t=>!t),collapseDetail:r,onToggleCollapse:()=>u(t=>!t),onFitView:()=>{var t;return(t=o.current)==null?void 0:t.fitView()},onRelayout:(t,c)=>{var n;return(n=o.current)==null?void 0:n.relayout(t,c)},onResetLayout:()=>{var t;return(t=o.current)==null?void 0:t.resetLayout()},onRefresh:C,readOnly:R,dirty:K,panelTab:S,onPanelTab:t=>I(c=>c===t?null:t),onNewEntity:()=>{var t;return(t=o.current)==null?void 0:t.newEntity()},exec:G,onImported:C,onImportBatch:je,openImport:ce,onToggleDrawer:()=>$(t=>!t),drawerOpen:_,filterCount:z.size}),e.jsxs("div",{className:"canvas-wrap",children:[e.jsx(me,{doc:s,caps:ne,exec:G,selectedId:S?null:q,onSelect:A,query:p,showTypes:f,collapseDetail:r,expandedEntities:k,onToggleExpand:re,activeAreas:z,onError:m,handleRef:o,layoutStore:ae}),!T&&e.jsx(Ae,{doc:s,saColors:O,countByArea:te,activeAreas:z,onSetActive:D,open:_,onClose:()=>$(!1)}),S&&e.jsx(fe,{tab:S,onClose:()=>I(null),readOnly:R,refreshKey:F,onModelChanged:C,onFocusEntity:t=>{var c;I(null),(c=o.current)==null||c.focusEntity(t)}}),g&&e.jsx("div",{className:"toast",children:g})]})]}):e.jsxs("div",{className:"splash",children:["◮"," loading model…"]})}function Ve(){return e.jsx(he,{children:e.jsx(He,{})})}ie.createRoot(document.getElementById("root")).render(e.jsx(le.StrictMode,{children:e.jsx(Ve,{})})); diff --git a/packages/server/src/mdl_server/static/assets/main-DScWOCuj.js b/packages/server/src/mdl_server/static/assets/main-DScWOCuj.js deleted file mode 100644 index 08556aa..0000000 --- a/packages/server/src/mdl_server/static/assets/main-DScWOCuj.js +++ /dev/null @@ -1,2 +0,0 @@ -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-Zxw1Myjj.js b/packages/server/src/mdl_server/static/assets/sme-B1K9phBm.js similarity index 99% rename from packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js rename to packages/server/src/mdl_server/static/assets/sme-B1K9phBm.js index 1da7f69..f9f1e96 100644 --- a/packages/server/src/mdl_server/static/assets/sme-Zxw1Myjj.js +++ b/packages/server/src/mdl_server/static/assets/sme-B1K9phBm.js @@ -1,2 +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,{})})); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ModelDiagram-CxAF-B4F.js","assets/client-DOmNpTln.js","assets/styles-DqoimPMg.js","assets/styles-D9kpX-nz.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,i as ve,I as Ue,R as Fe,M as Te,O as Ye,L as Je,j as Qe,k as Xe,A as Ze,l as es,m as ss,n as ns,o as ts,q as as,s as rs,f as ie,r as is,t as ls,u as cs,b as os}from"./styles-DqoimPMg.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-CxAF-B4F.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 deleted file mode 100644 index 6cbaefa..0000000 --- a/packages/server/src/mdl_server/static/assets/styles-B90OKxCj.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 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-DxhL4SV-.css b/packages/server/src/mdl_server/static/assets/styles-D9kpX-nz.css similarity index 58% rename from packages/server/src/mdl_server/static/assets/styles-DxhL4SV-.css rename to packages/server/src/mdl_server/static/assets/styles-D9kpX-nz.css index 442fd83..a7ca221 100644 --- a/packages/server/src/mdl_server/static/assets/styles-DxhL4SV-.css +++ b/packages/server/src/mdl_server/static/assets/styles-D9kpX-nz.css @@ -1 +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)}.hamburger{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:6px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer}.hamburger:hover{color:var(--text);border-color:var(--border-strong)}.hamburger.active{color:var(--accent);border-color:var(--accent)}.hamburger-badge{position:absolute;top:-6px;right:-6px;min-width:15px;height:15px;padding:0 3px;border-radius:99px;background:var(--accent);color:var(--accent-ink);font-size:10px;font-weight:700;line-height:15px;text-align:center}.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:#ffffff0d}.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}.dir-arrow{display:inline-flex;transition:transform .15s ease}.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)}.tool-btn.active{color:var(--accent);border-color:var(--accent)}.tool-btn.primary{color:var(--accent);border-color:#e0a63a66;font-weight:600}.layout-picker{position:relative;display:inline-flex;gap:2px}.layout-picker .caret{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)}.canvas-drawer{position:absolute;top:12px;left:12px;bottom:12px;width:300px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:16px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080;display:flex;flex-direction:column;gap:10px;transform:translate(calc(-100% - 16px));opacity:0;pointer-events:none;transition:transform .2s ease,opacity .2s ease}.canvas-drawer.open{transform:translate(0);opacity:1;pointer-events:auto}.drawer-header{display:flex;align-items:center;justify-content:space-between}.drawer-header h2{margin:0;font-size:15px;display:inline-flex;align-items:center;gap:7px}.drawer-header h2 svg{color:var(--accent)}.drawer-hint{font-size:11px;color:var(--text-dim);line-height:1.4}.sa-all{align-self:flex-start;padding:4px 10px;border-radius:7px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer;font-size:12px}.sa-all:hover:not(:disabled){color:var(--text);border-color:var(--border-strong)}.sa-all.active{color:var(--accent);border-color:var(--accent)}.sa-all:disabled{opacity:.5;cursor:default}.sa-list{display:flex;flex-direction:column;gap:3px;margin-top:2px}.sa-row{display:flex;align-items:center;gap:9px;padding:7px 8px;border-radius:8px;cursor:pointer;border:1px solid transparent;-webkit-user-select:none;user-select:none}.sa-row:hover{background:var(--bg-card)}.sa-row.active{background:var(--accent-soft);border-color:#e0a63a66}.sa-row.muted{cursor:default;opacity:.7}.sa-row.muted:hover{background:transparent}.sa-row input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.sa-chip{width:12px;height:12px;border-radius:3px;flex:0 0 auto}.sa-chip.none{background:repeating-linear-gradient(45deg,#4a3c2c,#4a3c2c 2px,transparent 2px,transparent 4px)}.sa-name{flex:1;font-size:13px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sa-row.muted .sa-name{color:var(--text-dim)}.sa-count{font-size:11px;color:var(--text-dim);background:var(--bg-card);padding:1px 7px;border-radius:99px;flex:0 0 auto}.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)}.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} +.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)}.hamburger{position:relative;display:inline-flex;align-items:center;justify-content:center;padding:6px;border-radius:8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer}.hamburger:hover{color:var(--text);border-color:var(--border-strong)}.hamburger.active{color:var(--accent);border-color:var(--accent)}.hamburger-badge{position:absolute;top:-6px;right:-6px;min-width:15px;height:15px;padding:0 3px;border-radius:99px;background:var(--accent);color:var(--accent-ink);font-size:10px;font-weight:700;line-height:15px;text-align:center}.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:#ffffff0d}.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}.dir-arrow{display:inline-flex;transition:transform .15s ease}.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)}.tool-btn.active{color:var(--accent);border-color:var(--accent)}.tool-btn.primary{color:var(--accent);border-color:#e0a63a66;font-weight:600}.layout-picker{position:relative;display:inline-flex;gap:2px}.layout-picker .caret{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)}.canvas-drawer{position:absolute;top:12px;left:12px;bottom:12px;width:300px;background:var(--bg-raised);border:1px solid var(--border);border-radius:14px;padding:16px;overflow-y:auto;z-index:9;box-shadow:0 12px 40px #00000080;display:flex;flex-direction:column;gap:10px;transform:translate(calc(-100% - 16px));opacity:0;pointer-events:none;transition:transform .2s ease,opacity .2s ease}.canvas-drawer.open{transform:translate(0);opacity:1;pointer-events:auto}.drawer-header{display:flex;align-items:center;justify-content:space-between}.drawer-header h2{margin:0;font-size:15px;display:inline-flex;align-items:center;gap:7px}.drawer-header h2 svg{color:var(--accent)}.drawer-hint{font-size:11px;color:var(--text-dim);line-height:1.4}.sa-all{align-self:flex-start;padding:4px 10px;border-radius:7px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-dim);cursor:pointer;font-size:12px}.sa-all:hover:not(:disabled){color:var(--text);border-color:var(--border-strong)}.sa-all.active{color:var(--accent);border-color:var(--accent)}.sa-all:disabled{opacity:.5;cursor:default}.sa-list{display:flex;flex-direction:column;gap:3px;margin-top:2px}.sa-row{display:flex;align-items:center;gap:9px;padding:7px 8px;border-radius:8px;cursor:pointer;border:1px solid transparent;-webkit-user-select:none;user-select:none}.sa-row:hover{background:var(--bg-card)}.sa-row.active{background:var(--accent-soft);border-color:#e0a63a66}.sa-row.muted{cursor:default;opacity:.7}.sa-row.muted:hover{background:transparent}.sa-row input[type=checkbox]{accent-color:var(--accent);cursor:pointer}.sa-chip{width:12px;height:12px;border-radius:3px;flex:0 0 auto}.sa-chip.none{background:repeating-linear-gradient(45deg,#4a3c2c,#4a3c2c 2px,transparent 2px,transparent 4px)}.sa-name{flex:1;font-size:13px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sa-row.muted .sa-name{color:var(--text-dim)}.sa-count{font-size:11px;color:var(--text-dim);background:var(--bg-card);padding:1px 7px;border-radius:99px;flex:0 0 auto}.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}.rel-handle{cursor:ew-resize}.rel-handle,.rel-handle *{cursor:ew-resize!important}.rel-handle .rel-grip{fill:var(--accent);stroke:var(--bg);opacity:1}.rel-handle .rel-grip-dot{fill:var(--bg)}.rel-handle:hover .rel-grip{fill:var(--accent);filter:brightness(1.25) drop-shadow(0 0 3px var(--accent))}.rel-endpoint,.rel-endpoint *{cursor:move!important}.rel-endpoint .rel-endpoint-dot{fill:var(--accent);stroke:var(--bg);opacity:1}.rel-endpoint:hover .rel-endpoint-dot{filter:brightness(1.3) drop-shadow(0 0 3px var(--accent))}.rel-edge.selected .rel-path{stroke-width:2.2;filter:drop-shadow(0 0 2px var(--accent))}.layout-menu-sep{height:1px;margin:4px 0;background:var(--border)}.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)}.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-DqoimPMg.js b/packages/server/src/mdl_server/static/assets/styles-DqoimPMg.js new file mode 100644 index 0000000..81542fa --- /dev/null +++ b/packages/server/src/mdl_server/static/assets/styles-DqoimPMg.js @@ -0,0 +1,23 @@ +var dc=Object.defineProperty;var fc=(e,t,n)=>t in e?dc(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var _e=(e,t,n)=>fc(e,typeof t!="symbol"?t+"":t,n);import{r as w,g as $o,R as A,j as h}from"./client-DOmNpTln.js";function Me(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"?bc:wc;To.useSyncExternalStore=Lt.useSyncExternalStore!==void 0?Lt.useSyncExternalStore:vc;Io.exports=To;var _c=Io.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 zn=w,Ec=_c;function Nc(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var Sc=typeof Object.is=="function"?Object.is:Nc,kc=Ec.useSyncExternalStore,jc=zn.useRef,Cc=zn.useEffect,Mc=zn.useMemo,$c=zn.useDebugValue;Ro.useSyncExternalStoreWithSelector=function(e,t,n,r,s){var o=jc(null);if(o.current===null){var i={hasValue:!1,value:null};o.current=i}else i=o.current;o=Mc(function(){function c(f){if(!u){if(u=!0,l=f,f=r(f),s!==void 0&&i.hasValue){var p=i.value;if(s(p,f))return d=p}return d=f}if(p=d,Sc(l,f))return p;var x=r(f);return s!==void 0&&s(p,x)?(l=f,p):(l=f,d=x)}var u=!1,l,d,m=n===void 0?null:n;return[function(){return c(t())},m===null?void 0:function(){return c(m())}]},[t,n,r,s]);var a=kc(e,o[0],o[1]);return Cc(function(){i.hasValue=!0,i.value=a},[a]),$c(a),a};Ao.exports=Ro;var Ac=Ao.exports;const Rc=$o(Ac),Ic={},rs=e=>{let t;const n=new Set,r=(l,d)=>{const m=typeof l=="function"?l(t):l;if(!Object.is(m,t)){const f=t;t=d??(typeof m!="object"||m===null)?m:Object.assign({},t,m),n.forEach(p=>p(t,f))}},s=()=>t,c={setState:r,getState:s,getInitialState:()=>u,subscribe:l=>(n.add(l),()=>n.delete(l)),destroy:()=>{(Ic?"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()}},u=t=e(r,s,c);return c},Tc=e=>e?rs(e):rs,{useDebugValue:Oc}=A,{useSyncExternalStoreWithSelector:Lc}=Rc,Dc=e=>e;function Oo(e,t=Dc,n){const r=Lc(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return Oc(r),r}const ss=(e,t)=>{const n=Tc(e),r=(s,o=t)=>Oo(n,s,o);return Object.assign(r,n),r},Pc=(e,t)=>e?ss(e,t):ss;function Ce(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 zc={value:()=>{}};function Hn(){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}})}_n.prototype=Hn.prototype={constructor:_n,on:function(e,t){var n=this._,r=Hc(e+"",n),s,o=-1,i=r.length;if(arguments.length<2){for(;++o0)for(var n=new Array(s),r=0,s,o;r=0&&(t=e.slice(0,n))!=="xmlns"&&(e=e.slice(n+1)),is.hasOwnProperty(t)?{space:is[t],local:e}:e}function Vc(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===mr&&t.documentElement.namespaceURI===mr?t.createElement(e):t.createElementNS(n,e)}}function Bc(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function Lo(e){var t=Fn(e);return(t.local?Bc:Vc)(t)}function Gc(){}function Ar(e){return e==null?Gc:function(){return this.querySelector(e)}}function Yc(e){typeof e!="function"&&(e=Ar(e));for(var t=this._groups,n=t.length,r=new Array(n),s=0;s=M&&(M=E+1);!($=v[M])&&++M=0;)(i=r[s])&&(o&&i.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(i,o),o=i);return this}function gl(e){e||(e=yl);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),o=0;ot?1:e>=t?0:NaN}function xl(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}function wl(){return Array.from(this)}function bl(){for(var e=this._groups,t=0,n=e.length;t1?this.each((t==null?Al:typeof t=="function"?Il:Rl)(e,t,n??"")):Dt(this.node(),e)}function Dt(e,t){return e.style.getPropertyValue(t)||Fo(e).getComputedStyle(e,null).getPropertyValue(t)}function Ol(e){return function(){delete this[e]}}function Ll(e,t){return function(){this[e]=t}}function Dl(e,t){return function(){var n=t.apply(this,arguments);n==null?delete this[e]:this[e]=n}}function Pl(e,t){return arguments.length>1?this.each((t==null?Ol:typeof t=="function"?Dl:Ll)(e,t)):this.node()[e]}function Vo(e){return e.trim().split(/^|\s+/)}function Rr(e){return e.classList||new Bo(e)}function Bo(e){this._node=e,this._names=Vo(e.getAttribute("class")||"")}Bo.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 Go(e,t){for(var n=Rr(e),r=-1,s=t.length;++r=0&&(n=t.slice(r+1),t=t.slice(0,r)),{type:t,name:n}})}function fu(e){return function(){var t=this.__on;if(t){for(var n=0,r=-1,s=t.length,o;n()=>e;function pr(e,{sourceEvent:t,subject:n,target:r,identifier:s,active:o,x:i,y:a,dx:c,dy:u,dispatch:l}){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:o,enumerable:!0,configurable:!0},x:{value:i,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:u,enumerable:!0,configurable:!0},_:{value:l}})}pr.prototype.on=function(){var e=this._.on.apply(this._,arguments);return e===this._?this:e};function _u(e){return!e.ctrlKey&&!e.button}function Eu(){return this.parentNode}function Nu(e,t){return t??{x:e.x,y:e.y}}function Su(){return navigator.maxTouchPoints||"ontouchstart"in this}function ku(){var e=_u,t=Eu,n=Nu,r=Su,s={},o=Hn("start","drag","end"),i=0,a,c,u,l,d=0;function m(b){b.on("mousedown.drag",f).filter(r).on("touchstart.drag",v).on("touchmove.drag",g,vu).on("touchend.drag touchcancel.drag",E).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(b,$){if(!(l||!e.call(this,b,$))){var j=M(this,t.call(this,b,$),b,$,"mouse");j&&(Fe(b.view).on("mousemove.drag",p,Zt).on("mouseup.drag",x,Zt),Uo(b.view),Kn(b),u=!1,a=b.clientX,c=b.clientY,j("start",b))}}function p(b){if(It(b),!u){var $=b.clientX-a,j=b.clientY-c;u=$*$+j*j>d}s.mouse("drag",b)}function x(b){Fe(b.view).on("mousemove.drag mouseup.drag",null),qo(b.view,u),It(b),s.mouse("end",b)}function v(b,$){if(e.call(this,b,$)){var j=b.changedTouches,_=t.call(this,b,$),R=j.length,N,I;for(N=0;N>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?hn(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?hn(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=Cu.exec(e))?new De(t[1],t[2],t[3],1):(t=Mu.exec(e))?new De(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=$u.exec(e))?hn(t[1],t[2],t[3],t[4]):(t=Au.exec(e))?hn(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=Ru.exec(e))?hs(t[1],t[2]/100,t[3]/100,1):(t=Iu.exec(e))?hs(t[1],t[2]/100,t[3]/100,t[4]):as.hasOwnProperty(e)?us(as[e]):e==="transparent"?new De(NaN,NaN,NaN,0):null}function us(e){return new De(e>>16&255,e>>8&255,e&255,1)}function hn(e,t,n,r){return r<=0&&(e=t=n=NaN),new De(e,t,n,r)}function Lu(e){return e instanceof an||(e=en(e)),e?(e=e.rgb(),new De(e.r,e.g,e.b,e.opacity)):new De}function gr(e,t,n,r){return arguments.length===1?Lu(e):new De(e,t,n,r??1)}function De(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}Ir(De,gr,Ko(an,{brighter(e){return e=e==null?jn:Math.pow(jn,e),new De(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?Qt:Math.pow(Qt,e),new De(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new De(_t(this.r),_t(this.g),_t(this.b),Cn(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:ds,formatHex:ds,formatHex8:Du,formatRgb:fs,toString:fs}));function ds(){return`#${bt(this.r)}${bt(this.g)}${bt(this.b)}`}function Du(){return`#${bt(this.r)}${bt(this.g)}${bt(this.b)}${bt((isNaN(this.opacity)?1:this.opacity)*255)}`}function fs(){const e=Cn(this.opacity);return`${e===1?"rgb(":"rgba("}${_t(this.r)}, ${_t(this.g)}, ${_t(this.b)}${e===1?")":`, ${e})`}`}function Cn(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function _t(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function bt(e){return e=_t(e),(e<16?"0":"")+e.toString(16)}function hs(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new We(e,t,n,r)}function Zo(e){if(e instanceof We)return new We(e.h,e.s,e.l,e.opacity);if(e instanceof an||(e=en(e)),!e)return new We;if(e instanceof We)return e;e=e.rgb();var t=e.r/255,n=e.g/255,r=e.b/255,s=Math.min(t,n,r),o=Math.max(t,n,r),i=NaN,a=o-s,c=(o+s)/2;return a?(t===o?i=(n-r)/a+(n0&&c<1?0:i,new We(i,a,c,e.opacity)}function Pu(e,t,n,r){return arguments.length===1?Zo(e):new We(e,t,n,r??1)}function We(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}Ir(We,Pu,Ko(an,{brighter(e){return e=e==null?jn:Math.pow(jn,e),new We(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?Qt:Math.pow(Qt,e),new We(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 De(Zn(e>=240?e-240:e+120,s,r),Zn(e,s,r),Zn(e<120?e+240:e-120,s,r),this.opacity)},clamp(){return new We(ms(this.h),mn(this.s),mn(this.l),Cn(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=Cn(this.opacity);return`${e===1?"hsl(":"hsla("}${ms(this.h)}, ${mn(this.s)*100}%, ${mn(this.l)*100}%${e===1?")":`, ${e})`}`}}));function ms(e){return e=(e||0)%360,e<0?e+360:e}function mn(e){return Math.max(0,Math.min(1,e||0))}function Zn(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 Qo=e=>()=>e;function zu(e,t){return function(n){return e+n*t}}function Hu(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 Fu(e){return(e=+e)==1?Jo:function(t,n){return n-t?Hu(t,n,e):Qo(isNaN(t)?n:t)}}function Jo(e,t){var n=t-e;return n?zu(e,n):Qo(isNaN(e)?t:e)}const ps=function e(t){var n=Fu(t);function r(s,o){var i=n((s=gr(s)).r,(o=gr(o)).r),a=n(s.g,o.g),c=n(s.b,o.b),u=Jo(s.opacity,o.opacity);return function(l){return s.r=i(l),s.g=a(l),s.b=c(l),s.opacity=u(l),s+""}}return r.gamma=e,r}(1);function ft(e,t){return e=+e,t=+t,function(n){return e*(1-n)+t*n}}var yr=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Qn=new RegExp(yr.source,"g");function Vu(e){return function(){return e}}function Bu(e){return function(t){return e(t)+""}}function Gu(e,t){var n=yr.lastIndex=Qn.lastIndex=0,r,s,o,i=-1,a=[],c=[];for(e=e+"",t=t+"";(r=yr.exec(e))&&(s=Qn.exec(t));)(o=s.index)>n&&(o=t.slice(n,o),a[i]?a[i]+=o:a[++i]=o),(r=r[0])===(s=s[0])?a[i]?a[i]+=s:a[++i]=s:(a[++i]=null,c.push({i,x:ft(r,s)})),n=Qn.lastIndex;return n180?l+=360:l-u>180&&(u+=360),m.push({i:d.push(s(d)+"rotate(",null,r)-2,x:ft(u,l)})):l&&d.push(s(d)+"rotate("+l+r)}function a(u,l,d,m){u!==l?m.push({i:d.push(s(d)+"skewX(",null,r)-2,x:ft(u,l)}):l&&d.push(s(d)+"skewX("+l+r)}function c(u,l,d,m,f,p){if(u!==d||l!==m){var x=f.push(s(f)+"scale(",null,",",null,")");p.push({i:x-4,x:ft(u,d)},{i:x-2,x:ft(l,m)})}else(d!==1||m!==1)&&f.push(s(f)+"scale("+d+","+m+")")}return function(u,l){var d=[],m=[];return u=e(u),l=e(l),o(u.translateX,u.translateY,l.translateX,l.translateY,d,m),i(u.rotate,l.rotate,d,m),a(u.skewX,l.skewX,d,m),c(u.scaleX,u.scaleY,l.scaleX,l.scaleY,d,m),u=l=null,function(f){for(var p=-1,x=m.length,v;++p=0&&e._call.call(void 0,t),e=e._next;--Pt}function xs(){Nt=($n=tn.now())+Vn,Pt=Wt=0;try{ed()}finally{Pt=0,nd(),Nt=0}}function td(){var e=tn.now(),t=e-$n;t>ni&&(Vn-=t,$n=e)}function nd(){for(var e,t=Mn,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:Mn=n);Ut=e,wr(r)}function wr(e){if(!Pt){Wt&&(Wt=clearTimeout(Wt));var t=e-Nt;t>24?(e<1/0&&(Wt=setTimeout(xs,e-tn.now()-Vn)),Vt&&(Vt=clearInterval(Vt))):(Vt||($n=tn.now(),Vt=setInterval(td,ni)),Pt=1,ri(xs))}}function ws(e,t,n){var r=new An;return t=t==null?0:+t,r.restart(s=>{r.stop(),e(s+t)},t,n),r}var rd=Hn("start","end","cancel","interrupt"),sd=[],oi=0,bs=1,br=2,En=3,vs=4,vr=5,Nn=6;function Bn(e,t,n,r,s,o){var i=e.__transition;if(!i)e.__transition={};else if(n in i)return;od(e,n,{name:t,index:r,group:s,on:rd,tween:sd,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:oi})}function Or(e,t){var n=Ue(e,t);if(n.state>oi)throw new Error("too late; already scheduled");return n}function Je(e,t){var n=Ue(e,t);if(n.state>En)throw new Error("too late; already running");return n}function Ue(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw new Error("transition not found");return n}function od(e,t,n){var r=e.__transition,s;r[t]=n,n.timer=si(o,0,n.time);function o(u){n.state=bs,n.timer.restart(i,n.delay,n.time),n.delay<=u&&i(u-n.delay)}function i(u){var l,d,m,f;if(n.state!==bs)return c();for(l in r)if(f=r[l],f.name===n.name){if(f.state===En)return ws(i);f.state===vs?(f.state=Nn,f.timer.stop(),f.on.call("interrupt",e,e.__data__,f.index,f.group),delete r[l]):+lbr&&r.state=0&&(t=t.slice(0,n)),!t||t==="start"})}function Od(e,t,n){var r,s,o=Td(t)?Or:Je;return function(){var i=o(this,e),a=i.on;a!==r&&(s=(r=a).copy()).on(t,n),i.on=s}}function Ld(e,t){var n=this._id;return arguments.length<2?Ue(this.node(),n).on.on(e):this.each(Od(n,e,t))}function Dd(e){return function(){var t=this.parentNode;for(var n in this.__transition)if(+n!==e)return;t&&t.removeChild(this)}}function Pd(){return this.on("end.remove",Dd(this._id))}function zd(e){var t=this._name,n=this._id;typeof e!="function"&&(e=Ar(e));for(var r=this._groups,s=r.length,o=new Array(s),i=0;i()=>e;function df(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 st(e,t,n){this.k=e,this.x=t,this.y=n}st.prototype={constructor:st,scale:function(e){return e===1?this:new st(this.k*e,this.x,this.y)},translate:function(e,t){return e===0&t===0?this:new st(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 ot=new st(1,0,0);st.prototype;function Jn(e){e.stopImmediatePropagation()}function Bt(e){e.preventDefault(),e.stopImmediatePropagation()}function ff(e){return(!e.ctrlKey||e.type==="wheel")&&!e.button}function hf(){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 _s(){return this.__zoom||ot}function mf(e){return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*(e.ctrlKey?10:1)}function pf(){return navigator.maxTouchPoints||"ontouchstart"in this}function gf(e,t,n){var r=e.invertX(t[0][0])-n[0][0],s=e.invertX(t[1][0])-n[1][0],o=e.invertY(t[0][1])-n[0][1],i=e.invertY(t[1][1])-n[1][1];return e.translate(s>r?(r+s)/2:Math.min(0,r)||Math.max(0,s),i>o?(o+i)/2:Math.min(0,o)||Math.max(0,i))}function li(){var e=ff,t=hf,n=gf,r=mf,s=pf,o=[0,1/0],i=[[-1/0,-1/0],[1/0,1/0]],a=250,c=Qu,u=Hn("start","zoom","end"),l,d,m,f=500,p=150,x=0,v=10;function g(y){y.property("__zoom",_s).on("wheel.zoom",R,{passive:!1}).on("mousedown.zoom",N).on("dblclick.zoom",I).filter(s).on("touchstart.zoom",L).on("touchmove.zoom",B).on("touchend.zoom touchcancel.zoom",H).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}g.transform=function(y,C,S,O){var D=y.selection?y.selection():y;D.property("__zoom",_s),y!==D?$(y,C,S,O):D.interrupt().each(function(){j(this,arguments).event(O).start().zoom(null,typeof C=="function"?C.apply(this,arguments):C).end()})},g.scaleBy=function(y,C,S,O){g.scaleTo(y,function(){var D=this.__zoom.k,k=typeof C=="function"?C.apply(this,arguments):C;return D*k},S,O)},g.scaleTo=function(y,C,S,O){g.transform(y,function(){var D=t.apply(this,arguments),k=this.__zoom,T=S==null?b(D):typeof S=="function"?S.apply(this,arguments):S,P=k.invert(T),z=typeof C=="function"?C.apply(this,arguments):C;return n(M(E(k,z),T,P),D,i)},S,O)},g.translateBy=function(y,C,S,O){g.transform(y,function(){return n(this.__zoom.translate(typeof C=="function"?C.apply(this,arguments):C,typeof S=="function"?S.apply(this,arguments):S),t.apply(this,arguments),i)},null,O)},g.translateTo=function(y,C,S,O,D){g.transform(y,function(){var k=t.apply(this,arguments),T=this.__zoom,P=O==null?b(k):typeof O=="function"?O.apply(this,arguments):O;return n(ot.translate(P[0],P[1]).scale(T.k).translate(typeof C=="function"?-C.apply(this,arguments):-C,typeof S=="function"?-S.apply(this,arguments):-S),k,i)},O,D)};function E(y,C){return C=Math.max(o[0],Math.min(o[1],C)),C===y.k?y:new st(C,y.x,y.y)}function M(y,C,S){var O=C[0]-S[0]*y.k,D=C[1]-S[1]*y.k;return O===y.x&&D===y.y?y:new st(y.k,O,D)}function b(y){return[(+y[0][0]+ +y[1][0])/2,(+y[0][1]+ +y[1][1])/2]}function $(y,C,S,O){y.on("start.zoom",function(){j(this,arguments).event(O).start()}).on("interrupt.zoom end.zoom",function(){j(this,arguments).event(O).end()}).tween("zoom",function(){var D=this,k=arguments,T=j(D,k).event(O),P=t.apply(D,k),z=S==null?b(P):typeof S=="function"?S.apply(D,k):S,U=Math.max(P[1][0]-P[0][0],P[1][1]-P[0][1]),W=D.__zoom,Q=typeof C=="function"?C.apply(D,k):C,te=c(W.invert(z).concat(U/W.k),Q.invert(z).concat(U/Q.k));return function(se){if(se===1)se=Q;else{var J=te(se),ae=U/J[2];se=new st(ae,z[0]-J[0]*ae,z[1]-J[1]*ae)}T.zoom(null,se)}})}function j(y,C,S){return!S&&y.__zooming||new _(y,C)}function _(y,C){this.that=y,this.args=C,this.active=0,this.sourceEvent=null,this.extent=t.apply(y,C),this.taps=0}_.prototype={event:function(y){return y&&(this.sourceEvent=y),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(y,C){return this.mouse&&y!=="mouse"&&(this.mouse[1]=C.invert(this.mouse[0])),this.touch0&&y!=="touch"&&(this.touch0[1]=C.invert(this.touch0[0])),this.touch1&&y!=="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(y){var C=Fe(this.that).datum();u.call(y,this.that,new df(y,{sourceEvent:this.sourceEvent,target:g,transform:this.that.__zoom,dispatch:u}),C)}};function R(y,...C){if(!e.apply(this,arguments))return;var S=j(this,C).event(y),O=this.__zoom,D=Math.max(o[0],Math.min(o[1],O.k*Math.pow(2,r.apply(this,arguments)))),k=Xe(y);if(S.wheel)(S.mouse[0][0]!==k[0]||S.mouse[0][1]!==k[1])&&(S.mouse[1]=O.invert(S.mouse[0]=k)),clearTimeout(S.wheel);else{if(O.k===D)return;S.mouse=[k,O.invert(k)],Sn(this),S.start()}Bt(y),S.wheel=setTimeout(T,p),S.zoom("mouse",n(M(E(O,D),S.mouse[0],S.mouse[1]),S.extent,i));function T(){S.wheel=null,S.end()}}function N(y,...C){if(m||!e.apply(this,arguments))return;var S=y.currentTarget,O=j(this,C,!0).event(y),D=Fe(y.view).on("mousemove.zoom",z,!0).on("mouseup.zoom",U,!0),k=Xe(y,S),T=y.clientX,P=y.clientY;Uo(y.view),Jn(y),O.mouse=[k,this.__zoom.invert(k)],Sn(this),O.start();function z(W){if(Bt(W),!O.moved){var Q=W.clientX-T,te=W.clientY-P;O.moved=Q*Q+te*te>x}O.event(W).zoom("mouse",n(M(O.that.__zoom,O.mouse[0]=Xe(W,S),O.mouse[1]),O.extent,i))}function U(W){D.on("mousemove.zoom mouseup.zoom",null),qo(W.view,O.moved),Bt(W),O.event(W).end()}}function I(y,...C){if(e.apply(this,arguments)){var S=this.__zoom,O=Xe(y.changedTouches?y.changedTouches[0]:y,this),D=S.invert(O),k=S.k*(y.shiftKey?.5:2),T=n(M(E(S,k),O,D),t.apply(this,C),i);Bt(y),a>0?Fe(this).transition().duration(a).call($,T,O,y):Fe(this).call(g.transform,T,O,y)}}function L(y,...C){if(e.apply(this,arguments)){var S=y.touches,O=S.length,D=j(this,C,y.changedTouches.length===O).event(y),k,T,P,z;for(Jn(y),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.`},ui=at.error001();function me(e,t){const n=w.useContext(Gn);if(n===null)throw new Error(ui);return Oo(n,e,t)}const ke=()=>{const e=w.useContext(Gn);if(e===null)throw new Error(ui);return w.useMemo(()=>({getState:e.getState,setState:e.setState,subscribe:e.subscribe,destroy:e.destroy}),[e])},xf=e=>e.userSelectionActive?"none":"all";function Dr({position:e,children:t,className:n,style:r,...s}){const o=me(xf),i=`${e}`.split("-");return A.createElement("div",{className:Me(["react-flow__panel",n,...i]),style:{...r,pointerEvents:o},...s},t)}function wf({proOptions:e,position:t="bottom-right"}){return e!=null&&e.hideAttribution?null:A.createElement(Dr,{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 bf=({x:e,y:t,label:n,labelStyle:r={},labelShowBg:s=!0,labelBgStyle:o={},labelBgPadding:i=[2,4],labelBgBorderRadius:a=2,children:c,className:u,...l})=>{const d=w.useRef(null),[m,f]=w.useState({x:0,y:0,width:0,height:0}),p=Me(["react-flow__edge-textwrapper",u]);return w.useEffect(()=>{if(d.current){const x=d.current.getBBox();f({x:x.x,y:x.y,width:x.width,height:x.height})}},[n]),typeof n>"u"||!n?null:A.createElement("g",{transform:`translate(${e-m.width/2} ${t-m.height/2})`,className:p,visibility:m.width?"visible":"hidden",...l},s&&A.createElement("rect",{width:m.width+2*i[0],x:-i[0],y:-i[1],height:m.height+2*i[1],className:"react-flow__edge-textbg",style:o,rx:a,ry:a}),A.createElement("text",{className:"react-flow__edge-text",y:m.height/2,dy:"0.3em",ref:d,style:r},n),c)};var vf=w.memo(bf);const Pr=e=>({width:e.offsetWidth,height:e.offsetHeight}),zt=(e,t=0,n=1)=>Math.min(Math.max(e,t),n),zr=(e={x:0,y:0},t)=>({x:zt(e.x,t[0][0],t[1][0]),y:zt(e.y,t[0][1],t[1][1])}),Es=(e,t,n)=>en?-zt(Math.abs(e-n),1,50)/50:0,di=(e,t)=>{const n=Es(e.x,35,t.width-35)*20,r=Es(e.y,35,t.height-35)*20;return[n,r]},fi=e=>{var t;return((t=e.getRootNode)==null?void 0:t.call(e))||(window==null?void 0:window.document)},hi=(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)}),nn=({x:e,y:t,width:n,height:r})=>({x:e,y:t,x2:e+n,y2:t+r}),mi=({x:e,y:t,x2:n,y2:r})=>({x:e,y:t,width:n-e,height:r-t}),Ns=e=>({...e.positionAbsolute||{x:0,y:0},width:e.width||0,height:e.height||0}),_f=(e,t)=>mi(hi(nn(e),nn(t))),_r=(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)},Ef=e=>Ve(e.width)&&Ve(e.height)&&Ve(e.x)&&Ve(e.y),Ve=e=>!isNaN(e)&&isFinite(e),we=Symbol.for("internals"),pi=["Enter"," ","Escape"],Nf=(e,t)=>{},Sf=e=>"nativeEvent"in e;function Er(e){var s,o;const t=Sf(e)?e.nativeEvent:e,n=((o=(s=t.composedPath)==null?void 0:s.call(t))==null?void 0:o[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 gi=e=>"clientX"in e,yt=(e,t)=>{var o,i;const n=gi(e),r=n?e.clientX:(o=e.touches)==null?void 0:o[0].clientX,s=n?e.clientY:(i=e.touches)==null?void 0:i[0].clientY;return{x:r-((t==null?void 0:t.left)??0),y:s-((t==null?void 0:t.top)??0)}},Rn=()=>{var e;return typeof navigator<"u"&&((e=navigator==null?void 0:navigator.userAgent)==null?void 0:e.indexOf("Mac"))>=0},cn=({id:e,path:t,labelX:n,labelY:r,label:s,labelStyle:o,labelShowBg:i,labelBgStyle:a,labelBgPadding:c,labelBgBorderRadius:u,style:l,markerEnd:d,markerStart:m,interactionWidth:f=20})=>A.createElement(A.Fragment,null,A.createElement("path",{id:e,style:l,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&&Ve(n)&&Ve(r)?A.createElement(vf,{x:n,y:r,label:s,labelStyle:o,labelShowBg:i,labelBgStyle:a,labelBgPadding:c,labelBgBorderRadius:u}):null);cn.displayName="BaseEdge";function Gt(e,t,n){return n===void 0?n:r=>{const s=t().edges.find(o=>o.id===e);s&&n(r,{...s})}}function yi({sourceX:e,sourceY:t,targetX:n,targetY:r}){const s=Math.abs(n-e)/2,o=n{const[v,g,E]=wi({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:o});return A.createElement(cn,{path:v,labelX:g,labelY:E,label:i,labelStyle:a,labelShowBg:c,labelBgStyle:u,labelBgPadding:l,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:p,interactionWidth:x})});Hr.displayName="SimpleBezierEdge";const ks={[K.Left]:{x:-1,y:0},[K.Right]:{x:1,y:0},[K.Top]:{x:0,y:-1},[K.Bottom]:{x:0,y:1}},kf=({source:e,sourcePosition:t=K.Bottom,target:n})=>t===K.Left||t===K.Right?e.xMath.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2));function jf({source:e,sourcePosition:t=K.Bottom,target:n,targetPosition:r=K.Top,center:s,offset:o}){const i=ks[t],a=ks[r],c={x:e.x+i.x*o,y:e.y+i.y*o},u={x:n.x+a.x*o,y:n.y+a.y*o},l=kf({source:c,sourcePosition:t,target:u}),d=l.x!==0?"x":"y",m=l[d];let f=[],p,x;const v={x:0,y:0},g={x:0,y:0},[E,M,b,$]=yi({sourceX:e.x,sourceY:e.y,targetX:n.x,targetY:n.y});if(i[d]*a[d]===-1){p=s.x??E,x=s.y??M;const _=[{x:p,y:c.y},{x:p,y:u.y}],R=[{x:c.x,y:x},{x:u.x,y:x}];i[d]===m?f=d==="x"?_:R:f=d==="x"?R:_}else{const _=[{x:c.x,y:u.y}],R=[{x:u.x,y:c.y}];if(d==="x"?f=i.x===m?R:_:f=i.y===m?_:R,t===r){const H=Math.abs(e[d]-n[d]);if(H<=o){const y=Math.min(o-1,o-H);i[d]===m?v[d]=(c[d]>e[d]?-1:1)*y:g[d]=(u[d]>n[d]?-1:1)*y}}if(t!==r){const H=d==="x"?"y":"x",y=i[d]===a[H],C=c[H]>u[H],S=c[H]=B?(p=(N.x+I.x)/2,x=f[0].y):(p=f[0].x,x=(N.y+I.y)/2)}return[[e,{x:c.x+v.x,y:c.y+v.y},...f,{x:u.x+g.x,y:u.y+g.y},n],p,x,b,$]}function Cf(e,t,n,r){const s=Math.min(js(e,t)/2,js(t,n)/2,r),{x:o,y:i}=t;if(e.x===o&&o===n.x||e.y===i&&i===n.y)return`L${o} ${i}`;if(e.y===i){const u=e.x{let M="";return E>0&&E{const[g,E,M]=Tn({sourceX:e,sourceY:t,sourcePosition:d,targetX:n,targetY:r,targetPosition:m,borderRadius:x==null?void 0:x.borderRadius,offset:x==null?void 0:x.offset});return A.createElement(cn,{path:g,labelX:E,labelY:M,label:s,labelStyle:o,labelShowBg:i,labelBgStyle:a,labelBgPadding:c,labelBgBorderRadius:u,style:l,markerEnd:f,markerStart:p,interactionWidth:v})});Yn.displayName="SmoothStepEdge";const Fr=w.memo(e=>{var t;return A.createElement(Yn,{...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])})});Fr.displayName="StepEdge";function Mf({sourceX:e,sourceY:t,targetX:n,targetY:r}){const[s,o,i,a]=yi({sourceX:e,sourceY:t,targetX:n,targetY:r});return[`M ${e},${t}L ${n},${r}`,s,o,i,a]}const Vr=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,label:s,labelStyle:o,labelShowBg:i,labelBgStyle:a,labelBgPadding:c,labelBgBorderRadius:u,style:l,markerEnd:d,markerStart:m,interactionWidth:f})=>{const[p,x,v]=Mf({sourceX:e,sourceY:t,targetX:n,targetY:r});return A.createElement(cn,{path:p,labelX:x,labelY:v,label:s,labelStyle:o,labelShowBg:i,labelBgStyle:a,labelBgPadding:c,labelBgBorderRadius:u,style:l,markerEnd:d,markerStart:m,interactionWidth:f})});Vr.displayName="StraightEdge";function yn(e,t){return e>=0?.5*e:t*25*Math.sqrt(-e)}function Cs({pos:e,x1:t,y1:n,x2:r,y2:s,c:o}){switch(e){case K.Left:return[t-yn(t-r,o),n];case K.Right:return[t+yn(r-t,o),n];case K.Top:return[t,n-yn(n-s,o)];case K.Bottom:return[t,n+yn(s-n,o)]}}function bi({sourceX:e,sourceY:t,sourcePosition:n=K.Bottom,targetX:r,targetY:s,targetPosition:o=K.Top,curvature:i=.25}){const[a,c]=Cs({pos:n,x1:e,y1:t,x2:r,y2:s,c:i}),[u,l]=Cs({pos:o,x1:r,y1:s,x2:e,y2:t,c:i}),[d,m,f,p]=xi({sourceX:e,sourceY:t,targetX:r,targetY:s,sourceControlX:a,sourceControlY:c,targetControlX:u,targetControlY:l});return[`M${e},${t} C${a},${c} ${u},${l} ${r},${s}`,d,m,f,p]}const On=w.memo(({sourceX:e,sourceY:t,targetX:n,targetY:r,sourcePosition:s=K.Bottom,targetPosition:o=K.Top,label:i,labelStyle:a,labelShowBg:c,labelBgStyle:u,labelBgPadding:l,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:p,pathOptions:x,interactionWidth:v})=>{const[g,E,M]=bi({sourceX:e,sourceY:t,sourcePosition:s,targetX:n,targetY:r,targetPosition:o,curvature:x==null?void 0:x.curvature});return A.createElement(cn,{path:g,labelX:E,labelY:M,label:i,labelStyle:a,labelShowBg:c,labelBgStyle:u,labelBgPadding:l,labelBgBorderRadius:d,style:m,markerEnd:f,markerStart:p,interactionWidth:v})});On.displayName="BezierEdge";const Br=w.createContext(null),$f=Br.Provider;Br.Consumer;const Af=()=>w.useContext(Br),Rf=e=>"id"in e&&"source"in e&&"target"in e,If=({source:e,sourceHandle:t,target:n,targetHandle:r})=>`reactflow__edge-${e}${t||""}-${n}${r||""}`,Nr=(e,t)=>typeof e>"u"?"":typeof e=="string"?e:`${t?`${t}__`:""}${Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("&")}`,Tf=(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)),Of=(e,t)=>{if(!e.source||!e.target)return t;let n;return Rf(e)?n={...e}:n={...e,id:If(e)},Tf(n,t)?t:t.concat(n)},Sr=({x:e,y:t},[n,r,s],o,[i,a])=>{const c={x:(e-n)/s,y:(t-r)/s};return o?{x:i*Math.round(c.x/i),y:a*Math.round(c.y/a)}:c},vi=({x:e,y:t},[n,r,s])=>({x:e*s+n,y:t*s+r}),Et=(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}},ln=(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:o,y:i}=Et(s,t).positionAbsolute;return hi(r,nn({x:o,y:i,width:s.width||0,height:s.height||0}))},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return mi(n)},Lf=(e,t=[0,0])=>(console.warn("[DEPRECATED] `getRectOfNodes` is deprecated. Instead use `getNodesBounds` https://reactflow.dev/api-reference/utils/get-nodes-bounds."),ln(e,t)),_i=(e,t,[n,r,s]=[0,0,1],o=!1,i=!1,a=[0,0])=>{const c={x:(t.x-n)/s,y:(t.y-r)/s,width:t.width/s,height:t.height/s},u=[];return e.forEach(l=>{const{width:d,height:m,selectable:f=!0,hidden:p=!1}=l;if(i&&!f||p)return!1;const{positionAbsolute:x}=Et(l,a),v={x:x.x,y:x.y,width:d||0,height:m||0},g=_r(c,v),E=typeof d>"u"||typeof m>"u"||d===null||m===null,M=o&&g>0,b=(d||0)*(m||0);(E||M||g>=b||l.dragging)&&u.push(l)}),u},Ei=(e,t)=>{const n=e.map(r=>r.id);return t.filter(r=>n.includes(r.source)||n.includes(r.target))},Ni=(e,t,n,r,s,o=.1)=>{const i=t/(e.width*(1+o)),a=n/(e.height*(1+o)),c=Math.min(i,a),u=zt(c,r,s),l=e.x+e.width/2,d=e.y+e.height/2,m=t/2-l*u,f=n/2-d*u;return{x:m,y:f,zoom:u}},wt=(e,t=0)=>e.transition().duration(t);function Ms(e,t,n,r){return(t[n]||[]).reduce((s,o)=>{var i,a;return`${e.id}-${o.id}-${n}`!==r&&s.push({id:o.id||null,type:n,nodeId:e.id,x:(((i=e.positionAbsolute)==null?void 0:i.x)??0)+o.x+o.width/2,y:(((a=e.positionAbsolute)==null?void 0:a.y)??0)+o.y+o.height/2}),s},[])}function Df(e,t,n,r,s,o){const{x:i,y:a}=yt(e),u=t.elementsFromPoint(i,a).find(p=>p.classList.contains("react-flow__handle"));if(u){const p=u.getAttribute("data-nodeid");if(p){const x=Gr(void 0,u),v=u.getAttribute("data-handleid"),g=o({nodeId:p,id:v,type:x});if(g){const E=s.find(M=>M.nodeId===p&&M.type===x&&M.id===v);return{handle:{id:v,type:x,nodeId:p,x:(E==null?void 0:E.x)||n.x,y:(E==null?void 0:E.y)||n.y},validHandleResult:g}}}}let l=[],d=1/0;if(s.forEach(p=>{const x=Math.sqrt((p.x-n.x)**2+(p.y-n.y)**2);if(x<=r){const v=o(p);x<=d&&(xp.isValid),f=l.some(({handle:p})=>p.type==="target");return l.find(({handle:p,validHandleResult:x})=>f?p.type==="target":m?x.isValid:!0)||l[0]}const Pf={source:null,target:null,sourceHandle:null,targetHandle:null},Si=()=>({handleDomNode:null,isValid:!1,connection:Pf,endHandle:null});function ki(e,t,n,r,s,o,i){const a=s==="target",c=i.querySelector(`.react-flow__handle[data-id="${e==null?void 0:e.nodeId}-${e==null?void 0:e.id}-${e==null?void 0:e.type}"]`),u={...Si(),handleDomNode:c};if(c){const l=Gr(void 0,c),d=c.getAttribute("data-nodeid"),m=c.getAttribute("data-handleid"),f=c.classList.contains("connectable"),p=c.classList.contains("connectableend"),x={source:a?d:n,sourceHandle:a?m:r,target:a?n:d,targetHandle:a?r:m};u.connection=x,f&&p&&(t===St.Strict?a&&l==="source"||!a&&l==="target":d!==n||m!==r)&&(u.endHandle={nodeId:d,handleId:m,type:l},u.isValid=o(x))}return u}function zf({nodes:e,nodeId:t,handleId:n,handleType:r}){return e.reduce((s,o)=>{if(o[we]){const{handleBounds:i}=o[we];let a=[],c=[];i&&(a=Ms(o,i,"source",`${t}-${n}-${r}`),c=Ms(o,i,"target",`${t}-${n}-${r}`)),s.push(...a,...c)}return s},[])}function Gr(e,t){return e||(t!=null&&t.classList.contains("target")?"target":t!=null&&t.classList.contains("source")?"source":null)}function er(e){e==null||e.classList.remove("valid","connecting","react-flow__handle-valid","react-flow__handle-connecting")}function Hf(e,t){let n=null;return t?n="valid":e&&!t&&(n="invalid"),n}function ji({event:e,handleId:t,nodeId:n,onConnect:r,isTarget:s,getState:o,setState:i,isValidConnection:a,edgeUpdaterType:c,onReconnectEnd:u}){const l=fi(e.target),{connectionMode:d,domNode:m,autoPanOnConnect:f,connectionRadius:p,onConnectStart:x,panBy:v,getNodes:g,cancelConnection:E}=o();let M=0,b;const{x:$,y:j}=yt(e),_=l==null?void 0:l.elementFromPoint($,j),R=Gr(c,_),N=m==null?void 0:m.getBoundingClientRect();if(!N||!R)return;let I,L=yt(e,N),B=!1,H=null,y=!1,C=null;const S=zf({nodes:g(),nodeId:n,handleId:t,handleType:R}),O=()=>{if(!f)return;const[T,P]=di(L,N);v({x:T,y:P}),M=requestAnimationFrame(O)};i({connectionPosition:L,connectionStatus:null,connectionNodeId:n,connectionHandleId:t,connectionHandleType:R,connectionStartHandle:{nodeId:n,handleId:t,type:R},connectionEndHandle:null}),x==null||x(e,{nodeId:n,handleId:t,handleType:R});function D(T){const{transform:P}=o();L=yt(T,N);const{handle:z,validHandleResult:U}=Df(T,l,Sr(L,P,!1,[1,1]),p,S,W=>ki(W,d,n,t,s?"target":"source",a,l));if(b=z,B||(O(),B=!0),C=U.handleDomNode,H=U.connection,y=U.isValid,i({connectionPosition:b&&y?vi({x:b.x,y:b.y},P):L,connectionStatus:Hf(!!b,y),connectionEndHandle:U.endHandle}),!b&&!y&&!C)return er(I);H.source!==H.target&&C&&(er(I),I=C,C.classList.add("connecting","react-flow__handle-connecting"),C.classList.toggle("valid",y),C.classList.toggle("react-flow__handle-valid",y))}function k(T){var P,z;(b||C)&&H&&y&&(r==null||r(H)),(z=(P=o()).onConnectEnd)==null||z.call(P,T),c&&(u==null||u(T)),er(I),E(),cancelAnimationFrame(M),B=!1,y=!1,H=null,C=null,l.removeEventListener("mousemove",D),l.removeEventListener("mouseup",k),l.removeEventListener("touchmove",D),l.removeEventListener("touchend",k)}l.addEventListener("mousemove",D),l.addEventListener("mouseup",k),l.addEventListener("touchmove",D),l.addEventListener("touchend",k)}const $s=()=>!0,Ff=e=>({connectionStartHandle:e.connectionStartHandle,connectOnClick:e.connectOnClick,noPanClassName:e.noPanClassName}),Vf=(e,t,n)=>r=>{const{connectionStartHandle:s,connectionEndHandle:o,connectionClickStartHandle:i}=r;return{connecting:(s==null?void 0:s.nodeId)===e&&(s==null?void 0:s.handleId)===t&&(s==null?void 0:s.type)===n||(o==null?void 0:o.nodeId)===e&&(o==null?void 0:o.handleId)===t&&(o==null?void 0:o.type)===n,clickConnecting:(i==null?void 0:i.nodeId)===e&&(i==null?void 0:i.handleId)===t&&(i==null?void 0:i.type)===n}},Ci=w.forwardRef(({type:e="source",position:t=K.Top,isValidConnection:n,isConnectable:r=!0,isConnectableStart:s=!0,isConnectableEnd:o=!0,id:i,onConnect:a,children:c,className:u,onMouseDown:l,onTouchStart:d,...m},f)=>{var N,I;const p=i||null,x=e==="target",v=ke(),g=Af(),{connectOnClick:E,noPanClassName:M}=me(Ff,Ce),{connecting:b,clickConnecting:$}=me(Vf(g,p,e),Ce);g||(I=(N=v.getState()).onError)==null||I.call(N,"010",at.error010());const j=L=>{const{defaultEdgeOptions:B,onConnect:H,hasDefaultEdges:y}=v.getState(),C={...B,...L};if(y){const{edges:S,setEdges:O}=v.getState();O(Of(C,S))}H==null||H(C),a==null||a(C)},_=L=>{if(!g)return;const B=gi(L);s&&(B&&L.button===0||!B)&&ji({event:L,handleId:p,nodeId:g,onConnect:j,isTarget:x,getState:v.getState,setState:v.setState,isValidConnection:n||v.getState().isValidConnection||$s}),B?l==null||l(L):d==null||d(L)},R=L=>{const{onClickConnectStart:B,onClickConnectEnd:H,connectionClickStartHandle:y,connectionMode:C,isValidConnection:S}=v.getState();if(!g||!y&&!s)return;if(!y){B==null||B(L,{nodeId:g,handleId:p,handleType:e}),v.setState({connectionClickStartHandle:{nodeId:g,type:e,handleId:p}});return}const O=fi(L.target),D=n||S||$s,{connection:k,isValid:T}=ki({nodeId:g,id:p,type:e},C,y.nodeId,y.handleId||null,y.type,D,O);T&&j(k),H==null||H(L),v.setState({connectionClickStartHandle:null})};return A.createElement("div",{"data-handleid":p,"data-nodeid":g,"data-handlepos":t,"data-id":`${g}-${p}-${e}`,className:Me(["react-flow__handle",`react-flow__handle-${t}`,"nodrag",M,u,{source:!x,target:x,connectable:r,connectablestart:s,connectableend:o,connecting:$,connectionindicator:r&&(s&&!b||o&&b)}]),onMouseDown:_,onTouchStart:_,onClick:E?R:void 0,ref:f,...m},c)});Ci.displayName="Handle";var pt=w.memo(Ci);const Mi=({data:e,isConnectable:t,targetPosition:n=K.Top,sourcePosition:r=K.Bottom})=>A.createElement(A.Fragment,null,A.createElement(pt,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label,A.createElement(pt,{type:"source",position:r,isConnectable:t}));Mi.displayName="DefaultNode";var kr=w.memo(Mi);const $i=({data:e,isConnectable:t,sourcePosition:n=K.Bottom})=>A.createElement(A.Fragment,null,e==null?void 0:e.label,A.createElement(pt,{type:"source",position:n,isConnectable:t}));$i.displayName="InputNode";var Ai=w.memo($i);const Ri=({data:e,isConnectable:t,targetPosition:n=K.Top})=>A.createElement(A.Fragment,null,A.createElement(pt,{type:"target",position:n,isConnectable:t}),e==null?void 0:e.label);Ri.displayName="OutputNode";var Ii=w.memo(Ri);const Yr=()=>null;Yr.displayName="GroupNode";const Bf=e=>({selectedNodes:e.getNodes().filter(t=>t.selected),selectedEdges:e.edges.filter(t=>t.selected).map(t=>({...t}))}),xn=e=>e.id;function Gf(e,t){return Ce(e.selectedNodes.map(xn),t.selectedNodes.map(xn))&&Ce(e.selectedEdges.map(xn),t.selectedEdges.map(xn))}const Ti=w.memo(({onSelectionChange:e})=>{const t=ke(),{selectedNodes:n,selectedEdges:r}=me(Bf,Gf);return w.useEffect(()=>{const s={nodes:n,edges:r};e==null||e(s),t.getState().onSelectionChange.forEach(o=>o(s))},[n,r,e]),null});Ti.displayName="SelectionListener";const Yf=e=>!!e.onSelectionChange;function Xf({onSelectionChange:e}){const t=me(Yf);return e||t?A.createElement(Ti,{onSelectionChange:e}):null}const Wf=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 jt(e,t){w.useEffect(()=>{typeof e<"u"&&t(e)},[e])}function ce(e,t,n){w.useEffect(()=>{typeof t<"u"&&n({[e]:t})},[t])}const Uf=({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:s,onConnectStart:o,onConnectEnd:i,onClickConnectStart:a,onClickConnectEnd:c,nodesDraggable:u,nodesConnectable:l,nodesFocusable:d,edgesFocusable:m,edgesUpdatable:f,elevateNodesOnSelect:p,minZoom:x,maxZoom:v,nodeExtent:g,onNodesChange:E,onEdgesChange:M,elementsSelectable:b,connectionMode:$,snapGrid:j,snapToGrid:_,translateExtent:R,connectOnClick:N,defaultEdgeOptions:I,fitView:L,fitViewOptions:B,onNodesDelete:H,onEdgesDelete:y,onNodeDrag:C,onNodeDragStart:S,onNodeDragStop:O,onSelectionDrag:D,onSelectionDragStart:k,onSelectionDragStop:T,noPanClassName:P,nodeOrigin:z,rfId:U,autoPanOnConnect:W,autoPanOnNodeDrag:Q,onError:te,connectionRadius:se,isValidConnection:J,nodeDragThreshold:ae})=>{const{setNodes:oe,setEdges:he,setDefaultNodesAndEdges:ye,setMinZoom:Ee,setMaxZoom:ve,setTranslateExtent:ue,setNodeExtent:ge,reset:le}=me(Wf,Ce),q=ke();return w.useEffect(()=>{const fe=r==null?void 0:r.map(Oe=>({...Oe,...I}));return ye(n,fe),()=>{le()}},[]),ce("defaultEdgeOptions",I,q.setState),ce("connectionMode",$,q.setState),ce("onConnect",s,q.setState),ce("onConnectStart",o,q.setState),ce("onConnectEnd",i,q.setState),ce("onClickConnectStart",a,q.setState),ce("onClickConnectEnd",c,q.setState),ce("nodesDraggable",u,q.setState),ce("nodesConnectable",l,q.setState),ce("nodesFocusable",d,q.setState),ce("edgesFocusable",m,q.setState),ce("edgesUpdatable",f,q.setState),ce("elementsSelectable",b,q.setState),ce("elevateNodesOnSelect",p,q.setState),ce("snapToGrid",_,q.setState),ce("snapGrid",j,q.setState),ce("onNodesChange",E,q.setState),ce("onEdgesChange",M,q.setState),ce("connectOnClick",N,q.setState),ce("fitViewOnInit",L,q.setState),ce("fitViewOnInitOptions",B,q.setState),ce("onNodesDelete",H,q.setState),ce("onEdgesDelete",y,q.setState),ce("onNodeDrag",C,q.setState),ce("onNodeDragStart",S,q.setState),ce("onNodeDragStop",O,q.setState),ce("onSelectionDrag",D,q.setState),ce("onSelectionDragStart",k,q.setState),ce("onSelectionDragStop",T,q.setState),ce("noPanClassName",P,q.setState),ce("nodeOrigin",z,q.setState),ce("rfId",U,q.setState),ce("autoPanOnConnect",W,q.setState),ce("autoPanOnNodeDrag",Q,q.setState),ce("onError",te,q.setState),ce("connectionRadius",se,q.setState),ce("isValidConnection",J,q.setState),ce("nodeDragThreshold",ae,q.setState),jt(e,oe),jt(t,he),jt(x,Ee),jt(v,ve),jt(R,ue),jt(g,ge),null},As={display:"none"},qf={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},Oi="react-flow__node-desc",Li="react-flow__edge-desc",Kf="react-flow__aria-live",Zf=e=>e.ariaLiveMessage;function Qf({rfId:e}){const t=me(Zf);return A.createElement("div",{id:`${Kf}-${e}`,"aria-live":"assertive","aria-atomic":"true",style:qf},t)}function Jf({rfId:e,disableKeyboardA11y:t}){return A.createElement(A.Fragment,null,A.createElement("div",{id:`${Oi}-${e}`,style:As},"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:`${Li}-${e}`,style:As},"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel."),!t&&A.createElement(Qf,{rfId:e}))}var sn=(e=null,t={actInsideInputWithModifier:!0})=>{const[n,r]=w.useState(!1),s=w.useRef(!1),o=w.useRef(new Set([])),[i,a]=w.useMemo(()=>{if(e!==null){const u=(Array.isArray(e)?e:[e]).filter(d=>typeof d=="string").map(d=>d.split("+")),l=u.reduce((d,m)=>d.concat(...m),[]);return[u,l]}return[[],[]]},[e]);return w.useEffect(()=>{const c=typeof document<"u"?document:null,u=(t==null?void 0:t.target)||c;if(e!==null){const l=f=>{if(s.current=f.ctrlKey||f.metaKey||f.shiftKey,(!s.current||s.current&&!t.actInsideInputWithModifier)&&Er(f))return!1;const x=Is(f.code,a);o.current.add(f[x]),Rs(i,o.current,!1)&&(f.preventDefault(),r(!0))},d=f=>{if((!s.current||s.current&&!t.actInsideInputWithModifier)&&Er(f))return!1;const x=Is(f.code,a);Rs(i,o.current,!0)?(r(!1),o.current.clear()):o.current.delete(f[x]),f.key==="Meta"&&o.current.clear(),s.current=!1},m=()=>{o.current.clear(),r(!1)};return u==null||u.addEventListener("keydown",l),u==null||u.addEventListener("keyup",d),window.addEventListener("blur",m),()=>{u==null||u.removeEventListener("keydown",l),u==null||u.removeEventListener("keyup",d),window.removeEventListener("blur",m)}}},[e,r]),n};function Rs(e,t,n){return e.filter(r=>n||r.length===t.size).some(r=>r.every(s=>t.has(s)))}function Is(e,t){return t.includes(e)?"code":"key"}function Di(e,t,n,r){var a,c;const s=e.parentNode||e.parentId;if(!s)return n;const o=t.get(s),i=Et(o,r);return Di(o,t,{x:(n.x??0)+i.x,y:(n.y??0)+i.y,z:(((a=o[we])==null?void 0:a.z)??0)>(n.z??0)?((c=o[we])==null?void 0:c.z)??0:n.z??0},r)}function Pi(e,t,n){e.forEach(r=>{var o;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:i,y:a,z:c}=Di(r,e,{...r.position,z:((o=r[we])==null?void 0:o.z)??0},t);r.positionAbsolute={x:i,y:a},r[we].z=c,n!=null&&n[r.id]&&(r[we].isParent=!0)}})}function tr(e,t,n,r){const s=new Map,o={},i=r?1e3:0;return e.forEach(a=>{var f;const c=(Ve(a.zIndex)?a.zIndex:0)+(a.selected?i:0),u=t.get(a.id),l={...a,positionAbsolute:{x:a.position.x,y:a.position.y}},d=a.parentNode||a.parentId;d&&(o[d]=!0);const m=(u==null?void 0:u.type)&&(u==null?void 0:u.type)!==a.type;Object.defineProperty(l,we,{enumerable:!1,value:{handleBounds:m||(f=u==null?void 0:u[we])==null?void 0:f.handleBounds,z:c}}),s.set(a.id,l)}),Pi(s,n,o),s}function zi(e,t={}){const{getNodes:n,width:r,height:s,minZoom:o,maxZoom:i,d3Zoom:a,d3Selection:c,fitViewOnInitDone:u,fitViewOnInit:l,nodeOrigin:d}=e(),m=t.initial&&!u&&l;if(a&&c&&(m||!t.initial)){const p=n().filter(v=>{var E;const g=t.includeHiddenNodes?v.width&&v.height:!v.hidden;return(E=t.nodes)!=null&&E.length?g&&t.nodes.some(M=>M.id===v.id):g}),x=p.every(v=>v.width&&v.height);if(p.length>0&&x){const v=ln(p,d),{x:g,y:E,zoom:M}=Ni(v,r,s,t.minZoom??o,t.maxZoom??i,t.padding??.1),b=ot.translate(g,E).scale(M);return typeof t.duration=="number"&&t.duration>0?a.transform(wt(c,t.duration),b):a.transform(c,b),!0}}return!1}function eh(e,t){return e.forEach(n=>{const r=t.get(n.id);r&&t.set(r.id,{...r,[we]:r[we],selected:n.selected})}),new Map(t)}function th(e,t){return t.map(n=>{const r=e.find(s=>s.id===n.id);return r&&(n.selected=r.selected),n})}function wn({changedNodes:e,changedEdges:t,get:n,set:r}){const{nodeInternals:s,edges:o,onNodesChange:i,onEdgesChange:a,hasDefaultNodes:c,hasDefaultEdges:u}=n();e!=null&&e.length&&(c&&r({nodeInternals:eh(e,s)}),i==null||i(e)),t!=null&&t.length&&(u&&r({edges:th(t,o)}),a==null||a(t))}const Ct=()=>{},nh={zoomIn:Ct,zoomOut:Ct,zoomTo:Ct,getZoom:()=>1,setViewport:Ct,getViewport:()=>({x:0,y:0,zoom:1}),fitView:()=>!1,setCenter:Ct,fitBounds:Ct,project:e=>e,screenToFlowPosition:e=>e,flowToScreenPosition:e=>e,viewportInitialized:!1},rh=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection}),sh=()=>{const e=ke(),{d3Zoom:t,d3Selection:n}=me(rh,Ce);return w.useMemo(()=>n&&t?{zoomIn:s=>t.scaleBy(wt(n,s==null?void 0:s.duration),1.2),zoomOut:s=>t.scaleBy(wt(n,s==null?void 0:s.duration),1/1.2),zoomTo:(s,o)=>t.scaleTo(wt(n,o==null?void 0:o.duration),s),getZoom:()=>e.getState().transform[2],setViewport:(s,o)=>{const[i,a,c]=e.getState().transform,u=ot.translate(s.x??i,s.y??a).scale(s.zoom??c);t.transform(wt(n,o==null?void 0:o.duration),u)},getViewport:()=>{const[s,o,i]=e.getState().transform;return{x:s,y:o,zoom:i}},fitView:s=>zi(e.getState,s),setCenter:(s,o,i)=>{const{width:a,height:c,maxZoom:u}=e.getState(),l=typeof(i==null?void 0:i.zoom)<"u"?i.zoom:u,d=a/2-s*l,m=c/2-o*l,f=ot.translate(d,m).scale(l);t.transform(wt(n,i==null?void 0:i.duration),f)},fitBounds:(s,o)=>{const{width:i,height:a,minZoom:c,maxZoom:u}=e.getState(),{x:l,y:d,zoom:m}=Ni(s,i,a,c,u,(o==null?void 0:o.padding)??.1),f=ot.translate(l,d).scale(m);t.transform(wt(n,o==null?void 0:o.duration),f)},project:s=>{const{transform:o,snapToGrid:i,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"),Sr(s,o,i,a)},screenToFlowPosition:s=>{const{transform:o,snapToGrid:i,snapGrid:a,domNode:c}=e.getState();if(!c)return s;const{x:u,y:l}=c.getBoundingClientRect(),d={x:s.x-u,y:s.y-l};return Sr(d,o,i,a)},flowToScreenPosition:s=>{const{transform:o,domNode:i}=e.getState();if(!i)return s;const{x:a,y:c}=i.getBoundingClientRect(),u=vi(s,o);return{x:u.x+a,y:u.y+c}},viewportInitialized:!0}:nh,[t,n])};function un(){const e=sh(),t=ke(),n=w.useCallback(()=>t.getState().getNodes().map(x=>({...x})),[]),r=w.useCallback(x=>t.getState().nodeInternals.get(x),[]),s=w.useCallback(()=>{const{edges:x=[]}=t.getState();return x.map(v=>({...v}))},[]),o=w.useCallback(x=>{const{edges:v=[]}=t.getState();return v.find(g=>g.id===x)},[]),i=w.useCallback(x=>{const{getNodes:v,setNodes:g,hasDefaultNodes:E,onNodesChange:M}=t.getState(),b=v(),$=typeof x=="function"?x(b):x;if(E)g($);else if(M){const j=$.length===0?b.map(_=>({type:"remove",id:_.id})):$.map(_=>({item:_,type:"reset"}));M(j)}},[]),a=w.useCallback(x=>{const{edges:v=[],setEdges:g,hasDefaultEdges:E,onEdgesChange:M}=t.getState(),b=typeof x=="function"?x(v):x;if(E)g(b);else if(M){const $=b.length===0?v.map(j=>({type:"remove",id:j.id})):b.map(j=>({item:j,type:"reset"}));M($)}},[]),c=w.useCallback(x=>{const v=Array.isArray(x)?x:[x],{getNodes:g,setNodes:E,hasDefaultNodes:M,onNodesChange:b}=t.getState();if(M){const j=[...g(),...v];E(j)}else if(b){const $=v.map(j=>({item:j,type:"add"}));b($)}},[]),u=w.useCallback(x=>{const v=Array.isArray(x)?x:[x],{edges:g=[],setEdges:E,hasDefaultEdges:M,onEdgesChange:b}=t.getState();if(M)E([...g,...v]);else if(b){const $=v.map(j=>({item:j,type:"add"}));b($)}},[]),l=w.useCallback(()=>{const{getNodes:x,edges:v=[],transform:g}=t.getState(),[E,M,b]=g;return{nodes:x().map($=>({...$})),edges:v.map($=>({...$})),viewport:{x:E,y:M,zoom:b}}},[]),d=w.useCallback(({nodes:x,edges:v})=>{const{nodeInternals:g,getNodes:E,edges:M,hasDefaultNodes:b,hasDefaultEdges:$,onNodesDelete:j,onEdgesDelete:_,onNodesChange:R,onEdgesChange:N}=t.getState(),I=(x||[]).map(C=>C.id),L=(v||[]).map(C=>C.id),B=E().reduce((C,S)=>{const O=S.parentNode||S.parentId,D=!I.includes(S.id)&&O&&C.find(T=>T.id===O);return(typeof S.deletable=="boolean"?S.deletable:!0)&&(I.includes(S.id)||D)&&C.push(S),C},[]),H=M.filter(C=>typeof C.deletable=="boolean"?C.deletable:!0),y=H.filter(C=>L.includes(C.id));if(B||y){const C=Ei(B,H),S=[...y,...C],O=S.reduce((D,k)=>(D.includes(k.id)||D.push(k.id),D),[]);if(($||b)&&($&&t.setState({edges:M.filter(D=>!O.includes(D.id))}),b&&(B.forEach(D=>{g.delete(D.id)}),t.setState({nodeInternals:new Map(g)}))),O.length>0&&(_==null||_(S),N&&N(O.map(D=>({id:D,type:"remove"})))),B.length>0&&(j==null||j(B),R)){const D=B.map(k=>({id:k.id,type:"remove"}));R(D)}}},[]),m=w.useCallback(x=>{const v=Ef(x),g=v?null:t.getState().nodeInternals.get(x.id);return!v&&!g?[null,null,v]:[v?x:Ns(g),g,v]},[]),f=w.useCallback((x,v=!0,g)=>{const[E,M,b]=m(x);return E?(g||t.getState().getNodes()).filter($=>{if(!b&&($.id===M.id||!$.positionAbsolute))return!1;const j=Ns($),_=_r(j,E);return v&&_>0||_>=E.width*E.height}):[]},[]),p=w.useCallback((x,v,g=!0)=>{const[E]=m(x);if(!E)return!1;const M=_r(E,v);return g&&M>0||M>=E.width*E.height},[]);return w.useMemo(()=>({...e,getNodes:n,getNode:r,getEdges:s,getEdge:o,setNodes:i,setEdges:a,addNodes:c,addEdges:u,toObject:l,deleteElements:d,getIntersectingNodes:f,isNodeIntersecting:p}),[e,n,r,s,o,i,a,c,u,l,d,f,p])}const oh={actInsideInputWithModifier:!1};var ih=({deleteKeyCode:e,multiSelectionKeyCode:t})=>{const n=ke(),{deleteElements:r}=un(),s=sn(e,oh),o=sn(t);w.useEffect(()=>{if(s){const{edges:i,getNodes:a}=n.getState(),c=a().filter(l=>l.selected),u=i.filter(l=>l.selected);r({nodes:c,edges:u}),n.setState({nodesSelectionActive:!1})}},[s]),w.useEffect(()=>{n.setState({multiSelectionActive:o})},[o])};function ah(e){const t=ke();w.useEffect(()=>{let n;const r=()=>{var o,i;if(!e.current)return;const s=Pr(e.current);(s.height===0||s.width===0)&&((i=(o=t.getState()).onError)==null||i.call(o,"004",at.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 Xr={position:"absolute",width:"100%",height:"100%",top:0,left:0},ch=(e,t)=>e.x!==t.x||e.y!==t.y||e.zoom!==t.k,bn=e=>({x:e.x,y:e.y,zoom:e.k}),Mt=(e,t)=>e.target.closest(`.${t}`),Ts=(e,t)=>t===2&&Array.isArray(e)&&e.includes(2),Os=e=>{const t=e.ctrlKey&&Rn()?10:1;return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*t},lh=e=>({d3Zoom:e.d3Zoom,d3Selection:e.d3Selection,d3ZoomHandler:e.d3ZoomHandler,userSelectionActive:e.userSelectionActive}),uh=({onMove:e,onMoveStart:t,onMoveEnd:n,onPaneContextMenu:r,zoomOnScroll:s=!0,zoomOnPinch:o=!0,panOnScroll:i=!1,panOnScrollSpeed:a=.5,panOnScrollMode:c=vt.Free,zoomOnDoubleClick:u=!0,elementsSelectable:l,panOnDrag:d=!0,defaultViewport:m,translateExtent:f,minZoom:p,maxZoom:x,zoomActivationKeyCode:v,preventScrolling:g=!0,children:E,noWheelClassName:M,noPanClassName:b})=>{const $=w.useRef(),j=ke(),_=w.useRef(!1),R=w.useRef(!1),N=w.useRef(null),I=w.useRef({x:0,y:0,zoom:0}),{d3Zoom:L,d3Selection:B,d3ZoomHandler:H,userSelectionActive:y}=me(lh,Ce),C=sn(v),S=w.useRef(0),O=w.useRef(!1),D=w.useRef();return ah(N),w.useEffect(()=>{if(N.current){const k=N.current.getBoundingClientRect(),T=li().scaleExtent([p,x]).translateExtent(f),P=Fe(N.current).call(T),z=ot.translate(m.x,m.y).scale(zt(m.zoom,p,x)),U=[[0,0],[k.width,k.height]],W=T.constrain()(z,U,f);T.transform(P,W),T.wheelDelta(Os),j.setState({d3Zoom:T,d3Selection:P,d3ZoomHandler:P.on("wheel.zoom"),transform:[W.x,W.y,W.k],domNode:N.current.closest(".react-flow")})}},[]),w.useEffect(()=>{B&&L&&(i&&!C&&!y?B.on("wheel.zoom",k=>{if(Mt(k,M))return!1;k.preventDefault(),k.stopImmediatePropagation();const T=B.property("__zoom").k||1;if(k.ctrlKey&&o){const J=Xe(k),ae=Os(k),oe=T*Math.pow(2,ae);L.scaleTo(B,oe,J,k);return}const P=k.deltaMode===1?20:1;let z=c===vt.Vertical?0:k.deltaX*P,U=c===vt.Horizontal?0:k.deltaY*P;!Rn()&&k.shiftKey&&c!==vt.Vertical&&(z=k.deltaY*P,U=0),L.translateBy(B,-(z/T)*a,-(U/T)*a,{internal:!0});const W=bn(B.property("__zoom")),{onViewportChangeStart:Q,onViewportChange:te,onViewportChangeEnd:se}=j.getState();clearTimeout(D.current),O.current||(O.current=!0,t==null||t(k,W),Q==null||Q(W)),O.current&&(e==null||e(k,W),te==null||te(W),D.current=setTimeout(()=>{n==null||n(k,W),se==null||se(W),O.current=!1},150))},{passive:!1}):typeof H<"u"&&B.on("wheel.zoom",function(k,T){if(!g&&k.type==="wheel"&&!k.ctrlKey||Mt(k,M))return null;k.preventDefault(),H.call(this,k,T)},{passive:!1}))},[y,i,c,B,L,H,C,o,g,M,t,e,n]),w.useEffect(()=>{L&&L.on("start",k=>{var z,U;if(!k.sourceEvent||k.sourceEvent.internal)return null;S.current=(z=k.sourceEvent)==null?void 0:z.button;const{onViewportChangeStart:T}=j.getState(),P=bn(k.transform);_.current=!0,I.current=P,((U=k.sourceEvent)==null?void 0:U.type)==="mousedown"&&j.setState({paneDragging:!0}),T==null||T(P),t==null||t(k.sourceEvent,P)})},[L,t]),w.useEffect(()=>{L&&(y&&!_.current?L.on("zoom",null):y||L.on("zoom",k=>{var P;const{onViewportChange:T}=j.getState();if(j.setState({transform:[k.transform.x,k.transform.y,k.transform.k]}),R.current=!!(r&&Ts(d,S.current??0)),(e||T)&&!((P=k.sourceEvent)!=null&&P.internal)){const z=bn(k.transform);T==null||T(z),e==null||e(k.sourceEvent,z)}}))},[y,L,e,d,r]),w.useEffect(()=>{L&&L.on("end",k=>{if(!k.sourceEvent||k.sourceEvent.internal)return null;const{onViewportChangeEnd:T}=j.getState();if(_.current=!1,j.setState({paneDragging:!1}),r&&Ts(d,S.current??0)&&!R.current&&r(k.sourceEvent),R.current=!1,(n||T)&&ch(I.current,k.transform)){const P=bn(k.transform);I.current=P,clearTimeout($.current),$.current=setTimeout(()=>{T==null||T(P),n==null||n(k.sourceEvent,P)},i?150:0)}})},[L,i,d,n,r]),w.useEffect(()=>{L&&L.filter(k=>{const T=C||s,P=o&&k.ctrlKey;if((d===!0||Array.isArray(d)&&d.includes(1))&&k.button===1&&k.type==="mousedown"&&(Mt(k,"react-flow__node")||Mt(k,"react-flow__edge")))return!0;if(!d&&!T&&!i&&!u&&!o||y||!u&&k.type==="dblclick"||Mt(k,M)&&k.type==="wheel"||Mt(k,b)&&(k.type!=="wheel"||i&&k.type==="wheel"&&!C)||!o&&k.ctrlKey&&k.type==="wheel"||!T&&!i&&!P&&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})},[y,L,s,o,i,u,d,l,C]),A.createElement("div",{className:"react-flow__renderer",ref:N,style:Xr},E)},dh=e=>({userSelectionActive:e.userSelectionActive,userSelectionRect:e.userSelectionRect});function fh(){const{userSelectionActive:e,userSelectionRect:t}=me(dh,Ce);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 Ls(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,o=t.position.y+t.height-r.height;if(s>0||o>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),o>0&&(r.style.height+=o),t.position.x<0){const i=Math.abs(t.position.x);r.position.x=r.position.x-i,r.style.width+=i,t.position.x=0}if(t.position.y<0){const i=Math.abs(t.position.y);r.position.y=r.position.y-i,r.style.height+=i,t.position.y=0}r.width=r.style.width,r.height=r.style.height}}}function hh(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 o=e.filter(a=>a.id===s.id);if(o.length===0)return r.push(s),r;const i={...s};for(const a of o)if(a)switch(a.type){case"select":{i.selected=a.selected;break}case"position":{typeof a.position<"u"&&(i.position=a.position),typeof a.positionAbsolute<"u"&&(i.positionAbsolute=a.positionAbsolute),typeof a.dragging<"u"&&(i.dragging=a.dragging),i.expandParent&&Ls(r,i);break}case"dimensions":{typeof a.dimensions<"u"&&(i.width=a.dimensions.width,i.height=a.dimensions.height),typeof a.updateStyle<"u"&&(i.style={...i.style||{},...a.dimensions}),typeof a.resizing=="boolean"&&(i.resizing=a.resizing),i.expandParent&&Ls(r,i);break}case"remove":return r}return r.push(i),r},n)}function mh(e,t){return hh(e,t)}const ht=(e,t)=>({id:e,type:"select",selected:t});function At(e,t){return e.reduce((n,r)=>{const s=t.includes(r.id);return!r.selected&&s?(r.selected=!0,n.push(ht(r.id,!0))):r.selected&&!s&&(r.selected=!1,n.push(ht(r.id,!1))),n},[])}const nr=(e,t)=>n=>{n.target===t.current&&(e==null||e(n))},ph=e=>({userSelectionActive:e.userSelectionActive,elementsSelectable:e.elementsSelectable,dragging:e.paneDragging}),Hi=w.memo(({isSelecting:e,selectionMode:t=rn.Full,panOnDrag:n,onSelectionStart:r,onSelectionEnd:s,onPaneClick:o,onPaneContextMenu:i,onPaneScroll:a,onPaneMouseEnter:c,onPaneMouseMove:u,onPaneMouseLeave:l,children:d})=>{const m=w.useRef(null),f=ke(),p=w.useRef(0),x=w.useRef(0),v=w.useRef(),{userSelectionActive:g,elementsSelectable:E,dragging:M}=me(ph,Ce),b=()=>{f.setState({userSelectionActive:!1,userSelectionRect:null}),p.current=0,x.current=0},$=H=>{o==null||o(H),f.getState().resetSelectedElements(),f.setState({nodesSelectionActive:!1})},j=H=>{if(Array.isArray(n)&&(n!=null&&n.includes(2))){H.preventDefault();return}i==null||i(H)},_=a?H=>a(H):void 0,R=H=>{const{resetSelectedElements:y,domNode:C}=f.getState();if(v.current=C==null?void 0:C.getBoundingClientRect(),!E||!e||H.button!==0||H.target!==m.current||!v.current)return;const{x:S,y:O}=yt(H,v.current);y(),f.setState({userSelectionRect:{width:0,height:0,startX:S,startY:O,x:S,y:O}}),r==null||r(H)},N=H=>{const{userSelectionRect:y,nodeInternals:C,edges:S,transform:O,onNodesChange:D,onEdgesChange:k,nodeOrigin:T,getNodes:P}=f.getState();if(!e||!v.current||!y)return;f.setState({userSelectionActive:!0,nodesSelectionActive:!1});const z=yt(H,v.current),U=y.startX??0,W=y.startY??0,Q={...y,x:z.xoe.id),ae=se.map(oe=>oe.id);if(p.current!==ae.length){p.current=ae.length;const oe=At(te,ae);oe.length&&(D==null||D(oe))}if(x.current!==J.length){x.current=J.length;const oe=At(S,J);oe.length&&(k==null||k(oe))}f.setState({userSelectionRect:Q})},I=H=>{if(H.button!==0)return;const{userSelectionRect:y}=f.getState();!g&&y&&H.target===m.current&&($==null||$(H)),f.setState({nodesSelectionActive:p.current>0}),b(),s==null||s(H)},L=H=>{g&&(f.setState({nodesSelectionActive:p.current>0}),s==null||s(H)),b()},B=E&&(e||g);return A.createElement("div",{className:Me(["react-flow__pane",{dragging:M,selection:e}]),onClick:B?void 0:nr($,m),onContextMenu:nr(j,m),onWheel:nr(_,m),onMouseEnter:B?void 0:c,onMouseDown:B?R:void 0,onMouseMove:B?N:u,onMouseUp:B?I:void 0,onMouseLeave:B?L:l,ref:m,style:Xr},d,A.createElement(fh,null))});Hi.displayName="Pane";function Fi(e,t){const n=e.parentNode||e.parentId;if(!n)return!1;const r=t.get(n);return r?r.selected?!0:Fi(r,t):!1}function Ds(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 gh(e,t,n,r){return Array.from(e.values()).filter(s=>(s.selected||s.id===r)&&(!s.parentNode||s.parentId||!Fi(s,e))&&(s.draggable||t&&typeof s.draggable>"u")).map(s=>{var o,i;return{id:s.id,position:s.position||{x:0,y:0},positionAbsolute:s.positionAbsolute||{x:0,y:0},distance:{x:n.x-(((o=s.positionAbsolute)==null?void 0:o.x)??0),y:n.y-(((i=s.positionAbsolute)==null?void 0:i.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 yh(e,t){return!t||t==="parent"?t:[t[0],[t[1][0]-(e.width||0),t[1][1]-(e.height||0)]]}function Vi(e,t,n,r,s=[0,0],o){const i=yh(e,e.extent||r);let a=i;const c=e.parentNode||e.parentId;if(e.extent==="parent"&&!e.expandParent)if(c&&e.width&&e.height){const d=n.get(c),{x:m,y:f}=Et(d,s).positionAbsolute;a=d&&Ve(m)&&Ve(f)&&Ve(d.width)&&Ve(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 o==null||o("005",at.error005()),a=i;else if(e.extent&&c&&e.extent!=="parent"){const d=n.get(c),{x:m,y:f}=Et(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 u={x:0,y:0};if(c){const d=n.get(c);u=Et(d,s).positionAbsolute}const l=a&&a!=="parent"?zr(t,a):t;return{position:{x:l.x-u.x,y:l.y-u.y},positionAbsolute:l}}function rr({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 Ps=(e,t,n,r)=>{const s=t.querySelectorAll(e);if(!s||!s.length)return null;const o=Array.from(s),i=t.getBoundingClientRect(),a={x:i.width*r[0],y:i.height*r[1]};return o.map(c=>{const u=c.getBoundingClientRect();return{id:c.getAttribute("data-handleid"),position:c.getAttribute("data-handlepos"),x:(u.left-i.left-a.x)/n,y:(u.top-i.top-a.y)/n,...Pr(c)}})};function Yt(e,t,n){return n===void 0?n:r=>{const s=t().nodeInternals.get(e);s&&n(r,{...s})}}function jr({id:e,store:t,unselect:n=!1,nodeRef:r}){const{addSelectedNodes:s,unselectNodesAndEdges:o,multiSelectionActive:i,nodeInternals:a,onError:c}=t.getState(),u=a.get(e);if(!u){c==null||c("012",at.error012(e));return}t.setState({nodesSelectionActive:!1}),u.selected?(n||u.selected&&i)&&(o({nodes:[u],edges:[]}),requestAnimationFrame(()=>{var l;return(l=r==null?void 0:r.current)==null?void 0:l.blur()})):s([e])}function xh(){const e=ke();return w.useCallback(({sourceEvent:n})=>{const{transform:r,snapGrid:s,snapToGrid:o}=e.getState(),i=n.touches?n.touches[0].clientX:n.clientX,a=n.touches?n.touches[0].clientY:n.clientY,c={x:(i-r[0])/r[2],y:(a-r[1])/r[2]};return{xSnapped:o?s[0]*Math.round(c.x/s[0]):c.x,ySnapped:o?s[1]*Math.round(c.y/s[1]):c.y,...c}},[])}function sr(e){return(t,n,r)=>e==null?void 0:e(t,r)}function Bi({nodeRef:e,disabled:t=!1,noDragClassName:n,handleSelector:r,nodeId:s,isSelectable:o,selectNodesOnDrag:i}){const a=ke(),[c,u]=w.useState(!1),l=w.useRef([]),d=w.useRef({x:null,y:null}),m=w.useRef(0),f=w.useRef(null),p=w.useRef({x:0,y:0}),x=w.useRef(null),v=w.useRef(!1),g=w.useRef(!1),E=w.useRef(!1),M=xh();return w.useEffect(()=>{if(e!=null&&e.current){const b=Fe(e.current),$=({x:R,y:N})=>{const{nodeInternals:I,onNodeDrag:L,onSelectionDrag:B,updateNodePositions:H,nodeExtent:y,snapGrid:C,snapToGrid:S,nodeOrigin:O,onError:D}=a.getState();d.current={x:R,y:N};let k=!1,T={x:0,y:0,x2:0,y2:0};if(l.current.length>1&&y){const z=ln(l.current,O);T=nn(z)}if(l.current=l.current.map(z=>{const U={x:R-z.distance.x,y:N-z.distance.y};S&&(U.x=C[0]*Math.round(U.x/C[0]),U.y=C[1]*Math.round(U.y/C[1]));const W=[[y[0][0],y[0][1]],[y[1][0],y[1][1]]];l.current.length>1&&y&&!z.extent&&(W[0][0]=z.positionAbsolute.x-T.x+y[0][0],W[1][0]=z.positionAbsolute.x+(z.width??0)-T.x2+y[1][0],W[0][1]=z.positionAbsolute.y-T.y+y[0][1],W[1][1]=z.positionAbsolute.y+(z.height??0)-T.y2+y[1][1]);const Q=Vi(z,U,I,W,O,D);return k=k||z.position.x!==Q.position.x||z.position.y!==Q.position.y,z.position=Q.position,z.positionAbsolute=Q.positionAbsolute,z}),!k)return;H(l.current,!0,!0),u(!0);const P=s?L:sr(B);if(P&&x.current){const[z,U]=rr({nodeId:s,dragItems:l.current,nodeInternals:I});P(x.current,z,U)}},j=()=>{if(!f.current)return;const[R,N]=di(p.current,f.current);if(R!==0||N!==0){const{transform:I,panBy:L}=a.getState();d.current.x=(d.current.x??0)-R/I[2],d.current.y=(d.current.y??0)-N/I[2],L({x:R,y:N})&&$(d.current)}m.current=requestAnimationFrame(j)},_=R=>{var O;const{nodeInternals:N,multiSelectionActive:I,nodesDraggable:L,unselectNodesAndEdges:B,onNodeDragStart:H,onSelectionDragStart:y}=a.getState();g.current=!0;const C=s?H:sr(y);(!i||!o)&&!I&&s&&((O=N.get(s))!=null&&O.selected||B()),s&&o&&i&&jr({id:s,store:a,nodeRef:e});const S=M(R);if(d.current=S,l.current=gh(N,L,S,s),C&&l.current){const[D,k]=rr({nodeId:s,dragItems:l.current,nodeInternals:N});C(R.sourceEvent,D,k)}};if(t)b.on(".drag",null);else{const R=ku().on("start",N=>{const{domNode:I,nodeDragThreshold:L}=a.getState();L===0&&_(N),E.current=!1;const B=M(N);d.current=B,f.current=(I==null?void 0:I.getBoundingClientRect())||null,p.current=yt(N.sourceEvent,f.current)}).on("drag",N=>{var H,y;const I=M(N),{autoPanOnNodeDrag:L,nodeDragThreshold:B}=a.getState();if(N.sourceEvent.type==="touchmove"&&N.sourceEvent.touches.length>1&&(E.current=!0),!E.current){if(!v.current&&g.current&&L&&(v.current=!0,j()),!g.current){const C=I.xSnapped-(((H=d==null?void 0:d.current)==null?void 0:H.x)??0),S=I.ySnapped-(((y=d==null?void 0:d.current)==null?void 0:y.y)??0);Math.sqrt(C*C+S*S)>B&&_(N)}(d.current.x!==I.xSnapped||d.current.y!==I.ySnapped)&&l.current&&g.current&&(x.current=N.sourceEvent,p.current=yt(N.sourceEvent,f.current),$(I))}}).on("end",N=>{if(!(!g.current||E.current)&&(u(!1),v.current=!1,g.current=!1,cancelAnimationFrame(m.current),l.current)){const{updateNodePositions:I,nodeInternals:L,onNodeDragStop:B,onSelectionDragStop:H}=a.getState(),y=s?B:sr(H);if(I(l.current,!1,!1),y){const[C,S]=rr({nodeId:s,dragItems:l.current,nodeInternals:L});y(N.sourceEvent,C,S)}}}).filter(N=>{const I=N.target;return!N.button&&(!n||!Ds(I,`.${n}`,e))&&(!r||Ds(I,r,e))});return b.call(R),()=>{b.on(".drag",null)}}}},[e,t,n,r,o,a,s,i,M]),c}function Gi(){const e=ke();return w.useCallback(n=>{const{nodeInternals:r,nodeExtent:s,updateNodePositions:o,getNodes:i,snapToGrid:a,snapGrid:c,onError:u,nodesDraggable:l}=e.getState(),d=i().filter(E=>E.selected&&(E.draggable||l&&typeof E.draggable>"u")),m=a?c[0]:5,f=a?c[1]:5,p=n.isShiftPressed?4:1,x=n.x*m*p,v=n.y*f*p,g=d.map(E=>{if(E.positionAbsolute){const M={x:E.positionAbsolute.x+x,y:E.positionAbsolute.y+v};a&&(M.x=c[0]*Math.round(M.x/c[0]),M.y=c[1]*Math.round(M.y/c[1]));const{positionAbsolute:b,position:$}=Vi(E,M,r,s,void 0,u);E.position=$,E.positionAbsolute=b}return E});o(g,!0,!1)},[])}const Ot={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}};var Xt=e=>{const t=({id:n,type:r,data:s,xPos:o,yPos:i,xPosOrigin:a,yPosOrigin:c,selected:u,onClick:l,onMouseEnter:d,onMouseMove:m,onMouseLeave:f,onContextMenu:p,onDoubleClick:x,style:v,className:g,isDraggable:E,isSelectable:M,isConnectable:b,isFocusable:$,selectNodesOnDrag:j,sourcePosition:_,targetPosition:R,hidden:N,resizeObserver:I,dragHandle:L,zIndex:B,isParent:H,noDragClassName:y,noPanClassName:C,initialized:S,disableKeyboardA11y:O,ariaLabel:D,rfId:k,hasHandleBounds:T})=>{const P=ke(),z=w.useRef(null),U=w.useRef(null),W=w.useRef(_),Q=w.useRef(R),te=w.useRef(r),se=M||E||l||d||m||f,J=Gi(),ae=Yt(n,P.getState,d),oe=Yt(n,P.getState,m),he=Yt(n,P.getState,f),ye=Yt(n,P.getState,p),Ee=Yt(n,P.getState,x),ve=le=>{const{nodeDragThreshold:q}=P.getState();if(M&&(!j||!E||q>0)&&jr({id:n,store:P,nodeRef:z}),l){const fe=P.getState().nodeInternals.get(n);fe&&l(le,{...fe})}},ue=le=>{if(!Er(le)&&!O)if(pi.includes(le.key)&&M){const q=le.key==="Escape";jr({id:n,store:P,unselect:q,nodeRef:z})}else E&&u&&Object.prototype.hasOwnProperty.call(Ot,le.key)&&(P.setState({ariaLiveMessage:`Moved selected node ${le.key.replace("Arrow","").toLowerCase()}. New position, x: ${~~o}, y: ${~~i}`}),J({x:Ot[le.key].x,y:Ot[le.key].y,isShiftPressed:le.shiftKey}))};w.useEffect(()=>()=>{U.current&&(I==null||I.unobserve(U.current),U.current=null)},[]),w.useEffect(()=>{if(z.current&&!N){const le=z.current;(!S||!T||U.current!==le)&&(U.current&&(I==null||I.unobserve(U.current)),I==null||I.observe(le),U.current=le)}},[N,S,T]),w.useEffect(()=>{const le=te.current!==r,q=W.current!==_,fe=Q.current!==R;z.current&&(le||q||fe)&&(le&&(te.current=r),q&&(W.current=_),fe&&(Q.current=R),P.getState().updateNodeDimensions([{id:n,nodeElement:z.current,forceUpdate:!0}]))},[n,r,_,R]);const ge=Bi({nodeRef:z,disabled:N||!E,noDragClassName:y,handleSelector:L,nodeId:n,isSelectable:M,selectNodesOnDrag:j});return N?null:A.createElement("div",{className:Me(["react-flow__node",`react-flow__node-${r}`,{[C]:E},g,{selected:u,selectable:M,parent:H,dragging:ge}]),ref:z,style:{zIndex:B,transform:`translate(${a}px,${c}px)`,pointerEvents:se?"all":"none",visibility:S?"visible":"hidden",...v},"data-id":n,"data-testid":`rf__node-${n}`,onMouseEnter:ae,onMouseMove:oe,onMouseLeave:he,onContextMenu:ye,onClick:ve,onDoubleClick:Ee,onKeyDown:$?ue:void 0,tabIndex:$?0:void 0,role:$?"button":void 0,"aria-describedby":O?void 0:`${Oi}-${k}`,"aria-label":D},A.createElement($f,{value:n},A.createElement(e,{id:n,data:s,type:r,xPos:o,yPos:i,selected:u,isConnectable:b,sourcePosition:_,targetPosition:R,dragging:ge,dragHandle:L,zIndex:B})))};return t.displayName="NodeWrapper",w.memo(t)};const wh=e=>{const t=e.getNodes().filter(n=>n.selected);return{...ln(t,e.nodeOrigin),transformString:`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`,userSelectionActive:e.userSelectionActive}};function bh({onSelectionContextMenu:e,noPanClassName:t,disableKeyboardA11y:n}){const r=ke(),{width:s,height:o,x:i,y:a,transformString:c,userSelectionActive:u}=me(wh,Ce),l=Gi(),d=w.useRef(null);if(w.useEffect(()=>{var p;n||(p=d.current)==null||p.focus({preventScroll:!0})},[n]),Bi({nodeRef:d}),u||!s||!o)return null;const m=e?p=>{const x=r.getState().getNodes().filter(v=>v.selected);e(p,x)}:void 0,f=p=>{Object.prototype.hasOwnProperty.call(Ot,p.key)&&l({x:Ot[p.key].x,y:Ot[p.key].y,isShiftPressed:p.shiftKey})};return A.createElement("div",{className:Me(["react-flow__nodesselection","react-flow__container",t]),style:{transform:c}},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:o,top:a,left:i}}))}var vh=w.memo(bh);const _h=e=>e.nodesSelectionActive,Yi=({children:e,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:o,onPaneScroll:i,deleteKeyCode:a,onMove:c,onMoveStart:u,onMoveEnd:l,selectionKeyCode:d,selectionOnDrag:m,selectionMode:f,onSelectionStart:p,onSelectionEnd:x,multiSelectionKeyCode:v,panActivationKeyCode:g,zoomActivationKeyCode:E,elementsSelectable:M,zoomOnScroll:b,zoomOnPinch:$,panOnScroll:j,panOnScrollSpeed:_,panOnScrollMode:R,zoomOnDoubleClick:N,panOnDrag:I,defaultViewport:L,translateExtent:B,minZoom:H,maxZoom:y,preventScrolling:C,onSelectionContextMenu:S,noWheelClassName:O,noPanClassName:D,disableKeyboardA11y:k})=>{const T=me(_h),P=sn(d),z=sn(g),U=z||I,W=z||j,Q=P||m&&U!==!0;return ih({deleteKeyCode:a,multiSelectionKeyCode:v}),A.createElement(uh,{onMove:c,onMoveStart:u,onMoveEnd:l,onPaneContextMenu:o,elementsSelectable:M,zoomOnScroll:b,zoomOnPinch:$,panOnScroll:W,panOnScrollSpeed:_,panOnScrollMode:R,zoomOnDoubleClick:N,panOnDrag:!P&&U,defaultViewport:L,translateExtent:B,minZoom:H,maxZoom:y,zoomActivationKeyCode:E,preventScrolling:C,noWheelClassName:O,noPanClassName:D},A.createElement(Hi,{onSelectionStart:p,onSelectionEnd:x,onPaneClick:t,onPaneMouseEnter:n,onPaneMouseMove:r,onPaneMouseLeave:s,onPaneContextMenu:o,onPaneScroll:i,panOnDrag:U,isSelecting:!!Q,selectionMode:f},e,T&&A.createElement(vh,{onSelectionContextMenu:S,noPanClassName:D,disableKeyboardA11y:k})))};Yi.displayName="FlowRenderer";var Eh=w.memo(Yi);function Nh(e){return me(w.useCallback(n=>e?_i(n.nodeInternals,{x:0,y:0,width:n.width,height:n.height},n.transform,!0):n.getNodes(),[e]))}function Sh(e){const t={input:Xt(e.input||Ai),default:Xt(e.default||kr),output:Xt(e.output||Ii),group:Xt(e.group||Yr)},n={},r=Object.keys(e).filter(s=>!["input","default","output","group"].includes(s)).reduce((s,o)=>(s[o]=Xt(e[o]||kr),s),n);return{...t,...r}}const kh=({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]},jh=e=>({nodesDraggable:e.nodesDraggable,nodesConnectable:e.nodesConnectable,nodesFocusable:e.nodesFocusable,elementsSelectable:e.elementsSelectable,updateNodeDimensions:e.updateNodeDimensions,onError:e.onError}),Xi=e=>{const{nodesDraggable:t,nodesConnectable:n,nodesFocusable:r,elementsSelectable:s,updateNodeDimensions:o,onError:i}=me(jh,Ce),a=Nh(e.onlyRenderVisibleElements),c=w.useRef(),u=w.useMemo(()=>{if(typeof ResizeObserver>"u")return null;const l=new ResizeObserver(d=>{const m=d.map(f=>({id:f.target.getAttribute("data-id"),nodeElement:f.target,forceUpdate:!0}));o(m)});return c.current=l,l},[]);return w.useEffect(()=>()=>{var l;(l=c==null?void 0:c.current)==null||l.disconnect()},[]),A.createElement("div",{className:"react-flow__nodes",style:Xr},a.map(l=>{var $,j,_;let d=l.type||"default";e.nodeTypes[d]||(i==null||i("003",at.error003(d)),d="default");const m=e.nodeTypes[d]||e.nodeTypes.default,f=!!(l.draggable||t&&typeof l.draggable>"u"),p=!!(l.selectable||s&&typeof l.selectable>"u"),x=!!(l.connectable||n&&typeof l.connectable>"u"),v=!!(l.focusable||r&&typeof l.focusable>"u"),g=e.nodeExtent?zr(l.positionAbsolute,e.nodeExtent):l.positionAbsolute,E=(g==null?void 0:g.x)??0,M=(g==null?void 0:g.y)??0,b=kh({x:E,y:M,width:l.width??0,height:l.height??0,origin:e.nodeOrigin});return A.createElement(m,{key:l.id,id:l.id,className:l.className,style:l.style,type:d,data:l.data,sourcePosition:l.sourcePosition||K.Bottom,targetPosition:l.targetPosition||K.Top,hidden:l.hidden,xPos:E,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:!!l.selected,isDraggable:f,isSelectable:p,isConnectable:x,isFocusable:v,resizeObserver:u,dragHandle:l.dragHandle,zIndex:(($=l[we])==null?void 0:$.z)??0,isParent:!!((j=l[we])!=null&&j.isParent),noDragClassName:e.noDragClassName,noPanClassName:e.noPanClassName,initialized:!!l.width&&!!l.height,rfId:e.rfId,disableKeyboardA11y:e.disableKeyboardA11y,ariaLabel:l.ariaLabel,hasHandleBounds:!!((_=l[we])!=null&&_.handleBounds)})}))};Xi.displayName="NodeRenderer";var Ch=w.memo(Xi);const Mh=(e,t,n)=>n===K.Left?e-t:n===K.Right?e+t:e,$h=(e,t,n)=>n===K.Top?e-t:n===K.Bottom?e+t:e,zs="react-flow__edgeupdater",Hs=({position:e,centerX:t,centerY:n,radius:r=10,onMouseDown:s,onMouseEnter:o,onMouseOut:i,type:a})=>A.createElement("circle",{onMouseDown:s,onMouseEnter:o,onMouseOut:i,className:Me([zs,`${zs}-${a}`]),cx:Mh(t,r,e),cy:$h(n,r,e),r,stroke:"transparent",fill:"transparent"}),Ah=()=>!0;var $t=e=>{const t=({id:n,className:r,type:s,data:o,onClick:i,onEdgeDoubleClick:a,selected:c,animated:u,label:l,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:p,labelBgBorderRadius:x,style:v,source:g,target:E,sourceX:M,sourceY:b,targetX:$,targetY:j,sourcePosition:_,targetPosition:R,elementsSelectable:N,hidden:I,sourceHandleId:L,targetHandleId:B,onContextMenu:H,onMouseEnter:y,onMouseMove:C,onMouseLeave:S,reconnectRadius:O,onReconnect:D,onReconnectStart:k,onReconnectEnd:T,markerEnd:P,markerStart:z,rfId:U,ariaLabel:W,isFocusable:Q,isReconnectable:te,pathOptions:se,interactionWidth:J,disableKeyboardA11y:ae})=>{const oe=w.useRef(null),[he,ye]=w.useState(!1),[Ee,ve]=w.useState(!1),ue=ke(),ge=w.useMemo(()=>`url('#${Nr(z,U)}')`,[z,U]),le=w.useMemo(()=>`url('#${Nr(P,U)}')`,[P,U]);if(I)return null;const q=Z=>{var ne;const{edges:Y,addSelectedEdges:G,unselectNodesAndEdges:re,multiSelectionActive:xe}=ue.getState(),ee=Y.find(ie=>ie.id===n);ee&&(N&&(ue.setState({nodesSelectionActive:!1}),ee.selected&&xe?(re({nodes:[],edges:[ee]}),(ne=oe.current)==null||ne.blur()):G([n])),i&&i(Z,ee))},fe=Gt(n,ue.getState,a),Oe=Gt(n,ue.getState,H),Ke=Gt(n,ue.getState,y),Ye=Gt(n,ue.getState,C),$e=Gt(n,ue.getState,S),Ae=(Z,Y)=>{if(Z.button!==0)return;const{edges:G,isValidConnection:re}=ue.getState(),xe=Y?E:g,ee=(Y?B:L)||null,ne=Y?"target":"source",ie=re||Ah,pe=Y,Ie=G.find(Ne=>Ne.id===n);ve(!0),k==null||k(Z,Ie,ne);const Le=Ne=>{ve(!1),T==null||T(Ne,Ie,ne)};ji({event:Z,handleId:ee,nodeId:xe,onConnect:Ne=>D==null?void 0:D(Ie,Ne),isTarget:pe,getState:ue.getState,setState:ue.setState,isValidConnection:ie,edgeUpdaterType:ne,onReconnectEnd:Le})},je=Z=>Ae(Z,!0),Re=Z=>Ae(Z,!1),ze=()=>ye(!0),F=()=>ye(!1),X=!N&&!i,V=Z=>{var Y;if(!ae&&pi.includes(Z.key)&&N){const{unselectNodesAndEdges:G,addSelectedEdges:re,edges:xe}=ue.getState();Z.key==="Escape"?((Y=oe.current)==null||Y.blur(),G({edges:[xe.find(ne=>ne.id===n)]})):re([n])}};return A.createElement("g",{className:Me(["react-flow__edge",`react-flow__edge-${s}`,r,{selected:c,animated:u,inactive:X,updating:he}]),onClick:q,onDoubleClick:fe,onContextMenu:Oe,onMouseEnter:Ke,onMouseMove:Ye,onMouseLeave:$e,onKeyDown:Q?V:void 0,tabIndex:Q?0:void 0,role:Q?"button":"img","data-testid":`rf__edge-${n}`,"aria-label":W===null?void 0:W||`Edge from ${g} to ${E}`,"aria-describedby":Q?`${Li}-${U}`:void 0,ref:oe},!Ee&&A.createElement(e,{id:n,source:g,target:E,selected:c,animated:u,label:l,labelStyle:d,labelShowBg:m,labelBgStyle:f,labelBgPadding:p,labelBgBorderRadius:x,data:o,style:v,sourceX:M,sourceY:b,targetX:$,targetY:j,sourcePosition:_,targetPosition:R,sourceHandleId:L,targetHandleId:B,markerStart:ge,markerEnd:le,pathOptions:se,interactionWidth:J}),te&&A.createElement(A.Fragment,null,(te==="source"||te===!0)&&A.createElement(Hs,{position:_,centerX:M,centerY:b,radius:O,onMouseDown:je,onMouseEnter:ze,onMouseOut:F,type:"source"}),(te==="target"||te===!0)&&A.createElement(Hs,{position:R,centerX:$,centerY:j,radius:O,onMouseDown:Re,onMouseEnter:ze,onMouseOut:F,type:"target"})))};return t.displayName="EdgeWrapper",w.memo(t)};function Rh(e){const t={default:$t(e.default||On),straight:$t(e.bezier||Vr),step:$t(e.step||Fr),smoothstep:$t(e.step||Yn),simplebezier:$t(e.simplebezier||Hr)},n={},r=Object.keys(e).filter(s=>!["default","bezier"].includes(s)).reduce((s,o)=>(s[o]=$t(e[o]||On),s),n);return{...t,...r}}function Fs(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,o=(n==null?void 0:n.width)||t.width,i=(n==null?void 0:n.height)||t.height;switch(e){case K.Top:return{x:r+o/2,y:s};case K.Right:return{x:r+o,y:s+i/2};case K.Bottom:return{x:r+o/2,y:s+i};case K.Left:return{x:r,y:s+i/2}}}function Vs(e,t){return e?e.length===1||!t?e[0]:t&&e.find(n=>n.id===t)||null:null}const Ih=(e,t,n,r,s,o)=>{const i=Fs(n,e,t),a=Fs(o,r,s);return{sourceX:i.x,sourceY:i.y,targetX:a.x,targetY:a.y}};function Th({sourcePos:e,targetPos:t,sourceWidth:n,sourceHeight:r,targetWidth:s,targetHeight:o,width:i,height:a,transform:c}){const u={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+o)};u.x===u.x2&&(u.x2+=1),u.y===u.y2&&(u.y2+=1);const l=nn({x:(0-c[0])/c[2],y:(0-c[1])/c[2],width:i/c[2],height:a/c[2]}),d=Math.max(0,Math.min(l.x2,u.x2)-Math.max(l.x,u.x)),m=Math.max(0,Math.min(l.y2,u.y2)-Math.max(l.y,u.y));return Math.ceil(d*m)>0}function Bs(e){var r,s,o,i,a;const t=((r=e==null?void 0:e[we])==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((o=e==null?void 0:e.positionAbsolute)==null?void 0:o.y)<"u";return[{x:((i=e==null?void 0:e.positionAbsolute)==null?void 0:i.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 Oh=[{level:0,isMaxLevel:!0,edges:[]}];function Lh(e,t,n=!1){let r=-1;const s=e.reduce((i,a)=>{var l,d;const c=Ve(a.zIndex);let u=c?a.zIndex:0;if(n){const m=t.get(a.target),f=t.get(a.source),p=a.selected||(m==null?void 0:m.selected)||(f==null?void 0:f.selected),x=Math.max(((l=f==null?void 0:f[we])==null?void 0:l.z)||0,((d=m==null?void 0:m[we])==null?void 0:d.z)||0,1e3);u=(c?a.zIndex:0)+(p?x:0)}return i[u]?i[u].push(a):i[u]=[a],r=u>r?u:r,i},{}),o=Object.entries(s).map(([i,a])=>{const c=+i;return{edges:a,level:c,isMaxLevel:c===r}});return o.length===0?Oh:o}function Dh(e,t,n){const r=me(w.useCallback(s=>e?s.edges.filter(o=>{const i=t.get(o.source),a=t.get(o.target);return(i==null?void 0:i.width)&&(i==null?void 0:i.height)&&(a==null?void 0:a.width)&&(a==null?void 0:a.height)&&Th({sourcePos:i.positionAbsolute||{x:0,y:0},targetPos:a.positionAbsolute||{x:0,y:0},sourceWidth:i.width,sourceHeight:i.height,targetWidth:a.width,targetHeight:a.height,width:s.width,height:s.height,transform:s.transform})}):s.edges,[e,t]));return Lh(r,t,n)}const Ph=({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"}),zh=({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"}),Gs={[In.Arrow]:Ph,[In.ArrowClosed]:zh};function Hh(e){const t=ke();return w.useMemo(()=>{var s,o;return Object.prototype.hasOwnProperty.call(Gs,e)?Gs[e]:((o=(s=t.getState()).onError)==null||o.call(s,"009",at.error009(e)),null)},[e])}const Fh=({id:e,type:t,color:n,width:r=12.5,height:s=12.5,markerUnits:o="strokeWidth",strokeWidth:i,orient:a="auto-start-reverse"})=>{const c=Hh(t);return c?A.createElement("marker",{className:"react-flow__arrowhead",id:e,markerWidth:`${r}`,markerHeight:`${s}`,viewBox:"-10 -10 20 20",markerUnits:o,orient:a,refX:"0",refY:"0"},A.createElement(c,{color:n,strokeWidth:i})):null},Vh=({defaultColor:e,rfId:t})=>n=>{const r=[];return n.edges.reduce((s,o)=>([o.markerStart,o.markerEnd].forEach(i=>{if(i&&typeof i=="object"){const a=Nr(i,t);r.includes(a)||(s.push({id:a,color:i.color||e,...i}),r.push(a))}}),s),[]).sort((s,o)=>s.id.localeCompare(o.id))},Wi=({defaultColor:e,rfId:t})=>{const n=me(w.useCallback(Vh({defaultColor:e,rfId:t}),[e,t]),(r,s)=>!(r.length!==s.length||r.some((o,i)=>o.id!==s[i].id)));return A.createElement("defs",null,n.map(r=>A.createElement(Fh,{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})))};Wi.displayName="MarkerDefinitions";var Bh=w.memo(Wi);const Gh=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}),Ui=({defaultMarkerColor:e,onlyRenderVisibleElements:t,elevateEdgesOnSelect:n,rfId:r,edgeTypes:s,noPanClassName:o,onEdgeContextMenu:i,onEdgeMouseEnter:a,onEdgeMouseMove:c,onEdgeMouseLeave:u,onEdgeClick:l,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:p,reconnectRadius:x,children:v,disableKeyboardA11y:g})=>{const{edgesFocusable:E,edgesUpdatable:M,elementsSelectable:b,width:$,height:j,connectionMode:_,nodeInternals:R,onError:N}=me(Gh,Ce),I=Dh(t,R,n);return $?A.createElement(A.Fragment,null,I.map(({level:L,edges:B,isMaxLevel:H})=>A.createElement("svg",{key:L,style:{zIndex:L},width:$,height:j,className:"react-flow__edges react-flow__container"},H&&A.createElement(Bh,{defaultColor:e,rfId:r}),A.createElement("g",null,B.map(y=>{const[C,S,O]=Bs(R.get(y.source)),[D,k,T]=Bs(R.get(y.target));if(!O||!T)return null;let P=y.type||"default";s[P]||(N==null||N("011",at.error011(P)),P="default");const z=s[P]||s.default,U=_===St.Strict?k.target:(k.target??[]).concat(k.source??[]),W=Vs(S.source,y.sourceHandle),Q=Vs(U,y.targetHandle),te=(W==null?void 0:W.position)||K.Bottom,se=(Q==null?void 0:Q.position)||K.Top,J=!!(y.focusable||E&&typeof y.focusable>"u"),ae=y.reconnectable||y.updatable,oe=typeof m<"u"&&(ae||M&&typeof ae>"u");if(!W||!Q)return N==null||N("008",at.error008(W,y)),null;const{sourceX:he,sourceY:ye,targetX:Ee,targetY:ve}=Ih(C,W,te,D,Q,se);return A.createElement(z,{key:y.id,id:y.id,className:Me([y.className,o]),type:P,data:y.data,selected:!!y.selected,animated:!!y.animated,hidden:!!y.hidden,label:y.label,labelStyle:y.labelStyle,labelShowBg:y.labelShowBg,labelBgStyle:y.labelBgStyle,labelBgPadding:y.labelBgPadding,labelBgBorderRadius:y.labelBgBorderRadius,style:y.style,source:y.source,target:y.target,sourceHandleId:y.sourceHandle,targetHandleId:y.targetHandle,markerEnd:y.markerEnd,markerStart:y.markerStart,sourceX:he,sourceY:ye,targetX:Ee,targetY:ve,sourcePosition:te,targetPosition:se,elementsSelectable:b,onContextMenu:i,onMouseEnter:a,onMouseMove:c,onMouseLeave:u,onClick:l,onEdgeDoubleClick:d,onReconnect:m,onReconnectStart:f,onReconnectEnd:p,reconnectRadius:x,rfId:r,ariaLabel:y.ariaLabel,isFocusable:J,isReconnectable:oe,pathOptions:"pathOptions"in y?y.pathOptions:void 0,interactionWidth:y.interactionWidth,disableKeyboardA11y:g})})))),v):null};Ui.displayName="EdgeRenderer";var Yh=w.memo(Ui);const Xh=e=>`translate(${e.transform[0]}px,${e.transform[1]}px) scale(${e.transform[2]})`;function Wh({children:e}){const t=me(Xh);return A.createElement("div",{className:"react-flow__viewport react-flow__container",style:{transform:t}},e)}function Uh(e){const t=un(),n=w.useRef(!1);w.useEffect(()=>{!n.current&&t.viewportInitialized&&e&&(setTimeout(()=>e(t),1),n.current=!0)},[e,t.viewportInitialized])}const qh={[K.Left]:K.Right,[K.Right]:K.Left,[K.Top]:K.Bottom,[K.Bottom]:K.Top},qi=({nodeId:e,handleType:t,style:n,type:r=mt.Bezier,CustomComponent:s,connectionStatus:o})=>{var j,_,R;const{fromNode:i,handleId:a,toX:c,toY:u,connectionMode:l}=me(w.useCallback(N=>({fromNode:N.nodeInternals.get(e),handleId:N.connectionHandleId,toX:(N.connectionPosition.x-N.transform[0])/N.transform[2],toY:(N.connectionPosition.y-N.transform[1])/N.transform[2],connectionMode:N.connectionMode}),[e]),Ce),d=(j=i==null?void 0:i[we])==null?void 0:j.handleBounds;let m=d==null?void 0:d[t];if(l===St.Loose&&(m=m||(d==null?void 0:d[t==="source"?"target":"source"])),!i||!m)return null;const f=a?m.find(N=>N.id===a):m[0],p=f?f.x+f.width/2:(i.width??0)/2,x=f?f.y+f.height/2:i.height??0,v=(((_=i.positionAbsolute)==null?void 0:_.x)??0)+p,g=(((R=i.positionAbsolute)==null?void 0:R.y)??0)+x,E=f==null?void 0:f.position,M=E?qh[E]:null;if(!E||!M)return null;if(s)return A.createElement(s,{connectionLineType:r,connectionLineStyle:n,fromNode:i,fromHandle:f,fromX:v,fromY:g,toX:c,toY:u,fromPosition:E,toPosition:M,connectionStatus:o});let b="";const $={sourceX:v,sourceY:g,sourcePosition:E,targetX:c,targetY:u,targetPosition:M};return r===mt.Bezier?[b]=bi($):r===mt.Step?[b]=Tn({...$,borderRadius:0}):r===mt.SmoothStep?[b]=Tn($):r===mt.SimpleBezier?[b]=wi($):b=`M${v},${g} ${c},${u}`,A.createElement("path",{d:b,fill:"none",className:"react-flow__connection-path",style:n})};qi.displayName="ConnectionLine";const Kh=e=>({nodeId:e.connectionNodeId,handleType:e.connectionHandleType,nodesConnectable:e.nodesConnectable,connectionStatus:e.connectionStatus,width:e.width,height:e.height});function Zh({containerStyle:e,style:t,type:n,component:r}){const{nodeId:s,handleType:o,nodesConnectable:i,width:a,height:c,connectionStatus:u}=me(Kh,Ce);return!(s&&o&&a&&i)?null:A.createElement("svg",{style:e,width:a,height:c,className:"react-flow__edges react-flow__connectionline react-flow__container"},A.createElement("g",{className:Me(["react-flow__connection",u])},A.createElement(qi,{nodeId:s,handleType:o,style:t,type:n,CustomComponent:r,connectionStatus:u})))}function Ys(e,t){return w.useRef(null),ke(),w.useMemo(()=>t(e),[e])}const Ki=({nodeTypes:e,edgeTypes:t,onMove:n,onMoveStart:r,onMoveEnd:s,onInit:o,onNodeClick:i,onEdgeClick:a,onNodeDoubleClick:c,onEdgeDoubleClick:u,onNodeMouseEnter:l,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,onSelectionContextMenu:p,onSelectionStart:x,onSelectionEnd:v,connectionLineType:g,connectionLineStyle:E,connectionLineComponent:M,connectionLineContainerStyle:b,selectionKeyCode:$,selectionOnDrag:j,selectionMode:_,multiSelectionKeyCode:R,panActivationKeyCode:N,zoomActivationKeyCode:I,deleteKeyCode:L,onlyRenderVisibleElements:B,elementsSelectable:H,selectNodesOnDrag:y,defaultViewport:C,translateExtent:S,minZoom:O,maxZoom:D,preventScrolling:k,defaultMarkerColor:T,zoomOnScroll:P,zoomOnPinch:z,panOnScroll:U,panOnScrollSpeed:W,panOnScrollMode:Q,zoomOnDoubleClick:te,panOnDrag:se,onPaneClick:J,onPaneMouseEnter:ae,onPaneMouseMove:oe,onPaneMouseLeave:he,onPaneScroll:ye,onPaneContextMenu:Ee,onEdgeContextMenu:ve,onEdgeMouseEnter:ue,onEdgeMouseMove:ge,onEdgeMouseLeave:le,onReconnect:q,onReconnectStart:fe,onReconnectEnd:Oe,reconnectRadius:Ke,noDragClassName:Ye,noWheelClassName:$e,noPanClassName:Ae,elevateEdgesOnSelect:je,disableKeyboardA11y:Re,nodeOrigin:ze,nodeExtent:F,rfId:X})=>{const V=Ys(e,Sh),Z=Ys(t,Rh);return Uh(o),A.createElement(Eh,{onPaneClick:J,onPaneMouseEnter:ae,onPaneMouseMove:oe,onPaneMouseLeave:he,onPaneContextMenu:Ee,onPaneScroll:ye,deleteKeyCode:L,selectionKeyCode:$,selectionOnDrag:j,selectionMode:_,onSelectionStart:x,onSelectionEnd:v,multiSelectionKeyCode:R,panActivationKeyCode:N,zoomActivationKeyCode:I,elementsSelectable:H,onMove:n,onMoveStart:r,onMoveEnd:s,zoomOnScroll:P,zoomOnPinch:z,zoomOnDoubleClick:te,panOnScroll:U,panOnScrollSpeed:W,panOnScrollMode:Q,panOnDrag:se,defaultViewport:C,translateExtent:S,minZoom:O,maxZoom:D,onSelectionContextMenu:p,preventScrolling:k,noDragClassName:Ye,noWheelClassName:$e,noPanClassName:Ae,disableKeyboardA11y:Re},A.createElement(Wh,null,A.createElement(Yh,{edgeTypes:Z,onEdgeClick:a,onEdgeDoubleClick:u,onlyRenderVisibleElements:B,onEdgeContextMenu:ve,onEdgeMouseEnter:ue,onEdgeMouseMove:ge,onEdgeMouseLeave:le,onReconnect:q,onReconnectStart:fe,onReconnectEnd:Oe,reconnectRadius:Ke,defaultMarkerColor:T,noPanClassName:Ae,elevateEdgesOnSelect:!!je,disableKeyboardA11y:Re,rfId:X},A.createElement(Zh,{style:E,type:g,component:M,containerStyle:b})),A.createElement("div",{className:"react-flow__edgelabel-renderer"}),A.createElement(Ch,{nodeTypes:V,onNodeClick:i,onNodeDoubleClick:c,onNodeMouseEnter:l,onNodeMouseMove:d,onNodeMouseLeave:m,onNodeContextMenu:f,selectNodesOnDrag:y,onlyRenderVisibleElements:B,noPanClassName:Ae,noDragClassName:Ye,disableKeyboardA11y:Re,nodeOrigin:ze,nodeExtent:F,rfId:X})))};Ki.displayName="GraphView";var Qh=w.memo(Ki);const Cr=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],ut={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:Cr,nodeExtent:Cr,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionNodeId:null,connectionHandleId:null,connectionHandleType:"source",connectionPosition:{x:0,y:0},connectionStatus:null,connectionMode:St.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:Nf,isValidConnection:void 0},Jh=()=>Pc((e,t)=>({...ut,setNodes:n=>{const{nodeInternals:r,nodeOrigin:s,elevateNodesOnSelect:o}=t();e({nodeInternals:tr(n,r,s,o)})},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",o=typeof r<"u",i=s?tr(n,new Map,t().nodeOrigin,t().elevateNodesOnSelect):new Map;e({nodeInternals:i,edges:o?r:[],hasDefaultNodes:s,hasDefaultEdges:o})},updateNodeDimensions:n=>{const{onNodesChange:r,nodeInternals:s,fitViewOnInit:o,fitViewOnInitDone:i,fitViewOnInitOptions:a,domNode:c,nodeOrigin:u}=t(),l=c==null?void 0:c.querySelector(".react-flow__viewport");if(!l)return;const d=window.getComputedStyle(l),{m22:m}=new window.DOMMatrixReadOnly(d.transform),f=n.reduce((x,v)=>{const g=s.get(v.id);if(g!=null&&g.hidden)s.set(g.id,{...g,[we]:{...g[we],handleBounds:void 0}});else if(g){const E=Pr(v.nodeElement);!!(E.width&&E.height&&(g.width!==E.width||g.height!==E.height||v.forceUpdate))&&(s.set(g.id,{...g,[we]:{...g[we],handleBounds:{source:Ps(".source",v.nodeElement,m,u),target:Ps(".target",v.nodeElement,m,u)}},...E}),x.push({id:g.id,type:"dimensions",dimensions:E}))}return x},[]);Pi(s,u);const p=i||o&&!i&&zi(t,{initial:!0,...a});e({nodeInternals:new Map(s),fitViewOnInitDone:p}),(f==null?void 0:f.length)>0&&(r==null||r(f))},updateNodePositions:(n,r=!0,s=!1)=>{const{triggerNodeChanges:o}=t(),i=n.map(a=>{const c={id:a.id,type:"position",dragging:s};return r&&(c.positionAbsolute=a.positionAbsolute,c.position=a.position),c});o(i)},triggerNodeChanges:n=>{const{onNodesChange:r,nodeInternals:s,hasDefaultNodes:o,nodeOrigin:i,getNodes:a,elevateNodesOnSelect:c}=t();if(n!=null&&n.length){if(o){const u=mh(n,a()),l=tr(u,s,i,c);e({nodeInternals:l})}r==null||r(n)}},addSelectedNodes:n=>{const{multiSelectionActive:r,edges:s,getNodes:o}=t();let i,a=null;r?i=n.map(c=>ht(c,!0)):(i=At(o(),n),a=At(s,[])),wn({changedNodes:i,changedEdges:a,get:t,set:e})},addSelectedEdges:n=>{const{multiSelectionActive:r,edges:s,getNodes:o}=t();let i,a=null;r?i=n.map(c=>ht(c,!0)):(i=At(s,n),a=At(o(),[])),wn({changedNodes:a,changedEdges:i,get:t,set:e})},unselectNodesAndEdges:({nodes:n,edges:r}={})=>{const{edges:s,getNodes:o}=t(),i=n||o(),a=r||s,c=i.map(l=>(l.selected=!1,ht(l.id,!1))),u=a.map(l=>ht(l.id,!1));wn({changedNodes:c,changedEdges:u,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(),o=r().filter(a=>a.selected).map(a=>ht(a.id,!1)),i=n.filter(a=>a.selected).map(a=>ht(a.id,!1));wn({changedNodes:o,changedEdges:i,get:t,set:e})},setNodeExtent:n=>{const{nodeInternals:r}=t();r.forEach(s=>{s.positionAbsolute=zr(s.position,n)}),e({nodeExtent:n,nodeInternals:new Map(r)})},panBy:n=>{const{transform:r,width:s,height:o,d3Zoom:i,d3Selection:a,translateExtent:c}=t();if(!i||!a||!n.x&&!n.y)return!1;const u=ot.translate(r[0]+n.x,r[1]+n.y).scale(r[2]),l=[[0,0],[s,o]],d=i==null?void 0:i.constrain()(u,l,c);return i.transform(a,d),r[0]!==d.x||r[1]!==d.y||r[2]!==d.k},cancelConnection:()=>e({connectionNodeId:ut.connectionNodeId,connectionHandleId:ut.connectionHandleId,connectionHandleType:ut.connectionHandleType,connectionStatus:ut.connectionStatus,connectionStartHandle:ut.connectionStartHandle,connectionEndHandle:ut.connectionEndHandle}),reset:()=>e({...ut})}),Object.is),Zi=({children:e})=>{const t=w.useRef(null);return t.current||(t.current=Jh()),A.createElement(yf,{value:t.current},e)};Zi.displayName="ReactFlowProvider";const Qi=({children:e})=>w.useContext(Gn)?A.createElement(A.Fragment,null,e):A.createElement(Zi,null,e);Qi.displayName="ReactFlowWrapper";const em={input:Ai,default:kr,output:Ii,group:Yr},tm={default:On,straight:Vr,step:Fr,smoothstep:Yn,simplebezier:Hr},nm=[0,0],rm=[15,15],sm={x:0,y:0,zoom:1},om={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0},Ji=w.forwardRef(({nodes:e,edges:t,defaultNodes:n,defaultEdges:r,className:s,nodeTypes:o=em,edgeTypes:i=tm,onNodeClick:a,onEdgeClick:c,onInit:u,onMove:l,onMoveStart:d,onMoveEnd:m,onConnect:f,onConnectStart:p,onConnectEnd:x,onClickConnectStart:v,onClickConnectEnd:g,onNodeMouseEnter:E,onNodeMouseMove:M,onNodeMouseLeave:b,onNodeContextMenu:$,onNodeDoubleClick:j,onNodeDragStart:_,onNodeDrag:R,onNodeDragStop:N,onNodesDelete:I,onEdgesDelete:L,onSelectionChange:B,onSelectionDragStart:H,onSelectionDrag:y,onSelectionDragStop:C,onSelectionContextMenu:S,onSelectionStart:O,onSelectionEnd:D,connectionMode:k=St.Strict,connectionLineType:T=mt.Bezier,connectionLineStyle:P,connectionLineComponent:z,connectionLineContainerStyle:U,deleteKeyCode:W="Backspace",selectionKeyCode:Q="Shift",selectionOnDrag:te=!1,selectionMode:se=rn.Full,panActivationKeyCode:J="Space",multiSelectionKeyCode:ae=Rn()?"Meta":"Control",zoomActivationKeyCode:oe=Rn()?"Meta":"Control",snapToGrid:he=!1,snapGrid:ye=rm,onlyRenderVisibleElements:Ee=!1,selectNodesOnDrag:ve=!0,nodesDraggable:ue,nodesConnectable:ge,nodesFocusable:le,nodeOrigin:q=nm,edgesFocusable:fe,edgesUpdatable:Oe,elementsSelectable:Ke,defaultViewport:Ye=sm,minZoom:$e=.5,maxZoom:Ae=2,translateExtent:je=Cr,preventScrolling:Re=!0,nodeExtent:ze,defaultMarkerColor:F="#b1b1b7",zoomOnScroll:X=!0,zoomOnPinch:V=!0,panOnScroll:Z=!1,panOnScrollSpeed:Y=.5,panOnScrollMode:G=vt.Free,zoomOnDoubleClick:re=!0,panOnDrag:xe=!0,onPaneClick:ee,onPaneMouseEnter:ne,onPaneMouseMove:ie,onPaneMouseLeave:pe,onPaneScroll:Ie,onPaneContextMenu:Le,children:Te,onEdgeContextMenu:Ne,onEdgeDoubleClick:He,onEdgeMouseEnter:lt,onEdgeMouseMove:Ht,onEdgeMouseLeave:de,onEdgeUpdate:et,onEdgeUpdateStart:Ft,onEdgeUpdateEnd:Pa,onReconnect:za,onReconnectStart:Ha,onReconnectEnd:Fa,reconnectRadius:Va=10,edgeUpdaterRadius:Ba=10,onNodesChange:Ga,onEdgesChange:Ya,noDragClassName:Xa="nodrag",noWheelClassName:Wa="nowheel",noPanClassName:es="nopan",fitView:Ua=!1,fitViewOptions:qa,connectOnClick:Ka=!0,attributionPosition:Za,proOptions:Qa,defaultEdgeOptions:Ja,elevateNodesOnSelect:ec=!0,elevateEdgesOnSelect:tc=!1,disableKeyboardA11y:ts=!1,autoPanOnConnect:nc=!0,autoPanOnNodeDrag:rc=!0,connectionRadius:sc=20,isValidConnection:oc,onError:ic,style:ac,id:ns,nodeDragThreshold:cc,...lc},uc)=>{const Un=ns||"1";return A.createElement("div",{...lc,style:{...ac,...om},ref:uc,className:Me(["react-flow",s]),"data-testid":"rf__wrapper",id:ns},A.createElement(Qi,null,A.createElement(Qh,{onInit:u,onMove:l,onMoveStart:d,onMoveEnd:m,onNodeClick:a,onEdgeClick:c,onNodeMouseEnter:E,onNodeMouseMove:M,onNodeMouseLeave:b,onNodeContextMenu:$,onNodeDoubleClick:j,nodeTypes:o,edgeTypes:i,connectionLineType:T,connectionLineStyle:P,connectionLineComponent:z,connectionLineContainerStyle:U,selectionKeyCode:Q,selectionOnDrag:te,selectionMode:se,deleteKeyCode:W,multiSelectionKeyCode:ae,panActivationKeyCode:J,zoomActivationKeyCode:oe,onlyRenderVisibleElements:Ee,selectNodesOnDrag:ve,defaultViewport:Ye,translateExtent:je,minZoom:$e,maxZoom:Ae,preventScrolling:Re,zoomOnScroll:X,zoomOnPinch:V,zoomOnDoubleClick:re,panOnScroll:Z,panOnScrollSpeed:Y,panOnScrollMode:G,panOnDrag:xe,onPaneClick:ee,onPaneMouseEnter:ne,onPaneMouseMove:ie,onPaneMouseLeave:pe,onPaneScroll:Ie,onPaneContextMenu:Le,onSelectionContextMenu:S,onSelectionStart:O,onSelectionEnd:D,onEdgeContextMenu:Ne,onEdgeDoubleClick:He,onEdgeMouseEnter:lt,onEdgeMouseMove:Ht,onEdgeMouseLeave:de,onReconnect:za??et,onReconnectStart:Ha??Ft,onReconnectEnd:Fa??Pa,reconnectRadius:Va??Ba,defaultMarkerColor:F,noDragClassName:Xa,noWheelClassName:Wa,noPanClassName:es,elevateEdgesOnSelect:tc,rfId:Un,disableKeyboardA11y:ts,nodeOrigin:q,nodeExtent:ze}),A.createElement(Uf,{nodes:e,edges:t,defaultNodes:n,defaultEdges:r,onConnect:f,onConnectStart:p,onConnectEnd:x,onClickConnectStart:v,onClickConnectEnd:g,nodesDraggable:ue,nodesConnectable:ge,nodesFocusable:le,edgesFocusable:fe,edgesUpdatable:Oe,elementsSelectable:Ke,elevateNodesOnSelect:ec,minZoom:$e,maxZoom:Ae,nodeExtent:ze,onNodesChange:Ga,onEdgesChange:Ya,snapToGrid:he,snapGrid:ye,connectionMode:k,translateExtent:je,connectOnClick:Ka,defaultEdgeOptions:Ja,fitView:Ua,fitViewOptions:qa,onNodesDelete:I,onEdgesDelete:L,onNodeDragStart:_,onNodeDrag:R,onNodeDragStop:N,onSelectionDrag:y,onSelectionDragStart:H,onSelectionDragStop:C,noPanClassName:es,nodeOrigin:q,rfId:Un,autoPanOnConnect:nc,autoPanOnNodeDrag:rc,onError:ic,connectionRadius:sc,isValidConnection:oc,nodeDragThreshold:cc}),A.createElement(Xf,{onSelectionChange:B}),Te,A.createElement(wf,{proOptions:Qa,position:Za}),A.createElement(Jf,{rfId:Un,disableKeyboardA11y:ts})))});Ji.displayName="ReactFlow";const ea=({id:e,x:t,y:n,width:r,height:s,style:o,color:i,strokeColor:a,strokeWidth:c,className:u,borderRadius:l,shapeRendering:d,onClick:m,selected:f})=>{const{background:p,backgroundColor:x}=o||{},v=i||p||x;return A.createElement("rect",{className:Me(["react-flow__minimap-node",{selected:f},u]),x:t,y:n,rx:l,ry:l,width:r,height:s,fill:v,stroke:a,strokeWidth:c,shapeRendering:d,onClick:m?g=>m(g,e):void 0})};ea.displayName="MiniMapNode";var im=w.memo(ea);const am=e=>e.nodeOrigin,cm=e=>e.getNodes().filter(t=>!t.hidden&&t.width&&t.height),or=e=>e instanceof Function?e:()=>e;function lm({nodeStrokeColor:e="transparent",nodeColor:t="#e2e2e2",nodeClassName:n="",nodeBorderRadius:r=5,nodeStrokeWidth:s=2,nodeComponent:o=im,onClick:i}){const a=me(cm,Ce),c=me(am),u=or(t),l=or(e),d=or(n),m=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return A.createElement(A.Fragment,null,a.map(f=>{const{x:p,y:x}=Et(f,c).positionAbsolute;return A.createElement(o,{key:f.id,x:p,y:x,width:f.width,height:f.height,style:f.style,selected:f.selected,className:d(f),color:u(f),borderRadius:r,strokeColor:l(f),strokeWidth:s,shapeRendering:m,onClick:i,id:f.id})}))}var um=w.memo(lm);const dm=200,fm=150,hm=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?_f(ln(t,e.nodeOrigin),n):n,rfId:e.rfId}},mm="react-flow__minimap-desc";function ta({style:e,className:t,nodeStrokeColor:n="transparent",nodeColor:r="#e2e2e2",nodeClassName:s="",nodeBorderRadius:o=5,nodeStrokeWidth:i=2,nodeComponent:a,maskColor:c="rgb(240, 240, 240, 0.6)",maskStrokeColor:u="none",maskStrokeWidth:l=1,position:d="bottom-right",onClick:m,onNodeClick:f,pannable:p=!1,zoomable:x=!1,ariaLabel:v="React Flow mini map",inversePan:g=!1,zoomStep:E=10,offsetScale:M=5}){const b=ke(),$=w.useRef(null),{boundingRect:j,viewBB:_,rfId:R}=me(hm,Ce),N=(e==null?void 0:e.width)??dm,I=(e==null?void 0:e.height)??fm,L=j.width/N,B=j.height/I,H=Math.max(L,B),y=H*N,C=H*I,S=M*H,O=j.x-(y-j.width)/2-S,D=j.y-(C-j.height)/2-S,k=y+S*2,T=C+S*2,P=`${mm}-${R}`,z=w.useRef(0);z.current=H,w.useEffect(()=>{if($.current){const Q=Fe($.current),te=ae=>{const{transform:oe,d3Selection:he,d3Zoom:ye}=b.getState();if(ae.sourceEvent.type!=="wheel"||!he||!ye)return;const Ee=-ae.sourceEvent.deltaY*(ae.sourceEvent.deltaMode===1?.05:ae.sourceEvent.deltaMode?1:.002)*E,ve=oe[2]*Math.pow(2,Ee);ye.scaleTo(he,ve)},se=ae=>{const{transform:oe,d3Selection:he,d3Zoom:ye,translateExtent:Ee,width:ve,height:ue}=b.getState();if(ae.sourceEvent.type!=="mousemove"||!he||!ye)return;const ge=z.current*Math.max(1,oe[2])*(g?-1:1),le={x:oe[0]-ae.sourceEvent.movementX*ge,y:oe[1]-ae.sourceEvent.movementY*ge},q=[[0,0],[ve,ue]],fe=ot.translate(le.x,le.y).scale(oe[2]),Oe=ye.constrain()(fe,q,Ee);ye.transform(he,Oe)},J=li().on("zoom",p?se:null).on("zoom.wheel",x?te:null);return Q.call(J),()=>{Q.on("zoom",null)}}},[p,x,g,E]);const U=m?Q=>{const te=Xe(Q);m(Q,{x:te[0],y:te[1]})}:void 0,W=f?(Q,te)=>{const se=b.getState().nodeInternals.get(te);f(Q,se)}:void 0;return A.createElement(Dr,{position:d,style:e,className:Me(["react-flow__minimap",t]),"data-testid":"rf__minimap"},A.createElement("svg",{width:N,height:I,viewBox:`${O} ${D} ${k} ${T}`,role:"img","aria-labelledby":P,ref:$,onClick:U},v&&A.createElement("title",{id:P},v),A.createElement(um,{onClick:W,nodeColor:r,nodeStrokeColor:n,nodeBorderRadius:o,nodeClassName:s,nodeStrokeWidth:i,nodeComponent:a}),A.createElement("path",{className:"react-flow__minimap-mask",d:`M${O-S},${D-S}h${k+S*2}v${T+S*2}h${-k-S*2}z + M${_.x},${_.y}h${_.width}v${_.height}h${-_.width}z`,fill:c,fillRule:"evenodd",stroke:u,strokeWidth:l,pointerEvents:"none"})))}ta.displayName="MiniMap";var pm=w.memo(ta);function gm(){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 ym(){return A.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5"},A.createElement("path",{d:"M0 0h32v4.2H0z"}))}function xm(){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 wm(){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 bm(){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 qt=({children:e,className:t,...n})=>A.createElement("button",{type:"button",className:Me(["react-flow__controls-button",t]),...n},e);qt.displayName="ControlButton";const vm=e=>({isInteractive:e.nodesDraggable||e.nodesConnectable||e.elementsSelectable,minZoomReached:e.transform[2]<=e.minZoom,maxZoomReached:e.transform[2]>=e.maxZoom}),na=({style:e,showZoom:t=!0,showFitView:n=!0,showInteractive:r=!0,fitViewOptions:s,onZoomIn:o,onZoomOut:i,onFitView:a,onInteractiveChange:c,className:u,children:l,position:d="bottom-left"})=>{const m=ke(),[f,p]=w.useState(!1),{isInteractive:x,minZoomReached:v,maxZoomReached:g}=me(vm,Ce),{zoomIn:E,zoomOut:M,fitView:b}=un();if(w.useEffect(()=>{p(!0)},[]),!f)return null;const $=()=>{E(),o==null||o()},j=()=>{M(),i==null||i()},_=()=>{b(s),a==null||a()},R=()=>{m.setState({nodesDraggable:!x,nodesConnectable:!x,elementsSelectable:!x}),c==null||c(!x)};return A.createElement(Dr,{className:Me(["react-flow__controls",u]),position:d,style:e,"data-testid":"rf__controls"},t&&A.createElement(A.Fragment,null,A.createElement(qt,{onClick:$,className:"react-flow__controls-zoomin",title:"zoom in","aria-label":"zoom in",disabled:g},A.createElement(gm,null)),A.createElement(qt,{onClick:j,className:"react-flow__controls-zoomout",title:"zoom out","aria-label":"zoom out",disabled:v},A.createElement(ym,null))),n&&A.createElement(qt,{className:"react-flow__controls-fitview",onClick:_,title:"fit view","aria-label":"fit view"},A.createElement(xm,null)),r&&A.createElement(qt,{className:"react-flow__controls-interactive",onClick:R,title:"toggle interactivity","aria-label":"toggle interactivity"},x?A.createElement(bm,null):A.createElement(wm,null)),l)};na.displayName="Controls";var _m=w.memo(na),Be;(function(e){e.Lines="lines",e.Dots="dots",e.Cross="cross"})(Be||(Be={}));function Em({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 Nm({color:e,radius:t}){return A.createElement("circle",{cx:t,cy:t,r:t,fill:e})}const Sm={[Be.Dots]:"#91919a",[Be.Lines]:"#eee",[Be.Cross]:"#e2e2e2"},km={[Be.Dots]:1,[Be.Lines]:1,[Be.Cross]:6},jm=e=>({transform:e.transform,patternId:`pattern-${e.rfId}`});function ra({id:e,variant:t=Be.Dots,gap:n=20,size:r,lineWidth:s=1,offset:o=2,color:i,style:a,className:c}){const u=w.useRef(null),{transform:l,patternId:d}=me(jm,Ce),m=i||Sm[t],f=r||km[t],p=t===Be.Dots,x=t===Be.Cross,v=Array.isArray(n)?n:[n,n],g=[v[0]*l[2]||1,v[1]*l[2]||1],E=f*l[2],M=x?[E,E]:g,b=p?[E/o,E/o]:[M[0]/o,M[1]/o];return A.createElement("svg",{className:Me(["react-flow__background",c]),style:{...a,position:"absolute",width:"100%",height:"100%",top:0,left:0},ref:u,"data-testid":"rf__background"},A.createElement("pattern",{id:d+e,x:l[0]%g[0],y:l[1]%g[1],width:g[0],height:g[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${b[0]},-${b[1]})`},p?A.createElement(Nm,{color:m,radius:E/o}):A.createElement(Em,{dimensions:M,color:m,lineWidth:s})),A.createElement("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${d+e})`}))}ra.displayName="Background";var Cm=w.memo(ra);const Wr=(()=>{const e=window.location.pathname.match(/^(\/view\/[^/]+)(?:\/|$)/);return e?e[1]:""})();function Ln(e){return Wr+e}async function be(e){const t=await fetch(Ln(e));if(!t.ok)throw new Error(`${e}: ${t.status} ${await t.text()}`);return await t.json()}async function ct(e,t){const n=await fetch(Ln(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json();if(!n.ok)throw new sa(r.error??`${e}: ${n.status}`,n.status);return r}class sa extends Error{constructor(n,r){super(n);_e(this,"status");this.status=r}}const Hx=e=>be(e?`/api/model?subject_area=${encodeURIComponent(e)}`:"/api/model"),Fx=()=>be("/api/diagnostics"),oa=(e,t)=>be(`/api/ontology/search?q=${encodeURIComponent(e)}`+(t?`&within=${encodeURIComponent(t)}`:"")),ia=()=>be("/api/ontology/ontologies"),Mm=e=>be(`/api/ontology/term?ref=${encodeURIComponent(e)}`),$m=()=>be("/api/ontology/stack"),Am=()=>be("/api/ontology/coverage"),Rm=(e,t,n)=>ct("/api/command",{op:e,payload:t,fingerprint:n??""}),Im=()=>be("/api/git/status"),Tm=()=>be("/api/git/diff"),Om=e=>ct("/api/git/commit",{message:e}),Lm=()=>ct("/api/git/discard",{}),Dm=()=>be("/api/decisions"),Xs=(e,t)=>ct(`/api/decisions/${e}/verdict`,{verdict:t}),Vx=(e={})=>{const t=new URLSearchParams;return e.subject_area&&t.set("subject_area",e.subject_area),e.q&&t.set("q",e.q),be(`/api/glossary/terms?${t.toString()}`)},Bx=()=>be("/api/glossary/config"),Gx=e=>ct("/api/git/propose",e),Yx=(e="HEAD")=>be(`/api/git/diff/model?base=${encodeURIComponent(e)}`),Xx=(e="HEAD")=>be(`/api/git/classify?base=${encodeURIComponent(e)}`),Wx=e=>ct("/api/git/classify",{changes:e}),Ux=e=>be(`/api/git/conflicts?base=${encodeURIComponent(e)}`),qx=(e="")=>be(`/api/git/context?user=${encodeURIComponent(e)}`),Kx=(e="")=>be(`/api/git/proposals?user=${encodeURIComponent(e)}`),Pm=()=>be("/api/export"),zm=(e,t)=>{const n=t?`?dialect=${encodeURIComponent(t)}`:"";return`${Wr}/api/export/${encodeURIComponent(e)}${n}`},Hm=(e,t,n)=>ct("/api/import",{format:e,content:t,dialect:n}),Fm=(e,t)=>ct("/api/import/preview",{format:e,content:t}),Zx=e=>be(`/api/import/stash/${encodeURIComponent(e)}`),Qx=async(e,t=!1)=>{for(const n of e)try{await Rm(n.op,n.payload)}catch(r){if(t&&r instanceof Error&&/already exists/i.test(r.message))continue;throw r}},Jx=(e,t,n)=>fetch(`${Wr}/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 sa(await r.text(),r.status);return await r.json()}),ew=()=>be("/api/glossary/subject-areas"),tw=e=>be(`/api/glossary/subject-area/${e}`),nw=(e,t)=>ct(`/api/glossary/subject-area/${e}/expand`,t);function rw(){return{async load(e){try{const t=await be(`/api/layout?view=${encodeURIComponent(e)}`);return{nodes:t.nodes??{},edges:t.edges??{}}}catch{return{nodes:{},edges:{}}}},async save(e,t){try{await fetch(Ln("/api/layout"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({view:e,nodes:t.nodes,edges:t.edges})})}catch{}},async reset(e){try{const t=e?`?view=${encodeURIComponent(e)}`:"";await fetch(Ln(`/api/layout${t}`),{method:"DELETE"})}catch{}}}}const Vm={business_key:"🔑",surrogate_key:"#",measure:"Σ",attribute:""};function Bm(e){return e??""}const Ur="__entity_source",qr="__entity_target",Gm=w.memo(function({data:t,selected:n}){var j;const{entity:r,color:s,dimmed:o,highlighted:i,showTypes:a,collapseDetail:c,expanded:u,onToggleExpand:l,endpointAttrs:d,highlightAttrs:m,dimAttrs:f,onHoverAttr:p}=t,x=_=>_.role==="business_key"?0:d!=null&&d.has(_.id)?1:2,v=[...r.attributes].map((_,R)=>({a:_,i:R})).sort((_,R)=>x(_.a)-x(R.a)||_.i-R.i).map(_=>_.a),g=v.filter(_=>_.role==="business_key");let E=v.filter(_=>_.role!=="business_key");const M=!!c&&!u;let b=0;if(M){const _=E.filter(R=>d==null?void 0:d.has(R.id));b=E.length-_.length,E=_}const $=(_,R)=>{const N="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:N,onMouseEnter:p?()=>p(_.id):void 0,onMouseLeave:p?()=>p(null):void 0,children:[h.jsx(pt,{id:_.id,type:"target",position:K.Left,className:"port attr-port"}),h.jsx(pt,{id:_.id,type:"source",position:K.Right,className:"port attr-port"}),h.jsx("span",{className:"attr-icon",children:Vm[_.role]}),h.jsx("span",{className:"attr-name"+(R?" pk":""),children:_.name}),a&&h.jsx("span",{className:"attr-type",children:Bm(_.domain)}),!_.nullable&&h.jsx("span",{className:"attr-notnull",title:"not null",children:"●"})]},_.id)};return h.jsxs("div",{className:"entity-node"+(n?" selected":"")+(o?" dimmed":"")+(i?" highlighted":""),style:{borderTopColor:s},children:[h.jsx(pt,{id:qr,type:"target",position:K.Left,className:"port"}),h.jsx(pt,{id:Ur,type:"source",position:K.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 N;const _=(((N=r.conceptual)==null?void 0:N.ontology_refs)??[]).filter(I=>I.uri);if(_.length===0)return null;const R=_.map(I=>I.uri).join(` +`);return h.jsxs("span",{className:"badge ontology",title:R,children:["⚙",_.length>1?` ${_.length}`:""]})})()]})]}),g.length>0&&h.jsx("div",{className:"attr-section keys",children:g.map(_=>$(_,!0))}),h.jsxs("div",{className:"attr-section",children:[E.map(_=>$(_,!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:l?()=>l(r.id):void 0,children:`+${b} more`}),u&&!!c&&h.jsx("div",{className:"attr-row more",title:"Collapse to keys",onClick:l?()=>l(r.id):void 0,children:"− collapse"})]})]})}),Ym=["string","integer","bigint","decimal","boolean","date","timestamp"];function Xm({entity:e,doc:t,caps:n,exec:r,onClose:s,onFocusEntity:o,onAlign:i,onEditMapping:a}){var m,f,p,x,v,g,E,M,b,$,j,_,R;const c=!n.canEdit,u=e.conceptual,l=t.relationships.filter(N=>N.from.entity===e.id||N.to.entity===e.id),d=N=>{var I;return((I=t.entities.find(L=>L.id===N))==null?void 0:I.name)??N};return h.jsxs("aside",{className:"detail-panel",children:[h.jsxs("div",{className:"detail-header",children:[h.jsxs("div",{style:{flex:1},children:[h.jsx(nt,{value:e.name,className:"entity-title-input",readOnly:c,onCommit:N=>r("rename_entity",{id:e.id,name:N})}),(e.physical_name||!c)&&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(nt,{value:e.physical_name??"",className:"physical-name-input",placeholder:c?"—":"physical table name…",readOnly:c,onCommit:N=>r("rename_entity",{id:e.id,physical_name:N})})]}),h.jsxs("div",{children:[h.jsx(Um,{entity:e,doc:t,readOnly:c,exec:r}),h.jsx(Km,{entity:e,readOnly:c,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(nt,{value:((m=u==null?void 0:u.definition)==null?void 0:m.trim())??"",multiline:!0,placeholder:c?"—":"add a business definition…",className:"definition-input",readOnly:c,onCommit:N=>r("set_definition",{id:e.id,definition:N})})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Ontology",!c&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>i(e),children:(((f=u==null?void 0:u.ontology_refs)==null?void 0:f.length)??0)>0?"Add / re-align…":"Align…"}),(((p=u==null?void 0:u.ontology_refs)==null?void 0:p.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"})]})]}),(u==null?void 0:u.ontology_layer)&&h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"layer"}),h.jsx("span",{className:`chip layer-${u.ontology_layer}`,children:u.ontology_layer}),u.no_industry_equivalent&&h.jsx("span",{className:"chip",title:"reviewed: no industry equivalent",children:"no industry equivalent"})]}),h.jsx(Wm,{entityId:e.id,refs:(u==null?void 0:u.ontology_refs)??[],caps:n,exec:r})]}),h.jsxs("section",{children:[h.jsxs("h3",{children:["Knowledge Graph mapping",!c&&h.jsxs("span",{className:"h3-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>a(e),children:(x=e.term_map)!=null&&x.subject_template||(v=e.term_map)!=null&&v.class_iri?"Edit mapping…":"Map…"}),(((g=e.term_map)==null?void 0:g.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"})]})]}),(M=e.term_map)!=null&&M.subject_template||(b=e.term_map)!=null&&b.class_iri?h.jsxs(h.Fragment,{children:[(($=e.term_map)==null?void 0:$.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(nt,{value:((_=u==null?void 0:u.stewardship)==null?void 0:_.owner)??"",placeholder:"—",readOnly:c,onCommit:N=>{var I;return r("set_stewardship",{id:e.id,owner:N,steward:((I=u==null?void 0:u.stewardship)==null?void 0:I.steward)??null})}})]}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"steward"}),h.jsx(nt,{value:((R=u==null?void 0:u.stewardship)==null?void 0:R.steward)??"",placeholder:"—",readOnly:c,onCommit:N=>{var I;return r("set_stewardship",{id:e.id,owner:((I=u==null?void 0:u.stewardship)==null?void 0:I.owner)??null,steward:N})}})]})]}),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(N=>h.jsx(Qm,{a:N,entity:e,doc:t,readOnly:c,exec:r},N.id))})}),!c&&h.jsx(Jm,{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(N=>{const I=N.members.map(L=>{var B;return((B=e.attributes.find(H=>H.id===L))==null?void 0:B.name)??"?"}).join(", ");return h.jsxs("div",{className:"key-row",children:[h.jsx("span",{className:"key-badge "+N.type,children:N.type.toUpperCase()}),h.jsx("span",{className:"key-name",children:N.name}),h.jsx("span",{className:"key-cols",children:I})]},N.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(([N,I])=>h.jsxs("tr",{className:"udp-row",children:[h.jsx("td",{className:"udp-key",children:N}),h.jsx("td",{className:"udp-val",children:String(I)})]},N))})})]}),l.length>0&&h.jsxs("section",{children:[h.jsxs("h3",{children:["Relationships (",l.length,")"]}),l.map(N=>{const I=N.from.entity===e.id?N.to.entity:N.from.entity;return h.jsxs("div",{className:"rel-row",children:[h.jsxs("div",{className:"rel-main",children:[h.jsxs("button",{className:"rel-link",onClick:()=>o(I),children:[h.jsx("span",{className:"rel-card",children:ep(N,e.id)}),d(I)]}),(N.verb_phrase||N.inverse_verb_phrase||!c)&&h.jsxs("div",{className:"verb-phrases",title:"erwin verb phrases (parent → child, and inverse)",children:[h.jsx(nt,{value:N.verb_phrase??"",className:"verb-input",placeholder:c?"":"verb phrase…",readOnly:c,onCommit:L=>r("update_relationship",{id:N.id,verb_phrase:L})}),h.jsx("span",{className:"verb-sep",children:"/"}),h.jsx(nt,{value:N.inverse_verb_phrase??"",className:"verb-input",placeholder:c?"":"inverse…",readOnly:c,onCommit:L=>r("update_relationship",{id:N.id,inverse_verb_phrase:L})})]})]}),!c&&h.jsx("button",{className:"mini-btn danger",title:`delete ${N.name}`,onClick:()=>r("delete_relationship",{id:N.id}),children:"✕"})]},N.id)})]}),h.jsxs("section",{children:[h.jsx("h3",{children:"Identity"}),h.jsx("code",{className:"ulid",title:"Immutable ULID — click to copy",onClick:()=>{var N;return(N=navigator.clipboard)==null?void 0:N.writeText(e.id)},children:e.id}),!c&&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 Wm({entityId:e,refs:t,caps:n,exec:r}){const s=!n.canEdit,o=t.filter(i=>i.uri);return o.length===0?h.jsx("p",{className:"empty-hint",children:"not aligned to any ontology term"}):h.jsx("div",{className:"ref-list",children:o.map((i,a)=>{const c=i.status==="proposed";return h.jsxs("div",{className:"ref-card"+(c?" proposed":""),children:[h.jsxs("div",{className:"ref-head",children:[h.jsx("code",{className:"ref-uri",title:i.uri??void 0,children:i.uri}),h.jsx("span",{className:"ref-status "+(c?"proposed":"accepted"),children:c?"proposed":"accepted"})]}),h.jsxs("div",{className:"ref-meta",children:[i.predicate&&h.jsx("span",{className:"ref-pill",children:i.predicate}),i.layer&&h.jsx("span",{className:`chip layer-${i.layer}`,children:i.layer}),i.resolved_via&&h.jsxs("span",{className:"ref-via",title:"resolver that found this term",children:["via ",i.resolved_via]}),typeof i.confidence=="number"&&h.jsxs("span",{className:"ref-via",children:["conf ",Math.round(i.confidence*100),"%"]})]}),!s&&h.jsxs("div",{className:"ref-actions",children:[c&&n.canArbitrate&&h.jsx("button",{className:"mini-btn",onClick:()=>r("promote_alignment",{id:e,uri:i.uri}),title:"Accept this proposed alignment (architect action)",children:"Promote"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>r("clear_alignment",{id:e,uri:i.uri}),title:"Remove this alignment",children:"Remove"})]})]},`${i.uri}-${a}`)})})}function nt({value:e,onCommit:t,readOnly:n,multiline:r=!1,placeholder:s="",className:o=""}){const[i,a]=w.useState(e);w.useEffect(()=>a(e),[e]);const c=()=>{i.trim()!==e.trim()&&t(i.trim())};return n?h.jsx("span",{className:o,children:e||s}):r?h.jsx("textarea",{className:`edit-input ${o}`,value:i,placeholder:s,rows:3,onChange:u=>a(u.target.value),onBlur:c}):h.jsx("input",{className:`edit-input ${o}`,value:i,placeholder:s,onChange:u=>a(u.target.value),onBlur:c,onKeyDown:u=>u.key==="Enter"&&u.target.blur()})}function Um({entity:e,doc:t,readOnly:n,exec:r}){var o,i,a;const s=((i=(o=e.conceptual)==null?void 0:o.subject_area)==null?void 0:i.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:c=>r("set_subject_area",{id:e.id,subject_area:c.target.value||null}),children:[h.jsx("option",{value:"",children:"no subject area"}),t.subject_areas.map(c=>h.jsx("option",{value:c.id,children:c.name},c.id))]})}const qm=["","scd2","hub","link","satellite","bridge"];function Km({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:qm.map(r=>h.jsx("option",{value:r,children:r||"no pattern"},r))})}const Zm=["attribute","business_key","measure","surrogate_key"];function Qm({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 o=i=>s("update_attribute",{entity_id:t.id,attribute_id:e.id,...i});return h.jsxs("tr",{children:[h.jsxs("td",{className:e.role==="business_key"?"pk":"",children:[h.jsx(nt,{value:e.name,readOnly:!1,onCommit:i=>o({name:i})}),(e.physical_name||!1)&&h.jsx(nt,{value:e.physical_name??"",className:"physical-col-input",placeholder:"physical…",readOnly:!1,onCommit:i=>o({physical_name:i})}),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:i=>o({domain:i.target.value}),children:[...new Set([...Ym,...n.domains??[],e.domain??"string"])].sort().map(i=>h.jsx("option",{value:i,children:i},i))})}),h.jsx("td",{children:h.jsx("select",{className:"cell-select",value:e.role,onChange:i=>o({role:i.target.value}),title:"role",children:Zm.map(i=>h.jsx("option",{value:i,children:i==="business_key"?"🔑 key":i==="measure"?"Σ measure":i},i))})}),h.jsx("td",{className:"null",children:h.jsx("input",{type:"checkbox",checked:!e.nullable,title:"not null",onChange:i=>o({nullable:!i.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 Jm({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:o=>r(o.target.value),onKeyDown:o=>o.key==="Enter"&&s()}),n&&h.jsx("button",{className:"mini-btn",onClick:s,children:"Add"})]})}function ep(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 tp="\0",xt="\0",Ws="";let np=class{constructor(t){_e(this,"_isDirected",!0);_e(this,"_isMultigraph",!1);_e(this,"_isCompound",!1);_e(this,"_label");_e(this,"_defaultNodeLabelFn",()=>{});_e(this,"_defaultEdgeLabelFn",()=>{});_e(this,"_nodes",{});_e(this,"_in",{});_e(this,"_preds",{});_e(this,"_out",{});_e(this,"_sucs",{});_e(this,"_edgeObjs",{});_e(this,"_edgeLabels",{});_e(this,"_nodeCount",0);_e(this,"_edgeCount",0);_e(this,"_parent");_e(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[xt]={})}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(o){r.length>1?s.setNode(o,n):s.setNode(o)}),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]=xt,this._children[t]={},this._children[xt][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=xt;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!==xt)return n}}children(t=xt){if(this._isCompound){var n=this._children[t];if(n)return Object.keys(n)}else{if(t===xt)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([i,a]){t(i)&&n.setNode(i,a)}),Object.values(this._edgeObjs).forEach(function(i){n.hasNode(i.v)&&n.hasNode(i.w)&&n.setEdge(i,r.edge(i))});var s={};function o(i){var a=r.parent(i);return a===void 0||n.hasNode(a)?(s[i]=a,a):a in s?s[a]:o(a)}return this._isCompound&&n.nodes().forEach(i=>n.setParent(i,o(i))),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(o,i){return s.length>1?r.setEdge(o,i,n):r.setEdge(o,i),i}),this}setEdge(){var t,n,r,s,o=!1,i=arguments[0];typeof i=="object"&&i!==null&&"v"in i?(t=i.v,n=i.w,r=i.name,arguments.length===2&&(s=arguments[1],o=!0)):(t=i,n=arguments[1],r=arguments[3],arguments.length>2&&(s=arguments[2],o=!0)),t=""+t,n=""+n,r!==void 0&&(r=""+r);var a=Kt(this._isDirected,t,n,r);if(Object.hasOwn(this._edgeLabels,a))return o&&(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]=o?s:this._defaultEdgeLabelFn(t,n,r);var c=rp(this._isDirected,t,n,r);return t=c.v,n=c.w,Object.freeze(c),this._edgeObjs[a]=c,Us(this._preds[n],t),Us(this._sucs[t],n),this._in[n][a]=c,this._out[t][a]=c,this._edgeCount++,this}edge(t,n,r){var s=arguments.length===1?ir(this._isDirected,arguments[0]):Kt(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?ir(this._isDirected,arguments[0]):Kt(this._isDirected,t,n,r);return Object.hasOwn(this._edgeLabels,s)}removeEdge(t,n,r){var s=arguments.length===1?ir(this._isDirected,arguments[0]):Kt(this._isDirected,t,n,r),o=this._edgeObjs[s];return o&&(t=o.v,n=o.w,delete this._edgeLabels[s],delete this._edgeObjs[s],qs(this._preds[n],t),qs(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(o=>o.v===n):s}}outEdges(t,n){var r=this._out[t];if(r){var s=Object.values(r);return n?s.filter(o=>o.w===n):s}}nodeEdges(t,n){var r=this.inEdges(t,n);if(r)return r.concat(this.outEdges(t,n))}};function Us(e,t){e[t]?e[t]++:e[t]=1}function qs(e,t){--e[t]||delete e[t]}function Kt(e,t,n,r){var s=""+t,o=""+n;if(!e&&s>o){var i=s;s=o,o=i}return s+Ws+o+Ws+(r===void 0?tp:r)}function rp(e,t,n,r){var s=""+t,o=""+n;if(!e&&s>o){var i=s;s=o,o=i}var a={v:s,w:o};return r&&(a.name=r),a}function ir(e,t){return Kt(e,t.v,t.w,t.name)}var Kr=np,sp="2.2.4",op={Graph:Kr,version:sp},ip=Kr,ap={write:cp,read:dp};function cp(e){var t={options:{directed:e.isDirected(),multigraph:e.isMultigraph(),compound:e.isCompound()},nodes:lp(e),edges:up(e)};return e.graph()!==void 0&&(t.value=structuredClone(e.graph())),t}function lp(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 up(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 dp(e){var t=new ip(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 fp=hp;function hp(e){var t={},n=[],r;function s(o){Object.hasOwn(t,o)||(t[o]=!0,r.push(o),e.successors(o).forEach(s),e.predecessors(o).forEach(s))}return e.nodes().forEach(function(o){r=[],s(o),r.length&&n.push(r)}),n}let mp=class{constructor(){_e(this,"_arr",[]);_e(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,o=s.length;return r[t]=o,s.push({key:t,priority:n}),this._decrease(o),!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,o=t;r>1,!(n[s].priority1;function yp(e,t,n,r){return xp(e,String(t),n||gp,r||function(s){return e.outEdges(s)})}function xp(e,t,n,r){var s={},o=new pp,i,a,c=function(u){var l=u.v!==i?u.v:u.w,d=s[l],m=n(u),f=a.distance+m;if(m<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+u+" Weight: "+m);f0&&(i=o.removeMin(),a=s[i],a.distance!==Number.POSITIVE_INFINITY);)r(i).forEach(c);return s}var wp=ca,bp=vp;function vp(e,t,n){return e.nodes().reduce(function(r,s){return r[s]=wp(e,s,t,n),r},{})}var la=_p;function _p(e){var t=0,n=[],r={},s=[];function o(i){var a=r[i]={onStack:!0,lowlink:t,index:t++};if(n.push(i),e.successors(i).forEach(function(l){Object.hasOwn(r,l)?r[l].onStack&&(a.lowlink=Math.min(a.lowlink,r[l].index)):(o(l),a.lowlink=Math.min(a.lowlink,r[l].lowlink))}),a.lowlink===a.index){var c=[],u;do u=n.pop(),r[u].onStack=!1,c.push(u);while(i!==u);s.push(c)}}return e.nodes().forEach(function(i){Object.hasOwn(r,i)||o(i)}),s}var Ep=la,Np=Sp;function Sp(e){return Ep(e).filter(function(t){return t.length>1||t.length===1&&e.hasEdge(t[0],t[0])})}var kp=Cp,jp=()=>1;function Cp(e,t,n){return Mp(e,t||jp,n||function(r){return e.outEdges(r)})}function Mp(e,t,n){var r={},s=e.nodes();return s.forEach(function(o){r[o]={},r[o][o]={distance:0},s.forEach(function(i){o!==i&&(r[o][i]={distance:Number.POSITIVE_INFINITY})}),n(o).forEach(function(i){var a=i.v===o?i.w:i.v,c=t(i);r[o][a]={distance:c,predecessor:o}})}),s.forEach(function(o){var i=r[o];s.forEach(function(a){var c=r[a];s.forEach(function(u){var l=c[o],d=i[u],m=c[u],f=l.distance+d.distance;fe.successors(a):a=>e.neighbors(a),s=n==="post"?Ip:Tp,o=[],i={};return t.forEach(a=>{if(!e.hasNode(a))throw new Error("Graph does not have node: "+a);s(a,r,i,o)}),o}function Ip(e,t,n,r){for(var s=[[e,!1]];s.length>0;){var o=s.pop();o[1]?r.push(o[0]):Object.hasOwn(n,o[0])||(n[o[0]]=!0,s.push([o[0],!0]),ha(t(o[0]),i=>s.push([i,!1])))}}function Tp(e,t,n,r){for(var s=[e];s.length>0;){var o=s.pop();Object.hasOwn(n,o)||(n[o]=!0,r.push(o),ha(t(o),i=>s.push(i)))}}function ha(e,t){for(var n=e.length;n--;)t(e[n],n,e);return e}var Op=fa,Lp=Dp;function Dp(e,t){return Op(e,t,"post")}var Pp=fa,zp=Hp;function Hp(e,t){return Pp(e,t,"pre")}var Fp=Kr,Vp=aa,Bp=Gp;function Gp(e,t){var n=new Fp,r={},s=new Vp,o;function i(c){var u=c.v===o?c.w:c.v,l=s.priority(u);if(l!==void 0){var d=t(c);d0;){if(o=s.removeMin(),Object.hasOwn(r,o))n.setEdge(o,r[o]);else{if(a)throw new Error("Input graph is not connected: "+e);a=!0}e.nodeEdges(o).forEach(i)}return n}var Yp={components:fp,dijkstra:ca,dijkstraAll:bp,findCycles:Np,floydWarshall:kp,isAcyclic:$p,postorder:Lp,preorder:zp,prim:Bp,tarjan:la,topsort:da},Zs=op,qe={Graph:Zs.Graph,json:ap,alg:Yp,version:Zs.version};let Xp=class{constructor(){let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,n=t._prev;if(n!==t)return Qs(n),n}enqueue(t){let n=this._sentinel;t._prev&&t._next&&Qs(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,Wp)),r=r._prev;return"["+t.join(", ")+"]"}};function Qs(e){e._prev._next=e._next,e._next._prev=e._prev,delete e._next,delete e._prev}function Wp(e,t){if(e!=="_next"&&e!=="_prev")return t}var Up=Xp;let qp=qe.Graph,Kp=Up;var Zp=Jp;let Qp=()=>1;function Jp(e,t){if(e.nodeCount()<=1)return[];let n=tg(e,t||Qp);return eg(n.graph,n.buckets,n.zeroIdx).flatMap(s=>e.outEdges(s.v,s.w))}function eg(e,t,n){let r=[],s=t[t.length-1],o=t[0],i;for(;e.nodeCount();){for(;i=o.dequeue();)ar(e,t,n,i);for(;i=s.dequeue();)ar(e,t,n,i);if(e.nodeCount()){for(let a=t.length-2;a>0;--a)if(i=t[a].dequeue(),i){r=r.concat(ar(e,t,n,i,!0));break}}}return r}function ar(e,t,n,r,s){let o=s?[]:void 0;return e.inEdges(r.v).forEach(i=>{let a=e.edge(i),c=e.node(i.v);s&&o.push({v:i.v,w:i.w}),c.out-=a,$r(t,n,c)}),e.outEdges(r.v).forEach(i=>{let a=e.edge(i),c=i.w,u=e.node(c);u.in-=a,$r(t,n,u)}),e.removeNode(r.v),o}function tg(e,t){let n=new qp,r=0,s=0;e.nodes().forEach(a=>{n.setNode(a,{v:a,in:0,out:0})}),e.edges().forEach(a=>{let c=n.edge(a.v,a.w)||0,u=t(a),l=c+u;n.setEdge(a.v,a.w,l),s=Math.max(s,n.node(a.v).out+=u),r=Math.max(r,n.node(a.w).in+=u)});let o=ng(s+r+3).map(()=>new Kp),i=r+1;return n.nodes().forEach(a=>{$r(o,i,n.node(a))}),{graph:n,buckets:o,zeroIdx:i}}function $r(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 ng(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 sg(e){let t=new ma({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 og(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 ig(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 ag(e,t){let n=e.x,r=e.y,s=t.x-n,o=t.y-r,i=e.width/2,a=e.height/2;if(!s&&!o)throw new Error("Not possible to find intersection inside of the rectangle");let c,u;return Math.abs(o)*i>Math.abs(s)*a?(o<0&&(a=-a),c=a*s/o,u=a):(s<0&&(i=-i),c=i,u=i*o/s),{x:n+c,y:r+u}}function cg(e){let t=wa(ya(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 lg(e){let t=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MAX_VALUE:s}),n=Xn(Math.min,t);e.nodes().forEach(r=>{let s=e.node(r);Object.hasOwn(s,"rank")&&(s.rank-=n)})}function ug(e){let t=e.nodes().map(i=>e.node(i).rank),n=Xn(Math.min,t),r=[];e.nodes().forEach(i=>{let a=e.node(i).rank-n;r[a]||(r[a]=[]),r[a].push(i)});let s=0,o=e.graph().nodeRankFactor;Array.from(r).forEach((i,a)=>{i===void 0&&a%o!==0?--s:i!==void 0&&s&&i.forEach(c=>e.node(c).rank+=s)})}function dg(e,t,n,r){let s={width:0,height:0};return arguments.length>=4&&(s.rank=n,s.order=r),pa(e,"border",s,t)}function fg(e,t=ga){const n=[];for(let r=0;rga){const n=fg(t);return e.apply(null,n.map(r=>e.apply(null,r)))}else return e.apply(null,t)}function ya(e){const n=e.nodes().map(r=>{let s=e.node(r).rank;return s===void 0?Number.MIN_VALUE:s});return Xn(Math.max,n)}function hg(e,t){let n={lhs:[],rhs:[]};return e.forEach(r=>{t(r)?n.lhs.push(r):n.rhs.push(r)}),n}function mg(e,t){let n=Date.now();try{return t()}finally{console.log(e+" time: "+(Date.now()-n)+"ms")}}function pg(e,t){return t()}let gg=0;function xa(e){var t=++gg;return e+(""+t)}function wa(e,t,n=1){t==null&&(t=e,e=0);let r=o=>otr[t]),Object.entries(e).reduce((r,[s,o])=>(r[s]=n(o,s),r),{})}function Zr(e,t){return e.reduce((n,r,s)=>(n[r]=t[s],n),{})}let wg=Zp,bg=Se.uniqueId;var vg={run:_g,undo:Ng};function _g(e){(e.graph().acyclicer==="greedy"?wg(e,n(e)):Eg(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,bg("rev"))});function n(r){return s=>r.edge(s).weight}}function Eg(e){let t=[],n={},r={};function s(o){Object.hasOwn(r,o)||(r[o]=!0,n[o]=!0,e.outEdges(o).forEach(i=>{Object.hasOwn(n,i.w)?t.push(i):s(i.w)}),delete n[o])}return e.nodes().forEach(s),t}function Ng(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 Sg=Se;var kg={run:jg,undo:Mg};function jg(e){e.graph().dummyChains=[],e.edges().forEach(t=>Cg(e,t))}function Cg(e,t){let n=t.v,r=e.node(n).rank,s=t.w,o=e.node(s).rank,i=t.name,a=e.edge(t),c=a.labelRank;if(o===r+1)return;e.removeEdge(t);let u,l,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:$g}=Se;var Wn={longestPath:Ag,slack:Rg};function Ag(e){var t={};function n(r){var s=e.node(r);if(Object.hasOwn(t,r))return s.rank;t[r]=!0;let o=e.outEdges(r).map(a=>a==null?Number.POSITIVE_INFINITY:n(a.w)-e.edge(a).minlen);var i=$g(Math.min,o);return i===Number.POSITIVE_INFINITY&&(i=0),s.rank=i}e.sources().forEach(n)}function Rg(e,t){return e.node(t.w).rank-e.node(t.v).rank-e.edge(t).minlen}var Ig=qe.Graph,Dn=Wn.slack,ba=Tg;function Tg(e){var t=new Ig({directed:!1}),n=e.nodes()[0],r=e.nodeCount();t.setNode(n,{});for(var s,o;Og(t,e){var o=s.v,i=r===o?s.w:o;!e.hasNode(i)&&!Dn(t,s)&&(e.setNode(i,{}),e.setEdge(r,i,{}),n(i))})}return e.nodes().forEach(n),e.nodeCount()}function Lg(e,t){return t.edges().reduce((r,s)=>{let o=Number.POSITIVE_INFINITY;return e.hasNode(s.v)!==e.hasNode(s.w)&&(o=Dn(t,s)),ot.node(r).rank+=n)}var Pg=ba,Js=Wn.slack,zg=Wn.longestPath,Hg=qe.alg.preorder,Fg=qe.alg.postorder,Vg=Se.simplify,Bg=kt;kt.initLowLimValues=Jr;kt.initCutValues=Qr;kt.calcCutValue=va;kt.leaveEdge=Ea;kt.enterEdge=Na;kt.exchangeEdges=Sa;function kt(e){e=Vg(e),zg(e);var t=Pg(e);Jr(t),Qr(t,e);for(var n,r;n=Ea(t);)r=Na(t,e,n),Sa(t,e,n,r)}function Qr(e,t){var n=Fg(e,e.nodes());n=n.slice(0,n.length-1),n.forEach(r=>Gg(e,t,r))}function Gg(e,t,n){var r=e.node(n),s=r.parent;e.edge(n,s).cutvalue=va(e,t,n)}function va(e,t,n){var r=e.node(n),s=r.parent,o=!0,i=t.edge(n,s),a=0;return i||(o=!1,i=t.edge(s,n)),a=i.weight,t.nodeEdges(n).forEach(c=>{var u=c.v===n,l=u?c.w:c.v;if(l!==s){var d=u===o,m=t.edge(c).weight;if(a+=d?m:-m,Xg(e,n,l)){var f=e.edge(n,l).cutvalue;a+=d?-f:f}}}),a}function Jr(e,t){arguments.length<2&&(t=e.nodes()[0]),_a(e,{},1,t)}function _a(e,t,n,r,s){var o=n,i=e.node(r);return t[r]=!0,e.neighbors(r).forEach(a=>{Object.hasOwn(t,a)||(n=_a(e,t,n,a,r))}),i.low=o,i.lim=n++,s?i.parent=s:delete i.parent,n}function Ea(e){return e.edges().find(t=>e.edge(t).cutvalue<0)}function Na(e,t,n){var r=n.v,s=n.w;t.hasEdge(r,s)||(r=n.w,s=n.v);var o=e.node(r),i=e.node(s),a=o,c=!1;o.lim>i.lim&&(a=i,c=!0);var u=t.edges().filter(l=>c===eo(e,e.node(l.v),a)&&c!==eo(e,e.node(l.w),a));return u.reduce((l,d)=>Js(t,d)!t.node(s).parent),r=Hg(e,n);r=r.slice(1),r.forEach(s=>{var o=e.node(s).parent,i=t.edge(s,o),a=!1;i||(i=t.edge(o,s),a=!0),t.node(s).rank=t.node(o).rank+(a?i.minlen:-i.minlen)})}function Xg(e,t,n){return e.hasEdge(t,n)}function eo(e,t,n){return n.low<=t.lim&&t.lim<=n.lim}var Wg=Wn,ka=Wg.longestPath,Ug=ba,qg=Bg,Kg=Zg;function Zg(e){var t=e.graph().ranker;if(t instanceof Function)return t(e);switch(e.graph().ranker){case"network-simplex":to(e);break;case"tight-tree":Jg(e);break;case"longest-path":Qg(e);break;case"none":break;default:to(e)}}var Qg=ka;function Jg(e){ka(e),Ug(e)}function to(e){qg(e)}var ey=ty;function ty(e){let t=ry(e);e.graph().dummyChains.forEach(n=>{let r=e.node(n),s=r.edgeObj,o=ny(e,t,s.v,s.w),i=o.path,a=o.lca,c=0,u=i[c],l=!0;for(;n!==s.w;){if(r=e.node(n),l){for(;(u=i[c])!==a&&e.node(u).maxRanki||a>t[c].lim));for(u=c,c=r;(c=e.parent(c))!==u;)o.push(c);return{path:s.concat(o.reverse()),lca:u}}function ry(e){let t={},n=0;function r(s){let o=n;e.children(s).forEach(r),t[s]={low:o,lim:n++}}return e.children().forEach(r),t}let Pn=Se;var sy={run:oy,cleanup:cy};function oy(e){let t=Pn.addDummyNode(e,"root",{},"_root"),n=iy(e),r=Object.values(n),s=Pn.applyWithChunking(Math.max,r)-1,o=2*s+1;e.graph().nestingRoot=t,e.edges().forEach(a=>e.edge(a).minlen*=o);let i=ay(e)+1;e.children().forEach(a=>ja(e,t,o,i,s,n,a)),e.graph().nodeRankFactor=o}function ja(e,t,n,r,s,o,i){let a=e.children(i);if(!a.length){i!==t&&e.setEdge(t,i,{weight:0,minlen:n});return}let c=Pn.addBorderNode(e,"_bt"),u=Pn.addBorderNode(e,"_bb"),l=e.node(i);e.setParent(c,i),l.borderTop=c,e.setParent(u,i),l.borderBottom=u,a.forEach(d=>{ja(e,t,n,r,s,o,d);let m=e.node(d),f=m.borderTop?m.borderTop:d,p=m.borderBottom?m.borderBottom:d,x=m.borderTop?r:2*r,v=f!==p?1:s-o[i]+1;e.setEdge(c,f,{weight:x,minlen:v,nestingEdge:!0}),e.setEdge(p,u,{weight:x,minlen:v,nestingEdge:!0})}),e.parent(i)||e.setEdge(t,c,{weight:0,minlen:s+o[i]})}function iy(e){var t={};function n(r,s){var o=e.children(r);o&&o.length&&o.forEach(i=>n(i,s+1)),t[r]=s}return e.children().forEach(r=>n(r,1)),t}function ay(e){return e.edges().reduce((t,n)=>t+e.edge(n).weight,0)}function cy(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 ly=Se;var uy=dy;function dy(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 o=s.minRank,i=s.maxRank+1;oro(e.node(t))),e.edges().forEach(t=>ro(e.edge(t)))}function ro(e){let t=e.width;e.width=e.height,e.height=t}function py(e){e.nodes().forEach(t=>cr(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(cr),Object.hasOwn(n,"y")&&cr(n)})}function cr(e){e.y=-e.y}function gy(e){e.nodes().forEach(t=>lr(e.node(t))),e.edges().forEach(t=>{let n=e.edge(t);n.points.forEach(lr),Object.hasOwn(n,"x")&&lr(n)})}function lr(e){let t=e.x;e.x=e.y,e.y=t}let so=Se;var yy=xy;function xy(e){let t={},n=e.nodes().filter(c=>!e.children(c).length),r=n.map(c=>e.node(c).rank),s=so.applyWithChunking(Math.max,r),o=so.range(s+1).map(()=>[]);function i(c){if(t[c])return;t[c]=!0;let u=e.node(c);o[u.rank].push(c),e.successors(c).forEach(i)}return n.sort((c,u)=>e.node(c).rank-e.node(u).rank).forEach(i),o}let wy=Se.zipObject;var by=vy;function vy(e,t){let n=0;for(let r=1;rl)),s=t.flatMap(u=>e.outEdges(u).map(l=>({pos:r[l.w],weight:e.edge(l).weight})).sort((l,d)=>l.pos-d.pos)),o=1;for(;o{let l=u.pos+o;a[l]+=u.weight;let d=0;for(;l>0;)l%2&&(d+=a[l+1]),l=l-1>>1,a[l]+=u.weight;c+=u.weight*d}),c}var Ey=Ny;function Ny(e,t=[]){return t.map(n=>{let r=e.inEdges(n);if(r.length){let s=r.reduce((o,i)=>{let a=e.edge(i),c=e.node(i.v);return{sum:o.sum+a.weight*c.order,weight:o.weight+a.weight}},{sum:0,weight:0});return{v:n,barycenter:s.sum/s.weight,weight:s.weight}}else return{v:n}})}let Sy=Se;var ky=jy;function jy(e,t){let n={};e.forEach((s,o)=>{let i=n[s.v]={indegree:0,in:[],out:[],vs:[s.v],i:o};s.barycenter!==void 0&&(i.barycenter=s.barycenter,i.weight=s.weight)}),t.edges().forEach(s=>{let o=n[s.v],i=n[s.w];o!==void 0&&i!==void 0&&(i.indegree++,o.out.push(n[s.w]))});let r=Object.values(n).filter(s=>!s.indegree);return Cy(r)}function Cy(e){let t=[];function n(s){return o=>{o.merged||(o.barycenter===void 0||s.barycenter===void 0||o.barycenter>=s.barycenter)&&My(s,o)}}function r(s){return o=>{o.in.push(s),--o.indegree===0&&e.push(o)}}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=>Sy.pick(s,["vs","i","barycenter","weight"]))}function My(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 $y=Se;var Ay=Ry;function Ry(e,t){let n=$y.partition(e,l=>Object.hasOwn(l,"barycenter")),r=n.lhs,s=n.rhs.sort((l,d)=>d.i-l.i),o=[],i=0,a=0,c=0;r.sort(Iy(!!t)),c=oo(o,s,c),r.forEach(l=>{c+=l.vs.length,o.push(l.vs),i+=l.barycenter*l.weight,a+=l.weight,c=oo(o,s,c)});let u={vs:o.flat(!0)};return a&&(u.barycenter=i/a,u.weight=a),u}function oo(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 Iy(e){return(t,n)=>t.barycentern.barycenter?1:e?n.i-t.i:t.i-n.i}let Ty=Ey,Oy=ky,Ly=Ay;var Dy=Ma;function Ma(e,t,n,r){let s=e.children(t),o=e.node(t),i=o?o.borderLeft:void 0,a=o?o.borderRight:void 0,c={};i&&(s=s.filter(m=>m!==i&&m!==a));let u=Ty(e,s);u.forEach(m=>{if(e.children(m.v).length){let f=Ma(e,m.v,n,r);c[m.v]=f,Object.hasOwn(f,"barycenter")&&zy(m,f)}});let l=Oy(u,n);Py(l,c);let d=Ly(l,r);if(i&&(d.vs=[i,d.vs,a].flat(!0),e.predecessors(i).length)){let m=e.node(e.predecessors(i)[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 Py(e,t){e.forEach(n=>{n.vs=n.vs.flatMap(r=>t[r]?t[r].vs:r)})}function zy(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 Hy=qe.Graph,Fy=Se;var Vy=By;function By(e,t,n,r){r||(r=e.nodes());let s=Gy(e),o=new Hy({compound:!0}).setGraph({root:s}).setDefaultNodeLabel(i=>e.node(i));return r.forEach(i=>{let a=e.node(i),c=e.parent(i);(a.rank===t||a.minRank<=t&&t<=a.maxRank)&&(o.setNode(i),o.setParent(i,c||s),e[n](i).forEach(u=>{let l=u.v===i?u.w:u.v,d=o.edge(l,i),m=d!==void 0?d.weight:0;o.setEdge(l,i,{weight:e.edge(u).weight+m})}),Object.hasOwn(a,"minRank")&&o.setNode(i,{borderLeft:a.borderLeft[t],borderRight:a.borderRight[t]}))}),o}function Gy(e){for(var t;e.hasNode(t=Fy.uniqueId("_root")););return t}var Yy=Xy;function Xy(e,t,n){let r={},s;n.forEach(o=>{let i=e.parent(o),a,c;for(;i;){if(a=e.parent(i),a?(c=r[a],r[a]=i):(c=s,s=i),c&&c!==i){t.setEdge(c,i);return}i=a}})}let Wy=yy,Uy=by,qy=Dy,Ky=Vy,Zy=Yy,Qy=qe.Graph,vn=Se;var Jy=$a;function $a(e,t){if(t&&typeof t.customOrder=="function"){t.customOrder(e,$a);return}let n=vn.maxRank(e),r=io(e,vn.range(1,n+1),"inEdges"),s=io(e,vn.range(n-1,-1,-1),"outEdges"),o=Wy(e);if(ao(e,o),t&&t.disableOptimalOrderHeuristic)return;let i=Number.POSITIVE_INFINITY,a;for(let c=0,u=0;u<4;++c,++u){e0(c%2?r:s,c%4>=2),o=vn.buildLayerMatrix(e);let l=Uy(e,o);l{r.has(o)||r.set(o,[]),r.get(o).push(i)};for(const o of e.nodes()){const i=e.node(o);if(typeof i.rank=="number"&&s(i.rank,o),typeof i.minRank=="number"&&typeof i.maxRank=="number")for(let a=i.minRank;a<=i.maxRank;a++)a!==i.rank&&s(a,o)}return t.map(function(o){return Ky(e,o,n,r.get(o)||[])})}function e0(e,t){let n=new Qy;e.forEach(function(r){let s=r.graph().root,o=qy(r,s,n,t);o.vs.forEach((i,a)=>r.node(i).order=a),Zy(r,n,o.vs)})}function ao(e,t){Object.values(t).forEach(n=>n.forEach((r,s)=>e.node(r).order=s))}let t0=qe.Graph,rt=Se;var n0={positionX:h0};function r0(e,t){let n={};function r(s,o){let i=0,a=0,c=s.length,u=o[o.length-1];return o.forEach((l,d)=>{let m=o0(e,l),f=m?e.node(m).order:c;(m||l===u)&&(o.slice(a,d+1).forEach(p=>{e.predecessors(p).forEach(x=>{let v=e.node(x),g=v.order;(g{l=o[d],e.node(l).dummy&&e.predecessors(l).forEach(m=>{let f=e.node(m);f.dummy&&(f.orderu)&&Aa(n,m,l)})})}function s(o,i){let a=-1,c,u=0;return i.forEach((l,d)=>{if(e.node(l).dummy==="border"){let m=e.predecessors(l);m.length&&(c=e.node(m[0]).order,r(i,u,d,a,c),u=d,a=c)}r(i,u,i.length,c,o.length)}),i}return t.length&&t.reduce(s),n}function o0(e,t){if(e.node(t).dummy)return e.predecessors(t).find(n=>e.node(n).dummy)}function Aa(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 i0(e,t,n){if(t>n){let r=t;t=n,n=r}return!!e[t]&&Object.hasOwn(e[t],n)}function a0(e,t,n,r){let s={},o={},i={};return t.forEach(a=>{a.forEach((c,u)=>{s[c]=c,o[c]=c,i[c]=u})}),t.forEach(a=>{let c=-1;a.forEach(u=>{let l=r(u);if(l.length){l=l.sort((m,f)=>i[m]-i[f]);let d=(l.length-1)/2;for(let m=Math.floor(d),f=Math.ceil(d);m<=f;++m){let p=l[m];o[u]===u&&cMath.max(m,o[f.v]+i.edge(f)),0)}function l(d){let m=i.outEdges(d).reduce((p,x)=>Math.min(p,o[x.w]-i.edge(x)),Number.POSITIVE_INFINITY),f=e.node(d);m!==Number.POSITIVE_INFINITY&&f.borderType!==a&&(o[d]=Math.max(o[d],m))}return c(u,i.predecessors.bind(i)),c(l,i.successors.bind(i)),Object.keys(r).forEach(d=>o[d]=o[n[d]]),o}function l0(e,t,n,r){let s=new t0,o=e.graph(),i=m0(o.nodesep,o.edgesep,r);return t.forEach(a=>{let c;a.forEach(u=>{let l=n[u];if(s.setNode(l),c){var d=n[c],m=s.edge(d,l);s.setEdge(d,l,Math.max(i(e,u,c),m||0))}c=u})}),s}function u0(e,t){return Object.values(t).reduce((n,r)=>{let s=Number.NEGATIVE_INFINITY,o=Number.POSITIVE_INFINITY;Object.entries(r).forEach(([a,c])=>{let u=p0(e,a)/2;s=Math.max(c+u,s),o=Math.min(c-u,o)});const i=s-o;return i{["l","r"].forEach(i=>{let a=o+i,c=e[a];if(c===t)return;let u=Object.values(c),l=r-rt.applyWithChunking(Math.min,u);i!=="l"&&(l=s-rt.applyWithChunking(Math.max,u)),l&&(e[a]=rt.mapValues(c,d=>d+l))})})}function f0(e,t){return rt.mapValues(e.ul,(n,r)=>{if(t)return e[t.toLowerCase()][r];{let s=Object.values(e).map(o=>o[r]).sort((o,i)=>o-i);return(s[1]+s[2])/2}})}function h0(e){let t=rt.buildLayerMatrix(e),n=Object.assign(r0(e,t),s0(e,t)),r={},s;["u","d"].forEach(i=>{s=i==="u"?t:Object.values(t).reverse(),["l","r"].forEach(a=>{a==="r"&&(s=s.map(d=>Object.values(d).reverse()));let c=(i==="u"?e.predecessors:e.successors).bind(e),u=a0(e,s,n,c),l=c0(e,s,u.root,u.align,a==="r");a==="r"&&(l=rt.mapValues(l,d=>-d)),r[i+a]=l})});let o=u0(e,r);return d0(r,o),f0(r,e.graph().align)}function m0(e,t,n){return(r,s,o)=>{let i=r.node(s),a=r.node(o),c=0,u;if(c+=i.width/2,Object.hasOwn(i,"labelpos"))switch(i.labelpos.toLowerCase()){case"l":u=-i.width/2;break;case"r":u=i.width/2;break}if(u&&(c+=n?u:-u),u=0,c+=(i.dummy?t:e)/2,c+=(a.dummy?t:e)/2,c+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":u=a.width/2;break;case"r":u=-a.width/2;break}return u&&(c+=n?u:-u),u=0,c}}function p0(e,t){return e.node(t).width}let Ra=Se,g0=n0.positionX;var y0=x0;function x0(e){e=Ra.asNonCompoundGraph(e),w0(e),Object.entries(g0(e)).forEach(([t,n])=>e.node(t).x=n)}function w0(e){let t=Ra.buildLayerMatrix(e),n=e.graph().ranksep,r=0;t.forEach(s=>{const o=s.reduce((i,a)=>{const c=e.node(a).height;return i>c?i:c},0);s.forEach(i=>e.node(i).y=r+o/2),r+=o+n})}let co=vg,lo=kg,b0=Kg,v0=Se.normalizeRanks,_0=ey,E0=Se.removeEmptyRanks,uo=sy,N0=uy,fo=fy,S0=Jy,k0=y0,Ge=Se,j0=qe.Graph;var C0=M0;function M0(e,t){let n=t&&t.debugTiming?Ge.time:Ge.notime;n("layout",()=>{let r=n(" buildLayoutGraph",()=>z0(e));n(" runLayout",()=>$0(r,n,t)),n(" updateInputGraph",()=>A0(e,r))})}function $0(e,t,n){t(" makeSpaceForEdgeLabels",()=>H0(e)),t(" removeSelfEdges",()=>q0(e)),t(" acyclic",()=>co.run(e)),t(" nestingGraph.run",()=>uo.run(e)),t(" rank",()=>b0(Ge.asNonCompoundGraph(e))),t(" injectEdgeLabelProxies",()=>F0(e)),t(" removeEmptyRanks",()=>E0(e)),t(" nestingGraph.cleanup",()=>uo.cleanup(e)),t(" normalizeRanks",()=>v0(e)),t(" assignRankMinMax",()=>V0(e)),t(" removeEdgeLabelProxies",()=>B0(e)),t(" normalize.run",()=>lo.run(e)),t(" parentDummyChains",()=>_0(e)),t(" addBorderSegments",()=>N0(e)),t(" order",()=>S0(e,n)),t(" insertSelfEdges",()=>K0(e)),t(" adjustCoordinateSystem",()=>fo.adjust(e)),t(" position",()=>k0(e)),t(" positionSelfEdges",()=>Z0(e)),t(" removeBorderNodes",()=>U0(e)),t(" normalize.undo",()=>lo.undo(e)),t(" fixupEdgeLabelCoords",()=>X0(e)),t(" undoCoordinateSystem",()=>fo.undo(e)),t(" translateGraph",()=>G0(e)),t(" assignNodeIntersects",()=>Y0(e)),t(" reversePoints",()=>W0(e)),t(" acyclic.undo",()=>co.undo(e))}function A0(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 R0=["nodesep","edgesep","ranksep","marginx","marginy"],I0={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},T0=["acyclicer","ranker","rankdir","align"],O0=["width","height","rank"],ho={width:0,height:0},L0=["minlen","weight","width","height","labeloffset"],D0={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},P0=["labelpos"];function z0(e){let t=new j0({multigraph:!0,compound:!0}),n=dr(e.graph());return t.setGraph(Object.assign({},I0,ur(n,R0),Ge.pick(n,T0))),e.nodes().forEach(r=>{let s=dr(e.node(r));const o=ur(s,O0);Object.keys(ho).forEach(i=>{o[i]===void 0&&(o[i]=ho[i])}),t.setNode(r,o),t.setParent(r,e.parent(r))}),e.edges().forEach(r=>{let s=dr(e.edge(r));t.setEdge(r,Object.assign({},D0,ur(s,L0),Ge.pick(s,P0)))}),t}function H0(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 F0(e){e.edges().forEach(t=>{let n=e.edge(t);if(n.width&&n.height){let r=e.node(t.v),o={rank:(e.node(t.w).rank-r.rank)/2+r.rank,e:t};Ge.addDummyNode(e,"edge-proxy",o,"_ep")}})}function V0(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 B0(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 G0(e){let t=Number.POSITIVE_INFINITY,n=0,r=Number.POSITIVE_INFINITY,s=0,o=e.graph(),i=o.marginx||0,a=o.marginy||0;function c(u){let l=u.x,d=u.y,m=u.width,f=u.height;t=Math.min(t,l-m/2),n=Math.max(n,l+m/2),r=Math.min(r,d-f/2),s=Math.max(s,d+f/2)}e.nodes().forEach(u=>c(e.node(u))),e.edges().forEach(u=>{let l=e.edge(u);Object.hasOwn(l,"x")&&c(l)}),t-=i,r-=a,e.nodes().forEach(u=>{let l=e.node(u);l.x-=t,l.y-=r}),e.edges().forEach(u=>{let l=e.edge(u);l.points.forEach(d=>{d.x-=t,d.y-=r}),Object.hasOwn(l,"x")&&(l.x-=t),Object.hasOwn(l,"y")&&(l.y-=r)}),o.width=n-t+i,o.height=s-r+a}function Y0(e){e.edges().forEach(t=>{let n=e.edge(t),r=e.node(t.v),s=e.node(t.w),o,i;n.points?(o=n.points[0],i=n.points[n.points.length-1]):(n.points=[],o=s,i=r),n.points.unshift(Ge.intersectRect(r,o)),n.points.push(Ge.intersectRect(s,i))})}function X0(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 W0(e){e.edges().forEach(t=>{let n=e.edge(t);n.reversed&&n.points.reverse()})}function U0(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),o=e.node(n.borderLeft[n.borderLeft.length-1]),i=e.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(i.x-o.x),n.height=Math.abs(s.y-r.y),n.x=o.x+n.width/2,n.y=r.y+n.height/2}}),e.nodes().forEach(t=>{e.node(t).dummy==="border"&&e.removeNode(t)})}function q0(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 K0(e){var t=Ge.buildLayerMatrix(e);t.forEach(n=>{var r=0;n.forEach((s,o)=>{var i=e.node(s);i.order=o+r,(i.selfEdges||[]).forEach(a=>{Ge.addDummyNode(e,"selfedge",{width:a.label.width,height:a.label.height,rank:i.rank,order:o+ ++r,e:a.e,label:a.label},"_se")}),delete i.selfEdges})})}function Z0(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,o=r.y,i=n.x-s,a=r.height/2;e.setEdge(n.e,n.label),e.removeNode(t),n.label.points=[{x:s+2*i/3,y:o-a},{x:s+5*i/6,y:o-a},{x:s+i,y:o},{x:s+5*i/6,y:o+a},{x:s+2*i/3,y:o+a}],n.label.x=n.x,n.label.y=n.y}})}function ur(e,t){return Ge.mapValues(Ge.pick(e,t),Number)}function dr(e){var t={};return e&&Object.entries(e).forEach(([n,r])=>{typeof n=="string"&&(n=n.toLowerCase()),t[n]=r}),t}let Q0=Se,J0=qe.Graph;var ex={debugOrdering:tx};function tx(e){let t=Q0.buildLayerMatrix(e),n=new J0({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 o="layer"+s;n.setNode(o,{rank:"same"}),r.reduce((i,a)=>(n.setEdge(i,a,{style:"invis"}),a))}),n}var nx="1.1.8",rx={graphlib:qe,layout:C0,debug:ex,util:{time:Se.time,notime:Se.notime},version:nx};const mo=$o(rx),Qe=264,sx=44,ox=26,ix=14;function gt(e){const t=Math.max(e.attributes.length,1);return sx+t*ox+ix}const ax=40;function fr(e,t,n,r="auto",s,o="LR"){const i=cx(n,s);return r==="grid"?xo(e,n,i):r==="clustered"?yo(e,t,n,i,o):r==="hierarchical"||e.length<=ax?po(e,t,n,o):new Set(e.map(c=>i(c.id)).filter(Boolean)).size>1?yo(e,t,n,i,o):xo(e,n,i)}function cx(e,t){return n=>{var r,s,o;if(t){const i=t.get(n);if(i)return i.name}return((o=(s=(r=e.get(n))==null?void 0:r.conceptual)==null?void 0:s.subject_area)==null?void 0:o.name)??""}}function Ia(e,t,n,r=!1,s="LR"){const o=new mo.graphlib.Graph;o.setGraph({rankdir:s,nodesep:r?45:70,ranksep:r?110:150,edgesep:r?20:30,marginx:24,marginy:24,ranker:"network-simplex"}),o.setDefaultEdgeLabel(()=>({}));const i=new Set(e.map(l=>l.id));for(const l of e){const d=n.get(l.id);o.setNode(l.id,{width:Qe,height:d?gt(d):120})}for(const l of t)i.has(l.source)&&i.has(l.target)&&o.setEdge(l.source,l.target,{minlen:1,weight:2});mo.layout(o);let a=0,c=0;return{nodes:e.map(l=>{const d=o.node(l.id),m=n.get(l.id),f=m?gt(m):120,p=d.x-Qe/2,x=d.y-f/2;return a=Math.max(a,p+Qe),c=Math.max(c,x+f),{...l,position:{x:p,y:x}}}),width:a,height:c}}function po(e,t,n,r="LR"){return Ia(e,t,n,!1,r).nodes}function lx(e,t){const n=a=>{const c=t.get(a);return c?gt(c):120},r=Math.max(1,Math.round(Math.sqrt(e.length))),s=[];let o=0,i=0;for(let a=0;a{const d=l*(Qe+Ta);s.push({...u,position:{x:d,y:o}}),i=Math.max(i,d+Qe)}),o+=Math.max(...c.map(u=>n(u.id)))+Oa}return{nodes:s,width:i,height:o}}const go=120;function yo(e,t,n,r,s="LR"){const o=new Map;for(const m of e){const f=r(m.id);(o.get(f)??o.set(f,[]).get(f)).push(m)}const a=[...o.keys()].sort((m,f)=>m===""?1:f===""?-1:m.localeCompare(f)).map(m=>{const f=o.get(m),p=new Set(f.map(E=>E.id));let x=0;for(const E of t)p.has(E.source)&&p.has(E.target)&&x++;const g=f.length<=8&&x>=f.length-1?Ia(f,t,n,!0,s):lx(f,n);return{name:m,...g}}),c=Math.max(1,Math.ceil(Math.sqrt(a.length*1.6))),u=[];let l=0;for(let m=0;m[m.id,m.position]));return e.map(m=>({...m,position:d.get(m.id)??m.position}))}const Ta=40,Oa=40;function xo(e,t,n){const r=u=>{const l=t.get(u);return l?gt(l):120},s=[...e].sort((u,l)=>{var x,v;const d=n(u.id),m=n(l.id);if(d!==m)return d.localeCompare(m);const f=((x=t.get(u.id))==null?void 0:x.name)??u.id,p=((v=t.get(l.id))==null?void 0:v.name)??l.id;return f.localeCompare(p)}),o=Math.max(1,Math.round(Math.sqrt(s.length))),i=Array.from({length:o},(u,l)=>l*(Qe+Ta)),a=new Map;let c=0;for(let u=0;ua.set(m.id,{x:i[f],y:c}));const d=Math.max(...l.map(m=>r(m.id)));c+=d+Oa}return e.map(u=>({...u,position:a.get(u.id)??u.position}))}function dn({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 ux=["skos:exactMatch","skos:closeMatch","skos:broadMatch","skos:narrowMatch","skos:relatedMatch"],La=["industry","core","domain","specialised"];function dx({entity:e,exec:t,onClose:n}){var b,$;const[r,s]=w.useState(((b=e.conceptual)==null?void 0:b.name)??e.name),[o,i]=w.useState([]),[a,c]=w.useState(null),[u,l]=w.useState(null),[d,m]=w.useState("skos:exactMatch"),[f,p]=w.useState((($=e.conceptual)==null?void 0:$.ontology_layer)??"core"),[x,v]=w.useState([]),[g,E]=w.useState("");w.useEffect(()=>{ia().then(j=>v(j.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const j=setTimeout(()=>{if(!r.trim())return i([]);oa(r,g||void 0).then(_=>{i(_.results),c(_.loaded_terms)}).catch(()=>i([]))},200);return()=>clearTimeout(j)},[r,g]);const M=()=>{u&&t("set_alignment",{id:e.id,aligns_to:u.prefixed,alignment:d,layer:f,resolved_via:u.source}).then(n)};return h.jsxs(dn,{title:`Align '${e.name}' to ontology`,onClose:n,wide:!0,children:[x.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(g===""?" active":""),onClick:()=>E(""),children:"all sources"}),x.map(j=>h.jsx("button",{className:"src-chip"+(g===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:g?`Search within ${g}…`:"Search vocabulary terms…",value:r,onChange:j=>s(j.target.value)}),a===0&&x.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:[o.map(j=>h.jsxs("button",{className:"term-card"+((u==null?void 0:u.iri)===j.iri?" picked":""),onClick:()=>l(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&&o.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:ux.map(j=>h.jsx("option",{children:j},j))})]}),h.jsxs("label",{children:["layer",h.jsx("select",{value:f,onChange:j=>p(j.target.value),children:La.map(j=>h.jsx("option",{children:j},j))})]}),h.jsxs("button",{className:"primary-btn",disabled:!u,onClick:M,children:["Align",u?` to ${u.label}`:""]})]})]})}function fx({entity:e,doc:t,exec:n,onClose:r}){var b,$,j;const[s,o]=w.useState(((b=e.term_map)==null?void 0:b.subject_template)??""),[i,a]=w.useState((($=e.term_map)==null?void 0:$.class_iri)??""),c=new Set(e.attributes.map(_=>_.name)),l=Array.from(s.matchAll(/\{([^}]+)\}/g)).map(_=>_[1]).filter(_=>!c.has(_)),d=_=>!_||_.startsWith("http://")||_.startsWith("https://")||/^[^\s:]+:[^\s:]+/.test(_),m=l.length===0&&d(i),f=()=>{n("set_term_map",{id:e.id,subject_template:s.trim()||null,class_iri:i.trim()||null}).then(r)},p=(t.project.name||"").replace(/[^0-9a-zA-Z]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase()||"modelith-model",x=t.project.kg_base_iri||`urn:${p}:`,v=(j=(e.key_groups??[]).find(_=>_.type==="pk"))==null?void 0:j.members.map(_=>{var R;return(R=e.attributes.find(N=>N.id===_))==null?void 0:R.name}).filter(Boolean),g=v&&v.length?`${x}${e.id}/${v.map(_=>`{${_}}`).join("_")}`:"(blank-node: entity has no primary key)",E=i.trim()||`${x}${e.id}`,M=s.trim()||g;return h.jsxs(dn,{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:i,placeholder:"https://acme.com/Customer or fibo-fnd:Party",onChange:_=>a(_.target.value)}),d(i)?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:_=>o(_.target.value)}),l.length>0?h.jsxs("span",{className:"form-error",children:["unknown column",l.length>1?"s":"",": ",l.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(_=>_.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 hx({doc:e,exec:t,onClose:n,onCreated:r}){const[s,o]=w.useState(""),[i,a]=w.useState(""),[c,u]=w.useState(""),[l,d]=w.useState(""),m=()=>{s.trim()&&t("create_entity",{name:s,definition:i||null,subject_area:c||null,layer:l||null}).then(f=>{const p=f==null?void 0:f.created_id;n(),p&&r(p)})};return h.jsxs(dn,{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=>o(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:i,onChange:f=>a(f.target.value)})]}),h.jsxs("label",{className:"form-row",children:["subject area",h.jsxs("select",{value:c,onChange:f=>u(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:l,onChange:f=>d(f.target.value),children:[h.jsx("option",{value:"",children:"none"}),La.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 Da=["many_to_one","one_to_many","one_to_one","many_to_many"];function mx({doc:e,fromId:t,toId:n,initialFromAttr:r,initialToAttr:s,exec:o,onClose:i}){const a=e.entities.find(b=>b.id===t),c=e.entities.find(b=>b.id===n),u=w.useMemo(()=>c==null?void 0:c.attributes.find(b=>b.role==="business_key"),[c]),[l,d]=w.useState(r??""),[m,f]=w.useState(s??(u==null?void 0:u.id)??""),[p,x]=w.useState("many_to_one"),[v,g]=w.useState("mandatory");if(!a||!c)return null;const E=l==="__new__",M=()=>o("create_relationship",{from_entity:a.id,to_entity:c.id,from_attribute:E?null:l||null,to_attribute:m||null,create_from_fk:E,cardinality:p,optionality:v}).then(i);return h.jsxs(dn,{title:`${a.name} → ${c.name}`,onClose:i,children:[h.jsxs("label",{className:"form-row",children:["cardinality (",a.name," : ",c.name,")",h.jsx("select",{value:p,onChange:b=>x(b.target.value),children:Da.map(b=>h.jsx("option",{children:b},b))})]}),h.jsxs("label",{className:"form-row",children:["foreign key on ",a.name,h.jsxs("select",{value:l,onChange:b=>d(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),h.jsxs("option",{value:"__new__",children:["➕ create column",u?` "${u.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 ",c.name,h.jsxs("select",{value:m,onChange:b=>f(b.target.value),children:[h.jsx("option",{value:"",children:"(none)"}),c.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=>g(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 px({rel:e,doc:t,exec:n,onClose:r}){const s=g=>{var E;return((E=t.entities.find(M=>M.id===g))==null?void 0:E.name)??"?"},[o,i]=w.useState(e.name),[a,c]=w.useState(e.cardinality),[u,l]=w.useState(e.optionality),[d,m]=w.useState(e.identifying),[f,p]=w.useState(!1),x=async()=>{o.trim()&&o.trim()!==e.name&&await n("rename_relationship",{id:e.id,name:o.trim()}),(a!==e.cardinality||u!==e.optionality||d!==e.identifying)&&await n("update_relationship",{id:e.id,cardinality:a,optionality:u,identifying:d}),r()},v=()=>n("delete_relationship",{id:e.id}).then(r);return h.jsxs(dn,{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:o,onChange:g=>i(g.target.value),autoFocus:!0})]}),h.jsxs("label",{className:"form-row",children:["cardinality",h.jsx("select",{value:a,onChange:g=>c(g.target.value),children:Da.map(g=>h.jsx("option",{children:g},g))})]}),h.jsxs("label",{className:"form-row",children:["optionality",h.jsxs("select",{value:u,onChange:g=>l(g.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:g=>m(g.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:()=>p(!0),children:"Delete relationship"}),h.jsx("button",{className:"primary-btn",onClick:x,children:"Save"})]})]})}const wo=10,gx=6,Rt=24;function yx(e,t){return t?e:Math.round(e/wo)*wo}function xx(e,t,n){const r=[...t];for(const i of n)r.push(i.left-Rt,i.right+Rt);let s=e,o=gx;for(const i of r){const a=Math.abs(i-e);at.left&&ei.left&&no.left&&n=i.right?(l.push({x:n,y:t.y},{x:i.right,y:t.y}),m={ox:1,oy:0}):(l.push({x:n,y:t.y},{x:i.left,y:t.y}),m={ox:-1,oy:0});return{path:Nx(l),sourceOut:d,targetOut:m}}const dt=8;function kx(e,t,n){const r=Math.abs(e-n.left),s=Math.abs(e-n.right),o=Math.abs(t-n.top),i=Math.abs(t-n.bottom),a=Math.min(r,s,o,i),c=(u,l,d)=>Math.min(Math.max(u,l),d);return a===r?{side:"left",x:n.left,y:c(t,n.top+dt,n.bottom-dt)}:a===s?{side:"right",x:n.right,y:c(t,n.top+dt,n.bottom-dt)}:a===o?{side:"top",x:c(e,n.left+dt,n.right-dt),y:n.top}:{side:"bottom",x:c(e,n.left+dt,n.right-dt),y:n.bottom}}function vo(e,t){const n=(r,s,o)=>Math.min(Math.max(r,s),o);switch(e.side){case"left":return{x:t.left,y:n(e.y,t.top,t.bottom),out:{ox:-1,oy:0}};case"right":return{x:t.right,y:n(e.y,t.top,t.bottom),out:{ox:1,oy:0}};case"top":return{x:n(e.x??(t.left+t.right)/2,t.left,t.right),y:t.top,out:{ox:0,oy:-1}};default:return{x:n(e.x??(t.left+t.right)/2,t.left,t.right),y:t.bottom,out:{ox:0,oy:1}}}}const hr=3,jx=w.memo(function({id:t,source:n,target:r,sourceX:s,sourceY:o,targetX:i,targetY:a,sourcePosition:c,targetPosition:u,data:l,selected:d}){const{screenToFlowPosition:m}=un(),f=me(G=>G.transform[2]),p=me(G=>Eo(G.nodeInternals.get(n)),_o),x=me(G=>Eo(G.nodeInternals.get(r)),_o),[v,g]=w.useState(null),[E,M]=w.useState(null),b=w.useRef(!1),$=w.useRef(0),j=l==null?void 0:l.onRouteChange,_=!!j,R=v??(l==null?void 0:l.routeX),N=p??(l==null?void 0:l.sourceCard),I=x??(l==null?void 0:l.targetCard),L=(E==null?void 0:E.end)==="source"?E.a:l==null?void 0:l.sourceAnchor,B=(E==null?void 0:E.end)==="target"?E.a:l==null?void 0:l.targetAnchor,H=L&&N?vo(L,N):{x:s,y:o,out:null},y=B&&I?vo(B,I):{x:i,y:a,out:null},C=H.x,S=H.y,O=y.x,D=y.y,k=(L==null?void 0:L.side)==="right"?K.Right:(L==null?void 0:L.side)==="left"?K.Left:c,T=(B==null?void 0:B.side)==="right"?K.Right:(B==null?void 0:B.side)==="left"?K.Left:u,[P,z,U]=Tn({sourceX:C,sourceY:S,sourcePosition:k,targetX:O,targetY:D,targetPosition:T,borderRadius:10,...R!=null?{centerX:R}:{}}),W=N,Q=I,te=R!=null&&W&&Q?Sx({x:C,y:S},{y:D},R,k===K.Left?-1:1,T===K.Left?-1:1,W,Q):null,se=te?te.path:P,J=l==null?void 0:l.relationship,ae=(l==null?void 0:l.dimmed)??!1,oe=(J==null?void 0:J.cardinality)??"many_to_one",he=(J==null?void 0:J.optionality)==="optional",ye=(J==null?void 0:J.identifying)??!1,Ee=oe==="many_to_one"||oe==="many_to_many",ve=oe==="one_to_many"||oe==="many_to_many",ue="rel-edge"+(d||l!=null&&l.edgeSelected?" selected":"")+(ae?" dimmed":""),ge=((l==null?void 0:l.memberCount)??0)>1,le=l==null?void 0:l.onHoverEdge,q=R??z,fe=U,Oe=q,Ke=fe,Ye=te?te.sourceOut:H.out??{ox:k===K.Left?-1:1,oy:0},$e=te?te.targetOut:y.out??{ox:T===K.Left?-1:1,oy:0},Ae={x:C,y:S},je=te&&Q?$e.oy!==0?{x:R,y:$e.oy<0?Q.top:Q.bottom}:{x:$e.ox>0?Q.right:Q.left,y:D}:{x:O,y:D},Re=w.useCallback(G=>j&&J&&j(J.id,G),[j,J]),ze=w.useCallback(G=>{var Ie,Le;if(!_||G.button!==0)return;G.stopPropagation(),(Le=(Ie=G.target).setPointerCapture)==null||Le.call(Ie,G.pointerId),b.current=!0,$.current=0;const xe=m({x:G.clientX,y:G.clientY}).x;let ee=0,ne=R??q;const ie=Te=>{const Ne=m({x:Te.clientX,y:Te.clientY});$.current=Math.max($.current,Math.abs(Ne.x-xe)),ne=bx(Ne.x,{shift:Te.shiftKey,source:(l==null?void 0:l.sourceCard)??{left:s,right:s},target:(l==null?void 0:l.targetCard)??{left:i,right:i}}),ee||(ee=requestAnimationFrame(()=>{ee=0,g(ne)}))},pe=()=>{window.removeEventListener("pointermove",ie),window.removeEventListener("pointerup",pe),ee&&cancelAnimationFrame(ee),b.current=!1,g(null),$.current>=hr&&Re(ne)};window.addEventListener("pointermove",ie),window.addEventListener("pointerup",pe)},[_,m,R,q,l,s,i,Re]),F=w.useCallback(G=>{$.current>=hr&&(G.stopPropagation(),$.current=0)},[]),X=w.useCallback(G=>{_&&(G.stopPropagation(),Re(null))},[_,Re]),V=l==null?void 0:l.onAnchorChange,Z=w.useCallback(G=>re=>{var Le,Te;const xe=G==="source"?N:I;if(!V||!J||!xe||re.button!==0)return;re.stopPropagation(),(Te=(Le=re.target).setPointerCapture)==null||Te.call(Le,re.pointerId),$.current=0;const ee=m({x:re.clientX,y:re.clientY});let ne=0,ie=null;const pe=Ne=>{const He=m({x:Ne.clientX,y:Ne.clientY});$.current=Math.max($.current,Math.hypot(He.x-ee.x,He.y-ee.y)),ie=kx(He.x,He.y,xe),ne||(ne=requestAnimationFrame(()=>{ne=0,M({end:G,a:ie})}))},Ie=()=>{window.removeEventListener("pointermove",pe),window.removeEventListener("pointerup",Ie),ne&&cancelAnimationFrame(ne),M(null),$.current>=hr&&ie&&V(J.id,G,ie)};window.addEventListener("pointermove",pe),window.addEventListener("pointerup",Ie)},[V,J,N,I,m]),Y=w.useCallback(G=>re=>{!V||!J||(re.stopPropagation(),V(J.id,G,null))},[V,J]);return h.jsxs("g",{className:ue,onMouseEnter:le?()=>le(J.id):void 0,onMouseLeave:le?()=>le(null):void 0,children:[h.jsx("path",{id:t,className:"rel-path",d:se,fill:"none",strokeDasharray:ye?void 0:"6 4"}),h.jsx("path",{d:se,fill:"none",strokeWidth:16/f,stroke:"transparent"}),h.jsx(So,{x:Ae.x,y:Ae.y,out:Ye,many:Ee,optional:he}),h.jsx(So,{x:je.x,y:je.y,out:$e,many:ve,optional:!1}),!!V&&((l==null?void 0:l.hovered)||(l==null?void 0:l.edgeSelected)||d||E!=null)&&h.jsxs(h.Fragment,{children:[h.jsx(No,{x:Ae.x,y:Ae.y,zoom:f,onPointerDown:Z("source"),onDoubleClick:Y("source")}),h.jsx(No,{x:je.x,y:je.y,zoom:f,onPointerDown:Z("target"),onDoubleClick:Y("target")})]}),_&&((l==null?void 0:l.hovered)||(l==null?void 0:l.edgeSelected)||d||v!=null)&&h.jsxs("g",{className:"rel-handle",style:{cursor:"ew-resize"},onPointerDown:ze,onClickCapture:F,onDoubleClick:X,children:[h.jsx("rect",{x:q-20/f,y:Math.min(S,D),width:40/f,height:Math.max(Math.abs(D-S),24/f),fill:"transparent"}),h.jsx("rect",{className:"rel-grip",x:q-5.5/f,y:fe-11/f,width:11/f,height:22/f,rx:5/f,strokeWidth:1.5/f}),h.jsx("circle",{className:"rel-grip-dot",cx:q,cy:fe-4.5/f,r:1.1/f}),h.jsx("circle",{className:"rel-grip-dot",cx:q,cy:fe,r:1.1/f}),h.jsx("circle",{className:"rel-grip-dot",cx:q,cy:fe+4.5/f,r:1.1/f})]}),(d||ge)&&J&&h.jsxs("text",{className:"rel-label",x:Oe,y:Ke-8,children:[d?J.verb_phrase||J.name:"",ge?`${d?" ":""}(${l.memberCount})`:""]})]})});function _o(e,t){return e===t?!0:!e||!t?!1:e.left===t.left&&e.right===t.right&&e.top===t.top&&e.bottom===t.bottom}function Eo(e){if(!(e!=null&&e.positionAbsolute)||e.width==null||e.height==null)return null;const{x:t,y:n}=e.positionAbsolute;return{left:t,right:t+e.width,top:n,bottom:n+e.height}}function No({x:e,y:t,zoom:n,onPointerDown:r,onDoubleClick:s}){return h.jsxs("g",{className:"rel-endpoint",style:{cursor:"move"},onPointerDown:r,onDoubleClick:s,children:[h.jsx("circle",{cx:e,cy:t,r:10/n,fill:"transparent"}),h.jsx("circle",{className:"rel-endpoint-dot",cx:e,cy:t,r:3.5/n,strokeWidth:1.5/n})]})}function So({x:e,y:t,out:n,many:r,optional:s}){const{ox:a,oy:c}=n,u=-c,l=a,d=p=>({x:e+a*p,y:t+c*p}),m=(p,x)=>({x:e+a*p+u*x,y:t+c*p+l*x}),f=[];if(r){const p=d(12),x=m(0,-7),v=d(0),g=m(0,7);f.push(h.jsx("path",{className:"rel-glyph",d:`M ${p.x} ${p.y} L ${x.x} ${x.y} M ${p.x} ${p.y} L ${v.x} ${v.y} M ${p.x} ${p.y} L ${g.x} ${g.y}`,fill:"none"},"foot"))}else{const p=m(9,-7),x=m(9,7);f.push(h.jsx("line",{className:"rel-glyph",x1:p.x,y1:p.y,x2:x.x,y2:x.y},"one"))}if(s){const p=d(18);f.push(h.jsx("circle",{className:"rel-glyph ring",cx:p.x,cy:p.y,r:4},"opt"))}else{const p=m(17,-7),x=m(17,7);f.push(h.jsx("line",{className:"rel-glyph",x1:p.x,y1:p.y,x2:x.x,y2:x.y},"mand"))}return h.jsx(h.Fragment,{children:f})}const Cx={entity:Gm},Mx={relationship:jx},ko=["#e0a63a","#e8825a","#d98ba8","#c4a3e0","#7fb0d8","#7fc9a0","#e6c14a"],jo="#8a7d6a";function Co(e){return{side:e.side,y:Math.round(e.y),...e.x!=null?{x:Math.round(e.x)}:{}}}function $x(e,t,n){const s=Qe;let{x:o,y:i}=e,a=!0,c=0;for(;a&&c++<200;){a=!1;for(const u of n)ou.x&&iu.y&&(i=u.y+u.h+16,a=!0)}return{x:o,y:i}}function Ax(e,t){const n=(o,i)=>{var a;return!!i&&!!((a=t.get(o))!=null&&a.attributes.some(c=>c.id===i))},r=e.from.attributes[0],s=e.to.attributes[0];return{sourceHandle:n(e.from.entity,r)?r:Ur,targetHandle:n(e.to.entity,s)?s:qr}}function aw({doc:e,caps:t,exec:n,selectedId:r,onSelect:s,query:o="",showTypes:i=!0,collapseDetail:a=!1,expandedEntities:c,onToggleExpand:u,activeAreas:l,onError:d,handleRef:m,layoutStore:f}){const{fitView:p,fitBounds:x,getNodes:v}=un(),[g,E]=w.useState([]),[M,b]=w.useState([]),$=w.useRef(!1),j=w.useMemo(()=>{const F=l&&l.size>0?[...l].sort():null;return F?"sa:"+F.join("+"):"__all__"},[l]),_=w.useRef(null),[R,N]=w.useState(0),I=w.useRef(new Map),L=w.useRef(null);w.useEffect(()=>{if(!f){_.current=null;return}const F=I.current.get(j);if(F){_.current=F,$.current=!1,N(V=>V+1);return}let X=!1;return f.load(j).then(V=>{X||(I.current.set(j,V),_.current=V,$.current=!1,N(Z=>Z+1))}),()=>{X=!0}},[f,j]);const[B,H]=w.useState(null),[y,C]=w.useState(null),[S,O]=w.useState(!1),[D,k]=w.useState(null),[T,P]=w.useState(null),[z,U]=w.useState(null),[W,Q]=w.useState(null),[te,se]=w.useState(null),J=w.useRef({}),ae=!t.canEdit,oe=w.useRef(null),he=w.useCallback(()=>{f&&(oe.current&&clearTimeout(oe.current),oe.current=setTimeout(()=>{const F={};for(const Z of v())Z.hidden||(F[Z.id]={x:Math.round(Z.position.x),y:Math.round(Z.position.y)});const X={};for(const[Z,Y]of Object.entries(J.current)){const G={};Y.routeX!=null&&(G.routeX=Math.round(Y.routeX)),Y.sourceAnchor&&(G.sourceAnchor=Co(Y.sourceAnchor)),Y.targetAnchor&&(G.targetAnchor=Co(Y.targetAnchor)),(G.routeX!=null||G.sourceAnchor||G.targetAnchor)&&(X[Z]=G)}const V={nodes:F,edges:X};I.current.set(j,V),_.current=V,f.save(j,V)},800))},[f,v,j]),ye=w.useCallback((F,X)=>{const V=J.current[F]??{};X==null?delete V.routeX:V.routeX=X,J.current[F]=V,b(Z=>Z.map(Y=>Y.id===F?{...Y,data:{...Y.data,routeX:X??void 0}}:Y)),he()},[he]),Ee=w.useCallback((F,X,V)=>{const Z=J.current[F]??{},Y=X==="source"?"sourceAnchor":"targetAnchor";V==null?delete Z[Y]:Z[Y]=V,J.current[F]=Z,b(G=>G.map(re=>re.id===F?{...re,data:{...re.data,[Y]:V??void 0}}:re)),he()},[he]),ve=w.useMemo(()=>{const F=new Map;return e.subject_areas.forEach((X,V)=>F.set(X.id,ko[V%ko.length])),F},[e]),ue=w.useMemo(()=>{const F=new Map;return e.entities.forEach(X=>F.set(X.id,X)),F},[e]),ge=w.useMemo(()=>{const F=new Map,X=new Map;return e.subject_areas.forEach(V=>{(V.members??[]).forEach(Z=>X.set(Z,{id:V.id,name:V.name}))}),e.entities.forEach(V=>{var re,xe;const Z=(re=V.conceptual)!=null&&re.id?X.get(V.conceptual.id):void 0,Y=(xe=V.conceptual)==null?void 0:xe.subject_area,G=Z??(Y?{id:Y.id,name:Y.name??Y.id}:void 0);G&&F.set(V.id,G)}),F},[e]),le=w.useCallback(F=>{var V;if(!l||l.size===0)return!1;const X=(V=ge.get(F))==null?void 0:V.id;return!(X&&l.has(X))},[l,ge]),q=w.useMemo(()=>{const F=new Map,X=(V,Z)=>{if(!Z.length)return;const Y=F.get(V)??new Set;Z.forEach(G=>Y.add(G)),F.set(V,Y)};for(const V of e.relationships)X(V.from.entity,V.from.attributes),X(V.to.entity,V.to.attributes);return F},[e]);w.useEffect(()=>{var xe;const F=o.trim().toLowerCase(),X=new Set;if(F)for(const ee of e.entities)(ee.name.toLowerCase()+" "+(((xe=ee.conceptual)==null?void 0:xe.name.toLowerCase())??"")+" "+ee.attributes.map(ie=>ie.name.toLowerCase()).join(" ")).includes(F)&&X.add(ee.id);const V=new Set;if(r){V.add(r);for(const ee of e.relationships)ee.from.entity===r&&V.add(ee.to.entity),ee.to.entity===r&&V.add(ee.from.entity)}const Z=e.entities.map(ee=>{const ne=F!==""&&!X.has(ee.id),ie=r!==null&&!V.has(ee.id);return{id:ee.id,type:"entity",position:{x:0,y:0},hidden:le(ee.id),data:{entity:ee,color:(()=>{const pe=ge.get(ee.id);return pe?ve.get(pe.id)??jo:jo})(),dimmed:ne||ie,highlighted:F!==""&&X.has(ee.id),showTypes:i,collapseDetail:a,expanded:(c==null?void 0:c.has(ee.id))??!1,onToggleExpand:u,endpointAttrs:q.get(ee.id),highlightAttrs:void 0,onHoverAttr:U}}}),Y=e.relationships.filter(ee=>ue.has(ee.from.entity)&&ue.has(ee.to.entity)).map(ee=>{const{sourceHandle:ne,targetHandle:ie}=Ax(ee,ue),pe=r!==null&&ee.from.entity!==r&&ee.to.entity!==r;return{id:ee.id,source:ee.from.entity,target:ee.to.entity,sourceHandle:ne,targetHandle:ie,type:"relationship",data:{relationship:ee,dimmed:pe,memberCount:Math.max(ee.from.attributes.length,ee.to.attributes.length),onHoverEdge:Q}}}),G=_.current;if(J.current={},G)for(const[ee,ne]of Object.entries(G.edges))J.current[ee]={...ne.routeX!=null?{routeX:ne.routeX}:{},...ne.sourceAnchor?{sourceAnchor:ne.sourceAnchor}:{},...ne.targetAnchor?{targetAnchor:ne.targetAnchor}:{}};const re=ee=>{const ne=new Map;for(const ie of ee)ne.set(ie.id,{left:ie.position.x,right:ie.position.x+Qe,top:ie.position.y,bottom:ie.position.y+gt(ie.data.entity)});b(Y.map(ie=>{const pe=J.current[ie.id]??{};return{...ie,data:{...ie.data,routeX:pe.routeX,sourceAnchor:pe.sourceAnchor,targetAnchor:pe.targetAnchor,onRouteChange:f?ye:void 0,onAnchorChange:f?Ee:void 0,sourceCard:ne.get(ie.source),targetCard:ne.get(ie.target)}}}))};E(ee=>{const ne=new Map(ee.map(de=>[de.id,de.position])),ie=Z.every(de=>ne.has(de.id)),pe=((l==null?void 0:l.size)??0)>0;if($.current&&ie&&ee.length>=Z.length&&!pe){const de=Z.map(et=>({...et,position:ne.get(et.id)??et.position}));return re(de),de}const Ie=pe?Z.filter(de=>!de.hidden):Z,Le=fr(Ie,Y,ue,"auto",ge),Te=new Map(Le.map(de=>[de.id,de.position])),Ne=(G==null?void 0:G.nodes)??{},He=Object.keys(Ne).length>0,lt=[],Ht=Z.map(de=>{const et=Ne[de.id];if(et){const Ft={x:et.x,y:et.y};return lt.push({x:Ft.x,y:Ft.y,w:Qe,h:gt(de.data.entity)}),{...de,position:Ft}}return Te.has(de.id)?{...de,position:Te.get(de.id)}:ne.has(de.id)&&$.current?{...de,position:ne.get(de.id)}:de});if(He)for(const de of Ht)Ne[de.id]||de.hidden||(de.position=$x(de.position,gt(de.data.entity),lt),lt.push({x:de.position.x,y:de.position.y,w:Qe,h:gt(de.data.entity)}));return $.current=!0,He||requestAnimationFrame(()=>p({padding:.15,duration:300})),re(Ht),Ht})},[e,o,r,i,a,c,u,ve,ue,ge,le,l,q,p,f,ye,Ee,R]);const fe=w.useMemo(()=>{const F=new Set,X=new Set,V=new Map,Z=(Y,G)=>{if(!G.length)return;const re=V.get(Y)??new Set;G.forEach(xe=>re.add(xe)),V.set(Y,re)};if(z)for(const Y of e.relationships)(Y.from.attributes.includes(z)||Y.to.attributes.includes(z))&&(F.add(Y.id),X.add(Y.from.entity),X.add(Y.to.entity),Z(Y.from.entity,Y.from.attributes),Z(Y.to.entity,Y.to.attributes));if(W){const Y=e.relationships.find(G=>G.id===W);Y&&(Z(Y.from.entity,Y.from.attributes),Z(Y.to.entity,Y.to.attributes))}return{active:z!==null,relsForAttr:F,entsForAttr:X,hitAttrsByEntity:V}},[e,z,W]);w.useEffect(()=>{var Z;const F=o.trim().toLowerCase(),X=new Set;if(F)for(const Y of e.entities)(Y.name.toLowerCase()+" "+(((Z=Y.conceptual)==null?void 0:Z.name.toLowerCase())??"")+" "+Y.attributes.map(re=>re.name.toLowerCase()).join(" ")).includes(F)&&X.add(Y.id);const V=new Set;if(r){V.add(r);for(const Y of e.relationships)Y.from.entity===r&&V.add(Y.to.entity),Y.to.entity===r&&V.add(Y.from.entity)}E(Y=>Y.map(G=>{const re=F!==""&&!X.has(G.id),xe=r!==null&&!V.has(G.id),ee=fe.active&&!fe.entsForAttr.has(G.id),ne=re||xe||ee,ie=F!==""&&X.has(G.id)||fe.active&&fe.entsForAttr.has(G.id),pe=fe.hitAttrsByEntity.get(G.id);return G.data.dimmed===ne&&G.data.highlighted===ie&&G.data.highlightAttrs===pe?G:{...G,data:{...G.data,dimmed:ne,highlighted:ie,highlightAttrs:pe}}})),b(Y=>Y.map(G=>{var Te,Ne,He,lt;const re=(Te=G.data)==null?void 0:Te.relationship,xe=r!==null&&re!==void 0&&re.from.entity!==r&&re.to.entity!==r,ee=fe.active&&!fe.relsForAttr.has(G.id),ne=xe||ee,ie=W===G.id,pe=te===G.id,Ie=r!==null&&re!==void 0&&(re.from.entity===r||re.to.entity===r),Le=ie||pe||Ie?1e3:0;return((Ne=G.data)==null?void 0:Ne.dimmed)===ne&&((He=G.data)==null?void 0:He.hovered)===ie&&((lt=G.data)==null?void 0:lt.edgeSelected)===pe&&G.zIndex===Le?G:{...G,zIndex:Le,data:{...G.data,dimmed:ne,hovered:ie,edgeSelected:pe}}}))},[fe,W,te,o,r,e]),w.useEffect(()=>{var Z;const F=o.trim().toLowerCase();if(!F)return;const X=new Set;for(const Y of e.entities)(Y.name.toLowerCase()+" "+(((Z=Y.conceptual)==null?void 0:Z.name.toLowerCase())??"")+" "+Y.attributes.map(re=>re.name.toLowerCase()).join(" ")).includes(F)&&X.add(Y.id);if(!X.size)return;const V=requestAnimationFrame(()=>{const Y=v().filter(re=>X.has(re.id));if(!Y.length)return;const G=Lf(Y);x(G,{padding:.35,duration:400})});return()=>cancelAnimationFrame(V)},[o,e,v,x]);const Oe=w.useCallback((F="auto",X="LR")=>{L.current=Object.fromEntries(v().map(V=>[V.id,{x:V.position.x,y:V.position.y}])),E(V=>fr(V,M,ue,F,ge,X)),requestAnimationFrame(()=>p({padding:.15,duration:300})),he()},[M,ue,ge,p,v,he]),Ke=w.useCallback(()=>{const F=L.current;F&&(E(X=>X.map(V=>F[V.id]?{...V,position:F[V.id]}:V)),L.current=null,he())},[he]),Ye=w.useCallback(()=>{f==null||f.reset(j),I.current.delete(j),_.current=null,J.current={},$.current=!1,b(F=>F.map(X=>({...X,data:{...X.data,routeX:void 0}}))),E(F=>{const X=fr(F,M,ue,"auto",ge);return requestAnimationFrame(()=>p({padding:.15,duration:300})),X})},[f,j,M,ue,ge,p]),$e=w.useCallback(F=>{s(F),E(X=>(X.find(Z=>Z.id===F)&&requestAnimationFrame(()=>p({nodes:[{id:F}],padding:.4,duration:400})),X))},[p,s]);w.useImperativeHandle(m,()=>({relayout:Oe,fitView:()=>p({padding:.15,duration:300}),focusEntity:$e,newEntity:()=>O(!0),resetLayout:Ye,undoRelayout:Ke}),[Oe,p,$e,Ye,Ke]);const Ae=w.useCallback(F=>{if(ae||!F.source||!F.target||F.source===F.target)return;const X=F.sourceHandle&&F.sourceHandle!==Ur?F.sourceHandle:void 0,V=F.targetHandle&&F.targetHandle!==qr?F.targetHandle:void 0;k({from:F.source,to:F.target,fromAttr:X,toAttr:V})},[ae]),je=w.useCallback(async(F,X)=>{try{return await n(F,X)}catch(V){throw d==null||d(V instanceof Error?V.message:String(V)),V}},[n,d]),Re=r?ue.get(r)??null:null,ze=e.relationships.find(F=>F.id===T)??null;return h.jsxs(h.Fragment,{children:[h.jsxs(Ji,{nodes:g,edges:M,nodeTypes:Cx,edgeTypes:Mx,onNodeClick:(F,X)=>{s(X.id),se(null)},onEdgeClick:(F,X)=>se(X.id),onEdgeDoubleClick:(F,X)=>!ae&&P(X.id),onPaneClick:()=>{s(null),se(null),P(null)},onNodeDragStop:(F,X)=>{E(V=>V.map(Z=>Z.id===X.id?{...Z,position:X.position}:Z)),he()},onConnect:Ae,nodesConnectable:!ae,minZoom:.05,onlyRenderVisibleElements:!0,proOptions:{hideAttribution:!0},fitView:!0,children:[h.jsx(Cm,{variant:Be.Dots,gap:22,size:1.5,color:"#33291f"}),h.jsx(pm,{pannable:!0,zoomable:!0,nodeColor:F=>{var X;return((X=F.data)==null?void 0:X.color)??"#5a4f42"},maskColor:"rgba(20, 17, 15, 0.75)"}),h.jsx(_m,{showInteractive:!1})]}),Re&&h.jsx(Xm,{entity:Re,doc:e,caps:t,exec:je,onClose:()=>s(null),onFocusEntity:$e,onAlign:H,onEditMapping:C}),B&&h.jsx(dx,{entity:B,exec:je,onClose:()=>H(null)}),y&&h.jsx(fx,{entity:y,doc:e,exec:je,onClose:()=>C(null)}),S&&h.jsx(hx,{doc:e,exec:je,onClose:()=>O(!1),onCreated:$e}),D&&h.jsx(mx,{doc:e,fromId:D.from,toId:D.to,initialFromAttr:D.fromAttr,initialToAttr:D.toAttr,exec:je,onClose:()=>k(null)}),ze&&h.jsx(px,{rel:ze,doc:e,exec:je,onClose:()=>P(null)})]})}function cw({tab:e,onClose:t,readOnly:n,refreshKey:r,onModelChanged:s,onFocusEntity:o}){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(Rx,{}),e==="layers"&&h.jsx(Tx,{refreshKey:r,onFocusEntity:o}),e==="changes"&&h.jsx(Ox,{readOnly:n,refreshKey:r,onModelChanged:s}),e==="decisions"&&h.jsx(Lx,{readOnly:n})]})}function Rx(){const[e,t]=w.useState(""),[n,r]=w.useState([]),[s,o]=w.useState({loaded:0,vocabs:[]}),[i,a]=w.useState(null),[c,u]=w.useState([]),[l,d]=w.useState("");w.useEffect(()=>{ia().then(f=>u(f.ontologies)).catch(()=>{})},[]),w.useEffect(()=>{const f=setTimeout(()=>{oa(e||"a",l||void 0).then(p=>{r(e?p.results:[]),o({loaded:p.loaded_terms,vocabs:p.vocabularies})}).catch(()=>{})},200);return()=>clearTimeout(f)},[e,l]);const m=w.useCallback(f=>{Mm(f).then(a).catch(()=>{})},[]);return i?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:i.label}),h.jsx("code",{className:"term-iri",children:i.prefixed}),i.definition&&h.jsx("p",{className:"term-def",children:i.definition}),h.jsxs("div",{className:"kv",children:[h.jsx("span",{className:"k",children:"source"}),h.jsx("span",{className:"v",children:i.source})]}),i.broader.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"broader"}),i.broader.map(f=>h.jsxs("button",{className:"rel-link",onClick:()=>m(f.iri),children:["↑ ",f.label]},f.iri))]}),i.narrower.length>0&&h.jsxs(h.Fragment,{children:[h.jsx("h4",{children:"narrower"}),i.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:[c.length>0&&h.jsxs("div",{className:"src-picker",children:[h.jsx("button",{className:"src-chip"+(l===""?" active":""),onClick:()=>d(""),children:"all sources"}),c.map(f=>h.jsxs("button",{className:"src-chip"+(l===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:l?`Search within ${l}…`:"Search vocabulary terms…",value:e,onChange:f=>t(f.target.value)}),h.jsx("p",{className:"meta-line",children:c.length>0?`${c.length} source${c.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 Ix=["industry","core","domain","specialised"];function Tx({refreshKey:e,onFocusEntity:t}){const[n,r]=w.useState(null),[s,o]=w.useState(null);if(w.useEffect(()=>{$m().then(r).catch(()=>{}),Am().then(o).catch(()=>{})},[e]),!n)return h.jsx("div",{className:"panel-body",children:"loading…"});const i=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))]}),Ix.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(c=>{var u;return h.jsxs("button",{className:"stack-term"+(i.has(c.name)?" uncovered":""),title:c.definition??void 0,onClick:()=>t(c.id),children:[c.name,(c.aligned_refs??(c.aligned_to?[c.aligned_to]:[])).map((l,d)=>h.jsxs("span",{className:"align-arrow"+(l.resolved?"":" unresolved")+(l.status==="proposed"?" proposed":""),title:l.ref+(l.resolved_via?` (via ${l.resolved_via})`:""),children:["↑ ",l.label,l.status==="proposed"?" ·prop":""]},`${l.ref}-${d}`)),!c.aligned_to&&(((u=c.aligned_refs)==null?void 0:u.length)??0)===0&&!c.no_industry_equivalent&&h.jsx("span",{className:"align-arrow missing",children:"unaligned"})]},c.id)})]},a))]})}function Ox({readOnly:e,refreshKey:t,onModelChanged:n}){const[r,s]=w.useState(null),[o,i]=w.useState(""),[a,c]=w.useState(""),[u,l]=w.useState(!1),d=w.useCallback(()=>{Im().then(s).catch(()=>s(null)),Tm().then(m=>i([m.diff,...m.untracked].filter(Boolean).join(` + +`))).catch(()=>i(""))},[]);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:o.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=>c(m.target.value)}),h.jsxs("div",{className:"modal-footer",children:[h.jsx("button",{className:"mini-btn danger",disabled:u,onClick:()=>{confirm("Discard ALL uncommitted model changes?")&&(l(!0),Lm().then(()=>{l(!1),d(),n()}))},children:"Discard all"}),h.jsx("button",{className:"primary-btn",disabled:u||!a.trim(),onClick:()=>{l(!0),Om(a).then(()=>{l(!1),c(""),d()})},children:"Commit"})]})]})]}):h.jsx("div",{className:"panel-body",children:"model directory is not in a git repo"})}function Lx({readOnly:e}){const[t,n]=w.useState([]),r=w.useCallback(()=>{Dm().then(o=>n(o.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(o=>h.jsxs("div",{className:"decision "+o.verdict,children:[h.jsxs("div",{className:"decision-head",children:[h.jsx("span",{className:"decision-mark",children:s[o.verdict]}),h.jsx("span",{className:"chip",children:o.confidence}),h.jsx("span",{className:"chip",children:o.signal})]}),h.jsx("p",{className:"decision-subject",children:o.subject}),!e&&o.verdict==="proposed"&&h.jsxs("div",{className:"decision-actions",children:[h.jsx("button",{className:"mini-btn",onClick:()=>Xs(o.signal_key,"accepted").then(r),children:"Accept"}),h.jsx("button",{className:"mini-btn danger",onClick:()=>Xs(o.signal_key,"rejected").then(r),children:"Reject"})]})]},o.signal_key))})}function lw({exec:e,canEdit:t,onImported:n,submitLabel:r="Import & review",buttonClass:s="erd-action ghost",applyBatch:o,openImport:i=!1,prefillImport:a="",onPreview:c}){const[u,l]=w.useState(i&&t?"import":null),[d,m]=w.useState([]),[f,p]=w.useState("postgres"),x=w.useRef(null);w.useEffect(()=>{Pm().then(g=>m(g.formats)).catch(()=>m([]))},[]),w.useEffect(()=>{if(!u)return;const g=E=>{x.current&&!x.current.contains(E.target)&&l(null)};return document.addEventListener("mousedown",g),()=>document.removeEventListener("mousedown",g)},[u]);const v=d.find(g=>g.id==="sql");return h.jsxs("div",{className:"ie-wrap",ref:x,children:[h.jsxs("button",{className:s+(u==="export"?" on":""),onClick:()=>l(u==="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+(u==="import"?" on":""),onClick:()=>l(u==="import"?null:"import"),children:[h.jsx("span",{className:"erd-ico","aria-hidden":"true",children:"↥"}),"Import ",h.jsx("span",{className:"erd-caret",children:"▾"})]}),u==="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:g=>p(g.target.value),children:v.dialects.map(g=>h.jsx("option",{value:g,children:g},g))})]}),d.map(g=>h.jsx("a",{className:"ie-item",href:zm(g.id,g.id==="sql"?f:void 0),download:!0,onClick:()=>l(null),children:g.label},g.id))]}),u==="import"&&h.jsx(Dx,{exec:e,submitLabel:r,buttonClass:s,applyBatch:o,onClose:()=>l(null),onImported:g=>{l(null),n==null||n(g)},onPreview:c,prefillImport:a})]})}const Mo=[{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 Dx({exec:e,submitLabel:t,buttonClass:n,applyBatch:r,onClose:s,onImported:o,onPreview:i,prefillImport:a}){var j;const[c,u]=w.useState(i?"erwin":"sql"),[l,d]=w.useState("postgres"),[m,f]=w.useState(a??""),[p,x]=w.useState(!1),[v,g]=w.useState(null),[E,M]=w.useState([]);w.useEffect(()=>{a&&f(a)},[a]);const b=_=>{_&&_.text().then(f).catch(()=>g("could not read that file"))},$=async()=>{x(!0),g(null),M([]);try{if(i&&c==="erwin"){const R=await Fm(c,m);if(!R.changes.length){g(R.warnings[0]??"nothing to import from that document"),x(!1);return}s(),i(R),x(!1);return}const _=await Hm(c,m,c==="sql"?l:void 0);if(!_.changes.length){g(_.warnings[0]??"nothing to import from that document"),x(!1);return}if(r)await r(_.changes);else for(const R of _.changes)await e(R.op,R.payload);M(_.warnings),o(_.tables)}catch(_){g(_ instanceof Error?_.message:String(_))}finally{x(!1)}};return h.jsxs("div",{className:"ie-menu ie-import",role:"dialog",children:[h.jsx("div",{className:"ie-tabs",children:Mo.map(_=>h.jsx("button",{className:"ie-tab"+(c===_.id?" active":""),onClick:()=>u(_.id),children:_.label},_.id))}),h.jsx("p",{className:"ie-hint",children:(j=Mo.find(_=>_.id===c))==null?void 0:j.hint}),c==="sql"&&h.jsxs("label",{className:"ie-dialect",children:[h.jsx("span",{children:"dialect"}),h.jsx("select",{value:l,onChange:_=>d(_.target.value),children:["postgres","snowflake","mysql","duckdb"].map(_=>h.jsx("option",{children:_},_))})]}),h.jsx("textarea",{className:"ie-textarea",placeholder:`Paste ${c} 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 R;return b(((R=_.target.files)==null?void 0:R[0])??null)}}),c==="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((_,R)=>h.jsx("p",{className:"ie-warn",children:_},R)),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:p||!m.trim(),onClick:$,children:p?"Importing…":t})]})]})}export{sa as A,Cm as B,_m as C,Gm as E,lw as I,Tx as L,aw as M,Rx as O,ko as P,Zi as R,cw as S,Fx as a,Qx as b,qx as c,ew as d,tw as e,Hx as f,Im as g,rw as h,nw as i,Kx as j,Gx as k,Yx as l,Xx as m,Ux as n,oa as o,Jx as p,Zx as q,Wx as r,Rm as s,Vx as t,Bx as u,fr as v,Ji as w,jx as x,pm as y}; diff --git a/packages/server/src/mdl_server/static/index.html b/packages/server/src/mdl_server/static/index.html index 3ef1a9a..3c8d71c 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 ccd2daa..2eb62d9 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 - + - - + + diff --git a/packages/server/tests/test_layout_api.py b/packages/server/tests/test_layout_api.py new file mode 100644 index 0000000..770ce96 --- /dev/null +++ b/packages/server/tests/test_layout_api.py @@ -0,0 +1,380 @@ +"""Visual layout sidecar API + its strict isolation from the model. + +Layout is visual only: it must never change the model fingerprint, the /api/model +document, validation/drift/export output, git dirtiness, or survive a revert as a +model change. These tests pin every one of those guarantees. +""" + +from __future__ import annotations + +import json +import subprocess +from pathlib import Path + +import pytest +from fastapi.testclient import TestClient +from mdl_server import create_app + +from mdl_core import commands + + +def _git(d: Path, *args: str) -> str: + return subprocess.run( + ["git", "-C", str(d), *args], capture_output=True, text=True + ).stdout.strip() + + +def _init(d: Path) -> None: + _git(d, "init") + _git(d, "config", "user.email", "t@example.com") + _git(d, "config", "user.name", "t") + _git(d, "add", "-A") + _git(d, "commit", "-m", "initial") + _git(d, "branch", "-M", "main") + + +@pytest.fixture +def client(model_dir): + return TestClient(create_app(model_dir)) + + +def test_get_empty_returns_empty_maps(client): + r = client.get("/api/layout?view=__all__") + assert r.status_code == 200 + d = r.json() + assert d == {"version": 1, "view": "__all__", "nodes": {}, "edges": {}} + + +def test_put_then_get_roundtrips(client, model_dir): + # use a real entity + relationship id so nothing is dropped + model = client.get("/api/model").json() + ent = model["entities"][0]["id"] + rel = model["relationships"][0]["id"] if model["relationships"] else None + nodes = {ent: {"x": 120.6, "y": 340.2}} + edges = {rel: {"routeX": 480.9}} if rel else {} + r = client.put("/api/layout", json={"view": "__all__", "nodes": nodes, "edges": edges}) + assert r.status_code == 200 and r.json()["ok"] + got = client.get("/api/layout?view=__all__").json() + assert got["nodes"][ent] == {"x": 121, "y": 340} # rounded to int + if rel: + assert got["edges"][rel] == {"routeX": 481} + + +def test_edge_endpoint_anchors_roundtrip(client): + """Dragged endpoint anchors (visual only) persist: {side, y, x?}, rounded, and a + route record may carry routeX + anchors together.""" + model = client.get("/api/model").json() + rel = model["relationships"][0]["id"] + edges = { + rel: { + "routeX": 300.4, + "sourceAnchor": {"side": "right", "y": 140.7}, + "targetAnchor": {"side": "top", "y": 50.2, "x": 210.9}, + } + } + r = client.put("/api/layout", json={"view": "__all__", "nodes": {}, "edges": edges}) + assert r.status_code == 200, r.text + got = client.get("/api/layout?view=__all__").json()["edges"][rel] + assert got["routeX"] == 300 + assert got["sourceAnchor"] == {"side": "right", "y": 141} + assert got["targetAnchor"] == {"side": "top", "y": 50, "x": 211} + + +def test_bad_anchor_side_422(client): + rel = client.get("/api/model").json()["relationships"][0]["id"] + r = client.put( + "/api/layout", + json={"view": "__all__", "nodes": {}, "edges": {rel: {"sourceAnchor": {"side": "nope", "y": 1}}}}, + ) + assert r.status_code == 422 + + +def test_file_is_key_sorted_integers(client, model_dir): + model = client.get("/api/model").json() + e0, e1 = model["entities"][0]["id"], model["entities"][1]["id"] + client.put( + "/api/layout", + json={"view": "__all__", "nodes": {e1: {"x": 1.4, "y": 2.6}, e0: {"x": 3, "y": 4}}, "edges": {}}, + ) + text = (model_dir / ".mdl" / "layout.json").read_text() + doc = json.loads(text) + assert doc["version"] == 1 + # integer values, and keys serialized sorted + assert all(isinstance(v["x"], int) and isinstance(v["y"], int) for v in doc["views"]["__all__"]["nodes"].values()) + assert text.endswith("\n") + # sort_keys=True => the smaller ulid appears first in the raw text + assert text.index(f'"{min(e0, e1)}"') < text.index(f'"{max(e0, e1)}"') + + +def test_put_drops_ids_not_in_model(client): + r = client.put( + "/api/layout", + json={"view": "__all__", "nodes": {"01GHOSTNOTAREALENTITYID00": {"x": 1, "y": 2}}, "edges": {}}, + ) + assert r.status_code == 200 + assert client.get("/api/layout?view=__all__").json()["nodes"] == {} + + +def test_corrupt_file_treated_as_empty_and_overwritten(client, model_dir): + mdl = model_dir / ".mdl" + mdl.mkdir(parents=True, exist_ok=True) + (mdl / "layout.json").write_text("{ this is not json") + # GET does not crash + assert client.get("/api/layout?view=__all__").json()["nodes"] == {} + # next PUT overwrites cleanly + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 5, "y": 6}}, "edges": {}}) + assert client.get("/api/layout?view=__all__").json()["nodes"][ent] == {"x": 5, "y": 6} + + +def test_non_finite_and_long_keys_422(client): + ent = client.get("/api/model").json()["entities"][0]["id"] + # send Infinity as a raw body so it reaches the server (Python's json emits it, + # the server's _finite guard must reject it) + raw = f'{{"view": "__all__", "nodes": {{"{ent}": {{"x": Infinity, "y": 0}}}}, "edges": {{}}}}' + r = client.put("/api/layout", content=raw, headers={"content-type": "application/json"}) + assert r.status_code in (422, 400) + r2 = client.put("/api/layout", json={"view": "x" * 65, "nodes": {}, "edges": {}}) + assert r2.status_code == 422 + + +def test_put_and_delete_403_when_read_only(model_dir): + ro = TestClient(create_app(model_dir, read_only=True)) + assert ro.get("/api/layout?view=__all__").status_code == 200 # read still works + assert ro.put("/api/layout", json={"view": "__all__", "nodes": {}, "edges": {}}).status_code == 403 + assert ro.delete("/api/layout").status_code == 403 + + +def test_delete_one_view_and_all(client): + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 1}}, "edges": {}}) + client.put("/api/layout", json={"view": "sa:abc", "nodes": {ent: {"x": 2, "y": 2}}, "edges": {}}) + client.delete("/api/layout?view=sa:abc") + assert client.get("/api/layout?view=sa:abc").json()["nodes"] == {} + assert client.get("/api/layout?view=__all__").json()["nodes"] != {} + client.delete("/api/layout") # the whole file + assert client.get("/api/layout?view=__all__").json()["nodes"] == {} + + +# --- isolation: layout must be invisible to the model ------------------------- + + +def test_layout_does_not_change_fingerprint_or_model_doc(client, model_dir): + fp_before = commands.dir_fingerprint(model_dir) + doc_before = client.get("/api/model").json() + ent = doc_before["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 9, "y": 9}}, "edges": {}}) + assert commands.dir_fingerprint(model_dir) == fp_before + assert client.get("/api/model").json() == doc_before + + +def test_validate_identical_with_and_without_layout(model_dir): + from mdl_core.repo import ModelRepo + from mdl_core.validate import validate + + before = [(d.code, d.message) for d in validate(ModelRepo.load(model_dir).model).items] + (model_dir / ".mdl").mkdir(parents=True, exist_ok=True) + (model_dir / ".mdl" / "layout.json").write_text( + json.dumps({"version": 1, "views": {"__all__": {"nodes": {"x": {"x": 1, "y": 2}}, "edges": {}}}}) + ) + after = [(d.code, d.message) for d in validate(ModelRepo.load(model_dir).model).items] + assert before == after + + +def test_export_identical_with_and_without_layout(client, model_dir): + before = client.get("/api/export/sql").text + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + assert client.get("/api/export/sql").text == before + + +def _write_rich_layout(model_dir: Path) -> None: + """A FULLY-populated sidecar: node positions, an edge routeX, and both dragged + endpoint anchors. Isolation must hold for every field, not just positions.""" + from mdl_core.repo import ModelRepo + + model = ModelRepo.load(model_dir).model + ent = next(iter(model.logical_entities)) + rel = next(iter(model.relationships), None) + edges = {} + if rel: + edges[rel] = { + "routeX": 480, + "sourceAnchor": {"side": "right", "y": 200}, + "targetAnchor": {"side": "top", "y": 50, "x": 210}, + } + (model_dir / ".mdl").mkdir(parents=True, exist_ok=True) + (model_dir / ".mdl" / "layout.json").write_text( + json.dumps({"version": 1, "views": {"__all__": {"nodes": {ent: {"x": 120, "y": 340}}, "edges": edges}}}) + ) + + +def test_every_emitter_byte_identical_with_rich_layout(client, model_dir): + """The strongest isolation check: validation and EVERY export format produce + byte-identical output with and without a full (positions + routeX + anchors) + layout sidecar. If any backend ever reads the sidecar, one of these diverges.""" + formats = [f["id"] for f in client.get("/api/export").json()["formats"]] + before = {f: client.get(f"/api/export/{f}").text for f in formats} + before_model = client.get("/api/model").json() + _write_rich_layout(model_dir) + for f in formats: + assert client.get(f"/api/export/{f}").text == before[f], f"{f} export changed with layout" + assert client.get("/api/model").json() == before_model + + +def test_drift_identical_with_and_without_layout(model_dir): + """Drift (model vs compiled dbt manifest) must be identical with/without layout — + the user-facing backend feature the sidecar must never perturb.""" + import sys + + sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "reverse" / "tests")) + from mdl_core.repo import ModelRepo + from mdl_reverse.drift import compute_drift + from mdl_reverse.manifest import read_manifest_dict + + from manifest_fixtures import manifest_from_model + + proj = read_manifest_dict(manifest_from_model(ModelRepo.load(model_dir).model, "duckdb_dev")) + + def drift_items(): + rep = compute_drift(ModelRepo.load(model_dir).model, proj, "duckdb_dev") + return [(i.kind, i.severity, i.detail) for i in rep.items] + + before = drift_items() + _write_rich_layout(model_dir) + assert drift_items() == before + + +# --- isolation: git never sees layout as dirty, never deletes it -------------- + + +def test_git_status_not_dirty_from_layout(model_dir): + _init(model_dir) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + st = client.get("/api/git/status").json() + assert st["git"] is True + assert st["clean"] is True + assert not any(d["path"] == ".mdl/layout.json" for d in st["dirty"]) + + +def test_gitignore_created_after_first_write(model_dir): + _init(model_dir) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + gi = model_dir / ".mdl" / ".gitignore" + assert gi.exists() + assert "layout.json" in gi.read_text().splitlines() + + +def test_gitignore_not_created_outside_git(model_dir): + # model_dir is a bare tmp dir (no git init here) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + assert not (model_dir / ".mdl" / ".gitignore").exists() + assert (model_dir / ".mdl" / "layout.json").exists() # file still written + + +# --- team sharing: `layout: {shared: true}` opt-in ----------------------------- + + +def _set_shared(model_dir: Path) -> None: + p = model_dir / "mdl-project.yaml" + p.write_text(p.read_text() + "\nlayout:\n shared: true\n") + + +def test_shared_layout_is_not_gitignored(model_dir): + """With `layout: {shared: true}`, the sidecar is NOT ignored, so a team can commit it.""" + _init(model_dir) + _set_shared(model_dir) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + gi = model_dir / ".mdl" / ".gitignore" + # no ignore of layout.json -> committable + assert not (gi.exists() and "layout.json" in gi.read_text().splitlines()) + + +def test_switching_to_shared_removes_auto_ignore(model_dir): + """A project that was ignoring layout (solo) and later opts into sharing: the next + write drops OUR auto-added ignore line so the file becomes committable.""" + _init(model_dir) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + assert "layout.json" in (model_dir / ".mdl" / ".gitignore").read_text().splitlines() + _set_shared(model_dir) + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 3, "y": 4}}, "edges": {}}) + gi = model_dir / ".mdl" / ".gitignore" + assert not (gi.exists() and "layout.json" in gi.read_text().splitlines()) + + +def test_shared_setting_does_not_leak_into_cli_features(model_dir): + """THE guardrail the user named: whatever the sharing setting is, layout stays + invisible to every functional CLI feature. With `shared: true` AND a rich committed + sidecar, validate / every export / drift / the model doc are all identical to a model + with no layout and no setting.""" + client = TestClient(create_app(model_dir)) + formats = [f["id"] for f in client.get("/api/export").json()["formats"]] + before_exports = {f: client.get(f"/api/export/{f}").text for f in formats} + before_model = client.get("/api/model").json() + before_fp = commands.dir_fingerprint(model_dir) + # opt into sharing AND write a rich sidecar + _set_shared(model_dir) + _write_rich_layout(model_dir) + client2 = TestClient(create_app(model_dir)) + for f in formats: + assert client2.get(f"/api/export/{f}").text == before_exports[f], f + assert client2.get("/api/model").json() == before_model + assert commands.dir_fingerprint(model_dir) == before_fp + + +# --- discard / dirtiness: the sidecar survives a model revert, and a layout-only ---- +# `.mdl/` never reads as dirty (the git_api.py filtering paths). + + +def test_discard_reverts_the_model_but_keeps_the_layout_sidecar(model_dir): + """`/api/git/discard` reverts MODEL edits (git checkout + clean) yet must preserve the + visual layout sidecar — it is excluded with `clean -e .mdl/layout.json`. Guards the + 'survive a revert' promise in this module's docstring.""" + _init(model_dir) + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + # a real, uncommitted MODEL change the discard should sweep. Use a non-object file + # (not under an *.yaml object glob) so writing it doesn't break ModelRepo.load, while + # still being an untracked file `git clean` must remove. + stray = model_dir / "_stray.txt" + stray.write_text("scratch\n", encoding="utf-8") + # and a layout the discard must NOT touch + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 7, "y": 8}}, "edges": {}}) + layout = model_dir / ".mdl" / "layout.json" + assert layout.exists() and stray.exists() + + r = client.post("/api/git/discard") + assert r.status_code == 200 and r.json()["ok"] is True + + assert not stray.exists(), "discard must revert the untracked model file" + assert layout.exists(), "discard must KEEP the visual layout sidecar" + assert client.get("/api/layout?view=__all__").json()["nodes"][ent] == {"x": 7, "y": 8} + + +def test_untracked_layout_only_mdl_dir_is_not_dirty_but_real_mdl_content_is(model_dir): + """When the whole `.mdl/` dir is new, `git status --porcelain` collapses it to a single + `?? .mdl/` line. That must read as CLEAN when the dir holds only layout artifacts, but + DIRTY when it also holds real state (e.g. decisions.yaml) — the `_mdl_only_layout` branch + in git_api.py. Here `.mdl/` is created by the first layout write AFTER the initial commit, + so it is entirely untracked (the collapsed case).""" + _init(model_dir) # commits the model BEFORE any .mdl/ exists -> .mdl/ is untracked + client = TestClient(create_app(model_dir)) + ent = client.get("/api/model").json()["entities"][0]["id"] + client.put("/api/layout", json={"view": "__all__", "nodes": {ent: {"x": 1, "y": 2}}, "edges": {}}) + st = client.get("/api/git/status").json() + assert st["git"] is True and st["clean"] is True, "layout-only .mdl/ must read clean" + + # now add REAL .mdl/ content: the same untracked dir must now read dirty + (model_dir / ".mdl" / "decisions.yaml").write_text("decisions: []\n", encoding="utf-8") + st2 = client.get("/api/git/status").json() + assert st2["clean"] is False, "real .mdl/ content must read dirty even alongside layout" diff --git a/pyproject.toml b/pyproject.toml index 2f516d3..0abebaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ # project, so the install name is `modelith-dbt` (install: uv tool install # modelith-dbt). The command stays `mdl`; the import modules stay mdl_*. name = "modelith-dbt" -version = "0.6.13" +version = "0.6.15" description = "Ontology-anchored, git-native data modeling tool for dbt-core teams" readme = "README.md" requires-python = ">=3.11" diff --git a/specs/modelith-canvas-edge-drag-and-layout-spec.md b/specs/modelith-canvas-edge-drag-and-layout-spec.md new file mode 100644 index 0000000..bd4dcd5 --- /dev/null +++ b/specs/modelith-canvas-edge-drag-and-layout-spec.md @@ -0,0 +1,154 @@ +# Canvas: draggable relationship edges and saved layout + +Spec for Claude Code. Target repo: github.com/dbose/modelith, branch from `main` at or after `7dd170b`. Put this file at `specs/modelith-canvas-edge-drag-and-layout-spec.md`. + +## Context + +Author has imported a 7 MB erwin file, reversed it, and used the canvas with GitHub Copilot driving the MCP tools. Two gaps showed up against erwin's diagram editor (see the screen recording of erwin: selecting a relationship line shows a handle on its vertical segment, and dragging it slides the segment sideways while the ends stay glued to the entities). + +1. A relationship edge cannot be dragged. Every edge is drawn by `getSmoothStepPath` in `canvas/src/RelationshipEdge.tsx`, so its elbow position is decided by the library and cannot be adjusted. On a dense model many edges run through the same corridor and the diagram is unreadable. +2. Layout is not saved. `ModelCanvas.tsx` keeps node positions only in React state (`setNodes`), and recomputes them with `layoutGraph` on load. Every reload throws away manual placement. Nothing in the server stores layout. + +Layout is a visual concern only. It must never appear in the model, drift, validation, exports, diffs, proposals or dbt output. + +## Decisions (already made, do not reopen) + +- Layout lives in a sidecar file, `/.mdl/layout.json`, not in any model YAML and not in `mdl-project.yaml`. +- JSON, not YAML. Several code paths glob `*.yaml` or `*.y*ml` under the model dir (`commands.dir_fingerprint`, `status.py`, `app._dir_fingerprint`). A `.json` file stays out of all of them by construction. +- The file is git-ignored by default (see Part B). Sharing layout across a team is a later opt-in, not v1. +- Edge routing is orthogonal, same style as today. The only adjustable parameter per edge is the x position of its vertical segment (`routeX`). The canvas has ports on left and right sides only (`EntityNode.tsx`), so a vertical segment is the only free degree of freedom. No free-form waypoints in v1. +- The canvas component stays free of API calls. Persistence is injected through a prop, the same way `exec` is. + +## Part A: draggable relationship edges + +### Behaviour + +1. Hovering or selecting an edge shows a drag handle at the midpoint of its vertical segment (a small grip, cursor `ew-resize`). The existing invisible 14 px hit path stays for hover and click. +2. Dragging the handle moves the vertical segment horizontally. Both endpoints stay anchored to their attribute handles. The path redraws live while dragging, at 60 fps on a 7 MB erwin model (about 60 to 100 entities, a few hundred edges). +3. Smart adjustment, applied while dragging and on release: + - Snap to a 10 px grid. Holding Shift disables snapping. + - Magnetise: if the segment is within 6 px of another edge's segment x or of a card's left or right edge plus margin, snap to it so lines can be aligned. + - Never allow the segment inside a card's horizontal extent. If the drop lands inside a card, move it to the nearest outer edge plus a 24 px margin. For a forward edge (source left of target) with room, clamp to `[sourceX + 24, targetX - 24]`. For a backward edge (target left of source), allow any x outside both cards. +4. Double-clicking the handle (or the edge, when selected) resets that edge to automatic routing by removing its `routeX`. +5. A click still opens `RelEditModal` as today. A press on the handle that moves less than 3 px counts as a click. A real drag must not open the modal (call `stopPropagation` on the handle's pointer events and swallow the click that follows a drag). +6. When a node is dragged, its connected edges keep their stored `routeX`, re-clamped at render time. The stored value is not rewritten by a node move, so moving a card back restores the old routing. +7. The selected-edge label (`rel.verb_phrase || rel.name`) and the composite-key count label should sit at the vertical segment's x, not the geometric midpoint, once `routeX` is set. + +### Implementation notes + +- Add `routeX?: number` (flow coordinates) and `onRouteChange?: (relId: string, routeX: number | null) => void` to `RelationshipEdgeData`. +- Path: pass `centerX: routeX` to `getSmoothStepPath`. In `reactflow@11.11.4` (`@reactflow/core@11.11.4`, pinned in `canvas/package-lock.json`) `getSmoothStepPath` accepts `centerX` and `centerY`. Verify this in `node_modules/@reactflow/core` types before relying on it. If it behaves badly for backward edges, write a small `orthogonalPath(sourceX, sourceY, targetX, targetY, routeX, radius)` in `edgeRouting.ts` that draws horizontal stub, vertical run at `routeX`, horizontal stub, with the existing 10 px corner radius. +- Convert pointer position to flow coordinates with `useReactFlow().screenToFlowPosition` (available in 11.10+). Use pointer events with `setPointerCapture` and update on `requestAnimationFrame`. +- During a drag keep the in-progress `routeX` in local state inside the edge component. Do not call `setEdges` for every move; that rebuilds the whole array and will stutter on a large model (the hover overlay in `ModelCanvas.tsx` was already refactored for exactly this reason). Commit once on pointer release through `onRouteChange`. +- Put the geometry in a new pure module `canvas/src/edgeRouting.ts`: `snapRouteX`, `clampRouteX`, `magnetise`. Unit-test it without React. +- Card rectangles for clamping: node `position` from the store, width `NODE_WIDTH` (264) from `layout.ts`, height from `nodeHeight(entity)`. Expose them to the edge through edge data or `useStore`, whichever avoids re-rendering every edge on every node move. +- `onlyRenderVisibleElements` is on. An edge being dragged must stay mounted while off-screen endpoints scroll away. Check this. +- Readonly (`caps.canEdit === false`): dragging is still allowed. Routing is layout, not a model edit. It saves only if a layout store is provided (Part B). + +## Part B: saved layout, visual only + +### File and schema + +Path: `/.mdl/layout.json`. `model_dir` is the directory `mdl serve -m ` was started with (the same root `DecisionLedger` uses for `.mdl/decisions.yaml`). + +```json +{ + "version": 1, + "views": { + "__all__": { + "nodes": { "": { "x": 120, "y": 340 } }, + "edges": { "": { "routeX": 480 } } + }, + "sa:": { "nodes": {}, "edges": {} } + } +} +``` + +- A view is one canvas arrangement. `__all__` is the unfiltered canvas. When the subject-area filter (`activeAreas`) is active, the view key is `sa:` plus the selected ids, sorted and joined with `+`. This matters because `ModelCanvas.tsx` already re-lays-out the visible subset when filtering, so one flat position map would be overwritten every time the filter changes. +- Numbers are rounded to integers. Keys are sorted and the file is written with 1-space indent and a trailing newline, so any diff is small and stable. +- Unknown top-level keys and a higher `version` are preserved on read-modify-write and never crash the server. An unreadable or corrupt file is treated as empty (log once), never fatal. Never raise from `GET /api/model` because of it. + +### Server (`packages/server/src/mdl_server`) + +New module `layout_store.py`, plus two routes in `app.py`. + +- `GET /api/layout?view=` returns `{ "version": 1, "view": key, "nodes": {...}, "edges": {...} }`, empty maps when absent. Always 200 for a readable model. +- `PUT /api/layout` takes `{ "view": key, "nodes": {...}, "edges": {...} }` and replaces that view. It writes atomically (temp file in the same directory, then `os.replace`). Body limit 1 MB. Reject non-finite numbers and keys longer than 64 characters with 422. On write, drop entries whose id is not an entity or relationship in the current model (`_load().model`), so the file does not accumulate dead ids. +- `DELETE /api/layout?view=` removes one view. Omitting `view` removes the whole file (the "reset all layout" action). +- Under `read_only` (catalog browse, SME just looking) PUT and DELETE return 403. The canvas treats that as "session only" and does not show an error. +- Layout routes do not go through `commands.apply_command`, do not bump the model fingerprint, do not call `cache["repo"] = None`, and do not write an audit record. They are not model mutations. +- On first successful write, ensure the file is ignored by git: create `/.mdl/.gitignore` containing `layout.json` if that file is not already ignored. Do this only when `.mdl/` is inside a git work tree and never edit a user's existing ignore files beyond creating this one. + +### Canvas (`canvas/src`) + +- Add `LayoutStore` to `exec.ts` or a new `layoutStore.ts`: `{ load(view): Promise, save(view, layout): void, reset(view?): Promise }`. Implement `httpLayoutStore` in `api.ts` using the existing `get` helper and the `API_BASE` prefix. Add an optional prop `layoutStore?: LayoutStore` to `ModelCanvasProps`. The architect app (`App.tsx`) passes it. The SME and catalog shells do not, so staged or previewed models never persist layout. +- Load: after the model is derived and nodes are created, call `layoutStore.load(viewKey)` and apply saved positions in place of the `layoutGraph` result for entities that have one. Entities without a saved position (new ones, such as those Copilot just created) keep their auto-layout position, then get a simple overlap nudge so they do not land on top of an existing card. Run auto-layout once for the whole view as today, then override. Fit the view only when no saved layout exists, so a restored layout is not re-zoomed away. +- Save: on `onNodeDragStop`, on edge route commit, and on the toolbar Re-layout action. Debounce 800 ms and send the whole view. Show nothing on success, a quiet toast on failure. +- Re-layout is destructive of manual work. Keep the previous positions in memory and offer Undo in a toast for the session. Add "Reset layout" to the toolbar menu, calling `reset(viewKey)`. +- A node drag today only updates React state in `onNodeDragStop`. Keep that behaviour and add the save call there. +- Viewport (pan and zoom) is not saved in v1. + +### Isolation guarantees (must hold, with tests) + +`layout.json` must be invisible to: + +- `ModelRepo.load` and everything built on it: `mdl validate`, drift, `mdl diff`, emitters (dbt, ODCS, Pydantic, graph, RDF, OSI, MetricFlow). +- `/api/export` and `mdl export`. +- `commands.dir_fingerprint` and `app._dir_fingerprint` (they only glob `*.yaml`). +- The reverse writer prune (`writer.py` already excludes `.mdl/`; add a test so it stays that way). +- `git_api.py`: `status`, `diff`, propose, commit and the revert path that removes untracked files (around the `git status --porcelain` calls near lines 635, 793, 969, and the revert near line 901). If a user commits `layout.json` anyway, or ignores nothing, these paths must filter `.mdl/layout.json` out of "dirty" and must never delete it on revert. + +## Files expected to change + +- `canvas/src/RelationshipEdge.tsx`: handle, path, labels. +- `canvas/src/edgeRouting.ts`: new, pure geometry. +- `canvas/src/ModelCanvas.tsx`: layout load and save, route commit, overlap nudge, undo. +- `canvas/src/exec.ts` or `canvas/src/layoutStore.ts`, `canvas/src/api.ts`, `canvas/src/App.tsx`, `canvas/src/TopBar.tsx` (Reset layout). +- `canvas/src/types.ts`: layout payload types. +- `packages/server/src/mdl_server/layout_store.py` (new), `app.py` (routes), `git_api.py` (path filter). +- The built bundle under `packages/server/src/mdl_server/static/` is committed. Run `cd canvas && npm ci && npm run build` and commit the regenerated assets. Expect hashed filenames to change. +- `README.md` or docs: one short paragraph on saved layout and the git-ignore behaviour. Changelog line. + +`vscode/` needs no change: the canvas webview loads the served page. + +## Tests + +Python (`packages/server/tests/test_layout_api.py`, new): + +- GET with no file returns empty maps. PUT then GET round-trips. Output is key-sorted with integer values. +- PUT drops ids not present in the model. Corrupt existing file is treated as empty and overwritten on the next PUT. +- PUT and DELETE return 403 with `read_only=True`. +- Layout writes do not change `commands.dir_fingerprint`, and `GET /api/model` returns an identical document before and after a PUT. +- With `layout.json` present, `mdl validate`, drift and every emitter produce byte-identical output to the same model without it. Use the demo or the erwin test fixture. +- `/api/export` output is identical with and without the file. +- `git_api` status is not dirty because of `layout.json`, and revert does not delete it. +- The auto-created `.mdl/.gitignore` exists after the first PUT inside a git repo and is not created outside one. + +Canvas (`vitest`): + +- `edgeRouting.ts`: grid snap, Shift bypass, magnet within 6 px, clamp out of a card, forward and backward edge cases, reset returns auto. +- `RelationshipEdge`: renders handle on hover or select, calls `onRouteChange` once on release (not per move), a sub-3 px press does not call it, double-click calls it with `null`. +- Restore: saved positions override auto-layout, a new entity is placed without overlap, no `fitView` when a saved layout exists, a 403 from the store does not surface an error. + +## Acceptance criteria + +1. On the erwin-imported model, an edge can be dragged horizontally with both ends staying attached, snaps to the grid, and cannot be dropped inside a card. +2. Double-click returns the edge to automatic routing. +3. Reload the page or restart `mdl serve`: card positions and edge routes are restored exactly. Switching the subject-area filter and back restores each view's own arrangement. +4. New entities created through MCP (Copilot) appear without overlapping existing cards and do not disturb restored positions. +5. `git status` in the model repo stays clean after a layout change. `mdl validate`, drift, export and all emitters give identical results with and without `layout.json`. +6. Re-layout can be undone within the session. Reset layout removes the saved view. +7. `npm run build` (includes `tsc --noEmit`), `vitest run`, the server pytest suite, and `ruff` all pass. Dragging an edge on a 100-entity model stays smooth (no per-move rebuild of all nodes and edges). + +## Out of scope for v1 + +- Free-form multi-bend waypoints or horizontal segment dragging for top and bottom ports. +- Saving viewport, selection, collapse state or the chosen layout mode. +- Sharing layout through git as a first-class feature (a `layout: shared` setting) and merge handling for it. +- Layout for the SME and catalog views. +- Importing erwin's own diagram coordinates. This is a good follow-up: the erwin file carries positions, which would make the first import look like the original diagram. Note it, do not build it here. + +## Open questions + +- Should `__all__` and per-area views be separate, as specified, or should filtered views reuse `__all__` positions and only fall back to auto-layout for entities missing from it? Separate views are simpler and match current behaviour; reuse looks more consistent. Default to separate unless it feels wrong in use. +- Team sharing: ignore by default (as specified) or commit by default? Defaulting to ignored avoids noisy diffs and any chance of layout entering review flows. Revisit after v1 if teams ask. \ No newline at end of file diff --git a/uv.lock b/uv.lock index 9e9d8b1..4ab7560 100644 --- a/uv.lock +++ b/uv.lock @@ -939,7 +939,7 @@ requires-dist = [ [[package]] name = "modelith-dbt" -version = "0.6.13" +version = "0.6.15" source = { editable = "." } dependencies = [ { name = "dbt-artifacts-parser" },