{"version":3,"sources":["../src/utils/handle-ids.ts","../src/utils/distribution-board-layout.ts","../src/utils/constants.ts","../src/utils/splice-positioning.ts","../src/utils/coupler-positioning.ts","../src/types/index.ts","../src/utils/port-position.ts","../src/utils/render-ports.ts","../src/utils/rack-mount-mode.ts","../src/utils/rack-layout.ts","../src/utils/resolve-node-dimensions.ts","../src/utils/initial-viewport.ts","../src/utils/zoom-lod.ts","../src/utils/fit-view-policy.ts","../src/components/use-diagram-viewport.ts","../src/utils/rack-device-types.ts","../src/utils/node-dimensions.ts","../src/utils/diagram-selection.ts","../src/utils/u-positioning.ts","../src/context/DiagramRackDropTargetContext.tsx","../src/components/use-diagram-rack-drag.ts","../src/utils/document-runtime-merge.ts","../src/utils/connection-rules.ts","../src/utils/power-connectors.ts","../src/utils/document-adapter.ts","../src/utils/endpoint-metadata.ts","../src/validation/connectors.ts","../src/core/connection-validation.ts","../src/utils/endpoint-kind.ts","../src/utils/device-port-spacing.ts","../src/utils/port-visuals.ts","../src/utils/device-port-layout.ts","../src/utils/device-port-faces.ts","../src/utils/diagram-connection.ts","../src/core/endpoint-occupancy.ts","../src/utils/diagram-logger.ts","../src/components/use-diagram-state.ts","../src/utils/diagram-image-options.ts","../src/components/use-diagram-flow-methods.ts","../src/context/DiagramMutationContext.tsx","../src/utils/graph-index.ts","../src/components/use-child-nodes.ts","../src/utils/status-colors.ts","../src/components/RackNode.tsx","../src/utils/a11y.ts","../src/components/FibreNode.tsx","../src/utils/device-image-layout.ts","../src/context/DiagramPortContext.tsx","../src/components/DeviceNode.tsx","../src/components/VerticalPDU.tsx","../src/components/DistributionBoardNode.tsx","../src/utils/fibre-colors.ts","../src/components/use-node-edges.ts","../src/components/SpliceNode.tsx","../src/components/SpliceTrayNode.tsx","../src/components/TubeNode.tsx","../src/components/CableNode.tsx","../src/components/MultiTubeCableNode.tsx","../src/utils/fibre-edge-color.ts","../src/components/CouplerNode.tsx","../src/components/PatchPanelNode.tsx","../src/components/ClosureNode.tsx","../src/components/FibreSplitNode.tsx","../src/components/diagram-node-types.tsx","../src/components/DiagramViewportTracker.tsx","../src/components/DiagramActionPanel.tsx","../src/components/NetworkDiagramDocumentBridge.tsx","../src/components/DiagramZoomLod.tsx","../src/utils/react-flow-theming.ts","../src/components/use-resolved-color-mode.ts","../src/components/FibreEdge.tsx","../src/components/PowerEdge.tsx","../src/components/SmoothStepFibreEdge.tsx","../src/components/StepFibreEdge.tsx","../src/components/ThickCableEdge.tsx","../src/components/DiagramErrorBoundary.tsx","../src/utils/edge-connection.ts","../src/utils/edge-data.ts","../src/core/connection-plan.ts","../src/utils/diagram-focus.ts","../src/utils/highlight-groups.ts","../src/utils/device-display.ts","../src/components/NetworkDiagram.tsx","../src/utils/fibre-flow-layout.ts","../src/components/FibreFlowMap.tsx","../src/components/otdr-theme.ts","../src/components/OtdrTraceStrip.tsx","../src/utils/placement-validation.ts","../src/utils/port-contract.ts","../src/utils/device-bays.ts","../src/utils/rack-schema.ts","../src/utils/splice-schema.ts","../src/utils/node-ids.ts","../src/utils/path-layout.ts","../src/utils/power-energization.ts","../src/utils/power-chain.ts","../src/utils/rack-geometry.ts","../src/components/use-node-connections.ts","../src/utils/template-schema.ts","../src/utils/template-parser.ts"],"names":["DIRECTIONAL_HANDLE_SUFFIX","FACE_ALIAS_HANDLE_SUFFIX","normalizeDirectionalHandleId","handleId","normalizeHandleId","isPowerPortType","portType","PDU_INLET_HANDLE_ID","BOARD_INCOMER_HANDLE_ID","POWER_INLET_HANDLE_IDS","isPowerInletHandleId","HEX_PAIR","RESERVED_SEGMENT_CHAR","encodeHandleSegment","segment","decodeHandleSegment","decoded","i","percentEscapeChar","ch","hex","baseCollidesWithGrammar","base","classifyHandleBase","escapeCollidingLabel","label","buildDevicePortBase","group","encodedGroup","buildPortHandleId","portBlockName","port","decodePortHandleId","delimiter","looksLikePowerHandleId","compact","applyDirectionalPortHandleId","direction","options","OUTLET_BASE","FIBER_TUBE_BASE","FIBER_BASE","TUBE_BASE","SIDE_PORT_BASE","SPLIT_IO_BASE","BREAKER_WAY_BASE","fiberTube","fiber","tube","sidePort","splitIo","breakerWay","alternateShapes","shape","parseHandleId","raw","directionMatch","afterDirection","faceMatch","parsed","alternates","buildFibreHandleId","buildTubeHandleId","buildSpliceHandleId","side","buildCouplerHandleId","index","buildSplitIoHandleId","io","buildBreakerWayHandleId","wayId","applyFaceAliasHandleId","face","sanitizeHandleIdForMatching","handlesMatch","a","b","findEdgesForPortHandle","edges","nodeId","matched","edge","getOppositeSideHandleId","encodeHandle","opts","out","decodeHandle","normalizeColumns","wayColumns","value","buildDistributionBoardLayout","ways","list","columns","rows","bodyHeight","wayRects","way","column","row","U_HEIGHT_PX","RACK_HEADER_HEIGHT","RACK_WIDTH_PX","HANDLE_EXTENSION_PX","HOLDER_HEIGHT_PX_SINGLE","HOLDER_HEIGHT_PX_RIBBON","HOLDER_HEIGHT_PX","TRAY_HEADER_HEIGHT","TRAY_BOTTOM_PADDING","HOLDERS_PER_TRAY","SPLICE_WIDTH_PX","SPLICE_HEIGHT_PX_SINGLE","SPLICE_HEIGHT_PX_RIBBON","SPLICE_HEIGHT_PX","TRAY_WIDTH_PX","TRAY_NODE_HEIGHT_SINGLE","TRAY_NODE_HEIGHT_RIBBON","TRAY_NODE_HEIGHT","getTrayNodeHeight","isRibbonMode","SPLICE_CENTER_X","snapToSplicePosition","splice","tray","newPosition","allNodes","holderHeight","spliceHeight","startY","relativeY","calculatedPosition","snappedPosition","isHolderPositionAvailable","availablePosition","findNextAvailableHolderPosition","baseOffset","snappedY","position","n","child","startFrom","pos","calculateSplicePositionFromNumber","holderPosition","calculateSplicePositionFromNumberWithData","spliceY","PANEL_HEADER_HEIGHT","PANEL_WIDTH_PX","PANEL_HEIGHT_PX","PANEL_PADDING","PANEL_TUBE_TRAY_SPACING","getCouplerDimensions","node","couplerFiberCount","normalizedFibreCount","portCount","handleSize","handleSpacing","padding","minWidth","minHeight","totalHandlesHeight","height","getTubeDimensions","fibersPerTube","fibers","borderWidth","totalHandlesWidth","getTrayDimensions","getNodeDimensions","calculateMaxChildRight","childNodes","maxRight","dims","childRight","calculateMaxChildBottom","maxBottom","childBottom","getPanelWidth","panelNode","getPanelHeight","calculatePanelDimensions","hasTubes","hasTrays","hasCouplers","width","tubeWidth","couplerWidth","contentWidth","sidePadding","CLOSURE_TUBE_TRAY_SPACING","CLOSURE_WIDTH_PX","CLOSURE_HEIGHT_PX","calculateClosureDimensions","closureNode","leftTubeWidth","rightTubeWidth","trayWidth","tubes","trays","trayPositions","t","avgTrayX","Width","isRecord","toNumber","normalizeSemanticPositionTokens","token","canonicalizeSideToken","hasCenterToken","tokens","resolveAxisOffsetPercent","primarySide","hasLeft","hasRight","hasTop","hasBottom","hasCenter","resolveSemanticEdgePortPosition","offsetPercent","resolveSemanticCompositePortPosition","hasFront","hasRear","xPercent","yPercent","parseSemanticPortPosition","normalizePortPositionInput","sideRaw","semanticPosition","offsetPx","widthPercent","heightPercent","DEFAULT_FRONT_GROUP_NAME","DEFAULT_REAR_GROUP_NAME","DEFAULT_POWER_GROUP_NAME","DISTRIBUTED_OFFSET_MIN_PERCENT","DISTRIBUTED_OFFSET_MAX_PERCENT","clampPercent","normalizePortAnchor","getNormalizedPortPosition","hasExplicitAbsoluteCoordinates","hasExplicitOffset","resolveRenderPortFace","resolveRenderPortGroupName","getEntrySortValue","entry","toDistributedOffsetPercent","count","buildDistributedOffsetMap","entries","distributedEntries","offsetPercentByIndex","entriesByAnchor","anchor","anchorEntries","sortedEntries","left","right","orderDifference","toPortPosition","normalizedPosition","distributedOffsetPercent","resolvedOffsetPercent","toPort","normalizePortBlockCollection","portBlocks","normalizedBlocks","block","portsByUnpositionedSide","ports","isGroupedPortBlockArray","isFlatDevicePortArray","buildPortBlocksFromPorts","devicePorts","bucketsByKey","indexedPorts","distributedOffsetsByFace","groupName","key","existingBucket","rearPorts","bucket","distributedOffsetByIndex","getResolvedPortBlocks","data","getFrontPortBlocks","getRearPortBlocks","countPortBlocks","total","hasFrontPorts","hasRearPorts","getDefaultDeviceFace","hasImage","url","inferRackMountModeFromFaces","nodeData","resolvePreferredFace","resolveSplitRackMountModeForDrop","dropRelativeX","rackWidthPx","rackFaceGapPx","explicitMountMode","inferredMountMode","preferredFace","splitMidpointX","DEFAULT_RACK_FACE_GAP_PX","MAX_RACK_U","DEFAULT_RACK_U_HEIGHT","resolveRackUHeight","RACK_DEVICE_SINGLE_FACE_GUTTER_PX","getRackLayoutMode","isSplitFaceRackLayout","getRackFaceGapPx","getRackFaceWidthPx","getRackUnitHeightPx","getRackTotalWidthPx","faceWidth","getRackNodePixelDimensions","uHeight","rackUnitHeightPx","applyRackPixelDimensionsToNodes","nodes","getRackMountMode","getVisibleFacesForMountMode","mountMode","sharesMountedFace","mountModeA","mountModeB","facesA","getRackDeviceXPosition","rackData","deviceData","getRackDeviceImageContainerWidthPx","effectiveRackWidth","getRackDeviceBodyWidthPx","DEFAULT_DEVICE_WIDTH","DEFAULT_DEVICE_HEIGHT","getCableDimensions","getMultiTubeCableDimensions","getFibreSplitDimensions","inputCount","outputCount","inputHandlesHeight","outputHandlesHeight","getClosureDimensions","getDeviceDimensions","getDistributionBoardDimensions","layout","resolveNodeDimensions","fallback","resolveDefaultNodeDimensions","prepareTopLevelNodesForBounds","computeInitialViewport","container","prepared","top","bottom","nodeWidth","nodeHeight","x","y","bounds","minZoom","maxZoom","getViewportForBounds","DIAGRAM_LABEL_LOD_ZOOM","DIAGRAM_ZOOM_LOD_CLASS","isBelowLabelLodZoom","zoom","threshold","readTransformZoom","transform","DIAGRAM_DEFAULT_FIT_PADDING","DIAGRAM_SMALLEST_LABEL_PX","DIAGRAM_MIN_READABLE_LABEL_PX","DIAGRAM_MAX_READABLE_ZOOM_FLOOR","DIAGRAM_MAX_READABLE_ZOOM_LIFT","positiveOr","computeReadableZoomFloor","smallestLabelPx","minReadableLabelPx","maxFloorZoom","maxLift","fittedZoom","readableZoom","resolveFitViewOptions","context","resolved","candidateReadableZoomFloor","readableZoomFloor","MIN_FIT_VIEWPORT_PX","INITIAL_FIT_SETTLE_MS","matchesViewport","DEFAULT_FIT_SETTLE_MS","hasMeasuredSize","useDiagramViewport","reactFlowNodes","latestNodesRef","reactFlowWrapper","reactFlowApiRef","initialViewport","fitViewOnInit","fitViewOptions","onViewportReady","onInit","onError","onlyRenderVisibleElements","effectiveFitViewOnInit","viewportReady","setViewportReady","useState","flowReady","setFlowReady","topLevelLayoutSignature","useMemo","fitPaddingDefault","resolvedFitViewOptions","fitParams","fitParamsRef","useRef","onViewportReadyRef","onInitRef","initializedFlowRef","didInitialViewportRef","lastFitSignatureRef","firstFitAtRef","lastFitContainerRef","lastAutomaticViewportRef","cameraIntentRef","userMovedCameraRef","wasDraggingRef","nodesDragging","restoreInFlightRef","lastViewportRef","recoveryViewportRef","pauseViewportTrackingRef","topLevelLayoutSignatureRef","viewportLifecycleGeneration","setViewportLifecycleGeneration","trackViewport","useCallback","viewport","automatic","handleViewportMoveStart","event","applyRestoredViewport","instance","config","applied","handleReactFlowInit","recoveryViewport","generation","applyComputedViewport","fitPadding","explicitMinZoom","paramMinZoom","paramMaxZoom","rect","consumerMinZoom","maxZoomForFit","useEffect","wrapper","observer","fittedAt","current","last","initialViewportMode","signature","cancelled","runFit","rafId","frames","cameraIntent","retry","didDefaultFitRef","resolvedFitViewOptionsRef","applyMeasuredFit","candidateIds","requested","ids","targets","candidates","mounted","element","timer","hasCaughtError","setHasCaughtError","handleBoundaryError","error","errorInfo","handleBoundaryReset","RACK_DEVICE_NODE_TYPES","RACK_ASSIGNABLE_NODE_TYPES","rackDeviceTypeSet","rackAssignableTypeSet","isRackDeviceNodeType","nodeType","isRackAssignableNodeType","isRackDeviceNode","isRackAssignableNode","getNodeRenderedWidth","getNodeRenderedHeight","getNodeRenderedSize","resolveNodeFace","setNodeFaceData","selectNodesById","selectedNodeId","changed","nextNodes","shouldSelect","asPositiveInteger","asRackUHeight","getDefaultUNumberingDirection","getRackUNumberingDirection","rack","convertUPositionForDirection","uPosition","rackUHeight","convertUPositionFromDirection","uPositionForTopY","deviceUHeight","physicalUPosition","logicalPosition","resolveOccupiedUPosition","device","explicit","derived","snapToUPosition","deviceParentId","calculatedUPosition","maxUPosition","snappedUPosition","isUPositionAvailable","findNextAvailableUPosition","currentUPosition","topLogicalUPosition","topPhysicalUPosition","snappedPhysicalUPosition","rackDevices","splitFaceLayout","deviceMountMode","otherDevice","otherUHeight","otherUPosition","newDeviceStart","newDeviceEnd","otherDeviceStart","otherDeviceEnd","otherMountMode","normalizedStart","clampedStart","u","calculateDevicePositionFromU","deviceHeight","deviceY","updateDeviceUPosition","newUPosition","normalizedPorts","validateAndSnapDevice","validUPosition","createRackDropTargetStore","listeners","rackId","listener","RackDropTargetContext","React","RackDropTargetProvider","useRackDropTargetStore","ref","noopSubscribe","useIsRackDropTarget","store","useContext","subscribe","getSnapshot","useSyncExternalStore","useDiagramRackDrag","reAssignable","nodesMapRef","commitNodes","rackDropTargetStore","getIntersectingNodes","getInternalNode","useReactFlow","fallbackStore","setDragOverRackId","getNodeById","id","getNodeParentId","getParentRackNode","parentId","parentNode","isSplitFaceRackNode","isSplitFaceRackDeviceNode","parentRack","getAbsoluteNodePosition","internal","getRackDropPoint","absolute","getNearestRackAtPoint","point","draggingNodeId","intersectingRacks","candidate","nearestRack","minDistance","rackPosition","distance","getRackConstraintSize","clampRackChildPosition","maxX","minY","maxY","handleNodeDragStart","_event","_node","handleNodeDrag","_nodes","dropPoint","nextRackId","handleNodeDragStop","absolutePosition","currentNodes","relativeToRack","clampedPosition","updatedNodes","currentNode","baseNode","splitFaceRack","nextMountMode","initialDeviceX","nextView","candidateNode","nodesForSnapping","snapped","graphContentEqual","prev","next","NODE_RUNTIME_FIELDS","EDGE_RUNTIME_FIELDS","restoreOmittedRuntimeFields","incoming","previous","fields","restored","previousFields","field","shallowPropertiesEqual","comparePositions","aKeys","bKeys","aFields","bFields","mergeRuntimeStateIntoNodes","previousNodes","previousNodesById","merged","previousNode","dimensions","mergeRuntimeStateIntoEdges","previousEdges","previousEdgesById","previousEdge","isFibreNode","isFibreConnection","sourceNode","targetNode","classifyFibreEndpoint","handle","ALLOWED_FIBER_PAIRS","isTubeHandleClass","evaluateFibreConnectionRule","sourceHandle","targetHandle","sourceIsRibbon","targetIsRibbon","sourceClass","targetClass","POWER_CONNECTORS","getPowerPortStyle","connectorType","connector","isHighPower","isGenericAc","cornerStyle","getConnectorConfig","isHighPowerConnector","PORT_TYPE_CONNECTOR","inferConnectorFromPortType","match","hasPduInlet","inletConnector","inletAmps","inletGender","gender","getPDUPortType","portIndex","getPduOutletConnectorType","portTypes","toOutletPhaseIndex","phases","PHASE_LABELS","getPduOutletPhaseLabel","getDeviceConnectorType","deviceName","name","MAX_POWER_PORTS","KNOWN_DIAGRAM_NODE_KINDS","CONTAINER_SUBTYPES","IMAGE_RENDER_KEYS","DIMENSION_RENDER_KEYS","GENERAL_RENDER_KEYS","UI_NODE_STATE_KEYS","EXCLUDED_CONFIG_KEYS","setOwn","target","clonePosition","clonePortPosition","normalized","toDiagramNodeKind","toLegacyNodeType","normalizedSubtype","subtype","toDiagramPlacement","placement","toDiagramRender","render","images","acc","toDiagramExternalRefs","refs","toDiagramConfig","extractUiState","previousUi","nodeState","resolvedFace","inferEndpointMedium","normalizedType","buildPduPortBlocks","powerPorts","_","toPortBasedEndpoints","endpoints","blocks","frontIds","endpoint","rearAliasBase","collectFrontEndpointIds","createSyntheticEndpoints","tubeIndex","fiberCount","tubeCount","tubeStartIndex","toDiagramEndpoints","ENDPOINT_SOURCE_KEYS","endpointSourceFingerprint","fingerprint","sameFingerprint","endpointDerivationCache","copyEndpoints","toDiagramEndpointsCached","cached","mergeEndpoints","previousEndpoints","nextEndpoints","previousById","mergeDocumentNode","nextNode","networkGraphToDiagramDocument","snapshot","previousDocument","aliasedIdsByNodeId","aliased","toEndpointId","nextEdge","endpointToPortType","endpointPortFields","connectedEndpointIds","type","endpointsToDevicePorts","endpointsToPortBlocks","grouped","existing","applyRenderToLegacyData","uiEntry","nextData","applyExternalRefsToLegacyData","buildEndpointIndex","byId","findEndpointRecord","endpointId","endpointById","effectiveEquipmentDeviceType","configured","resolveEndpointHandleId","role","deviceType","isPowerEndpoint","resolveNodePlacement","sanitizeDocumentNodes","rawNodes","seenIds","isEdgeEnd","diagramDocumentToNetworkGraph","document","input","documentNodes","rawEdges","documentEdges","nodesById","endpointsByNodeId","connectedByNodeId","markConnected","record","set","uiNodeState","noConnections","legacyType","connected","rearEndpoints","frontEndpoints","producedNodeIds","danglingEdgeIds","handleMetadataCache","warnedDuplicateHandleIds","warnDuplicateHandleId","normalizeCompatibilityToken","uniqueTokens","values","inferPowerCompatibilityToken","normalizedValues","inferNetworkCompatibilityToken","resolveEndpointCompatibilityKeys","connectorToken","mediumToken","fallbackKind","getFallbackPduPortBlocks","powerPortCount","defaultConnector","getPduPortBlocks","buildHandleMetadataMap","metadata","outletPosition","addBlocks","trackPosition","inferHandleKind","lowerHandle","isStandardNetworkHandle","isCustomPortName","PDU_OUTLET_HANDLE","inferPduOutletConnectorType","resolveOutletPosition","normalizedHandle","handleMetadata","inferHandleCompatibility","kind","POWER_DISTRIBUTION_NODE_TYPES","isPowerOutletPortType","resolvePowerEndpointRole","getPowerEndpointRole","getEndpointMetadata","normalizedHandleWithDirection","parsedHandle","explicitFace","IEC_60320_INLETS","IEC_60320_CONNECTORS","IEC_60320_MATES","getConnectorRole","upper","isValidIecMate","inlet","accepted","hasCompatibilityIntersection","sourceCompatibility","targetCompatibility","targetSet","POWER_SOURCE_NODE_TYPES","powerSourceNodeTypeSet","isPowerSourceNodeType","CIRCUIT_TERMINATION_REASON","PDU_INLET_FEED_REASON","WAY_TO_WAY_REASON","OUTLET_TO_OUTLET_REASON","evaluatePowerConnectionSemantics","sourceMetadata","targetMetadata","sourceRole","targetRole","roles","has","other","far","evaluateCordMate","outlet","deviceInlet","cord","createNodeMap","defaultValidateConnection","params","ctx","fiberConnection","sourceKind","targetKind","semantics","isPowerConnection","hasPowerSourceEndpoint","sourceOccupants","findEndpointOccupants","targetOccupants","sourceHandleAlreadyConnected","targetHandleAlreadyConnected","validateNetworkConnection","fn","getHandleSide","computeEdgeZIndex","getVisibleFaces","resolveEndpointVisibility","visibleFaces","handleLc","isPowerHandle","isNetworkHandle","targetVisibility","sourceVisibility","getEndpointConnectionKind","isPowerEndpointHandle","isPowerEdgeEndpoints","clampNumber","min","max","resolvePortSpacingDimensions","centers","baseDimensions","imageBacked","minHorizontalGap","minVerticalGap","rowTolerance","columnTolerance","candidateIndex","deltaX","deltaY","horizontalPaddingPx","verticalPaddingPx","maxWidth","maxHeight","STANDARD_PORT_VISUAL_DIMENSIONS","buildPortVisualSearchText","resolvePortVisualKind","text","getStandardPortVisualDimensions","inputOrKind","DISCONNECTED_PORT_COLOR","DISCONNECTED_PORT_BORDER_COLOR","DEFAULT_CONNECTED_PORT_COLOR","FILL_BACKGROUND_SIZE","DEVICE_FACE_BORDER_PX","CONNECTED_CABLE_COLORS","isPowerPort","resolvePowerConnectorType","getHandleId","getConnectedPortColor","getPortColor","resolvePortPosition","getPortBlocksSignature","portsSignature","positionSignature","getBaseHandleStyle","portWidth","portHeight","portColor","borderColor","enforceMinSize","borderWidthPx","getGenericPortDimensions","faceRegionWidth","isServicePort","getServicePortDimensions","getUsbPortDimensions","resolvePortVisualKindForBlock","getAbsoluteImagePortDimensions","absoluteCoordinateFrame","authoredWidth","authoredHeight","standardDimensions","getSfpHandleHitExpansion","Position","getPortHandleHitExpansion","hasAbsolutePortCoordinates","hasResolvableCustomPortPlacement","getAbsolutePortCenter","frameWidth","frameHeight","frameOffsetX","frameOffsetY","getResolvedGenericPortDimensions","absolutePortCenters","center","mapPortSideToPosition","inferSideFromAbsolutePercent","distances","getCustomHandlePlacement","absoluteCenterYOverridePx","absoluteSideOverride","minCenterX","maxCenterX","minCenterY","maxCenterY","unclampedCenterX","unclampedCenterY","centerX","centerY","rawCenterX","rawCenterY","getDistributedRowCenterX","rowIndex","rowCount","getLabelClearSinglePortCenterX","rightPadding","getAlternatingTopBottomPlacement","topRow","topCount","bottomCount","getAlternatingTopStackPlacement","topPaddingPx","rowGapPx","maxTopPx","secondRowTopPx","topPx","getFrontPowerHandlePlacement","powerStyle","topValue","getAutomaticPowerPortTopPx","connectorHeight","lastTop","legacyTop","paddingPx","minTop","maxTop","getFrontHandlePlacement","leftPx","getRearHandlePlacement","normalizedDeviceType","getHandleRoles","isPower","_isConnected","getDevicePortVisibleFaces","isSwitchLikeNode","normalizeSwitchPortConnection","connection","isPatchPanelLikeNode","collectPortMounts","table","primary","portMountDeviceType","getDevicePortMounts","mountFace","mounts","emitted","onFront","onRear","resolveMountedHandle","slot","sibling","resolvableMountedHandle","isMountedPortHandle","getPatchPanelPortFaces","rewritePatchPanelHandle","faces","normalizePatchPanelPortConnection","sourceFaces","targetFaces","mountsCache","mountsForNode","canonicalFor","occupancyHandleId","canonical","wanted","faceTag","mount","endpointKey","isUnboundedEndpoint","ignoreEdgeId","occupants","classifyConnectionPair","power","fibre","cableButton","sourceIsNetworkEndpoint","targetIsNetworkEndpoint","switchNetwork","DEV_WARNING_PREFIX","isDevEnvironment","warnDev","message","details","useDiagramState","initialNodes","initialEdges","externalOnNodesChange","externalOnEdgesChange","debug","validateAndFilterEdges","edgesToValidate","nodesForValidation","seenEndpoints","validEdges","removedEdges","sourceKey","targetKey","existingSourceEdge","existingTargetEdge","isControlled","partialControlledWarnedRef","hasNodes","hasEdges","initialNodesForValidation","initialEdgesRaw","validatedInitialEdges","seedNodes","setNodes","useNodesState","reactFlowEdges","setEdges","onEdgesChange","useEdgesState","latestEdgesRef","edgesByTargetRef","map","validated","commitEdges","nextEdges","clampNumericOption","normalizeToImageOptions","useDiagramFlowMethods","onFlowMethods","colorMode","toObject","setViewport","getNodes","getEdges","fitView","commitNodesRef","commitEdgesRef","fitViewOptionsRef","isCapturingImage","setIsCapturingImage","captureDepthRef","isCapturingRef","beginImageCapture","outermost","flushSync","endImageCapture","flowMethodsValue","toPng","currentFitView","currentGetNodes","configuredOptions","requestedNodes","_unused","callOptions","mergedOptions","resolvedOptions","isDownloading","setIsDownloading","onDownload","dataUrl","link","noop","defaultValue","DiagramMutationContext","DiagramMutationProvider","useDiagramMutation","edgeIndexes","childIndexes","EMPTY_EDGES","EMPTY_NODES","append","indexEdges","getIncidentEdges","getIncomingEdges","getChildNodes","childNodesShallowEqual","useChildNodes","useStore","state","getStatusColorHex","status","getStatusColor","RackNodeComponent","selected","onFaceChange","onRackFaceChange","isDragOver","customHeaderText","safeUHeight","effectiveWidth","rackDimensions","faceGap","splitRackFaceStyle","updateNodeData","updateNodes","dataShowUMarkers","showUMarkers","setShowUMarkers","syncedShowUMarkers","setSyncedShowUMarkers","toggleUNumberingDirection","newDirection","toggleFace","renderUPositions","positions","yPos","displayNumber","jsx","jsxs","e","RackNode","toText","buildPortAriaLabel","suffixes","isActivationKey","activateOnEnterOrSpace","activate","FibreNodeInner","Handle","FibreNode","normalizePositionToken","rawToken","numericValue","normalizeBackgroundPosition","resolveAxisOffset","containerSize","renderedSize","resolveDeviceImageBackgroundSize","imageFit","stretchToFit","resolveDeviceImageFrame","containerWidth","containerHeight","naturalSize","backgroundSize","backgroundPosition","scale","xToken","yToken","DiagramPortContext","DiagramPortProvider","useDiagramPortInteraction","isDiagramPortHandleHighlighted","highlightedPortHandles","EMPTY_KEYS","createHighlightStore","keys","identical","DiagramPortHighlightStoreContext","DiagramBayHighlightStoreContext","useStableHighlightStore","DiagramPortHighlightStoreProvider","children","portStore","bayStore","highlightKey","useDiagramPortHandleHighlighted","DiagramPortHighlightSync","useDiagramBayHighlighted","bayId","DiagramBayHighlightSync","highlightedBays","DevicePrimaryPortHandle","renderedHandleId","ghost","portClickEnabled","handleClassName","handleStyle","ghostHandleStyle","title","ariaLabel","onPortClick","portLabel","isHighlighted","combinedHandleClassName","powerDisplayLabel","trimmed","interactive","onKeyDown","DEFAULT_BAY_WIDTH_PX","DEFAULT_BAY_HEIGHT_PX","MIN_BAY_SIZE_PX","resolveBayFace","bay","resolveBayFrame","frame","resolveBayRect","faceOffsetPx","hasWidth","hasHeight","DeviceBayMarker","slotRects","bayClickEnabled","onBayClick","clickable","className","style","GHOST_PARK_OFFSET_PX","GHOST_PARKED_HANDLE_STYLE","URL_SCHEME_PATTERN","SAFE_DATA_IMAGE_URL_PATTERN","useIsomorphicLayoutEffect","imageNaturalSizeCache","normalizeDeviceType","isRelativeUrl","sanitizeDeviceImageUrl","parsedUrl","protocol","hostname","toCssUrlValue","resolveDeviceImageObjectFit","loadImageNaturalSize","pending","resolve","image","size","useImageNaturalSize","setNaturalSize","resolvedSize","DeviceNodeComponent","updateNodeInternals","useUpdateNodeInternals","parentRackData","parent","isRackMounted","rackGeometryData","manufacturer","frontImageUrl","rearImageUrl","imagePosition","imageRepeat","showDeviceImages","resolvedBackgroundSize","safeFrontImageUrl","safeRearImageUrl","frontImageNaturalSize","rearImageNaturalSize","currentFace","showSplitBothFaces","rightFaceOffsetPx","imageContainerWidth","frontImageFrame","rearImageFrame","activeImageFrame","hasVisibleFaceImage","useRackImageLabelBadge","activeFacePortBlocks","activeFaceNonPowerPorts","shouldHideRackImageLabelBadge","handleLayoutSignature","resolveFaceBackgroundStyle","safeBackgroundImageUrl","frontBackgroundStyle","rearBackgroundStyle","activeBackgroundStyle","resolvedImageObjectFit","rackDeviceWidth","rackDeviceLeft","fullWidthPx","halfWidthPx","isOneUServer","statusColor","deviceContainerStyle","imageBadgeContentStyle","baseStyle","indicatorReservePx","labelInsetPx","leftGutterPx","rightGutterPx","placeOnRight","frontHandleIds","rearHandleIds","resolveRenderedHandleId","renderHandlesForPortBlocks","handles","parkGhostHandles","standardPortDimensions","hasImageBackedAbsolutePorts","totalPortsOnFace","shouldLineUpDenseSwitchPortsAgainstTop","automaticallyPlacedPorts","automaticDataPortCount","automaticPowerPortCount","automaticDataPortIndex","automaticPowerPortIndex","portBlock","hasCustomPlacement","automaticPortIndex","automaticPortCount","visualKind","actualPortWidth","actualPortHeight","usbPortDimensions","authoredPortDimensions","servicePortDimensions","defaultPlacement","customPlacement","hitExpansion","hitAreaStyle","translatedHandleStyle","isPatchPanelStrip","secondaryHandleStyle","secondaryTitle","renderedHandles","visibleFaceSet","nodeBays","bayState","renderedBays","markers","slotRect","Fragment","DeviceNode","PDU_HEADER_HEIGHT","PDU_BOTTOM_PADDING","PDU_TOP_PADDING","PDU_BORDER_WIDTH","INTER_PORT_GAP_PX","INTER_GROUP_GAP_PX","MGMT_UNIT_HEIGHT_PX","inferClosestSideFromPercent","normalizeConnectorType","resolvePortConnectorType","fromConnector","toPduPortGroups","frontPorts","getConnectorSequence","buildRenderablePorts","boundedPowerPorts","customGroups","connectorSequence","flatIndex","fallbackType","getPortRenderHeightPx","getMgmtInsertionSlot","mgmtUnit","totalPorts","buildLayoutStackItems","mgmtInsertionSlot","items","includeMgmt","getGapBetweenItems","getRequiredBodyHeightPx","stackHeight","buildAutoLayout","baseGaps","totalItemHeights","item","totalBaseGaps","gap","requiredStackHeight","drawableHeight","startOffset","adjustedGaps","extraHeight","weights","totalWeight","weight","portCenters","mgmtCenterY","cursor","resolveCustomPlacement","fallbackCenterY","widthPx","handleWidth","handleHeight","sourcePosition","resolvedSide","resolvedPosition","INLET_HANDLE_HEIGHT_PX","INLET_HANDLE_MIN_WIDTH_PX","readOptionalString","readOptionalNumber","resolvePduInlet","amps","genderRaw","feedRaw","feedType","voltage","renderInletHandle","renderMgmtUnit","yCenter","VerticalPDUComponent","heightCm","mgmt_unit","safePowerPorts","requestedHeightPx","minimumBodyHeightPx","bodyWidth","heightPx","autoLayout","VerticalPDU","WAY_MARKER_WIDTH_PX","WAY_MARKER_HEIGHT_PX","HANDLE_SIZE_PX","BOARD_BADGE_BY_TYPE","resolveBoardBadge","boardType","LEVER_POSITION_BY_STATE","normalizeSwitchState","BreakerWayRow","switchState","markerClassName","rating","detail","titleDetail","DistributionBoardNodeComponent","wayState","badge","incomerRating","DistributionBoardNode","FIBRE_COLORS_12","baseColorFor","normalizedId","isStriped","BLACK_BASE_COLORS","fibreTracerColorFor","getFibrePaint","overrides","striped","backgroundImage","fibreSolidOrStriped","paint","getFibreColor","getRibbonColors","edgesShallowEqual","useNodeEdges","SpliceNodeInner","lossDb","mode","ribbonFiberIds","findSideEdge","leftEdge","rightEdge","leftColor","rightColor","leftRibbonIds","rightRibbonIds","leftRibbonColors","rightRibbonColors","bothConnected","colorInfo","c","SpliceNode","SpliceTrayNodeInner","showHolderMarkers","setShowHolderMarkers","lastHeightRef","isRibbonTray","trayHeight","renderHolderPositions","holderHeightPx","SpliceTrayNode","TubeNodeInner","startOn","isRibbonTube","tubeConnectionHandleId","connectedTubeEdge","connectedTubeColor","defaultTubeBorderColor","tubeBorderColor","tubeConnectionPosition","isSideHandle","tubeHandleWidth","tubeHandleHeight","sideInsetShadow","tubeHandleBackground","connectionHandleStyle","fiberHandles","innerWidth","startLeft","handlePosition","reverseColors","fiberIdx","fiberHandleId","connectedEdge","connectedIsRibbon","edgeColor","edgeStriped","fiberPaint","TubeNode","CableNodeInner","providedFibreCount","cableId","color","rotation","containerRef","rotationDegrees","innerPaddingTop","primaryText","secondaryText","defaultCableColor","cableColor","getHandlePosition","rotationDeg","renderHandles","baseColor","handleBackground","buttonHandleId","buttonHandleSize","buttonHandleStyle","buttonHandlePosition","containerStyle","contentStyle","CableNode","MultiTubeCableNodeInner","rawTubeStartIndex","totalFibres","firstTubeColor","tubeIdx","tubePaint","tubeHandleId","MultiTubeCableNode","DEFAULT_COUPLER_OUTPUT_COLOR","normalizeEndpointHandle","edgeTouchesHandle","isCouplerRightHandle","getCouplerSiblingHandle","getOppositeSpliceHandle","colorFromFibreEndpoint","fiberNum","tubeNum","deriveFibreColorForConnection","sourceType","targetType","colorFromSpliceOppositeEdge","excludeEdgeId","oppositeHandle","colorFromCouplerPort","passThroughColor","leftHandle","resolveFibreEdgePaint","fromEndpoints","fromCouplerSource","fromCouplerTarget","CouplerNodeInner","portLabelsLeft","portLabelsRight","leftHandleId","labels","displayLabel","CouplerNode","PatchPanelNodeInner","header","dataWidth","dataHeight","lastDimensionsRef","dynamicDimensions","panelWidth","panelHeight","headerHeight","displayHeader","PatchPanelNode","ClosureNodeInner","closureWidth","closureHeight","semicircleRadius","ClosureNode","FibreSplitNodeInner","splitterType","inputCountNum","outputCountNum","getHandleColor","normalizedHandleId","renderInputHandles","startTop","handleBaseId","renderOutputHandles","leftEdgeHeight","leftTop","leftBottom","rightTop","rightBottom","FibreSplitNode","NodeWithDataType","wrapperRef","setDataTypeRef","attempts","RackNodeType","props","createNodeTypes","DiagramViewportTracker","onViewportChange","DiagramActionPanel","buttons","onToggleFace","onDeRack","onZoomToEdit","flowToScreenPosition","wrapperRect","setWrapperRect","useLayoutEffect","el","update","r","topCenterAnchor","bottomCenterAnchor","NetworkDiagramDocumentBridge","Inner","initialDocument","onDocumentChange","onNodesChange","rest","usesDocumentModel","isControlledDocument","uncontrolledDocument","setUncontrolledDocument","activeDocument","documentOverrideWarnedRef","suppliedNodes","suppliedEdges","ignored","documentGraph","mergeGraphWithRuntimeState","graph","latestGraphRef","latestDocumentRef","lastEmittedGraphRef","graphForRender","emitDocumentChange","nextGraph","isDirty","nextDocument","handleDocumentNodesChange","handleDocumentEdgesChange","DiagramZoomLod","wrapperClassName","belowDetail","resolveSystemColorMode","getReactFlowThemeClassName","themeClass","getReactFlowBackgroundColor","getReactFlowWrapperStyle","customStyle","LIGHT_QUERY","subscribeToSystemScheme","onChange","query","readSystemScheme","useResolvedColorMode","system","renderRibbonEdge","path","labelX","labelY","interactionWidth","ribbonColors","numColors","stripeWidth","highlightEnabled","baseStrokeWidth","highlightWidth","highlightColor","BaseEdge","currentStrokeWidth","EdgeLabelRenderer","FibreEdgeComponent","sourceX","sourceY","targetX","targetY","targetPosition","_className","edgePath","getBezierPath","bandwidth","length","isRibbon","highlight","highlightMaxWidth","baseEdgeStyle","FibreEdge","HIGHLIGHT_LAYER_OPACITY","HIGHLIGHT_BASE_OPACITY","readNumeric","PowerEdgeComponent","markerEnd","customStrokeWidth","customStrokeDasharray","resolvedOpacity","getEdgeStyle","opacity","strokeWidth","strokeDasharray","energized","flowDirection","energizedClassName","instanceId","arrowMarkerId","applyEnergizationStyle","PowerEdge","renderRibbonSmoothStepEdge","getSmoothStepPath","SmoothStepFibreEdgeComponent","edgeStyle","ReactFlowSmoothStepEdge","SmoothStepFibreEdge","getStepPath","_targetPosition","isHorizontalFirst","midX","midY","renderRibbonStepEdge","StepFibreEdgeInner","_props","StepFibreEdge","ThickCableEdgeInner","_selected","ThickCableEdge","DiagramErrorBoundary","Component","edgeIdSequence","createEdgeWithMetadata","edgeType","selectEdgeZIndex","zIndex","replaceEdge","findBy","matchOn","existingEdge","filtered","newEdge","isSpliceRibbonMode","spliceNode","deriveEdgeData","isButtonHandleConnection","resolveFibreColor","isPduSource","edgeData","isRibbonSpliceTarget","isRibbonSpliceSource","sourceNodeData","planConnection","sourceMounts","targetMounts","sourceWrongClass","targetWrongClass","swappedSource","swappedTarget","swap","classification","result","first","DEFAULT_DIAGRAM_DIM_OPACITY","hasDiagramFocus","isNodeInDiagramFocus","isEdgeInDiagramFocus","edgeId","clampDimOpacity","applyDiagramFocusToNodes","dimOpacity","nodeIds","applyDiagramFocusToEdges","edgeIds","expandEdges","deriveHighlightGroupEdgePaint","groups","collectUnion","select","seen","collectHighlightGroupNodeIds","collectHighlightGroupEdgeIds","applyHighlightGroupsToEdges","edgePaint","DEVICE_NODE_TYPES","isDeviceDiagramNode","mergedDeviceDataCache","shallowFieldsEqual","hasSameNodePropertiesExceptData","nextKeys","nextFields","before","after","applyDeviceDisplayToNodes","stabilized","isAnyNodeDragging","defaultEdgeTypes","DEFAULT_EMPTY_STATE","EMPTY_STATE_DELAY_MS","DiagramRenderBoundary","enabled","onReset","NetworkDiagramInner","onNodeClick","onEdgeClick","onPaneClick","externalNodeTypes","externalEdgeTypes","externalOnConnect","externalOnEdgeUpdate","externalIsValidConnection","externalConnectionMode","alignRacksToBottom","showDownloadButton","downloadButtonPosition","downloadButtonText","downloadButtonStyle","useSmoothstepEdgesForTubes","readOnly","interaction","actionPanel","highlightedNodeIds","highlightedEdgeIds","dimUnhighlighted","connectEdgesToHighlightedNodes","highlightGroups","connectionRadius","edgeInteractionWidth","onConnectRejected","hideUntilViewportReady","emptyState","errorBoundary","errorFallback","isMobile","setIsMobile","coarseQuery","checkMobile","isReadOnly","interactionConfig","actionPanelConfig","onFaceChangeRef","onRackFaceChangeRef","onFaceChangeStable","onRackFaceChangeStable","onConnectRejectedRef","notifyConnectRejected","reason","phase","pendingConnectRejectionRef","reconnectingRef","flushPendingConnectRejection","builtInNodeTypes","mergedNodeTypes","mergedEdgeTypes","UNSTABLE_TYPES_CHANGE_THRESHOLD","lastNodeTypesRef","lastEdgeTypesRef","nodeTypesChangeCountRef","edgeTypesChangeCountRef","unstableTypesWarnedRef","nodeTypesChanged","edgeTypesChanged","nodeTypesUnstable","edgeTypesUnstable","defaultEdgeOptions","proOptions","validateConnection","notifiedCorrectionsRef","correctionSignature","commitPersistentNodeCorrections","proposed","signatures","proposedById","presentIds","proposedNode","racks","rackBottoms","minBottom","baselineBottom","currentBottom","delta","corrected","diagramMutationValue","dataPatch","updater","deriveFibreColorFromEndpoint","inputColor","fromHandle","resolveFibreEdgeColor","edgesZIndexRef","nextZIndex","hasChanges","updated","newZIndex","uSlotKeysRef","parentPanel","parentTray","missingPosition","slotKey","slotChanged","originalNode","handleNodesChange","changes","isDimensionsOnly","change","holderPositionUpdates","uPositionUpdates","parentExtentIds","updatedChanges","parentClosure","snappedResult","applyNodeChanges","updatedNode","handleIsValidConnection","fiberRuleResult","planEdgeType","planEdgeData","handleConnect","plan","evicted","newEdges","setAnimatingEdges","edgeAnimationTimersRef","handleEdgeUpdate","oldEdge","newConnection","keepHandle","planned","previousNodeId","nextSourceHandle","nextTargetHandle","updateNodeSelection","toggleNodeFace","isRack","childIds","notify","childId","childIdSet","handleNodeClick","isCommandClick","selectedTarget","setSelectedNode","handleNodeDoubleClick","handleEdgeClick","handlePaneClick","selectedNode","animatingEdges","timers","handleBeforeDelete","deletableEdges","isTubeHandle","handleConnectStart","handleType","handleConnectEnd","connectionState","handleEdgeUpdateStart","handleEdgeUpdateEnd","_edge","_handleType","toggleSelectedFace","deRackSelectedDevice","edgesWithAnimation","faceHidden","nextClassName","hidden","highlightGroupNodeIds","highlightGroupEdgeIds","effectiveHighlightedNodeIds","effectiveHighlightedEdgeIds","diagramFocusOptions","nodesWithDeviceDisplayRef","nodesWithDeviceDisplay","nodesWithDiagramFocus","onPortClickRef","stableOnPortClick","payload","onBayClickRef","stableOnBayClick","diagramPortContextValue","edgesWithHighlightGroups","edgesWithDiagramFocus","edgesForRender","selectedNodeForActions","selectedActionButtons","supportsRearToggle","faceToggleReachable","showRearToggle","showDeRack","showZoomToEdit","zoomToEditSelectedRack","editingZoom","resolvedColorMode","flowClassName","shouldHideUntilReady","wrapperStyle","connectHandler","edgeUpdateHandler","connectionValidator","handleReactFlowNodesChange","handleReactFlowEdgesChange","filteredChanges","edgeToRemove","applyEdgeChanges","zeroSizeWarnedRef","emptyStateContent","isEmpty","emptyStateDeferralElapsed","setEmptyStateDeferralElapsed","showEmptyState","resolvedWrapperStyle","ReactFlow","ConnectionMode","Background","Controls","MiniMap","Panel","NetworkDiagram","ReactFlowProvider","MAX_FIBERS_PER_TUBE","MAX_TUBES","DEFAULT_FIBERS_PER_TUBE","fibreStrandKey","computeUsedFibreStrands","closures","used","add","closure","emitClampWarning","cable","what","resolveGrouping","warn","warnClamp","requestedTubes","requestedPerTube","colorIndexFor","grouping","fiberStartIndex","fiberIndex","offset","getFibreFlowStrandColorIndex","buildFibreFlowCableLayout","rowHeight","tubeGap","usedKeys","tubeLabels","indicators","realTubes","continuousFiberNumbers","totalFibers","currentY","tubeOffset","tubeColor","tubeTop","visibleInTube","totalInTube","fiberOffset","rowTubeIndex","colorIndex","hiddenCount","EMPTY_CLOSURES","EMPTY_CIRCUITS","FibreFlowInstanceContext","buildFibreFlowElementId","uid","suffix","buildFibreFlowGradientId","isFibreFlowNode","isFibreFlowEdgeType","hasEdgeEndpoints","inferFibreFlowEdgeType","isFibreFlowFibreHandle","edgeUsesHandle","normalizeFibreFlowEdges","handleIdForEndpoint","getEndpointPaint","formatDistanceLabel","distanceMeters","distanceLabel","buildFibreFlowStripeDasharray","needsStripeFirstHalf","needsStripeSecondHalf","firstHalfDashPattern","secondHalfDashPattern","halfLength","approximatePathLength","FibreFlowEdge","lossText","labelText","sourceColor","targetColor","sourceStriped","targetStriped","isDimmed","circuitId","circuitLabel","showCircuitId","needsGradient","dx","dy","totalDistance","labelDx","labelDy","labelDistance","gradientStopPercent","gradientId","pathId","stripePathId","needsStripeFull","dashGapPair","numPairsWithBuffer","secondHalfNumPairs","hasCircuitId","labelWidth","labelHeight","FibreFlowCableLinkEdge","FibreFlowCableNode","columnWidth","showTubeLabels","showFiberNumbers","isLightMode","jacketWidth","labelOffset","panelInset","jacketColor","effectiveShowTubeLabels","hasGroups","tubeExtentFor","tubeRows","indicator","tops","panelLeft","panelRight","cableRunHandleStyle","extent","tubeHeight","tubeBottom","nextTube","hasGapAfter","_tubeIdx","fibreGroupName","numberPosition","FibreFlowClosureNode","buildFlowNodes","closureGap","cableColumnWidth","spliceAreaWidth","hideUnusedFibres","usedByCable","leftLayout","rightLayout","closureId","leftCableId","rightCableId","headerLabel","leftOffsetY","rightOffsetY","buildFlowEdges","circuits","highlightedCircuitId","circuitColorMap","circuitLabelMap","lastCableById","circuit","circuitColor","FibreFlowMapInner","resetKey","layoutNodes","layoutEdges","resetSignature","prevResetSignatureRef","prevResetKeyRef","prevClosuresRef","prevCircuitsRef","wastedAllocWarnedRef","getHandleFibreInfo","isValidConnection","isSourceFibreHandle","isTargetFibreHandle","onConnect","isCableRun","sourceIsFibre","targetIsFibre","sourceInfo","targetInfo","eds","pruned","sourceTaken","targetTaken","addEdge","onEdgeUpdate","withoutOld","handleEdgesChange","nodeTypes","edgeTypes","handleFlowMethods","methods","FibreFlowMap","OTDR_COLOR_TOKENS","TOKEN_KEYS","resolveMode","otdrTraceStripPalette","otdrTraceStripCssPalette","VIEW_WIDTH","MARGIN","COMPACT_WIDTH_PX","LABEL_CLEARANCE_PX","formatOtdrDistance","metres","kindOf","markerForKind","colors","atFault","half","arm","LEGEND_ORDER","LEGEND_LABEL","otdrEventKindLabel","eventAccessibleName","OtdrTraceStrip","events","trace","focusRangeM","lengthM","totalLossDb","wavelengthNm","directionLabel","faultDistanceM","selectedEventIndex","density","onEventClick","faultM","svgRef","measured","setMeasured","measure","fontScale","pxPerUnit","ordered","axisEndM","maxEvent","traceEnd","cumulativeSegments","hasSlopes","running","previousM","sectionRise","lastMeasuredEventIndex","cumulativeTraceTotal","cumulativeTotal","cumulativeAllTotal","lossScaleMax","fibreY","lossTop","lossBottom","xFor","yForLoss","loss","cumulativePath","parts","measuredEndM","traceClip","stepM","levelsDb","startM","sampleCount","tracePath","minDb","maxDb","v","span","yFor","db","buckets","perBucket","from","to","lo","hi","xLeft","xRight","clipNote","visibleClipNote","traceSourceNote","labelVisible","visible","lastShown","clearance","focus","inFocus","caption","click","svg","pointerX","pointerY","targetRadiusX","targetRadiusY","nearestIndex","nearestDistance","isSelected","beyondFault","present","cy","glyphX","textX","validatePlacements","checkOutOfBounds","checkConflicts","conflicts","outOfBounds","boundsCheck","itemA","j","itemB","conflictCheck","normalizeFaceInput","CONNECTOR_TYPES","resolveDefaultFace","resolveDefaultGroup","resolveDevicePortFace","resolveDevicePortGroup","normalizeDevicePorts","order","normalizePortBlocks","rawPort","resolveNodeDevicePorts","flatPorts","frontFromPorts","rearFromGroups","blockPorts","flattenDevicePortsFromGroups","portGroups","rearPortGroups","appendFace","buildDevicePortGroupsFromPorts","listPossiblePortHandleIds","baseHandle","describePortHandle","sanitize","normalizedRequestedHandle","findInBlocks","validatePortHandleOnNodeData","resolveDevicePortBlocksFromFlatPorts","isDeviceFace","isPortPosition","readBayKind","readDeviceBaySlot","readDeviceBay","bayKind","slots","readDeviceBays","bays","DevicePlacementError","deviceId","conflictWith","issues","validateRackDevicePlacements","_options","deviceEnd","deviceA","deviceB","deviceAStart","deviceAEnd","deviceBStart","deviceBEnd","createDeviceNode","deviceTypeMapping","createRackNode","buildNodesFromRackConfig","conflictPolicy","deviceNodes","validation","errors","conflict","bound","firstConflict","firstBound","rackNode","siblingNodes","probe","placedDevice","createRackConfigFromNodes","rackNodes","devices","leftU","rightU","deviceNode","addDeviceToRack","removeDeviceFromRack","SplicePlacementError","trayId","spliceId","holder","validateSplicePlacements","spliceA","spliceB","createSpliceNode","createTrayNode","buildNodesFromSpliceConfig","spliceNodes","trayNode","resolvedHolder","placeholderSplice","allNodesForConflictCheck","adjustedSplice","createSpliceConfigFromNodes","trayNodes","splices","updateSpliceHolderPosition","newHolder","addSpliceToTray","removeSpliceFromTray","RACK_NODE_ID_PREFIX","DEVICE_NODE_ID_PREFIX","buildRackNodeId","buildDeviceNodeId","parseNodeId","EQUIPMENT_NODE_TYPES","CONTAINER_NODE_TYPES","getNodeKind","isRackNode","isDeviceNode","buildPathLayoutGraph","steps","spacing","origin","step","POWER_SOURCE_TYPE_SET","isPowerEdge","isPowerSourceNode","isInletEndpoint","powerConsumerOf","nodeById","wayIdOfHandle","computeEnergization","powerEdges","edgesByNode","feedsByConsumer","consumer","defaultWayState","canLeaveThrough","sourceIds","defaultSources","sourceIdSet","energizedNodes","edgeState","queue","head","leavingHandle","consumerId","feeds","live","feed","applyEnergizationToEdges","collectPowerChain","startNodeId","bySource","byTarget","sourceList","targetList","wayKeys","recordWay","walk","visited","getRackBounds","rackWidth","rackHeight","rackLeft","rackRight","rackTop","rackBottom","isPointInRack","findNearestRack","useNodeConnections","useIncomingNodeConnections","TEMPLATE_SCHEMA_VERSION","TEMPLATE_BAY_KINDS","toStr","toFiniteNumber","TEMPLATE_CONNECTORS","normalizeConnector","s","normalizePortType","rawType","inferConnectorFromType","normalizeRole","inferMountClass","normalizeWidth","generatePortId","blockIndex","usedIds","safeLabel","generateSlug","model","generateBayId","bayIndex","normalizeBayKind","invalidEntryIssue","code","PERCENT_FIELDS","collectPercentRangeIssues","normalizeBaySlot","rawSlot","slotIndex","normalizeBay","rawBay","slotIssues","normalizeFaceBays","rawBays","bayIssues","normalizePortBayMetadata","rawKind","accepts","stageIngest","slug","frontBlocks","rearBlocks","frontBays","rearBays","normalizedLinks","normalizePortBlock","rawBlock","rawPorts","rawConnector","collectExplicitPortIds","into","buildFace","imageUrl","imageAttribution","normalizeFaceData","rawBlocks","stageNormalize","ingested","mountClass","front","rear","addFacePrefixesIfNeeded","frontFace","rearFace","frontNames","rearNames","overlapping","stageEnrich","countFacePorts","countFaceBays","buildValidationReport","links","rawCounts","allPorts","idCounts","parsedFrontCount","parsedRearCount","allPortIds","p","frontPortIds","rearPortIds","countRawFacePorts","stageExport","enriched","source","parseNetboxTemplate","report","coerceRawFace","validateDeviceTemplate","template","fail","issue","rawFaces","rawFront","rawRear","normalizedTemplate","countTemplateFacePorts","validateRenderFidelity","renderedFrontHandleCount","renderedRearHandleCount","expectedFront","expectedRear"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { NetworkEdge, Port } from \"../types\";\n\nconst DIRECTIONAL_HANDLE_SUFFIX = /-(source|target)$/i;\nconst FACE_ALIAS_HANDLE_SUFFIX = /--(rear|front)$/i;\n\n/**\n * Normalize source/target directional suffixes used for bidirectional handles.\n * Example: `Rear Ports-1-target` -\\> `Rear Ports-1`.\n */\nexport function normalizeDirectionalHandleId(\n  handleId: string | null | undefined\n): string {\n  if (!handleId) return \"\";\n  return handleId.replace(DIRECTIONAL_HANDLE_SUFFIX, \"\");\n}\n\n/**\n * Normalize rendered handle ids back to their canonical endpoint id.\n * This removes the directional suffix and the split-face alias, in the order\n * the renderer composes them: `<base>[--rear|--front][-source|-target]`.\n * (Previously only `--rear` was stripped; `--front` ghost aliases now\n * normalize too.)\n *\n * Losslessness: the strip is unconditional, so it is only safe when the base\n * itself cannot end in a reserved suffix. {@link buildPortHandleId} guarantees\n * this for device ports — a label like `target` or `foo-target` is escaped at\n * build time (`<group>-%74arget`, `<group>-foo%2Dtarget`) so it survives\n * normalization intact. Bare power labels and explicit `port.id`s are emitted\n * verbatim, so a caller that chooses an id ending in `-source`/`-target`/\n * `--rear`/`--front` owns that collision.\n */\nexport function normalizeHandleId(handleId: string | null | undefined): string {\n  return normalizeDirectionalHandleId(handleId).replace(\n    FACE_ALIAS_HANDLE_SUFFIX,\n    \"\"\n  );\n}\n\n/**\n * Returns true when a port type is considered power-related.\n */\nexport function isPowerPortType(portType: string | undefined): boolean {\n  return (\n    typeof portType === \"string\" && portType.toLowerCase().includes(\"power\")\n  );\n}\n\n/**\n * Handle id of a vertical PDU's supply inlet — the single point where a branch\n * circuit from a distribution-board breaker way lands.\n *\n * Deliberately non-numeric so it can never collide with an outlet id, which is\n * always a bare 1-based number (`\"1\"…\"N\"`).\n * @public\n */\nexport const PDU_INLET_HANDLE_ID = \"inlet\";\n\n/**\n * Handle id of a distribution board's incomer — the board's own supply inlet,\n * mounted by `DistributionBoardNode`. The board analogue of\n * {@link PDU_INLET_HANDLE_ID}.\n * @public\n */\nexport const BOARD_INCOMER_HANDLE_ID = \"incomer\";\n\nconst POWER_INLET_HANDLE_IDS = new Set<string>([\n  PDU_INLET_HANDLE_ID,\n  BOARD_INCOMER_HANDLE_ID,\n]);\n\n/**\n * Whether a handle id names a power-distribution node's supply inlet\n * (`inlet` on a PDU strip, `incomer` on a distribution board). Directional and\n * face-alias suffixes are normalized away first.\n * @public\n */\nexport function isPowerInletHandleId(handleId: string | null | undefined): boolean {\n  if (!handleId) return false;\n  return POWER_INLET_HANDLE_IDS.has(normalizeHandleId(handleId).trim().toLowerCase());\n}\n\n// ---------------------------------------------------------------------------\n// Device-port group/label codec\n//\n// buildPortHandleId joins a non-power port's group and label with `-`. A raw\n// join is non-injective — group `A` + label `B-C` and group `A-B` + label `C`\n// both produce `A-B-C` — and a clean join can also reproduce a fixed grammar\n// production (group `fiber` + label `3` -> `fiber-3`) or a directional/face\n// suffix (label `target` -> `<group>-target`, which normalizeHandleId would\n// truncate). Each segment is therefore percent-escaped over the two structural\n// characters only — the delimiter `-` (`%2D`) and the escape introducer `%`\n// (`%25`) — so the joined base has exactly one literal `-` (the join) and is\n// unambiguously splittable. A segment containing neither character is emitted\n// verbatim, so the overwhelmingly common case is byte-for-byte unchanged.\n// ---------------------------------------------------------------------------\n\n/** Two hex digits, for the tolerant percent-decoder. */\nconst HEX_PAIR = /^[0-9a-fA-F]{2}$/;\n\n/** A character the join grammar reserves and must escape (`-` or `%`). */\nconst RESERVED_SEGMENT_CHAR = /[-%]/;\n\n/**\n * Percent-escape the two structural characters (`%` then `-`) in a group/label\n * segment. Segments with neither are returned unchanged.\n */\nfunction encodeHandleSegment(segment: string): string {\n  if (!RESERVED_SEGMENT_CHAR.test(segment)) {\n    return segment;\n  }\n  return segment.replace(/%/g, \"%25\").replace(/-/g, \"%2D\");\n}\n\n/**\n * Inverse of {@link encodeHandleSegment}: a tolerant single-pass percent-decode\n * that reverses `%XX` escapes and leaves any malformed `%` literal, so it never\n * throws.\n */\nfunction decodeHandleSegment(segment: string): string {\n  if (!segment.includes(\"%\")) {\n    return segment;\n  }\n  let decoded = \"\";\n  for (let i = 0; i < segment.length; i += 1) {\n    if (\n      segment[i] === \"%\" &&\n      i + 2 < segment.length &&\n      HEX_PAIR.test(segment.slice(i + 1, i + 3))\n    ) {\n      decoded += String.fromCharCode(Number.parseInt(segment.slice(i + 1, i + 3), 16));\n      i += 2;\n    } else {\n      decoded += segment[i];\n    }\n  }\n  return decoded;\n}\n\n/** Percent-escape a single (ASCII) character, e.g. `3` -> `%33`. */\nfunction percentEscapeChar(ch: string): string {\n  const hex = ch.charCodeAt(0).toString(16).toUpperCase();\n  return `%${hex.length === 1 ? `0${hex}` : hex}`;\n}\n\n/**\n * True when a freshly joined device-port base would be misread as a fixed\n * grammar shape or stripped by a normalizer (it ends in a directional or face\n * suffix). Such a base must be disambiguated at build time.\n */\nfunction baseCollidesWithGrammar(base: string): boolean {\n  return (\n    classifyHandleBase(base).kind !== \"port\" ||\n    DIRECTIONAL_HANDLE_SUFFIX.test(base) ||\n    FACE_ALIAS_HANDLE_SUFFIX.test(base)\n  );\n}\n\n/**\n * Escape a label whose plain join would collide with the fixed grammar or a\n * reserved suffix. The first character — always ASCII in the colliding cases\n * (a digit, or the leading letter of `source`/`target`/`cable-button`) — is\n * percent-escaped, which no fixed shape or reserved suffix can begin with; the\n * remainder is escaped normally. The standard decoder reverses it unchanged.\n */\nfunction escapeCollidingLabel(label: string): string {\n  return percentEscapeChar(label[0]) + encodeHandleSegment(label.slice(1));\n}\n\n/**\n * Build the `<group>-<label>` base for a non-power device port with both\n * segments escaped (see the codec note above). Injective and — via\n * `decodePortHandleId` — reversible.\n */\nfunction buildDevicePortBase(group: string, label: string): string {\n  const encodedGroup = encodeHandleSegment(group);\n  const base = `${encodedGroup}-${encodeHandleSegment(label)}`;\n  if (label.length > 0 && baseCollidesWithGrammar(base)) {\n    return `${encodedGroup}-${escapeCollidingLabel(label)}`;\n  }\n  return base;\n}\n\n/**\n * Build a stable handle id for a port.\n *\n * Three shapes, in priority order:\n * 1. An explicit `port.id` is returned verbatim — it is the caller's contract,\n *    including any collision with a fixed grammar shape.\n * 2. A power port returns its `label` verbatim, preserving legacy ids (bare\n *    outlet numbers, `PSU-A`, ...).\n * 3. Otherwise the group and label are percent-escaped and joined\n *    (`<group>-<label>`) so the result is injective and never aliases a fixed\n *    grammar production or a directional/face suffix. Invert with\n *    `decodePortHandleId`.\n *\n * NOTE (pre-1.0 wire-format revision): device-port segments containing `-` or\n * `%` now encode those characters (`%2D` / `%25`), and a clean label that would\n * otherwise collide (e.g. group `fiber` + label `3` becomes `fiber-%33`) gets a\n * first-character escape. Segments with no reserved character are unchanged, so\n * common ids are stable.\n */\nexport function buildPortHandleId(\n  portBlockName: string,\n  port: Pick<Port, \"label\" | \"id\"> & { type?: string }\n): string {\n  if (typeof port.id === \"string\" && port.id.length > 0) {\n    return port.id;\n  }\n  if (isPowerPortType(port.type)) {\n    return port.label;\n  }\n  return buildDevicePortBase(portBlockName, port.label);\n}\n\n/**\n * Invert {@link buildPortHandleId}'s device-port group/label encoding, returning\n * the original `{ group, label }`. Only meaningful for the `<group>-<label>`\n * form: power labels and explicit `port.id`s are emitted verbatim by\n * {@link buildPortHandleId} and must not be decoded (there is no group to\n * recover). The split is unambiguous because the encoding leaves exactly one\n * literal `-` (the join delimiter) in the id.\n *\n * Intentionally NOT part of the public entry surface: it is a partial inverse\n * (meaningless on power labels and explicit ids), so it stays internal to\n * avoid a foot-gun. Exported from the module for the grammar's own tests only.\n * @internal\n */\nexport function decodePortHandleId(handleId: string): { group: string; label: string } {\n  const delimiter = handleId.indexOf(\"-\");\n  if (delimiter === -1) {\n    return { group: decodeHandleSegment(handleId), label: \"\" };\n  }\n  return {\n    group: decodeHandleSegment(handleId.slice(0, delimiter)),\n    label: decodeHandleSegment(handleId.slice(delimiter + 1)),\n  };\n}\n\n/**\n * Heuristic for whether a handle id represents a power endpoint.\n *\n * Prefer {@link applyDirectionalPortHandleId}'s `portType` option when the port\n * is available — this string heuristic is a fallback only.\n *\n * Matches library power-id shapes (`Power Ports-1`, `pwr-1`, `power_ac-1`,\n * `PSU-A`, `outlet-3`) without treating every label that merely contains the\n * word \"power\" (e.g. `Power Room Uplinks-1`) as a power endpoint.\n */\nexport function looksLikePowerHandleId(handleId: string): boolean {\n  const normalized = normalizeDirectionalHandleId(handleId).toLowerCase().trim();\n  const compact = normalized.replace(/\\s+/g, \" \");\n\n  if (/^(pwr|power_ac|power_dc)([-_]|$)/.test(compact)) return true;\n  if (/^power([-_ ]?ports)?[-_ ]?\\d+$/.test(compact)) return true;\n  if (/^pwr[-_]?\\d+$/.test(compact)) return true;\n  if (/^(psu|outlet)([-_]|\\d|$)/.test(compact)) return true;\n  if (/^way-.+$/.test(compact)) return true;\n  // The supply inlet of a PDU strip / distribution board is a power endpoint,\n  // so (like a way) it never takes a directional suffix.\n  if (POWER_INLET_HANDLE_IDS.has(compact)) return true;\n  if (/(^|[-_/ ])(psu|outlet)([-_/ ]|\\d|$)/.test(compact)) return true;\n  return false;\n}\n\n/**\n * Options for {@link applyDirectionalPortHandleId}.\n */\nexport interface ApplyDirectionalPortHandleIdOptions {\n  /**\n   * The port's `type` (e.g. `rj45`, `power_ac`). When provided, power-ness is\n   * decided from it via {@link isPowerPortType} — the same rule the renderer\n   * uses to decide whether a port mounts directional handles at all. Always\n   * pass this when the port is available.\n   */\n  portType?: string;\n}\n\n/**\n * Apply a directional (`source`/`target`) suffix to a rendered handle id — the\n * inverse of {@link normalizeDirectionalHandleId}. Any existing directional\n * suffix is replaced first, and passing no direction is a no-op.\n * Example: `Rear Ports-1` + `target` -\\> `Rear Ports-1-target`.\n *\n * Power endpoints are matched without a direction, so power handles are\n * returned unchanged. Whether a handle is a power handle is decided from\n * `options.portType` when given (exact — the renderer's rule). Without it the\n * decision falls back to {@link looksLikePowerHandleId}, which recognises\n * library power-id shapes (`Power Ports-1`, `pwr-*`, `PSU-*`, `outlet-*`)\n * without treating labels that merely contain the word \"power\" as power.\n * Pass `portType` whenever you have the port.\n */\nexport function applyDirectionalPortHandleId(\n  handleId: string,\n  direction?: \"source\" | \"target\" | null,\n  options?: ApplyDirectionalPortHandleIdOptions\n): string {\n  if (!direction) return handleId;\n  const hasPortType =\n    typeof options?.portType === \"string\" && options.portType.length > 0;\n  const isPower = hasPortType\n    ? isPowerPortType(options.portType)\n    : looksLikePowerHandleId(handleId);\n  if (isPower) return handleId;\n  return `${normalizeDirectionalHandleId(handleId)}-${direction}`;\n}\n\n// ---------------------------------------------------------------------------\n// Typed handle-id grammar\n//\n// Handle ids are the library's de-facto wire protocol: they are what React\n// Flow stores on edges, what documents persist, and what consumers match\n// against. This section is the single authority for that grammar. The full\n// composed form is:\n//\n//   <base>[--rear|--front][-source|-target]\n//\n// where <base> is one of the shapes below. Everything here is *structural* —\n// it reads only the string. Shapes whose tokens can collide with port group\n// names (a group literally named `fiber`, a port label `left-3`) cannot be\n// told apart from the string alone; callers that have the node should combine\n// this with the node type (as `classifyFibreEndpoint` does).\n// ---------------------------------------------------------------------------\n\n/**\n * The structural shape of a handle id's base (suffixes already stripped).\n *\n * - `outlet` — vertical-PDU outlets: a bare number (`\"4\"`).\n * - `fiber` — `fiber-<n>` (tube/cable nodes) or `fiber-<tube>-<n>`\n *   (FibreFlowMap rows).\n * - `tube` — `tube-<n>` (tube and multi-tube-cable nodes).\n * - `splice-side` — bare `left` / `right` (splice nodes).\n * - `side-port` — `left-<n>` / `right-<n>` (coupler ports).\n * - `split-io` — `input-<n>` / `output-<n>` (fibre-split nodes).\n * - `cable-button` — the literal `cable-button` connector on cable nodes.\n * - `breaker-way` — `way-<wayId>` (distribution-board breaker ways).\n * - `port` — everything else: device-family ports built by\n *   {@link buildPortHandleId} (`<group>-<label>`, a bare power label, or an\n *   explicit `port.id`). A dashed base is decoded into `group`/`label` (the\n *   segments are percent-escaped so the join delimiter is unambiguous); a\n *   dash-free base, which can never be a group-label join, is flagged\n *   `verbatim` (an opaque `port.id` or bare power label).\n * @public\n */\nexport type HandleShape =\n  | { kind: \"outlet\"; outlet: number }\n  | { kind: \"fiber\"; fiber: number; tube?: number }\n  | { kind: \"tube\"; tube: number }\n  | { kind: \"splice-side\"; side: \"left\" | \"right\" }\n  | { kind: \"side-port\"; side: \"left\" | \"right\"; index: number }\n  | { kind: \"split-io\"; io: \"input\" | \"output\"; index: number }\n  | { kind: \"cable-button\" }\n  | { kind: \"breaker-way\"; wayId: string }\n  | {\n      kind: \"port\";\n      /**\n       * Decoded port group, when the base is a `<group>-<label>` join (has a\n       * dash). Best-effort structural decode: for a verbatim `port.id` or power\n       * label that happens to contain a dash (`PSU-A`) this is the split before\n       * the first dash, not necessarily a real group.\n       */\n      group?: string;\n      /** Decoded port label — same best-effort decode as `group` above. */\n      label?: string;\n      /**\n       * `true` when the base has no dash, so it cannot be a group-label join —\n       * it is an opaque `port.id` or a bare power label with no `group`/`label`\n       * reading.\n       */\n      verbatim?: boolean;\n    };\n\n/**\n * A structurally parsed handle id.\n * @public\n */\nexport interface ParsedHandleId {\n  /** The id exactly as given. */\n  raw: string;\n  /** `raw` with the directional suffix and face alias stripped. */\n  base: string;\n  /** Directional suffix, when present (`X-source` / `X-target`). */\n  direction?: \"source\" | \"target\";\n  /**\n   * Split-face alias, when present (`X--rear` / `X--front`) — added by the\n   * renderer when both faces of a device are shown and a handle id would\n   * otherwise collide with its opposite face.\n   */\n  faceAlias?: \"front\" | \"rear\";\n  /** Structural classification of {@link ParsedHandleId.base}. */\n  shape: HandleShape;\n  /**\n   * Other shapes the base could structurally be, when it is genuinely\n   * ambiguous. Present (and non-empty) only when there is a second reading;\n   * absent otherwise. The primary {@link ParsedHandleId.shape} is never changed\n   * by this — `alternates` is a pure signal.\n   *\n   * The classic case is a verbatim `port.id` that happens to match a fixed\n   * shape: `tube-5` classifies as `{ kind: \"tube\", tube: 5 }` but could be an\n   * explicit port id, so `alternates` is `[{ kind: \"port\", verbatim: true }]`.\n   * A dashed group-label port carries the same verbatim alternate (it could be\n   * one opaque id). Only node-context-aware classification (which handle-ids\n   * cannot do from the string alone) can pick the right reading.\n   */\n  alternates?: HandleShape[];\n}\n\nconst OUTLET_BASE = /^\\d+$/;\nconst FIBER_TUBE_BASE = /^fiber-(\\d+)-(\\d+)$/;\nconst FIBER_BASE = /^fiber-(\\d+)$/;\nconst TUBE_BASE = /^tube-(\\d+)$/;\nconst SIDE_PORT_BASE = /^(left|right)-(\\d+)$/;\nconst SPLIT_IO_BASE = /^(input|output)-(\\d+)$/;\nconst BREAKER_WAY_BASE = /^way-(.+)$/;\n\nfunction classifyHandleBase(base: string): HandleShape {\n  if (OUTLET_BASE.test(base)) {\n    return { kind: \"outlet\", outlet: Number(base) };\n  }\n  const fiberTube = FIBER_TUBE_BASE.exec(base);\n  if (fiberTube) {\n    return { kind: \"fiber\", tube: Number(fiberTube[1]), fiber: Number(fiberTube[2]) };\n  }\n  const fiber = FIBER_BASE.exec(base);\n  if (fiber) {\n    return { kind: \"fiber\", fiber: Number(fiber[1]) };\n  }\n  const tube = TUBE_BASE.exec(base);\n  if (tube) {\n    return { kind: \"tube\", tube: Number(tube[1]) };\n  }\n  if (base === \"left\" || base === \"right\") {\n    return { kind: \"splice-side\", side: base };\n  }\n  const sidePort = SIDE_PORT_BASE.exec(base);\n  if (sidePort) {\n    return {\n      kind: \"side-port\",\n      side: sidePort[1] as \"left\" | \"right\",\n      index: Number(sidePort[2]),\n    };\n  }\n  const splitIo = SPLIT_IO_BASE.exec(base);\n  if (splitIo) {\n    return {\n      kind: \"split-io\",\n      io: splitIo[1] as \"input\" | \"output\",\n      index: Number(splitIo[2]),\n    };\n  }\n  if (base === \"cable-button\") {\n    return { kind: \"cable-button\" };\n  }\n  const breakerWay = BREAKER_WAY_BASE.exec(base);\n  if (breakerWay) {\n    return { kind: \"breaker-way\", wayId: decodeHandleSegment(breakerWay[1]) };\n  }\n  // A dashed base is always a possible `<group>-<label>` join — decode it (the\n  // percent-escapes are reversed). A dash-free base cannot be a join, so it is\n  // an opaque verbatim id / bare power label.\n  if (base.includes(\"-\")) {\n    const { group, label } = decodePortHandleId(base);\n    return { kind: \"port\", group, label };\n  }\n  return { kind: \"port\", verbatim: true };\n}\n\n/**\n * The alternate structural readings of a classified base, when it is genuinely\n * ambiguous. Every fixed shape, and every dashed group-label port, could also\n * be an explicit verbatim `port.id`; a bare verbatim port has no second reading.\n */\nfunction alternateShapes(shape: HandleShape): HandleShape[] {\n  if (shape.kind === \"port\") {\n    return shape.verbatim ? [] : [{ kind: \"port\", verbatim: true }];\n  }\n  return [{ kind: \"port\", verbatim: true }];\n}\n\n/**\n * Parse a rendered/stored handle id into its structural parts: base, optional\n * directional suffix, optional split-face alias, and the {@link HandleShape}\n * of the base. Total — never throws; unrecognized bases classify as `port`.\n * @public\n */\nexport function parseHandleId(handleId: string | null | undefined): ParsedHandleId {\n  const raw = handleId ?? \"\";\n  const directionMatch = DIRECTIONAL_HANDLE_SUFFIX.exec(raw);\n  const afterDirection = directionMatch\n    ? raw.slice(0, raw.length - directionMatch[0].length)\n    : raw;\n  const faceMatch = FACE_ALIAS_HANDLE_SUFFIX.exec(afterDirection);\n  const base = faceMatch\n    ? afterDirection.slice(0, afterDirection.length - faceMatch[0].length)\n    : afterDirection;\n  const shape = classifyHandleBase(base);\n  const parsed: ParsedHandleId = { raw, base, shape };\n  if (directionMatch) {\n    parsed.direction = directionMatch[1].toLowerCase() as \"source\" | \"target\";\n  }\n  if (faceMatch) {\n    parsed.faceAlias = faceMatch[1].toLowerCase() as \"front\" | \"rear\";\n  }\n  const alternates = alternateShapes(shape);\n  if (alternates.length > 0) {\n    parsed.alternates = alternates;\n  }\n  return parsed;\n}\n\n/**\n * Build a fiber handle id: `fiber-<n>`, or `fiber-<tube>-<n>` when a tube\n * index is given (the FibreFlowMap row form).\n * @public\n */\nexport function buildFibreHandleId(fiber: number, tube?: number): string {\n  return tube === undefined ? `fiber-${fiber}` : `fiber-${tube}-${fiber}`;\n}\n\n/**\n * Build a tube handle id: `tube-<n>`.\n * @public\n */\nexport function buildTubeHandleId(tube: number): string {\n  return `tube-${tube}`;\n}\n\n/**\n * Build a splice handle id: the bare `left` / `right` sides of a splice node.\n * @public\n */\nexport function buildSpliceHandleId(side: \"left\" | \"right\"): string {\n  return side;\n}\n\n/**\n * Build a coupler port handle id: `left-<n>` / `right-<n>` (1-based).\n * @public\n */\nexport function buildCouplerHandleId(side: \"left\" | \"right\", index: number): string {\n  return `${side}-${index}`;\n}\n\n/**\n * Build a fibre-split io handle id: `input-<n>` / `output-<n>`.\n * @public\n */\nexport function buildSplitIoHandleId(io: \"input\" | \"output\", index: number): string {\n  return `${io}-${index}`;\n}\n\n/**\n * Build a distribution-board breaker way handle id: `way-<wayId>`.\n *\n * The way id is percent-escaped exactly as {@link buildPortHandleId} escapes\n * its segments, so a way id containing `-` or `%` (or the `--rear` face alias\n * suffix) cannot be misread by {@link parseHandleId}; the parse side decodes\n * it symmetrically. Ids with no reserved character are unchanged.\n *\n * Ways are power endpoints, so the id carries no directional suffix (see\n * {@link applyDirectionalPortHandleId}) and {@link looksLikePowerHandleId}\n * recognises the prefix.\n * @public\n */\nexport function buildBreakerWayHandleId(wayId: string): string {\n  return `way-${encodeHandleSegment(wayId)}`;\n}\n\n/**\n * Append a split-face alias to a handle id: `X` + `rear` -\\> `X--rear`.\n * Any existing alias is replaced. The renderer applies this when a hidden or\n * ghost face would mount a handle whose id collides with the visible face.\n * @public\n */\nexport function applyFaceAliasHandleId(\n  handleId: string,\n  face: \"front\" | \"rear\"\n): string {\n  return `${handleId.replace(FACE_ALIAS_HANDLE_SUFFIX, \"\")}--${face}`;\n}\n\n/**\n * Normalize a handle id for tolerant matching by stripping `+` characters.\n * Port labels like `SFP+-1` historically produced handles that some producers\n * emitted `+`-stripped (`SFP-1`), so comparisons between independently built\n * handles must be `+`-insensitive. Apply to BOTH sides of a comparison — this\n * is a matching normalization, not part of the build grammar:\n * {@link buildPortHandleId} keeps `+` so built ids always equal rendered ids.\n *\n * This mapping is intentionally MANY-TO-ONE and lossy: it cannot be inverted\n * and must never be persisted as a canonical id. Distinct handles collapse\n * together — e.g. `SFP+-1` and `SFP-1` both sanitize to `SFP-1`, so a port\n * genuinely labelled `SFP-1` is indistinguishable from one labelled `SFP+-1`\n * under this comparison. Use it only as a last-resort tolerant match, applied\n * to both sides.\n *\n * Interaction with the injective build grammar (pre-1.0 wire-format revision):\n * {@link buildPortHandleId} percent-escapes `-` (`%2D`) and `%` (`%25`) inside\n * group/label segments. This sanitizer strips ONLY `+` and deliberately does\n * NOT decode those escapes, so tolerant matching requires both sides to share\n * the same escaping discipline. A lookup built through {@link buildPortHandleId}\n * (or a bare, unescaped label) still matches `+`-insensitively — a `+`-dropped\n * but still-escaped composite `Group-SFP%2D1` matches the registered\n * `Group-SFP+%2D1`. But a fully hand-built composite that both drops `+` AND\n * writes a raw `-` where the builder now writes `%2D` — `Group-SFP-1` against\n * `Group-SFP+%2D1` — no longer matches. That narrowed case is unreachable\n * through the builder (the single source of composite ids) and is pinned as a\n * boundary in the tests. Decoding is intentionally kept OUT of this function:\n * mounts.\n *\n * @internal Internal building block of {@link handlesMatch}; not part of the\n * public API. Consumers must compare handles with {@link handlesMatch} (or find\n * edges with {@link findEdgesForPortHandle}), which apply this rule to both\n * sides along with directional/face normalization.\n */\nexport function sanitizeHandleIdForMatching(handleId: string): string {\n  return handleId.replace(/\\+/g, \"\");\n}\n\n/**\n * True iff two handle ids refer to the same handle under the library's own\n * equivalence relation. This is the one function to use for \"is this edge on\n * this port?\" — it replaces bespoke candidate-set matchers.\n *\n * @remarks\n * The relation, applied to BOTH sides: strip the directional suffix and the\n * split-face alias ({@link normalizeHandleId} semantics), then apply the\n * narrowed `+`-insensitive rule (drop `+`, escaping-aware), then\n * compare. So `Rear Ports-1-target`, `Rear Ports-1--rear` and `Rear Ports-1`\n * all match, and `SFP+-1` matches `SFP-1`.\n *\n * It is intentionally string-structural and does NOT percent-decode: it matches\n * builder-produced ids (the single source of composite ids), where both sides\n * share the same escaping discipline. A hand-built composite that drops `+` AND\n * writes a raw `-` where the builder writes `%2D` (`Group-SFP-1` vs\n * `Group-SFP+%2D1`) is a deliberate non-match — that form is unreachable through\n * {@link buildPortHandleId}. Prefer this over the raw sanitizer, which leaks\n * matching concerns onto callers.\n *\n * @example\n * ```ts\n * handlesMatch(\"Rear Ports-1-target\", \"Rear Ports-1\"); // true\n * handlesMatch(\"Ports-1--rear\", \"Ports-1\");            // true\n * handlesMatch(\"SFP+-1\", \"SFP-1\");                      // true\n * handlesMatch(\"Ports-1\", \"Ports-2\");                   // false\n * ```\n * @public\n */\nexport function handlesMatch(a: string, b: string): boolean {\n  return (\n    sanitizeHandleIdForMatching(normalizeHandleId(a)) ===\n    sanitizeHandleIdForMatching(normalizeHandleId(b))\n  );\n}\n\n/**\n * Return the ids of every edge attached to the given `handleId` on `nodeId`,\n * matching handles with {@link handlesMatch}. An edge qualifies when\n * `source === nodeId` and its `sourceHandle` matches, or `target === nodeId` and\n * its `targetHandle` matches. Edges with an undefined `sourceHandle`/\n * `targetHandle` on the relevant side are skipped.\n *\n * @remarks\n * This is the multi-edge companion to {@link handlesMatch} and the replacement\n * for consumers' hand-rolled per-port edge scans. A self-loop edge (both ends on\n * `nodeId`, both handles matching) is returned once. The scan is a single linear\n * pass over `edges`; it does not build an index, so for repeated lookups over a\n * large edge set a caller may still want its own Map.\n *\n * @example\n * ```ts\n * const handle = buildPortHandleId(\"Rear Ports\", { label: \"1\" });\n * findEdgesForPortHandle(edges, \"device-1\", handle); // [\"e1\", \"e2\"]\n * ```\n * @public\n */\nexport function findEdgesForPortHandle(\n  edges: NetworkEdge[],\n  nodeId: string,\n  handleId: string\n): string[] {\n  const matched: string[] = [];\n  for (const edge of edges) {\n    if (\n      edge.source === nodeId &&\n      edge.sourceHandle !== undefined &&\n      handlesMatch(edge.sourceHandle, handleId)\n    ) {\n      matched.push(edge.id);\n      continue;\n    }\n    if (\n      edge.target === nodeId &&\n      edge.targetHandle !== undefined &&\n      handlesMatch(edge.targetHandle, handleId)\n    ) {\n      matched.push(edge.id);\n    }\n  }\n  return matched;\n}\n\n/**\n * For pass-through nodes with two sides, return the handle id of the opposite\n * side: splice `left` \\<-\\> `right`, coupler `left-<n>` \\<-\\> `right-<n>`.\n * Returns `undefined` for every other shape. Directional suffixes and face\n * aliases are stripped (neither occurs on splice/coupler handles in practice).\n * @public\n */\nexport function getOppositeSideHandleId(handleId: string): string | undefined {\n  const { shape } = parseHandleId(handleId);\n  if (shape.kind === \"splice-side\") {\n    return shape.side === \"left\" ? \"right\" : \"left\";\n  }\n  if (shape.kind === \"side-port\") {\n    return buildCouplerHandleId(shape.side === \"left\" ? \"right\" : \"left\", shape.index);\n  }\n  return undefined;\n}\n\n/**\n * Options for {@link encodeHandle}: the optional suffixes composed onto the\n * base, in grammar order `<base>[--rear|--front][-source|-target]`.\n * @public\n */\nexport interface EncodeHandleOptions {\n  /** Split-face alias to append (`--rear` / `--front`). */\n  face?: \"front\" | \"rear\";\n  /**\n   * Directional suffix to append (`-source` / `-target`). Applied structurally\n   * regardless of power-ness — use {@link applyDirectionalPortHandleId} instead\n   * when you need power ports left un-suffixed.\n   */\n  direction?: \"source\" | \"target\";\n}\n\n/**\n * The single structured encoder: turn a {@link HandleShape} into its handle-id\n * string, dispatching to the individual typed builders. The inverse of\n * {@link decodeHandle}/{@link parseHandleId} for the base classification.\n *\n * @remarks\n * Use this when you hold structured handle data (e.g. from a `DiagramEndpoint`)\n * and need the React Flow boundary string, instead of remembering which builder\n * a kind maps to. A `port` shape REQUIRES string `group` and `label` — it is\n * encoded through {@link buildPortHandleId} (so the same escaping/injectivity\n * applies). A `verbatim` port has no id to emit and cannot be re-encoded: pass\n * an explicit id to {@link buildPortHandleId} yourself for that case.\n *\n * Round-trip: `decodeHandle(encodeHandle(shape)).shape` deep-equals `shape` for\n * every non-verbatim kind.\n *\n * @throws TypeError when a `port` shape is missing `group` or `label`.\n * @example\n * ```ts\n * encodeHandle({ kind: \"fiber\", tube: 2, fiber: 7 });          // \"fiber-2-7\"\n * encodeHandle({ kind: \"port\", group: \"Rear Ports\", label: \"1\" }); // \"Rear Ports-1\"\n * encodeHandle({ kind: \"port\", group: \"Ports\", label: \"1\" }, { face: \"rear\", direction: \"source\" });\n * // \"Ports-1--rear-source\"\n * ```\n * @public\n */\nexport function encodeHandle(\n  shape: HandleShape,\n  opts?: EncodeHandleOptions\n): string {\n  let base: string;\n  switch (shape.kind) {\n    case \"outlet\":\n      base = String(shape.outlet);\n      break;\n    case \"fiber\":\n      base = buildFibreHandleId(shape.fiber, shape.tube);\n      break;\n    case \"tube\":\n      base = buildTubeHandleId(shape.tube);\n      break;\n    case \"splice-side\":\n      base = buildSpliceHandleId(shape.side);\n      break;\n    case \"side-port\":\n      base = buildCouplerHandleId(shape.side, shape.index);\n      break;\n    case \"split-io\":\n      base = buildSplitIoHandleId(shape.io, shape.index);\n      break;\n    case \"cable-button\":\n      base = \"cable-button\";\n      break;\n    case \"breaker-way\":\n      base = buildBreakerWayHandleId(shape.wayId);\n      break;\n    case \"port\": {\n      if (typeof shape.group !== \"string\" || typeof shape.label !== \"string\") {\n        throw new TypeError(\n          \"encodeHandle: a 'port' shape requires string 'group' and 'label'. \" +\n            \"Verbatim/opaque port ids cannot be re-encoded (there is no id in the \" +\n            \"shape) — pass the explicit id to buildPortHandleId instead.\"\n        );\n      }\n      base = buildPortHandleId(shape.group, { label: shape.label });\n      break;\n    }\n  }\n  let out = opts?.face ? applyFaceAliasHandleId(base, opts.face) : base;\n  if (opts?.direction) {\n    out = `${out}-${opts.direction}`;\n  }\n  return out;\n}\n\n/**\n * Decode a handle-id string into its structural parts. A documented alias of\n * {@link parseHandleId} — identical behavior, named for symmetry with\n * {@link encodeHandle}.\n *\n * @remarks\n * Total (never throws). Use the {@link encodeHandle}/`decodeHandle` pair when\n * you think in structured {@link HandleShape}s; use `parseHandleId` directly\n * when the \"parse a rendered id\" framing reads better. See\n * {@link ParsedHandleId.alternates} for the ambiguity signal on verbatim ids.\n *\n * @example\n * ```ts\n * decodeHandle(\"fiber-2-7\").shape; // { kind: \"fiber\", tube: 2, fiber: 7 }\n * decodeHandle(encodeHandle(x)).shape; // deep-equals x for non-verbatim x\n * ```\n * @public\n */\nexport function decodeHandle(\n  handleId: string | null | undefined\n): ParsedHandleId {\n  return parseHandleId(handleId);\n}\n","import type { BreakerWay } from '../types';\n\n/**\n * Height of one breaker way row in the generated faceplate.\n *\n * A row is one breaker module plus the gap to the next, so the module face is\n * `BOARD_WAY_ROW_HEIGHT_PX - BOARD_WAY_GAP_PX` tall.\n */\nexport const BOARD_WAY_ROW_HEIGHT_PX = 22;\n/**\n * Gap between one breaker module and the next. The renderer reserves this\n * strip below each module face, so keep it in step with the\n * `.distribution-board-node__way` padding in the stylesheet.\n */\nexport const BOARD_WAY_GAP_PX = 4;\n/**\n * Width of one way column in the generated faceplate. Wide enough for the\n * toggle glyph plus `W01  32A  L1 1P` without any of the three eliding — a\n * breaker whose rating you cannot read is not a breaker.\n */\nexport const BOARD_WAY_COLUMN_WIDTH_PX = 108;\n/** Height of the board header strip carrying the board-type badge and label. */\nexport const BOARD_HEADER_HEIGHT_PX = 22;\n/** Padding around the way grid inside the board body. */\nexport const BOARD_PADDING_PX = 8;\n/** Maximum number of way columns the generated faceplate lays out. */\nexport const BOARD_MAX_COLUMNS = 6;\n\n/** One way's resolved rectangle inside the board body (px, body-relative). */\nexport interface BreakerWayRect {\n  way: BreakerWay;\n  column: number;\n  row: number;\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\n/** Resolved geometry for a whole board faceplate. */\nexport interface DistributionBoardLayout {\n  columns: number;\n  rows: number;\n  width: number;\n  height: number;\n  bodyHeight: number;\n  wayRects: BreakerWayRect[];\n}\n\n/**\n * One way per row is the default and the shape every demo uses: a real board's\n * ways read as a single vertical list of modules on one rail run, and stacking\n * two columns of 7px text made the faceplate unreadable at diagram zoom.\n * `wayColumns` stays available for consumers laying out very wide panelboards.\n */\nfunction normalizeColumns(wayColumns: unknown): number {\n  const value = Number(wayColumns);\n  if (!Number.isFinite(value) || value < 1) return 1;\n  return Math.min(BOARD_MAX_COLUMNS, Math.floor(value));\n}\n\n/**\n * Lay out a distribution board's ways into a generated faceplate grid: ways\n * fill column-major (top-to-bottom, then next column), which is how a real\n * board's way numbering runs. Pure geometry — no React, so\n * {@link resolveNodeDimensions} and the renderer agree by construction.\n */\nexport function buildDistributionBoardLayout(\n  ways: readonly BreakerWay[] | undefined,\n  wayColumns?: unknown,\n): DistributionBoardLayout {\n  const list = Array.isArray(ways) ? ways : [];\n  const columns = normalizeColumns(wayColumns);\n  const rows = Math.max(1, Math.ceil(Math.max(list.length, 1) / columns));\n  const bodyHeight = rows * BOARD_WAY_ROW_HEIGHT_PX + BOARD_PADDING_PX * 2;\n\n  const wayRects: BreakerWayRect[] = list.map((way, index) => {\n    const column = Math.floor(index / rows);\n    const row = index % rows;\n    return {\n      way,\n      column,\n      row,\n      left: BOARD_PADDING_PX + column * BOARD_WAY_COLUMN_WIDTH_PX,\n      top: BOARD_PADDING_PX + row * BOARD_WAY_ROW_HEIGHT_PX,\n      width: BOARD_WAY_COLUMN_WIDTH_PX,\n      height: BOARD_WAY_ROW_HEIGHT_PX,\n    };\n  });\n\n  return {\n    columns,\n    rows,\n    width: columns * BOARD_WAY_COLUMN_WIDTH_PX + BOARD_PADDING_PX * 2,\n    height: BOARD_HEADER_HEIGHT_PX + bodyHeight,\n    bodyHeight,\n    wayRects,\n  };\n}\n","// Shared layout constants for racks/devices\n\nexport const STANDARD_RACK_WIDTH_IN = 19;\nexport const STANDARD_RACK_UNIT_HEIGHT_IN = 1.75;\nexport const U_HEIGHT_PX = 20; // Default 1U height for standalone device rendering\nexport const RACK_HEADER_HEIGHT = 60; // Fixed rack header height in px\nexport const RACK_WIDTH_PX = 300; // Fixed rack width in px\nexport const DEFAULT_RACK_UNIT_HEIGHT_PX = (RACK_WIDTH_PX * STANDARD_RACK_UNIT_HEIGHT_IN) / STANDARD_RACK_WIDTH_IN;\n\n/**\n * Handle extension in pixels - handles extend this many pixels outside nodes\n * Used in dimension calculations for panels and closures\n */\nexport const HANDLE_EXTENSION_PX = 5;\n","import { NetworkNode } from '../types';\n\n/**\n * Height of each holder position in pixels (one splice per holder)\n * Different heights for ribbon mode (taller) vs single mode (compact)\n */\nexport const HOLDER_HEIGHT_PX_SINGLE = 20; // For single mode splices (16px height)\nexport const HOLDER_HEIGHT_PX_RIBBON = 36; // For ribbon mode splices (32px height)\n// Default to single mode for backward compatibility\nexport const HOLDER_HEIGHT_PX = HOLDER_HEIGHT_PX_SINGLE;\n\n/**\n * Tray header height in pixels (smaller than rack header for compact trays)\n */\nexport const TRAY_HEADER_HEIGHT = 32;\n\n/**\n * Bottom padding for tray in pixels\n */\nexport const TRAY_BOTTOM_PADDING = 4;\n\n/**\n * Number of holders per tray (12f per tray)\n */\nexport const HOLDERS_PER_TRAY = 12;\n\n/**\n * Rendered splice width in pixels — the exact width the SpliceNode renderer\n * uses.\n *\n * @remarks\n * Exposed so consumers composing a fibre scene by hand (positioning splices to\n * line up with rendered tray/holder geometry) use the real value instead of a\n * local copy that would silently drift when the renderer changes. Pairs with\n * {@link SPLICE_HEIGHT_PX} and {@link calculateSplicePositionFromNumber}.\n * @public\n */\nexport const SPLICE_WIDTH_PX = 69;\n\n/**\n * Rendered splice height in pixels for single-mode (compact) splices.\n * @public\n */\nexport const SPLICE_HEIGHT_PX_SINGLE = 16;\n\n/**\n * Rendered splice height in pixels for ribbon-mode (taller) splices.\n * @public\n */\nexport const SPLICE_HEIGHT_PX_RIBBON = 32;\n\n/**\n * Rendered splice height in pixels. Defaults to single-mode; for ribbon splices\n * use {@link SPLICE_HEIGHT_PX_RIBBON}.\n *\n * @remarks\n * The height counterpart to {@link SPLICE_WIDTH_PX}: the exact value the\n * SpliceNode renderer uses, exposed so hand-composed fibre scenes don't fork a\n * magic number that drifts.\n * @public\n */\nexport const SPLICE_HEIGHT_PX = SPLICE_HEIGHT_PX_SINGLE;\n\n/**\n * Tray width in pixels (narrower to fit splice properly)\n */\nexport const TRAY_WIDTH_PX = 80;\n\n/**\n * Rendered splice-tray height for single-mode splices (default).\n */\nexport const TRAY_NODE_HEIGHT_SINGLE =\n  TRAY_HEADER_HEIGHT + HOLDERS_PER_TRAY * HOLDER_HEIGHT_PX_SINGLE + TRAY_BOTTOM_PADDING;\n\n/**\n * Rendered splice-tray height when the tray holds ribbon splices.\n */\nexport const TRAY_NODE_HEIGHT_RIBBON =\n  TRAY_HEADER_HEIGHT + HOLDERS_PER_TRAY * HOLDER_HEIGHT_PX_RIBBON + TRAY_BOTTOM_PADDING;\n\n/** Default tray height — compact single-mode layout. */\nexport const TRAY_NODE_HEIGHT = TRAY_NODE_HEIGHT_SINGLE;\n\nexport function getTrayNodeHeight(isRibbonMode = false): number {\n  return isRibbonMode ? TRAY_NODE_HEIGHT_RIBBON : TRAY_NODE_HEIGHT_SINGLE;\n}\n\n/**\n * Fixed X position to center splices within tray content\n */\nexport const SPLICE_CENTER_X = (TRAY_WIDTH_PX - SPLICE_WIDTH_PX) / 2;\n\n/**\n * Calculate holder position from Y coordinate within a tray\n * Uses average height to work with both single and ribbon splices\n */\nexport const calculateHolderPositionFromY = (\n  tray: NetworkNode,\n  y: number\n): number => {\n  const startY = TRAY_HEADER_HEIGHT;\n  const relativeY = y - startY;\n  // Use average height to work reasonably with both modes\n  const avgHeight = (HOLDER_HEIGHT_PX_SINGLE + HOLDER_HEIGHT_PX_RIBBON) / 2;\n  const holderPosition = Math.round(relativeY / avgHeight) + 1;\n  const maxHolders = HOLDERS_PER_TRAY;\n  return Math.max(1, Math.min(maxHolders, holderPosition));\n};\n\n/**\n * Snap a splice to the nearest holder position within a tray\n */\nexport const snapToSplicePosition = (\n  splice: NetworkNode,\n  tray: NetworkNode,\n  newPosition: { x: number; y: number },\n  allNodes: NetworkNode[]\n): { x: number; y: number; holderPosition?: number } => {\n  if (!splice.parentId || splice.parentId !== tray.id) {\n    return newPosition;\n  }\n\n  // Determine if this is a ribbon splice\n  const isRibbonMode = splice.data?.mode === 'ribbon';\n  const holderHeight = isRibbonMode ? HOLDER_HEIGHT_PX_RIBBON : HOLDER_HEIGHT_PX_SINGLE;\n  const spliceHeight = isRibbonMode ? SPLICE_HEIGHT_PX_RIBBON : SPLICE_HEIGHT_PX_SINGLE;\n\n  const startY = TRAY_HEADER_HEIGHT;\n  const relativeY = newPosition.y - startY;\n  const calculatedPosition = Math.round(relativeY / holderHeight) + 1;\n  const maxPosition = HOLDERS_PER_TRAY;\n  const snappedPosition = Math.max(1, Math.min(maxPosition, calculatedPosition));\n\n  // Check if position is available\n  if (!isHolderPositionAvailable(tray, splice, snappedPosition, allNodes)) {\n    const availablePosition = findNextAvailableHolderPosition(tray, splice, allNodes, snappedPosition);\n    if (availablePosition !== snappedPosition) {\n      // Center splice vertically in holder slot\n      const baseOffset = (holderHeight - spliceHeight) / 2;\n      return {\n        x: SPLICE_CENTER_X, // Center splice in tray\n        y: startY + (availablePosition - 1) * holderHeight + baseOffset,\n        holderPosition: availablePosition,\n      };\n    }\n  }\n\n  // Snap to exact holder position, center vertically in holder slot\n  const baseOffset = (holderHeight - spliceHeight) / 2;\n  const snappedY = startY + (snappedPosition - 1) * holderHeight + baseOffset;\n\n  return {\n    x: SPLICE_CENTER_X, // Center splice in tray\n    y: snappedY,\n    holderPosition: snappedPosition,\n  };\n};\n\n/**\n * Check if a holder position is available in a tray\n */\nexport const isHolderPositionAvailable = (\n  tray: NetworkNode,\n  splice: NetworkNode,\n  position: number,\n  allNodes: NetworkNode[]\n): boolean => {\n  const trayChildren = allNodes.filter(\n    (n) => n.parentId === tray.id && n.type === 'splice' && n.id !== splice.id\n  );\n\n  return !trayChildren.some((child) => {\n    const childPosition = (child.data as NetworkNode['data']).holderPosition;\n    return childPosition === position;\n  });\n};\n\n/**\n * Find the next available holder position\n */\nexport const findNextAvailableHolderPosition = (\n  tray: NetworkNode,\n  splice: NetworkNode,\n  allNodes: NetworkNode[],\n  startFrom: number = 1\n): number => {\n  for (let pos = startFrom; pos <= HOLDERS_PER_TRAY; pos++) {\n    if (isHolderPositionAvailable(tray, splice, pos, allNodes)) {\n      return pos;\n    }\n  }\n\n  // Try positions before startFrom\n  for (let pos = startFrom - 1; pos >= 1; pos--) {\n    if (isHolderPositionAvailable(tray, splice, pos, allNodes)) {\n      return pos;\n    }\n  }\n\n  return 1; // Fallback to position 1\n};\n\n/**\n * Compute the `{ x, y }` offset (relative to the tray node) at which a splice in\n * a given 1-based holder position renders, in single (compact) mode.\n *\n * @remarks\n * Pure geometry — no DOM, React or reactflow. Exposed so consumers hand-building\n * a fibre closure scene (a tray with splices/tubes wired up and rendered through\n * `NetworkDiagram`) position splices with the same formula the renderer uses,\n * instead of forking it. This single-mode variant has no access to splice data;\n * for ribbon splices use {@link calculateSplicePositionFromNumberWithData} with\n * `isRibbonMode: true`. `x` is fixed at {@link SPLICE_CENTER_X}.\n *\n * @example\n * ```ts\n * calculateSplicePositionFromNumber(tray, 1); // { x: SPLICE_CENTER_X, y: 34 }\n * ```\n * @public\n */\nexport const calculateSplicePositionFromNumber = (\n  tray: NetworkNode,\n  holderPosition: number\n): { x: number; y: number } => {\n  // Default to single mode for backward compatibility\n  return calculateSplicePositionFromNumberWithData(tray, holderPosition, false);\n};\n\n/**\n * Compute the `{ x, y }` offset (relative to the tray node) at which a splice in\n * a given 1-based holder position renders, honoring the splice mode.\n *\n * @remarks\n * Pure geometry — no DOM, React or reactflow. The mode-aware companion to\n * {@link calculateSplicePositionFromNumber}: ribbon splices are taller\n * ({@link SPLICE_HEIGHT_PX_RIBBON} in {@link HOLDER_HEIGHT_PX_RIBBON} holders)\n * than single splices ({@link SPLICE_HEIGHT_PX_SINGLE} in\n * {@link HOLDER_HEIGHT_PX_SINGLE} holders); the splice is centered vertically in\n * its holder slot. `x` is fixed at {@link SPLICE_CENTER_X}.\n *\n * @example\n * ```ts\n * calculateSplicePositionFromNumberWithData(tray, 1, true); // ribbon: { x, y: 34 }\n * ```\n * @public\n */\nexport const calculateSplicePositionFromNumberWithData = (\n  tray: NetworkNode,\n  holderPosition: number,\n  isRibbonMode: boolean = false\n): { x: number; y: number } => {\n  const startY = TRAY_HEADER_HEIGHT;\n  const holderHeight = isRibbonMode ? HOLDER_HEIGHT_PX_RIBBON : HOLDER_HEIGHT_PX_SINGLE;\n  const spliceHeight = isRibbonMode ? SPLICE_HEIGHT_PX_RIBBON : SPLICE_HEIGHT_PX_SINGLE;\n  // Center splice vertically in holder slot\n  const baseOffset = (holderHeight - spliceHeight) / 2;\n  const spliceY = startY + (holderPosition - 1) * holderHeight + baseOffset;\n\n  return {\n    x: SPLICE_CENTER_X, // Center splice in tray\n    y: spliceY,\n  };\n};\n","import { NetworkNode } from '../types';\nimport { HANDLE_EXTENSION_PX } from './constants';\nimport { TRAY_WIDTH_PX, getTrayNodeHeight } from './splice-positioning';\n\n/**\n * Panel header height in pixels\n */\nexport const PANEL_HEADER_HEIGHT = 32;\n\n/**\n * Coupler slot height in pixels\n */\nexport const COUPLER_SLOT_HEIGHT_PX = 30;\n\n/**\n * Default number of couplers per panel\n */\nexport const COUPLERS_PER_PANEL = 24;\n\n/**\n * Default coupler width in pixels\n */\nexport const COUPLER_WIDTH_PX = 80;\n\n/**\n * Default panel width in pixels\n */\nexport const PANEL_WIDTH_PX = 400;\n\n/**\n * Default panel height in pixels\n */\nexport const PANEL_HEIGHT_PX = 300;\n\n/**\n * Panel border width in pixels\n */\nexport const PANEL_BORDER_WIDTH = 2;\n\n/**\n * Panel padding in pixels\n */\nexport const PANEL_PADDING = 8;\n\n/**\n * Spacing between tubes and trays in patch panels (horizontal spacing)\n */\nexport const PANEL_TUBE_TRAY_SPACING = 120;\n\n/**\n * Calculate coupler dimensions based on node data\n * \n * @param node - The coupler node with optional couplerFiberCount in data\n * @returns Object with width and height in pixels\n * @public\n * \n * @example\n * ```ts\n * const node = { type: 'coupler', data: { couplerFiberCount: 2 } };\n * const dims = getCouplerDimensions(node);\n * // Returns: { width: 80, height: 30 }\n * ```\n */\nexport function getCouplerDimensions(node: NetworkNode): { width: number; height: number } {\n  const { couplerFiberCount } = node.data || {};\n  const normalizedFibreCount = typeof couplerFiberCount === 'number' && [1, 2, 4].includes(couplerFiberCount)\n    ? couplerFiberCount\n    : 2;\n  const portCount = Math.max(1, Math.min(4, normalizedFibreCount));\n  const handleSize = 7;\n  const handleSpacing = 3;\n  const padding = 3;\n  const minWidth = 60;\n  const minHeight = 30;\n  \n  const totalHandlesHeight = (portCount * handleSize) + ((portCount - 1) * handleSpacing);\n  const height = Math.max(minHeight, totalHandlesHeight + (padding * 2));\n  const width = Math.max(minWidth, 80);\n  \n  return { width, height };\n}\n\n/**\n * Calculate tube dimensions based on node data\n * \n * @param node - The tube node with optional fibersPerTube in data (default: 12)\n * @returns Object with width and height in pixels\n * @public\n * \n * @example\n * ```ts\n * const node = { type: 'tube', data: { fibersPerTube: 12 } };\n * const dims = getTubeDimensions(node);\n * // Returns: { width: calculated, height: 20 }\n * ```\n */\nexport function getTubeDimensions(node: NetworkNode): { width: number; height: number } {\n  const { fibersPerTube = 12 } = node.data || {};\n  const fibers = Math.max(1, Math.min(24, Number(fibersPerTube) || 12));\n  const handleSize = 14;\n  const handleSpacing = 4;\n  const padding = 1;\n  const borderWidth = 2;\n  const height = 20;\n  const totalHandlesWidth = (fibers * handleSize) + ((fibers - 1) * handleSpacing);\n  const width = Math.max(60, totalHandlesWidth + (padding * 2) + (borderWidth * 2));\n  \n  return { width, height };\n}\n\n/**\n * Get tray dimensions\n * \n * Tray dimensions are fixed and do not depend on node data.\n * \n * @param _node - The tray node (unused, dimensions are fixed)\n * @returns Object with fixed width (TRAY_WIDTH_PX) and height (0)\n * @public\n */\nexport function getTrayDimensions(node: NetworkNode): { width: number; height: number } {\n  return {\n    width: TRAY_WIDTH_PX,\n    height: getTrayNodeHeight(node.data?.mode === 'ribbon'),\n  };\n}\n\n/**\n * Get node dimensions based on node type\n */\nfunction getNodeDimensions(node: NetworkNode): { width: number; height: number } {\n  if (node.type === 'coupler') {\n    return getCouplerDimensions(node);\n  } else if (node.type === 'tube') {\n    return getTubeDimensions(node);\n  } else if (node.type === 'splice-tray') {\n    return getTrayDimensions(node);\n  }\n  // Default fallback\n  return { width: 80, height: 30 };\n}\n\n/**\n * Calculate the maximum right edge (x + width) of child nodes\n * @param childNodes - Array of child nodes to check\n * @returns Maximum right edge position in pixels\n */\nfunction calculateMaxChildRight(childNodes: NetworkNode[]): number {\n  let maxRight = 0;\n  \n  for (const child of childNodes) {\n    const dims = getNodeDimensions(child);\n    const childRight = (child.position?.x || 0) + dims.width;\n    \n    if (childRight > maxRight) {\n      maxRight = childRight;\n    }\n  }\n  \n  return maxRight;\n}\n\n/**\n * Calculate the maximum bottom edge (y + height) of child nodes\n * @param childNodes - Array of child nodes to check\n * @param startY - Starting Y position (e.g., header height)\n * @returns Maximum bottom edge position in pixels\n */\nfunction calculateMaxChildBottom(childNodes: NetworkNode[], startY: number = 0): number {\n  let maxBottom = startY;\n  \n  for (const child of childNodes) {\n    const dims = getNodeDimensions(child);\n    const childBottom = (child.position?.y || 0) + dims.height;\n    \n    if (childBottom > maxBottom) {\n      maxBottom = childBottom;\n    }\n  }\n  \n  return maxBottom;\n}\n\n/**\n * Calculate panel width based on content\n * \n * @param panelNode - The panel node with optional panelWidth in data\n * @returns Panel width in pixels, or default PANEL_WIDTH_PX if not specified\n * @public\n */\nexport function getPanelWidth(panelNode: NetworkNode): number {\n  return panelNode.data?.panelWidth || PANEL_WIDTH_PX;\n}\n\n/**\n * Calculate panel height based on content\n * \n * @param panelNode - The panel node with optional panelHeight in data\n * @returns Panel height in pixels, or default PANEL_HEIGHT_PX if not specified\n * @public\n */\nexport function getPanelHeight(panelNode: NetworkNode): number {\n  return panelNode.data?.panelHeight || PANEL_HEIGHT_PX;\n}\n\n/**\n * Get inner content width (panel width minus borders/padding)\n * \n * Calculates the usable width inside a panel after accounting for borders and padding.\n * \n * @param panelNode - The panel node\n * @returns Inner content width in pixels\n * @public\n */\nexport function getInnerContentWidth(panelNode: NetworkNode): number {\n  const width = getPanelWidth(panelNode);\n  return width - (PANEL_BORDER_WIDTH * 2) - (PANEL_PADDING * 2);\n}\n\n/**\n * Calculate coupler right X position\n * \n * Calculates the X position for the right edge of a coupler within a panel.\n * \n * @param panelNode - The panel node containing the coupler\n * @param couplerWidth - Width of the coupler in pixels\n * @returns X position for the right edge of the coupler\n * @public\n */\nexport function calculateCouplerRightX(panelNode: NetworkNode, couplerWidth: number): number {\n  const innerWidth = getInnerContentWidth(panelNode);\n  return innerWidth - couplerWidth;\n}\n\n/**\n * Calculate slot position from Y coordinate\n * \n * Converts a Y pixel coordinate to a slot number (1-indexed).\n * \n * @param y - Y coordinate in pixels\n * @param slotHeight - Height of each slot in pixels (default: COUPLER_SLOT_HEIGHT_PX)\n * @returns Slot position (1-indexed)\n * @public\n */\nexport function calculateSlotPositionFromY(y: number, slotHeight: number = COUPLER_SLOT_HEIGHT_PX): number {\n  return Math.floor(y / slotHeight) + 1;\n}\n\n/**\n * Calculate Y coordinate from slot position\n * \n * Converts a slot number (1-indexed) to a Y pixel coordinate.\n * \n * @param slot - Slot position (1-indexed)\n * @param slotHeight - Height of each slot in pixels (default: COUPLER_SLOT_HEIGHT_PX)\n * @returns Y coordinate in pixels\n * @public\n */\nexport function calculateYFromSlotPosition(slot: number, slotHeight: number = COUPLER_SLOT_HEIGHT_PX): number {\n  return (slot - 1) * slotHeight;\n}\n\n/**\n * Snap coupler to nearest slot position\n * \n * Snaps a coupler's position to the nearest valid slot within a panel.\n * \n * @param coupler - The coupler node to snap\n * @param panel - The panel node containing the coupler\n * @param position - Current position { x, y }\n * @returns Snapped position with optional slotPosition\n * @public\n */\nexport function snapToCouplerPosition(\n  coupler: NetworkNode,\n  panel: NetworkNode,\n  position: { x: number; y: number }\n): { x: number; y: number; slotPosition?: number } {\n  const slotHeight = COUPLER_SLOT_HEIGHT_PX;\n  const slot = calculateSlotPositionFromY(position.y, slotHeight);\n  const snappedY = calculateYFromSlotPosition(slot, slotHeight);\n  \n  return {\n    x: position.x,\n    y: snappedY,\n    slotPosition: slot,\n  };\n}\n\n/**\n * Calculate panel dimensions based on child nodes\n * \n * Automatically calculates the width and height of a panel based on its child nodes.\n * Supports special layouts for patch panels with tubes, trays, and couplers.\n * \n * @param panelNode - The panel node\n * @param childNodes - Array of child nodes within the panel\n * @returns Object with calculated width and height in pixels\n * @public\n * \n * @example\n * ```ts\n * const panel = { id: 'panel-1', type: 'patch-panel', data: {} };\n * const children = [tubeNode, trayNode, couplerNode];\n * const dims = calculatePanelDimensions(panel, children);\n * ```\n */\nexport function calculatePanelDimensions(\n  panelNode: NetworkNode,\n  childNodes: NetworkNode[]\n): { width: number; height: number } {\n  if (childNodes.length === 0) {\n    return {\n      width: panelNode.data?.panelWidth || PANEL_WIDTH_PX,\n      height: panelNode.data?.panelHeight || PANEL_HEIGHT_PX,\n    };\n  }\n  \n  // Check if this panel has tubes, trays, and couplers (patch panel layout)\n  const hasTubes = childNodes.some(n => n.type === 'tube');\n  const hasTrays = childNodes.some(n => n.type === 'splice-tray');\n  const hasCouplers = childNodes.some(n => n.type === 'coupler');\n  \n  let width: number;\n  \n  if (hasTubes && hasTrays && hasCouplers) {\n    // Patch panel with tubes, trays, and couplers\n    // Layout: [minimal padding] [tube] [spacing] [tray] [coupler] [handle extension] [minimal padding]\n    // Content is centered with minimal equal padding on both sides\n    \n    // Find the leftmost tube and rightmost coupler\n    let tubeWidth = 0;\n    let couplerWidth = 0;\n    \n    for (const child of childNodes) {\n      if (child.type === 'tube') {\n        const dims = getTubeDimensions(child);\n        if (dims.width > tubeWidth) {\n          tubeWidth = dims.width;\n        }\n      } else if (child.type === 'coupler') {\n        const dims = getCouplerDimensions(child);\n        if (dims.width > couplerWidth) {\n          couplerWidth = dims.width;\n        }\n      }\n    }\n    \n    // Calculate content width: tube + spacing + tray + coupler + handle extension\n    const contentWidth = tubeWidth + PANEL_TUBE_TRAY_SPACING + TRAY_WIDTH_PX + couplerWidth + HANDLE_EXTENSION_PX;\n    // Add minimal equal padding on both sides to center content (reduced from PANEL_PADDING)\n    const sidePadding = 0; // Minimal padding to make panel thinner\n    width = sidePadding + contentWidth + sidePadding;\n  } else {\n    // Standard layout: calculate based on rightmost child\n    const maxRight = calculateMaxChildRight(childNodes);\n    \n    // Add minimal padding on the right (handle extension extends beyond panel, not included)\n    const sidePadding = 4; // Reduced from PANEL_PADDING (8px) to reduce width\n    width = maxRight + sidePadding;\n  }\n  \n  // Ensure width is at least the minimum or explicit width\n  width = Math.max(\n    panelNode.data?.panelWidth || PANEL_WIDTH_PX,\n    width\n  );\n  \n  // Calculate height based on bottommost child\n  const maxBottom = calculateMaxChildBottom(childNodes, PANEL_HEADER_HEIGHT);\n  \n  const height = Math.max(\n    panelNode.data?.panelHeight || PANEL_HEIGHT_PX,\n    maxBottom + PANEL_PADDING\n  );\n  \n  return { width, height };\n}\n\n/**\n * Calculate spacing between couplers\n * \n * Calculates the vertical spacing needed between couplers to fit them evenly\n * within the available height.\n * \n * @param numCouplers - Number of couplers to space\n * @param couplerHeight - Height of each coupler in pixels\n * @param availableHeight - Total available height in pixels\n * @returns Spacing between couplers in pixels (includes coupler height)\n * @public\n */\nexport function calculateCouplerSpacing(\n  numCouplers: number,\n  couplerHeight: number,\n  availableHeight: number\n): number {\n  if (numCouplers === 0) return 0;\n  if (numCouplers === 1) return couplerHeight;\n  \n  const totalCouplerHeight = numCouplers * couplerHeight;\n  const availableSpacing = availableHeight - totalCouplerHeight;\n  const spacingBetweenCouplers = Math.max(5, availableSpacing / (numCouplers - 1));\n  return couplerHeight + spacingBetweenCouplers;\n}\n\n/**\n * Calculate coupler slot spacing\n * \n * Alias for calculateCouplerSpacing. Calculates spacing between coupler slots.\n * \n * @param numCouplers - Number of couplers to space\n * @param couplerHeight - Height of each coupler in pixels\n * @param availableHeight - Total available height in pixels\n * @returns Spacing between coupler slots in pixels\n * @public\n */\nexport function calculateCouplerSlotSpacing(\n  numCouplers: number,\n  couplerHeight: number,\n  availableHeight: number\n): number {\n  return calculateCouplerSpacing(numCouplers, couplerHeight, availableHeight);\n}\n\n/**\n * Spacing between tubes and trays in closures (horizontal spacing)\n */\nexport const CLOSURE_TUBE_TRAY_SPACING = 20;\n\n/**\n * Default closure width in pixels\n */\nexport const CLOSURE_WIDTH_PX = 400;\n\n/**\n * Default closure height in pixels\n */\nexport const CLOSURE_HEIGHT_PX = 300;\n\n/**\n * Calculate closure dimensions based on child nodes\n * \n * Automatically calculates the width and height of a closure based on its child nodes.\n * Supports special layouts for closures with tubes and trays.\n * Closure layout: [padding] [left tubes] [spacing] [trays] [spacing] [right tubes] [padding]\n * \n * @param closureNode - The closure node\n * @param childNodes - Array of child nodes within the closure\n * @returns Object with calculated width and height in pixels\n * @public\n * \n * @example\n * ```ts\n * const closure = { id: 'closure-1', type: 'closure', data: {} };\n * const children = [leftTubeNode, trayNode, rightTubeNode];\n * const dims = calculateClosureDimensions(closure, children);\n * ```\n */\nexport function calculateClosureDimensions(\n  closureNode: NetworkNode,\n  childNodes: NetworkNode[]\n): { width: number; height: number } {\n  if (childNodes.length === 0) {\n    return {\n      width: closureNode.data?.closureWidth || closureNode.data?.panelWidth || CLOSURE_WIDTH_PX,\n      height: closureNode.data?.closureHeight || closureNode.data?.panelHeight || CLOSURE_HEIGHT_PX,\n    };\n  }\n  \n  // Check if this closure has tubes and trays (closure layout)\n  const hasTubes = childNodes.some(n => n.type === 'tube');\n  const hasTrays = childNodes.some(n => n.type === 'splice-tray');\n  \n  let width: number;\n  \n  if (hasTubes && hasTrays) {\n    // Closure with tubes and trays\n    // Layout: [padding] [left tube] [spacing] [tray] [spacing] [right tube] [handle extension]\n    \n    // Find the leftmost and rightmost tubes, and trays\n    let leftTubeWidth = 0;\n    let rightTubeWidth = 0;\n    let trayWidth = 0;\n    \n    // Group tubes by position to determine left vs right\n    const tubes = childNodes.filter(n => n.type === 'tube');\n    const trays = childNodes.filter(n => n.type === 'splice-tray');\n    \n    if (tubes.length > 0 && trays.length > 0) {\n      // Determine which tubes are left vs right based on position relative to trays\n      const trayPositions = trays.map(t => t.position?.x || 0);\n      const avgTrayX = trayPositions.reduce((a, b) => a + b, 0) / trayPositions.length;\n      \n      for (const tube of tubes) {\n        const dims = getTubeDimensions(tube);\n        const tubeX = tube.position?.x || 0;\n        \n        if (tubeX < avgTrayX) {\n          // Left tube\n          if (dims.width > leftTubeWidth) {\n            leftTubeWidth = dims.width;\n          }\n        } else {\n          // Right tube\n          if (dims.width > rightTubeWidth) {\n            rightTubeWidth = dims.width;\n          }\n        }\n      }\n      \n      // Get tray width\n      if (trays.length > 0) {\n        trayWidth = TRAY_WIDTH_PX;\n      }\n      \n      // Calculate width: padding + left tube + spacing + tray + spacing + right tube + handle extension\n      width = PANEL_PADDING + leftTubeWidth + CLOSURE_TUBE_TRAY_SPACING + trayWidth + CLOSURE_TUBE_TRAY_SPACING + rightTubeWidth + HANDLE_EXTENSION_PX;\n    } else {\n      // Fallback: calculate based on rightmost child\n      const maxRight = calculateMaxChildRight(childNodes);\n      width = maxRight + PANEL_PADDING + HANDLE_EXTENSION_PX;\n    }\n  } else {\n    // Standard layout: calculate based on rightmost child\n    const maxRight = calculateMaxChildRight(childNodes);\n    \n    // Add padding and handle extension space\n    width = maxRight + PANEL_PADDING + HANDLE_EXTENSION_PX;\n  }\n  \n  // Ensure width is at least the minimum or explicit width\n  width = Math.max(\n    closureNode.data?.closureWidth || closureNode.data?.panelWidth || CLOSURE_WIDTH_PX,\n    width\n  );\n  \n  // Calculate height based on bottommost child\n  const maxBottom = calculateMaxChildBottom(childNodes, PANEL_HEADER_HEIGHT);\n  \n  const height = Math.max(\n    closureNode.data?.closureHeight || closureNode.data?.panelHeight || CLOSURE_HEIGHT_PX,\n    maxBottom + PANEL_PADDING\n  );\n  \n  return { width, height };\n}\n\n","/**\n * Rack device width configuration\n * @public\n */\nimport type {\n  Connection,\n  Edge as ReactFlowEdge,\n  EdgeTypes,\n  Node as ReactFlowNode,\n  NodeTypes,\n  ConnectionMode,\n  FitViewOptions,\n  ReactFlowInstance,\n  Viewport,\n} from \"@xyflow/react\";\nimport type { FibreFlowCable } from \"./fibre-flow\";\n\nexport enum Width {\n  /** Full width device (19\" rack width) */\n  FULL = \"full\",\n  /** Half width device (9.5\" rack width) */\n  HALF = \"half\",\n}\n\n/**\n * Port configuration for network devices\n * @public\n */\n/**\n * Port type enumeration\n * @public\n */\nexport type PortType =\n  | \"ethernet\"\n  | \"rj45\"\n  | \"fiber\"\n  | \"console\"\n  | \"mgmt\"\n  | \"usb\"\n  | \"vga\"\n  | \"serial\"\n  | \"power_ac\"\n  | \"power_c13\"\n  | \"power_c19\";\n\n/**\n * Cable types used for connected network ports.\n * @public\n */\nexport type PortCableType =\n  | \"smf\"\n  | \"cat6\"\n  | \"om3\"\n  | \"om4\"\n  | \"om5\"\n  | \"cat5e\"\n  | \"cat6a\"\n  | \"cat7\"\n  | \"fiber\"\n  | \"ethernet\";\n\n/**\n * Device image fit options.\n *\n * Values mirror object-fit terms for API ergonomics and are mapped to valid\n * background-size values by renderers where needed.\n *\n * @public\n */\nexport type DeviceImageFit =\n  | \"cover\"\n  | \"contain\"\n  | \"contain-height\"\n  | \"fill\"\n  | \"none\"\n  | \"scale-down\";\n\n/**\n * Device image repeat options.\n * @public\n */\nexport type DeviceImageRepeat =\n  | \"no-repeat\"\n  | \"repeat\"\n  | \"repeat-x\"\n  | \"repeat-y\";\n\n/**\n * Shared image configuration for rack-mounted devices and device nodes.\n * @public\n */\nexport interface DeviceImageOptions {\n  /** Optional front image URL for device background (PNG, SVG, JPEG, etc.) */\n  frontImageUrl?: string;\n  /** Optional rear image URL for device background (PNG, SVG, JPEG, etc.) */\n  rearImageUrl?: string;\n  /** Optional attribution URL for front image (e.g. source/license link) */\n  frontImageAttribution?: string;\n  /** Optional attribution URL for rear image (e.g. source/license link) */\n  rearImageAttribution?: string;\n  /** Optional background sizing for device images */\n  imageFit?: DeviceImageFit;\n  /** Optional background position for device images */\n  imagePosition?: string;\n  /** Optional background repeat for device images */\n  imageRepeat?: DeviceImageRepeat;\n  /** When true, stretch device image to fill the device bounds (overrides imageFit) */\n  stretchToFit?: boolean;\n}\n\n/**\n * Explicit handle placement for a device/PDU port.\n * @public\n */\nexport interface PortPosition {\n  /**\n   * Handle side on the node. When the offset is omitted, ports on the same\n   * side and face are distributed automatically across groups.\n   */\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n  /** Side offset as percentage (0-100) */\n  offsetPercent?: number;\n  /** Side offset in pixels */\n  offsetPx?: number;\n  /** Absolute X position as percentage (0-100) of the owning face frame (element centre). Requires `yPercent`. */\n  xPercent?: number;\n  /** Absolute Y position as percentage (0-100) of the owning face frame (element centre). Requires `xPercent`. */\n  yPercent?: number;\n  /**\n   * Authored element width as a percentage (0-100) of the owning face frame.\n   *\n   * Contractual: percent-based geometry is the render contract shared by ports,\n   * cages, and bays across every surface (2D elevation, 3D, faceplate). Paired\n   * with `heightPercent` it is the element's bounding box, and because it is\n   * frame-relative it survives image rescaling.\n   */\n  widthPercent?: number;\n  /**\n   * Authored element height as a percentage (0-100) of the owning face frame.\n   * Contractual — see {@link PortPosition.widthPercent}.\n   */\n  heightPercent?: number;\n}\n\n/**\n * Authoring input for port/bay geometry: the canonical {@link PortPosition}\n * object, or a semantic edge string such as `'left-bottom'` that\n * {@link normalizePortPositionInput} expands. Renderers always normalize\n * before reading fields.\n * @public\n */\nexport type PortPositionInput = PortPosition | string;\n\n/**\n * IEC 60320 appliance couplers modelled by the renderer and the power\n * validation mate table. Inlets are even-numbered, connectors odd.\n * @public\n */\nexport type PowerConnectorType =\n  | \"C13\"\n  | \"C14\"\n  | \"C15\"\n  | \"C16\"\n  | \"C19\"\n  | \"C20\"\n  | \"C21\"\n  | \"C22\";\n\n/**\n * Recommended simple port input for device and PDU rendering.\n * Pass a flat array in `ports`; `react-networks` groups entries into front/rear\n * `PortBlock[]` internally using each port's `face` and `group`.\n * @public\n */\nexport interface DevicePort {\n  /** Stable handle identifier override (defaults to `group + label`, or `label` for power) */\n  id?: string;\n  /** Display label for the port */\n  label: string;\n  /** Front/rear device face. `back` is accepted as an alias for `rear`. */\n  face?: DeviceFace | \"back\";\n  /** Optional block/group name (for example `Network Ports`, `QSFP28`, `Power Ports`) */\n  group?: string;\n  /** Optional source/detected family label used for visual kind inference */\n  sourceTypeLabel?: string;\n  /** Whether the port is currently connected */\n  connected?: boolean;\n  /** Type of port for styling and validation */\n  type?: PortType;\n  /** Cable type for connection validation */\n  cableType?: PortCableType;\n  /** Optional power connector type for rendering (mainly for PDU ports) */\n  connectorType?: PowerConnectorType;\n  /** Optional compatibility keys. When omitted, defaults are derived from type/cable/connector data. */\n  compatibility?: string[];\n  /** Optional sort order within a face/group bucket */\n  order?: number;\n  /**\n   * Optional placement hint.\n   * Use `side` with `offsetPercent`/`offsetPx` for edge-anchored placement,\n   * or `xPercent`/`yPercent` for exact coordinates.\n   * With no resolvable placement, the renderer distributes ports across the\n   * whole face, including ports from other groups. A lone `xPercent` or\n   * `yPercent` is incomplete and uses that automatic placement.\n   * Semantic edge strings (e.g. `'left-bottom'`) are also accepted and\n   * normalized by the renderer.\n   */\n  position?: PortPositionInput;\n}\n\nexport interface Port {\n  /** Stable handle identifier override (defaults to `label`) */\n  id?: string;\n  /** Display label for the port */\n  label: string;\n  /** Optional source/detected family label used for visual kind inference */\n  sourceTypeLabel?: string;\n  /** Whether the port is currently connected */\n  connected: boolean;\n  /** Type of port for styling and validation */\n  type?: PortType;\n  /** Cable type for connection validation */\n  cableType?: PortCableType;\n  /** Optional power connector type for rendering (mainly for PDU ports) */\n  connectorType?: PowerConnectorType;\n  /** Optional compatibility keys. When omitted, defaults are derived from type/cable/connector data. */\n  compatibility?: string[];\n  /** Optional explicit handle placement (object or semantic edge string) */\n  position?: PortPositionInput;\n}\n\n/**\n * Group of related ports on a device\n * @public\n */\nexport interface PortBlock {\n  /** Name of the port group */\n  name: string;\n  /** Array of ports in this group */\n  ports: Port[];\n}\n\n/**\n * Containable bay classification. Mirrors dc_designer `bay_kind`.\n *\n * A bay HOLDS a field-replaceable unit (drive, PSU, fan, line card, …) and —\n * unlike a port — never mounts a cable handle. Transceiver cages are the one\n * capability overlap (containable AND connectable): they are authored as\n * ports with `bay` metadata (see `TemplatePort`) so the frozen handle-id\n * grammar and cable endpoints survive optic swaps, and therefore do NOT appear\n * as a `bayKind` value here — `'transceiver_cage'` lives on `TemplatePort.bay`.\n *\n * @public\n */\nexport type TemplateBayKind =\n  | \"storage_bay\"\n  | \"psu_bay\"\n  | \"fan_bay\"\n  | \"pcie_slot\"\n  | \"linecard_slot\"\n  | \"supervisor_slot\"\n  | \"fabric_slot\"\n  | \"io_slot\"\n  | \"other\";\n\n/**\n * Occupancy/health of a single bay instance.\n * `predicted` marks a modelled (not yet observed) state.\n * @public\n */\nexport type DeviceBayStatus = \"ok\" | \"failed\" | \"predicted\";\n\n/**\n * Per-bay instance state, keyed by bay id on node data. Rides on node data\n * alongside `handleSideMap`; the renderer paints occupancy/failed treatment\n * from it.\n * @public\n */\nexport interface DeviceBayInstanceState {\n  /** Whether the bay currently holds a unit. */\n  occupied?: boolean;\n  /** Health/occupancy status driving the bay marker's visual treatment. */\n  status?: DeviceBayStatus;\n}\n\n/**\n * A per-slot sub-rectangle inside a bay (e.g. one socket in a DIMM bank, one\n * drive position in a carrier). Geometry uses the same `PortPosition` percent\n * contract as ports and bays.\n * @public\n */\nexport interface DeviceBaySlot {\n  /** Stable identifier for the slot (unique within its bay). */\n  id: string;\n  /** Human-readable label. */\n  label: string;\n  /** Slot centre + bounding box as percentages of the owning face frame. */\n  position?: PortPosition;\n}\n\n/**\n * Containable-only element on a device face: the renderer draws an outline +\n * label for it but never mounts a React Flow handle. The node-data analogue of\n * the template `TemplateBay`; `face` selects the owning face (default\n * front, `back` accepted as an alias for `rear`).\n * @public\n */\nexport interface DeviceBay {\n  /** Stable identifier for the bay (unique within the device). */\n  id: string;\n  /** Human-readable label drawn on the bay outline. */\n  label: string;\n  /** Bay classification (drives visual treatment and click payloads). */\n  bayKind: TemplateBayKind;\n  /** Front/rear device face. `back` is accepted as an alias for `rear`. */\n  face?: DeviceFace | \"back\";\n  /** Bay centre + bounding box as percentages of the owning face frame. */\n  position?: PortPosition;\n  /** Optional per-slot sub-rectangles. */\n  slots?: DeviceBaySlot[];\n}\n\n/**\n * Network node types supported by the diagram\n * @public\n */\nexport type NetworkNodeType =\n  | \"rack\"\n  | \"switch\"\n  | \"router\"\n  | \"server\"\n  | \"fiber\"\n  | \"patch-panel\"\n  | \"device\"\n  | \"vertical-pdu\"\n  | \"distribution-board\"\n  | \"splice\"\n  | \"splice-tray\"\n  | \"tube\"\n  | \"cable\"\n  | \"multi-tube-cable\"\n  | \"coupler\"\n  | \"closure\"\n  | \"fibre-split\"\n  | \"fibre-flow-cable\"\n  | \"fibre-flow-closure\";\n\n/**\n * Device status states\n * @public\n */\nexport type DeviceStatus = \"active\" | \"inactive\" | \"maintenance\";\n\n/**\n * Device face orientations\n * @public\n */\nexport type DeviceFace = \"front\" | \"rear\";\n\nexport type DiagramPortHandleRef = {\n  nodeId: string;\n  handleId: string;\n};\n\nexport type DiagramPortClickPayload = {\n  nodeId: string;\n  handleId: string;\n  groupName: string;\n  portLabel: string;\n  face: DeviceFace;\n};\n\n/**\n * Reference to a single bay on a node — the bay analogue of\n * {@link DiagramPortHandleRef}. Used by `NetworkDiagram.highlightedBays`.\n * @public\n */\nexport type DiagramBayRef = {\n  nodeId: string;\n  bayId: string;\n};\n\n/**\n * Payload for a bay click — the bay analogue of {@link DiagramPortClickPayload}\n * (`bayId`/`bayKind`/`bayLabel` in place of `handleId`/`groupName`/`portLabel`).\n * @public\n */\nexport type DiagramBayClickPayload = {\n  nodeId: string;\n  bayId: string;\n  bayKind: TemplateBayKind;\n  bayLabel: string;\n  face: DeviceFace;\n};\n\n/**\n * Rack rendering layout modes.\n * @public\n */\nexport type RackLayoutMode = \"single-face\" | \"split-face\";\n\n/**\n * Rack mounting behavior for a device.\n * @public\n */\nexport type RackMountMode = \"front\" | \"rear\" | \"both\";\n\n/**\n * Power port side configuration\n * @public\n */\nexport type PowerSide = \"left\" | \"right\";\n\n/**\n * U numbering direction for racks\n * @public\n */\nexport type UNumberingDirection = \"bottom-up\" | \"top-down\";\n\n/**\n * Shared node data fields available across node types.\n * Specific node kinds can narrow these fields via `NetworkNodeDataByType`.\n * @public\n */\nexport interface NetworkNodeDataBase extends DeviceImageOptions {\n  /** Display label for the node */\n  label: string;\n  /** Current status of the node/device */\n  status?: DeviceStatus | string;\n  /** Simple flat port input or grouped port blocks. Use `portGroups` / `rearPortGroups` for explicit grouped control. */\n  ports?: DevicePort[] | PortBlock[] | number;\n  /** Advanced grouped front port input */\n  portGroups?: PortBlock[];\n  /** Advanced grouped rear port input */\n  rearPortGroups?: PortBlock[];\n  /** Grouped rear port blocks (rack JSON alias for `rearPortGroups`) */\n  rearPorts?: PortBlock[];\n  /** List of connected node IDs */\n  connections?: string[];\n  /** NetBox integration ID */\n  netboxId?: number | string;\n  /** NetBox object type */\n  netboxType?: \"device\" | \"rack\" | \"interface\" | \"cable\" | string;\n  /** Inventory integration ID */\n  inventoryId?: number | string;\n  /** Inventory object type */\n  inventoryType?: \"device\" | \"rack\" | \"interface\" | \"cable\" | string;\n  /** Rack height in U units */\n  uHeight?: number;\n  /** Device position within rack in U units */\n  uPosition?: number;\n  /** Whether this node is currently mounted inside a rack container */\n  isRackMounted?: boolean;\n  /** Front/rear orientation for devices and rack faces */\n  face?: DeviceFace;\n  /** Rack rendering layout mode */\n  rackLayout?: RackLayoutMode;\n  /** Gap in pixels between front and rear faces in split-face mode */\n  rackFaceGapPx?: number;\n  /** Device mount behavior within a rack */\n  mountMode?: RackMountMode;\n  /** Device type specification */\n  deviceType?: string;\n\n  /**\n   * When false, device elevation renders as a status-colored block without front/rear photos.\n   * Defaults to true (show photos when URLs exist).\n   */\n  showDeviceImages?: boolean;\n  /** Device manufacturer */\n  manufacturer?: string;\n  /** Device role in network */\n  role?: string;\n  /** Device width configuration */\n  width?: Width | number;\n  /** Power port side for rear face */\n  powerSide?: PowerSide;\n  /** Vertical PDU height in centimeters */\n  heightCm?: number;\n  /** Number of AC power ports */\n  powerPorts?: number;\n  /** Optional explicit PDU connector sequence */\n  portTypes?: Array<\"C13\" | \"C19\">;\n  /** Optional grouped PDU ports (supports custom positions/types) */\n  pduPortGroups?: PortBlock[];\n  /** Vertical PDU width in pixels */\n  widthPx?: number;\n  /** Optional management unit position for vertical PDUs */\n  mgmt_unit?: \"top\" | \"middle\" | \"bottom\";\n  /** U numbering direction for rack nodes */\n  uNumberingDirection?: UNumberingDirection;\n  /** Custom header text for rack nodes */\n  customHeaderText?: string;\n  /** Rack UI marker visibility */\n  showUMarkers?: boolean;\n  /** Holder position within tray (1-12) for splice nodes */\n  holderPosition?: number;\n  /** Loss value in dB for splice nodes */\n  lossDb?: number;\n  /** Splice mode */\n  mode?: \"single\" | \"ribbon\";\n  /** Legacy ribbon flag */\n  ribbon?: boolean;\n  /** Ribbon fiber IDs */\n  ribbonFiberIds?: number[];\n  /** Connector type for coupler nodes */\n  connector?: \"SC\" | \"LC\";\n  /** Number of fibers for coupler nodes (1, 2, or 4) */\n  couplerFiberCount?: 1 | 2 | 4;\n  /** Optional port labels for left side of coupler */\n  portLabelsLeft?: string[];\n  /** Optional port labels for right side of coupler */\n  portLabelsRight?: string[];\n  /** When true, coupler output (right) ports inherit left-side fiber color instead of default yellow. */\n  passThroughColor?: boolean;\n  /** Slot position for couplers inside panels (1-based, vertical slots aligned to right edge) */\n  slotPosition?: number;\n  /** Width override for panel-like container nodes */\n  panelWidth?: number;\n  /** Height override for panel-like container nodes */\n  panelHeight?: number;\n  /** Width override for closure nodes */\n  closureWidth?: number;\n  /** Height override for closure nodes */\n  closureHeight?: number;\n  /** Optional rack width in pixels (for rack nodes, default: 300px) */\n  rackWidthPx?: number;\n  /** Number of rows for container nodes (patch-panel, closure) */\n  rows?: number;\n  /** Number of columns for container nodes (patch-panel, closure) */\n  cols?: number;\n  /** Number of trays for closure nodes */\n  trayCount?: number;\n  /** Header text for container nodes */\n  header?: string;\n  /** Type of fibre splitter: 'splitter' or 'WDM' (default: 'splitter') */\n  splitterType?: \"splitter\" | \"WDM\";\n  /** Number of input fibers on the left (1 or 2, default: 1) */\n  inputCount?: number;\n  /** Number of output fibers on the right (1-64, default: 8) */\n  outputCount?: number;\n  /** Tube index (1-based) */\n  tubeIndex?: number;\n  /** Tube count for multi-tube cables */\n  tubeCount?: number;\n  /** Tube start index for multi-tube cables */\n  tubeStartIndex?: number;\n  /** Number of fibers per tube */\n  fibersPerTube?: number;\n  /** Total fiber count for cables */\n  fiberCount?: number;\n  /** Cable identifier */\n  cableId?: string;\n  /** Primary node color override */\n  color?: string;\n  /** Rotation in degrees for rotatable nodes */\n  rotation?: number;\n  /** Tube handle orientation */\n  startOn?: \"left-center\" | \"left\" | \"right\";\n  /** Rack metadata for integrations */\n  facilityId?: string | number | null;\n  /** Rack serial metadata */\n  serial?: string | null;\n  /** Asset tag metadata */\n  assetTag?: string | null;\n  /** Site metadata */\n  site?: string;\n  /** Tenant metadata */\n  tenant?: string;\n  /** Primary IPv4 metadata */\n  primaryIp4?: string;\n  /** Primary IPv6 metadata */\n  primaryIp6?: string;\n  /** Rack name metadata */\n  rack?: string | number | null;\n  /** Optional arrays used by inventory/netbox transforms */\n  racks?: unknown[];\n  devices?: unknown[];\n  cables?: unknown[];\n  /** UI color mode metadata */\n  colorMode?: \"light\" | \"dark\" | \"system\";\n  /** Generic dimensions used by some helpers */\n  height?: number;\n  headerHeight?: number;\n  /** Optional precomputed map for handle-to-face resolution */\n  handleSideMap?: Record<string, DeviceFace>;\n  /**\n   * Per-bay instance state keyed by bay id (occupancy/health). Rides on node\n   * data alongside `handleSideMap`; DeviceNode paints bay markers from it.\n   */\n  bayState?: Record<string, DeviceBayInstanceState>;\n  /** Additional extension metadata */\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Rack node data.\n * @public\n */\nexport interface RackNodeData extends NetworkNodeDataBase {\n  ports?: number;\n}\n\n/**\n * What kind of power-distribution equipment a `vertical-pdu` node stands for.\n * The renderer draws them all as a vertical strip; the label is carried so\n * consumers and validation can tell a rack PDU from an RPP, a busway tap, or a\n * sub-panel.\n * @public\n */\nexport type PduBoardType = \"pdu\" | \"rpp\" | \"panel\" | \"busway\" | \"other\";\n\n/** Supply family of a PDU strip. @public */\nexport type PduSupply = \"ac\" | \"dc\";\n\n/**\n * A/B designation of the branch circuit feeding a PDU. `primary` is the A\n * feed, `secondary` the B feed; redundancy rules read it to tell the two\n * halves of a 2N design apart.\n * @public\n */\nexport type PowerFeedType = \"primary\" | \"secondary\";\n\n/**\n * Which half of an IEC 60320 / IEC 60309 pairing a connector is. Inlets on\n * equipment are male (pins); cord connectors are female (sockets).\n * @public\n */\nexport type PowerConnectorGender = \"male\" | \"female\";\n\n/**\n * Vertical PDU node data.\n *\n * Everything here is optional and additive — a PDU authored before the inlet\n * model (label + `powerPorts` + `portTypes`) renders and validates exactly as\n * it did, and the inlet handle is not mounted at all unless inlet data is\n * present.\n *\n * The strip has two electrically distinct sides:\n *\n * - the **inlet** — where a branch circuit from a distribution-board breaker\n *   way lands (fixed wiring: IEC 60309, hardwired, C20, NEMA locking). One per\n *   strip, handle id `inlet`.\n * - the **outlets** — where equipment cords land, handle ids `\"1\"…\"N\"`\n *   top-down under IEC 60320 mate rules.\n *\n * **Reserved handle ids.** `inlet` (and `incomer`, the distribution-board\n * spelling) name the supply inlet. Do not give an outlet in `pduPortGroups` an\n * `id` or `label` that lowercases to either: the strip would mount two handles\n * with the same id. A declared outlet deliberately wins that collision — it\n * classifies as a cord source, not as the supply inlet, so the cords it carries\n * still validate — but the inlet handle then becomes unreachable. Number the\n * outlets, or give them a distinct prefix (`rear-power-1`).\n * @public\n */\nexport interface VerticalPduNodeData extends NetworkNodeDataBase {\n  /**\n   * Free-text inlet connector designation, e.g. `\"IEC 60309 32A\"`,\n   * `\"C20\"`, `\"NEMA L6-30P\"`, `\"hardwired\"`. Free text because the inlet\n   * families a rack PDU can carry are regional and open-ended; the connector\n   * *mate* rules only apply to the IEC 60320 cord side (the outlets).\n   */\n  inletConnector?: string;\n  /** Inlet current rating in amps (the branch-circuit rating the strip takes). */\n  inletAmps?: number;\n  /** Inlet gender — equipment inlets are normally `male`. */\n  inletGender?: PowerConnectorGender;\n  /** A/B designation of the feed landing on the inlet. */\n  feedType?: PowerFeedType;\n  /** What kind of distribution equipment this strip stands for. */\n  boardType?: PduBoardType;\n  /** Supply family. */\n  supply?: PduSupply;\n  /** Nominal supply voltage in volts (integer, e.g. 230). */\n  voltage?: number;\n  /** Supply phase count. Outlets are attributed round-robin across it. */\n  phases?: 1 | 3;\n  /**\n   * Rating in amps of the strip's own outlet/bank overcurrent protection\n   * (internal breakers or fuses), when it has any. PWR-A06 judges the cords\n   * below a strip that declares it against this rating instead of the upstream\n   * way's.\n   */\n  outletProtectionA?: number;\n  /** Optional equipment name, distinct from the drawn `label`. */\n  name?: string;\n}\n\n/**\n * Device-like node data (servers/switches/routers/generic device).\n * @public\n */\nexport interface DeviceNodeData extends NetworkNodeDataBase {\n  ports?: DevicePort[] | PortBlock[] | number;\n  /**\n   * Containable-only bays drawn as outlines + labels (never cable handles).\n   * Each bay's `face` selects the owning face (default front).\n   */\n  bays?: DeviceBay[];\n  /**\n   * Nameplate/maximum power draw in **watts**. Named after NetBox's\n   * `DeviceType.airflow`-adjacent power fields (`maximum_draw`), which are watts\n   * — keep the unit in watts so a NetBox sync is a straight field copy.\n   */\n  maximumDrawW?: number;\n  /**\n   * Allocated (design/budgeted) power draw in **watts**. NetBox's\n   * `allocated_draw`; used by load rules to sum a board way's committed load.\n   */\n  allocatedDrawW?: number;\n  /**\n   * How many power feeds this device is expected to have (NetBox models this as\n   * redundant power ports). `2` marks an A+B device, so losing one feed is\n   * `degraded` rather than unpowered.\n   */\n  expectedFeeds?: 1 | 2;\n}\n\n/**\n * Switch state of a single breaker way.\n * @public\n */\nexport type BreakerSwitchState = \"closed\" | \"open\" | \"tripped\";\n\n/**\n * Phase label carried by a breaker way. Purely a label — the library does not\n * model three-phase electrically (see PRD workstream C non-goals).\n * @public\n */\nexport type BreakerPhase = \"L1\" | \"L2\" | \"L3\" | \"3P\";\n\n/**\n * One breaker way (slot) on a distribution board. A way is not its own node in\n * the physical view: it is a connection endpoint on the board, mounting exactly\n * one power handle.\n * @public\n */\nexport interface BreakerWay {\n  /** Stable identifier for the way (unique within the board). */\n  id: string;\n  /** Human-readable label drawn on the way row (e.g. `1`, `L1-3`). */\n  label: string;\n  /** Pole count (1 = single-pole, 3 = three-pole). */\n  poles?: number;\n  /** Breaker rating in amps. */\n  ratingA?: number;\n  /** Phase label. `3P` (or `poles: 3`) marks a three-phase way. */\n  phase?: BreakerPhase;\n  /**\n   * Circuit voltage this way supplies, overriding the board's: line-to-neutral\n   * for a single-pole way, line-to-line for a 2-pole or three-phase way.\n   */\n  voltageV?: number;\n  /**\n   * Optional explicit placement as percentages of the board frame. Omitted ways\n   * are auto-laid-out in the generated faceplate grid, which is the normal case.\n   */\n  position?: PortPosition;\n}\n\n/**\n * Per-way instance state keyed by way id, mirroring {@link DeviceBayInstanceState}.\n * @public\n */\nexport interface BreakerWayInstanceState {\n  /** Whether the breaker is closed, deliberately open, or tripped. */\n  switchState: BreakerSwitchState;\n  /** Optional health status, reusing the bay status vocabulary. */\n  status?: DeviceBayStatus;\n}\n\n/**\n * Distribution board node data. Boards are free-floating by default; set\n * {@link DistributionBoardNodeData.rackMountable} to opt a board into\n * rack assignment.\n * @public\n */\nexport interface DistributionBoardNodeData extends NetworkNodeDataBase {\n  /** The board's breaker ways, rendered top-to-bottom in column order. */\n  ways: BreakerWay[];\n  /** Per-way switch/health state keyed by way id. */\n  wayState?: Record<string, BreakerWayInstanceState>;\n  /**\n   * Nominal board voltage in volts — the legacy single figure. When neither\n   * {@link DistributionBoardNodeData.phaseVoltageV} nor\n   * {@link DistributionBoardNodeData.lineToLineVoltageV} is set, the load rules\n   * (PWR-B01/B02) use it unchanged for every way: as the line-to-neutral\n   * voltage of single-pole ways and the line-to-line voltage of 2-pole /\n   * three-phase ways. That is only right when all the board's ways are one\n   * kind (PWR-B03 flags boards where it cannot be); declare the explicit pair\n   * for a mixed board.\n   */\n  voltageV?: number;\n  /**\n   * Nominal **line-to-neutral** (phase) voltage in volts — what a single-pole\n   * way supplies (`230` on a 230/400 V board, `120` on 120/208 V, `277` on\n   * 277/480 V). Defaults to `lineToLineVoltageV / √3` when only that is set.\n   */\n  phaseVoltageV?: number;\n  /**\n   * Nominal **line-to-line** voltage in volts — what a 2-pole or three-phase\n   * way supplies (`400`, `208`, `480`). Defaults to `phaseVoltageV × √3` when\n   * only that is set. With no voltage declared at all the rules assume\n   * 120/208 V under `NEC` and 230/400 V under `IEC` (and PWR-B03 says so).\n   */\n  lineToLineVoltageV?: number;\n  /**\n   * Power factor (0–1] the load rules apply when converting nameplate watts to\n   * amps. Defaults to 1 (watts treated as volt-amperes, NetBox's convention).\n   */\n  powerFactor?: number;\n  /**\n   * What kind of distribution asset this board is. Drives the header badge\n   * only — an `rpp` is drawn `RPP`, a `busway` `BUSWAY`, and anything else\n   * (including omitted) falls back to `DB`. Purely a labelling concern: every\n   * board type shares one electrical model of ways and an incomer.\n   */\n  boardType?: PduBoardType;\n  /**\n   * Rating of the board's incomer/main device in amps, shown in the header.\n   * Distinct from a way's `ratingA`: this is the device feeding the busbar,\n   * not one of the outgoing ways.\n   */\n  incomerRatingA?: number;\n  /** Which code vocabulary consumer rules should read this board under. */\n  codeMode?: \"NEC\" | \"IEC\";\n  /**\n   * Continuous-load derate ceiling as a percentage of a way's rating. Defaults\n   * by `codeMode`: 80 under `NEC` (the default mode), 100 under `IEC`.\n   */\n  maxUtilization?: number;\n  /**\n   * Opt-in flag marking this board as rack-mountable. `distribution-board` is\n   * deliberately NOT in `RACK_ASSIGNABLE_NODE_TYPES` — boards are wall-mounted\n   * free-floating nodes by default, and this flag is the hook for the\n   * rack-mounted minority.\n   */\n  rackMountable?: boolean;\n  /** Number of way columns in the generated faceplate (default 1). */\n  wayColumns?: number;\n}\n\n/**\n * Fibre flow cable node data.\n * @public\n */\nexport interface FibreFlowCableNodeData extends NetworkNodeDataBase {\n  cable: FibreFlowCable;\n  side: \"left\" | \"right\";\n  rowHeight: number;\n  columnWidth: number;\n  showTubeLabels: boolean;\n  showFiberNumbers: boolean;\n  layout: unknown;\n  colorMode?: \"dark\" | \"light\";\n}\n\n/**\n * Fibre flow closure node data.\n * @public\n */\nexport interface FibreFlowClosureNodeData extends NetworkNodeDataBase {\n  width?: number;\n  height?: number;\n  headerHeight?: number;\n  colorMode?: \"dark\" | \"light\";\n}\n\n/**\n * Mapping from node type to corresponding data type.\n * @public\n */\nexport interface NetworkNodeDataByType {\n  rack: RackNodeData;\n  switch: DeviceNodeData;\n  router: DeviceNodeData;\n  server: DeviceNodeData;\n  fiber: NetworkNodeDataBase;\n  \"patch-panel\": NetworkNodeDataBase;\n  device: DeviceNodeData;\n  \"vertical-pdu\": VerticalPduNodeData;\n  \"distribution-board\": DistributionBoardNodeData;\n  splice: NetworkNodeDataBase;\n  \"splice-tray\": NetworkNodeDataBase;\n  tube: NetworkNodeDataBase;\n  cable: NetworkNodeDataBase;\n  \"multi-tube-cable\": NetworkNodeDataBase;\n  coupler: NetworkNodeDataBase;\n  closure: NetworkNodeDataBase;\n  \"fibre-split\": NetworkNodeDataBase;\n  \"fibre-flow-cable\": FibreFlowCableNodeData;\n  \"fibre-flow-closure\": FibreFlowClosureNodeData;\n}\n\n/**\n * Union of all node data variants.\n * @public\n */\nexport type NetworkNodeData = NetworkNodeDataByType[NetworkNodeType];\n\n/**\n * Network node structure with data discriminated by `type`.\n * @public\n */\nexport type NetworkNode<\n  TType extends NetworkNodeType = NetworkNodeType,\n  TExtra = unknown,\n> = TType extends NetworkNodeType\n  ? ReactFlowNode<\n      NetworkNodeDataByType[TType] & TExtra & Record<string, unknown>,\n      TType\n    > & {\n      /** Type of network node */\n      type: TType;\n      /** Node data and configuration */\n      data: NetworkNodeDataByType[TType] & TExtra;\n    }\n  : never;\n\n/**\n * React Flow node specialized for this library's data. Use as the type argument\n * to `NodeProps` in custom node components under `@xyflow/react` v12\n * (v12 parameterizes `NodeProps`/`NodeType` by the node, not the data).\n * @public\n */\nexport type NetworkFlowNode = ReactFlowNode<\n  NetworkNodeData & Record<string, unknown>,\n  NetworkNodeType\n>;\n\n/**\n * Network edge types\n * @public\n */\nexport type NetworkEdgeType =\n  | \"fiber\"\n  | \"ethernet\"\n  | \"power\"\n  | \"smoothstep\"\n  | \"step\"\n  | \"thick-cable\"\n  | \"fibre-flow\"\n  | \"fibre-flow-link\";\n\n/**\n * Edge data payload.\n * @public\n */\nexport interface NetworkEdgeData {\n  /**\n   * Index signature so this data satisfies `@xyflow/react` v12's `Edge<Data>`\n   * constraint (`Record<string, unknown>`). Edge data is never `Omit`-ed, so\n   * (unlike the node data types) an index signature is safe here.\n   */\n  [key: string]: unknown;\n  /** Connection bandwidth */\n  bandwidth?: string;\n  /**\n   * Cable length, in the source system's unit (see {@link NetworkEdgeData.lengthUnit}).\n   * Unit-less: prefer {@link NetworkEdgeData.lengthCm} for comparisons.\n   */\n  length?: number;\n  /** Unit of {@link NetworkEdgeData.length} as the source system reported it (e.g. `m`, `ft`). */\n  lengthUnit?: string;\n  /**\n   * Cable length normalised to centimetres. The NetBox/inventory converters set\n   * it when the source length has a known unit; PWR-A12 reads it in preference\n   * to the unit-less `length`.\n   */\n  lengthCm?: number;\n  /** Edge color for styling */\n  color?: string;\n  /** Edge kind for styling/validation */\n  kind?: \"fiber\" | \"power\" | \"network\";\n  /** Whether this edge represents a ribbon fiber connection */\n  isRibbon?: boolean;\n  /** Array of fiber IDs for ribbon mode (6-12 fibers) */\n  ribbonFiberIds?: number[];\n  /** Highlight edge with a pulsing stroke */\n  highlight?: boolean;\n  /** Override highlight color (defaults to edge color) */\n  highlightColor?: string;\n  /** Override highlight max stroke width */\n  highlightWidth?: number;\n  /** Optional tag for grouping edges in demos */\n  traceTag?: string;\n  /** Legacy striped edge flag */\n  striped?: boolean;\n  /** Cable type metadata used by demos/helpers */\n  cableType?: PortCableType;\n  /** NetBox integration metadata */\n  netboxId?: number | string;\n  netboxType?: \"device\" | \"rack\" | \"interface\" | \"cable\" | string;\n  /** Inventory integration metadata */\n  inventoryId?: number | string;\n  inventoryType?: \"device\" | \"rack\" | \"interface\" | \"cable\" | string;\n  /** Status metadata */\n  status?: string;\n  /** Endpoint metadata */\n  terminationA?: string;\n  terminationB?: string;\n  /** Optional display label for the edge */\n  label?: string;\n  /** Optional opacity for styling (0–1) */\n  opacity?: number;\n  /** Optional power edge stroke width override */\n  powerStrokeWidth?: number;\n  /** Optional power edge stroke dasharray override */\n  powerStrokeDasharray?: string;\n  /** Optional power edge opacity override (0–1) */\n  powerOpacity?: number;\n  /**\n   * Power energisation overlay: `true` renders the edge as live (marching\n   * dashes), `false` renders it dimmed/grey. Omit to keep the classic\n   * (unaware) power edge styling. Produced by `computeEnergization`.\n   */\n  energized?: boolean;\n  /**\n   * Direction power travels across the edge, relative to its own endpoints.\n   * Draws a flow arrow on an energised edge; ignored otherwise.\n   */\n  flowDirection?: \"source-to-target\" | \"target-to-source\";\n  /** Additional extension metadata */\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Network edge data structure\n * @public\n */\nexport type NetworkEdge<TData extends NetworkEdgeData = NetworkEdgeData> = {\n  /** Unique identifier for the edge */\n  id: string;\n  /** Source node ID */\n  source: string;\n  /** Target node ID */\n  target: string;\n  /** Source handle/port ID */\n  sourceHandle?: string;\n  /** Target handle/port ID */\n  targetHandle?: string;\n  /** Type of network connection */\n  type?: NetworkEdgeType;\n  /** Whether the edge should be hidden from rendering */\n  hidden?: boolean;\n  /**\n   * Whether the edge may be deleted by user interaction (keyboard Delete,\n   * batch delete). Mirrors React Flow's `Edge.deletable`; defaults to true.\n   * Deletion programmatically through `edges` prop updates is unaffected.\n   */\n  deletable?: boolean;\n  /**\n   * Width (px) of the invisible interaction stroke rendered beneath the edge,\n   * forwarded to React Flow's `Edge.interactionWidth`. It widens the edge's\n   * click/hover target, but the stroke also sits above neighboring node\n   * handles in z-order — an oversized value can occlude nearby ports and make\n   * them unreachable for connection drops in dense layouts. When omitted, the\n   * diagram's `edgeInteractionWidth` default applies.\n   */\n  interactionWidth?: number;\n  /** Edge data and configuration */\n  data?: TData;\n};\n\n/**\n * React Flow edge specialized for this library's data. Use as the type argument\n * to `EdgeProps` in custom edge components under `@xyflow/react` v12\n * (v12 parameterizes `EdgeProps` by the edge, not the data).\n * @public\n */\nexport type NetworkFlowEdge = ReactFlowEdge<NetworkEdgeData, NetworkEdgeType>;\n\n/**\n * Canonical persisted/editor document schema independent from React Flow handle ids.\n * Prefer this model for saving diagrams, syncing external systems, and controlled\n * document-mode usage via `document` / `onDocumentChange`.\n * @public\n */\nexport interface DiagramDocument {\n  /**\n   * Document schema version. Always `1` for documents emitted by this library.\n   * A missing version on input is treated as legacy v0 (identical to `1` today);\n   * a version greater than `1` is parsed best-effort with a console warning.\n   */\n  version: 1;\n  nodes: DiagramNode[];\n  edges: DiagramEdge[];\n  ui?: DiagramUiState;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Node kinds used by the document model.\n * @public\n */\nexport type DiagramNodeKind =\n  | \"rack\"\n  | \"equipment\"\n  | \"pdu\"\n  | \"distribution-board\"\n  | \"fiber\"\n  | \"tube\"\n  | \"cable\"\n  | \"multi-tube-cable\"\n  | \"splice\"\n  | \"coupler\"\n  | \"splitter\"\n  | \"container\"\n  | \"fibre-flow-cable\"\n  | \"fibre-flow-closure\";\n\n/**\n * Placement data for document nodes.\n * @public\n */\nexport interface DiagramNodePlacement {\n  position: {\n    x: number;\n    y: number;\n  };\n  parentId?: string;\n  extent?: \"parent\";\n  rack?: {\n    uPosition?: number;\n    mountMode?: RackMountMode;\n    rackLayout?: RackLayoutMode;\n    rackWidthPx?: number;\n    rackFaceGapPx?: number;\n  };\n  container?: {\n    slotPosition?: number;\n    holderPosition?: number;\n  };\n}\n\n/**\n * Endpoint definition for document nodes.\n * @public\n */\nexport interface DiagramEndpoint {\n  /**\n   * Opaque unique key for this endpoint within its document. The shipped\n   * adapters mint handle-grammar strings here for render-side stability, but\n   * consumers may supply any unique string — the reverse adapter resolves\n   * edges through the endpoint RECORD, so renaming a port's `label` does not\n   * require re-keying when ids are consumer-minted. Do not parse this value.\n   *\n   * When a device mounts the same port id on both faces, the shipped forward\n   * adapter mints the rear endpoint as `<id>--rear` (the renderer's split-face\n   * handle alias) so ids stay unique; the reverse adapter gives the rebuilt rear\n   * port back its shared `<id>`.\n   */\n  id: string;\n  label: string;\n  face?: DeviceFace | \"internal\";\n  group?: string;\n  role?:\n    | \"network\"\n    | \"power\"\n    | \"fiber\"\n    | \"tube\"\n    | \"splice\"\n    | \"coupler\"\n    | \"splitter\"\n    | \"cable\"\n    | \"generic\";\n  medium?: \"ethernet\" | \"fiber\" | \"power\" | \"tube\" | \"generic\";\n  type?: PortType;\n  cableType?: PortCableType;\n  connector?: string;\n  /**\n   * Free-text source-of-truth port type label (e.g. `\"QSFP28\"`, `\"RJ45\"`) used\n   * to pick the port's connector visual. Preserved verbatim across the document\n   * round-trip so a port keeps its icon.\n   */\n  sourceTypeLabel?: string;\n  compatibility?: string[];\n  layout?: PortPosition;\n}\n\n/**\n * Render-focused node data for the document model.\n * @public\n */\nexport interface DiagramNodeRender {\n  status?: DeviceStatus | string;\n  face?: DeviceFace;\n  width?: Width | number;\n  widthPx?: number;\n  height?: number;\n  header?: string;\n  color?: string;\n  colorMode?: \"light\" | \"dark\" | \"system\";\n  rackWidthPx?: number;\n  rackFaceGapPx?: number;\n  images?: DeviceImageOptions;\n  dimensions?: Record<string, number | undefined>;\n  showUMarkers?: boolean;\n  customHeaderText?: string;\n  powerSide?: PowerSide;\n  rotation?: number;\n}\n\n/**\n * External reference attached to a document node.\n * @public\n */\nexport interface DiagramExternalRef {\n  source: \"netbox\" | \"inventory\" | string;\n  id: string | number;\n  type?: string;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Document node.\n * @public\n */\nexport interface DiagramNode {\n  id: string;\n  kind: DiagramNodeKind;\n  subtype?: string;\n  label: string;\n  placement: DiagramNodePlacement;\n  endpoints?: DiagramEndpoint[];\n  render?: DiagramNodeRender;\n  config?: Record<string, unknown>;\n  externalRefs?: DiagramExternalRef[];\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Endpoint reference used by document edges.\n * @public\n */\nexport interface DiagramEdgeEndpoint {\n  nodeId: string;\n  endpointId?: string | null;\n}\n\n/**\n * Document edge.\n * @public\n */\nexport interface DiagramEdge {\n  id: string;\n  source: DiagramEdgeEndpoint;\n  target: DiagramEdgeEndpoint;\n  type?: NetworkEdgeType;\n  hidden?: boolean;\n  /** Mirrors {@link NetworkEdge.deletable}; omitted when the runtime edge left it unset. */\n  deletable?: boolean;\n  /** Mirrors {@link NetworkEdge.interactionWidth}; omitted when unset. */\n  interactionWidth?: number;\n  data?: NetworkEdgeData;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Non-topology UI state stored alongside a document.\n * @public\n */\nexport interface DiagramUiState {\n  nodeState?: Record<\n    string,\n    {\n      face?: DeviceFace;\n      handleSideMap?: Record<string, DeviceFace>;\n    }\n  >;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Rack device types\n * @public\n */\nexport type RackDeviceType =\n  | \"server\"\n  | \"switch\"\n  | \"router\"\n  | \"patch-panel\"\n  | \"ups\";\n\n/**\n * Rack configuration structure\n * @public\n */\nexport interface RackConfig {\n  /** Unique rack identifier */\n  id: string;\n  /** Rack display name */\n  name: string;\n  /** Rack position coordinates */\n  position: { x: number; y: number };\n  /** Rack height in U units */\n  units: number;\n  /** U numbering direction (default: 'bottom-up') */\n  uNumberingDirection?: UNumberingDirection;\n  /** Custom header text for rack */\n  customHeaderText?: string;\n  /** Rack rendering layout mode */\n  rackLayout?: RackLayoutMode;\n  /** Gap in pixels between front and rear faces in split-face mode */\n  rackFaceGapPx?: number;\n  /** Devices mounted in the rack */\n  devices: RackDevice[];\n  /** Optional rack width in pixels (default: 300px which represents standard 48.26cm/19\" rack) */\n  rackWidthPx?: number;\n}\n\n/**\n * Device mounted in a rack\n * @public\n */\nexport interface RackDevice extends DeviceImageOptions {\n  /** Unique device identifier */\n  id: string;\n  /** Device display name */\n  name: string;\n  /** Starting U position in rack */\n  unit: number;\n  /** Device height in U units */\n  height: number;\n  /** Device type */\n  type: RackDeviceType;\n  /** Device width configuration */\n  width?: Width;\n  /** Device mount behavior within a rack */\n  mountMode?: RackMountMode;\n  /** Device role in network */\n  role?: string;\n  /** Recommended simple port input (flat `DevicePort[]` or grouped `PortBlock[]`). */\n  ports?: DevicePort[] | PortBlock[];\n  /** Current status of the device */\n  status?: DeviceStatus | string;\n  /** Advanced grouped front port input */\n  portGroups?: PortBlock[];\n  /** Advanced grouped rear port input */\n  rearPortGroups?: PortBlock[];\n  /** Grouped rear port blocks (rack JSON alias for `rearPortGroups`) */\n  rearPorts?: PortBlock[];\n  /** Power port side for rear face */\n  powerSide?: PowerSide;\n  /**\n   * Containable-only bays drawn as outlines + labels (never cable handles).\n   * Each bay's `face` selects the owning face (default front).\n   *\n   * Passed straight through to the device node's own `bays` node-data field, so\n   * authoring bays here is equivalent to authoring them on node data — hosts\n   * building from `rackConfig.devices[]` no longer need the per-device extra\n   * node-data channel just to render bays.\n   */\n  bays?: DeviceBay[];\n  /** Connected device IDs */\n  connections?: string[];\n}\n\n/**\n * Fiber cable types\n * @public\n */\nexport type FibreCableType = \"single-mode\" | \"multi-mode\";\n\n/**\n * Fiber cable configuration\n * @public\n */\nexport interface FibreCable {\n  /** Unique cable identifier */\n  id: string;\n  /** Cable display name */\n  name: string;\n  /** Fiber cable type */\n  type: FibreCableType;\n  /** Cable length */\n  length: number;\n  /** Cable color for visualization */\n  color: string;\n  /** Connection endpoints */\n  connections: {\n    /** Source connection point */\n    from: string;\n    /** Target connection point */\n    to: string;\n  };\n}\n\n/**\n * Splice configuration structure\n * @public\n */\nexport interface SpliceConfig {\n  /** Unique splice identifier */\n  id: string;\n  /** Splice display name */\n  name: string;\n  /** Holder position within tray (1-12) */\n  holder: number;\n  /** Loss value in dB */\n  lossDb?: number;\n  /** Splice mode: 'single' for individual fiber, 'ribbon' for ribbon splices */\n  mode?: \"single\" | \"ribbon\";\n  /** For ribbon mode: array of fiber IDs (6-12) in the ribbon */\n  ribbonFiberIds?: number[];\n}\n\n/**\n * Splice tray configuration structure\n * @public\n */\nexport interface SpliceTrayConfig {\n  /** Unique tray identifier */\n  id: string;\n  /** Tray display name */\n  name: string;\n  /** Tray position coordinates */\n  position: { x: number; y: number };\n  /** Splices in this tray */\n  splices: SpliceConfig[];\n}\n\n// Inventory/CMDB integrations are generic and not tied to specific vendors\n\n/**\n * Fine-grained interaction controls for network diagrams.\n * @public\n */\nexport interface NetworkDiagramInteractionOptions {\n  /** Enable node dragging. */\n  nodesDraggable?: boolean;\n  /** Enable creating connections from node handles. */\n  nodesConnectable?: boolean;\n  /** Enable selecting nodes/edges. */\n  elementsSelectable?: boolean;\n  /** Enable keyboard/mouse focus on edges. */\n  edgesFocusable?: boolean;\n  /** Enable reconnecting existing edges. */\n  edgesReconnectable?: boolean;\n  /** Enable click-to-connect behavior on handles. */\n  connectOnClick?: boolean;\n  /** Enable panning by dragging the canvas. */\n  panOnDrag?: boolean;\n  /** Enable panning by scrolling. */\n  panOnScroll?: boolean;\n  /** Enable zoom via mouse wheel/trackpad scroll. */\n  zoomOnScroll?: boolean;\n  /** Enable pinch-zoom gestures. */\n  zoomOnPinch?: boolean;\n  /** Enable zoom on double click. */\n  zoomOnDoubleClick?: boolean;\n  /** Enable deleting selected edges with keyboard keys. */\n  allowKeyboardDelete?: boolean;\n  /** Show or hide the built-in React Flow controls widget. */\n  showControls?: boolean;\n  /** Show or hide the minimap. */\n  showMiniMap?: boolean;\n  /** Show or hide the interactive lock/unlock button in controls. */\n  showInteractiveControl?: boolean;\n}\n\n/**\n * Selection action panel options rendered above selected racks/devices.\n * @public\n */\nexport interface NetworkDiagramActionPanelOptions {\n  /** Enable or disable the floating action panel entirely. Defaults to true. */\n  enabled?: boolean;\n  /** Show or hide the front/rear toggle button. Defaults to true. */\n  showRearToggle?: boolean;\n  /** Show or hide the De-rack button for rack-mounted devices. Defaults to true. */\n  showDeRack?: boolean;\n  /** Show a rack's Zoom to edit button, which enlarges its U rows. Defaults to true. */\n  showZoomToEdit?: boolean;\n}\n\n/**\n * Serializable flow snapshot payload for save/restore helpers.\n * @public\n */\nexport interface NetworkDiagramFlowObject {\n  /** Snapshot nodes */\n  nodes: NetworkNode[];\n  /** Snapshot edges */\n  edges: NetworkEdge[];\n  /** Optional viewport state */\n  viewport?: Viewport;\n}\n\n/**\n * Options for exported diagram images.\n * @public\n */\nexport interface NetworkDiagramToImageOptions {\n  /** Export width in px (clamped to 1-8192). */\n  width?: number;\n  /** Export height in px (clamped to 1-8192). */\n  height?: number;\n  backgroundColor?: string;\n  /** Export padding in px (clamped to 0-2048). */\n  padding?: number;\n  /** Device pixel ratio multiplier (clamped to 0.1-4). */\n  pixelRatio?: number;\n  /** JPEG/WebP quality (clamped to 0-1 when used). */\n  quality?: number;\n  filter?: (domNode: HTMLElement) => boolean;\n}\n\n/**\n * Fit-view options that support targeting node IDs directly.\n * @public\n */\nexport interface NetworkDiagramFitViewOptions extends Omit<\n  FitViewOptions,\n  \"nodes\"\n> {\n  /** Optional node IDs to fit */\n  nodes?: string[];\n}\n\n/**\n * Exposed methods for save/restore and export integrations.\n * @public\n */\nexport interface NetworkDiagramFlowMethods {\n  toObject: () => NetworkDiagramFlowObject;\n  setViewport: (viewport: Viewport) => void;\n  setNodes: (nodes: NetworkNode[]) => void;\n  setEdges: (edges: NetworkEdge[]) => void;\n  toImage: (options?: NetworkDiagramToImageOptions) => Promise<string>;\n  getNodes: () => NetworkNode[];\n  getEdges: () => NetworkEdge[];\n  /**\n   * Fit all nodes or an ID subset using configured policy defaults. Per-call\n   * options override them, and subset floors use current measured bounds.\n   */\n  fitView: (options?: NetworkDiagramFitViewOptions) => void;\n}\n\n/**\n * Network diagram component props\n * @public\n */\nexport interface NetworkDiagramProps {\n  /** Controlled document model. When provided, it takes precedence over nodes/edges props. */\n  document?: DiagramDocument;\n  /** Initial document for uncontrolled document mode. */\n  initialDocument?: DiagramDocument;\n  /** Callback when the document changes in document mode. */\n  onDocumentChange?: (document: DiagramDocument) => void;\n  /** Controlled nodes array */\n  nodes?: NetworkNode[];\n  /** Controlled edges array */\n  edges?: NetworkEdge[];\n  /** Callback when nodes change (controlled mode) */\n  onNodesChange?: (nodes: NetworkNode[]) => void;\n  /** Callback when edges change (controlled mode) */\n  onEdgesChange?: (edges: NetworkEdge[]) => void;\n  /** Initial nodes for uncontrolled mode */\n  initialNodes?: NetworkNode[];\n  /** Initial edges for uncontrolled mode */\n  initialEdges?: NetworkEdge[];\n  /** Enable debug mode */\n  debug?: boolean;\n  /** Callback when a node is clicked or selection changes (null when deselected) */\n  onNodeClick?: (node: NetworkNode | null) => void;\n  /** Callback when an edge is clicked */\n  onEdgeClick?: (edge: NetworkEdge) => void;\n  /** Callback when the diagram background (pane) is clicked */\n  onPaneClick?: () => void;\n  /** Custom React Flow node types to merge with defaults */\n  nodeTypes?: NodeTypes;\n  /** Custom React Flow edge types to merge with defaults */\n  edgeTypes?: EdgeTypes;\n  /** Optional onConnect override (React Flow connection handler) */\n  onConnect?: (connection: Connection) => void;\n  /** Optional onEdgeUpdate override */\n  onEdgeUpdate?: (oldEdge: ReactFlowEdge, newConnection: Connection) => void;\n  /** Optional isValidConnection override */\n  isValidConnection?: (connection: Connection) => boolean;\n  /** Optional connection mode override */\n  connectionMode?: ConnectionMode;\n  /** Callback when a device face changes */\n  onFaceChange?: (nodeId: string, face: DeviceFace) => void;\n  /** Callback when rack face changes */\n  onRackFaceChange?: (rackId: string, face: DeviceFace) => void;\n  /** CSS class name */\n  className?: string;\n  /** Inline styles */\n  style?: React.CSSProperties;\n  /** If true, vertically align rack bottoms to the same baseline */\n  alignRacksToBottom?: boolean;\n  /** Allow rack-mounted devices to move between racks/ungroup on drag and drop. Defaults to true. */\n  reAssignable?: boolean;\n  /** Color mode for the diagram */\n  colorMode?: \"light\" | \"dark\" | \"system\";\n  /** Callback when React Flow instance is initialized */\n  onInit?: (instance: ReactFlowInstance) => void;\n  /** Callback to get React Flow methods for save/restore */\n  onFlowMethods?: (methods: NetworkDiagramFlowMethods) => void;\n  /** Show download button */\n  showDownloadButton?: boolean;\n  /** Download button position */\n  downloadButtonPosition?:\n    | \"top-right\"\n    | \"top-left\"\n    | \"bottom-right\"\n    | \"bottom-left\";\n  /** Custom download button text */\n  downloadButtonText?: string;\n  /** Download button styling options */\n  downloadButtonStyle?: React.CSSProperties;\n  /** Use smoothstep edges for tube connections (both tube-to-cable and cable-to-tube). Default is fiber. */\n  useSmoothstepEdgesForTubes?: boolean;\n  /** Force read-only behavior for the diagram (no drag/connect/delete/reconnect/pan/zoom edits). */\n  readOnly?: boolean;\n  /** Centralized interaction options for consistent edit/lock behavior. */\n  interaction?: NetworkDiagramInteractionOptions;\n  /** Configuration for the floating action panel shown on selected rack/device nodes. */\n  actionPanel?: NetworkDiagramActionPanelOptions;\n  /** Optional fitView options passed to React Flow */\n  fitViewOptions?: FitViewOptions;\n  /** Whether React Flow performs its built-in fit-on-init. Defaults to `true`.\n   *\n   * The init fit does not run at mount — it runs once node dimensions are first\n   * measured, which can be seconds later. A host that restores a persisted\n   * viewport must set this to `false`, otherwise the init fit lands *after* the\n   * restore and silently overwrites the restored pan/zoom.\n   *\n   * Ignored when `initialViewport.mode` is `'fit'` (library computes bounds from\n   * declared node dimensions instead).\n   */\n  fitViewOnInit?: boolean;\n  /**\n   * Minimum zoom passed to React Flow. Defaults to 0.1 so the always-on\n   * fitView can actually fit tall scenes (React Flow's own 0.5 default clamps\n   * the fit and crops full-height racks, which viewport culling then never\n   * mounts).\n   */\n  minZoom?: number;\n  /** Maximum zoom passed to React Flow. Defaults to 4. */\n  maxZoom?: number;\n  /**\n   * Let React Flow render only viewport-visible nodes and edges (virtualization).\n   *\n   * Defaults to `false`, which renders every element — the safest choice for\n   * offscreen measurement, edge routing, and the initial fit-to-view, since all\n   * nodes mount and report their dimensions up front.\n   *\n   * Enable this for very large topologies where mounting every rack is a\n   * performance cost. The tradeoff: virtualization improves large-diagram\n   * performance but can affect offscreen behaviour — nodes outside the viewport\n   * are not mounted, so their measured dimensions, the edges routed to/from\n   * them, and any fit-to-view that depends on their bounds may be wrong until\n   * they scroll into view. With a tight `minZoom` a fit-to-view can cull\n   * off-screen nodes so they never mount at all.\n   *\n   * Export-safe: this is force-disabled for the duration of a\n   * {@link NetworkDiagramFlowMethods.toImage} capture (and the built-in download\n   * button), so a PNG export includes every element even when virtualization is\n   * on. Culling is restored once the capture completes.\n   */\n  onlyRenderVisibleElements?: boolean;\n  /** Node IDs to keep at full opacity when diagram focus dimming is enabled */\n  highlightedNodeIds?: string[];\n  /** Edge IDs to keep at full opacity when diagram focus dimming is enabled */\n  highlightedEdgeIds?: string[];\n  /**\n   * When true, non-highlighted nodes and edges are dimmed while focus IDs are set.\n   * Edges connected to highlighted nodes remain in focus unless\n   * `connectEdgesToHighlightedNodes` is false.\n   */\n  dimUnhighlighted?: boolean;\n  /** Opacity for dimmed nodes/edges (0-1). Defaults to 0.25 */\n  dimOpacity?: number;\n  /** When false, only explicit highlightedEdgeIds stay bright during focus. */\n  connectEdgesToHighlightedNodes?: boolean;\n  /**\n   * Declarative multi-severity highlighting. Each group paints its member edges\n   * with the group's `color`/`width` through the same per-edge highlight channel\n   * (`data.highlight`/`highlightColor`/`highlightWidth`) the built-in edge\n   * renderers read, so groups render identically to hand-authored highlights.\n   *\n   * Overlap policy: **later groups win** — treat the array as severity tiers\n   * ordered least-to-most specific, so the highest-priority tier is placed last.\n   *\n   * When `highlightGroups` is non-empty and `dimUnhighlighted` is set, the dim\n   * complement is the union of every group's `nodeIds`/`edgeIds` plus the legacy\n   * `highlightedNodeIds`/`highlightedEdgeIds` props. `label` is carried through\n   * for future legend rendering and is unused by the diagram today.\n   */\n  highlightGroups?: ReadonlyArray<NetworkDiagramHighlightGroup>;\n  /** When false, devices render without rack photos. Status border still uses `getStatusColor`. Default true. */\n  showDeviceImages?: boolean;\n  /** Port handles kept highlighted during diagram trace/focus. */\n  highlightedPortHandles?: DiagramPortHandleRef[];\n  /** Fired when a port handle is clicked (typically in read-only trace mode). */\n  onPortClick?: (payload: DiagramPortClickPayload) => void;\n  /**\n   * Bays kept highlighted (e.g. the exact failed-drive slot to pull). The bay\n   * analogue of `highlightedPortHandles`; delivered through the same\n   * instance-scoped highlight store.\n   */\n  highlightedBays?: DiagramBayRef[];\n  /** Fired when a bay outline is clicked. The bay analogue of `onPortClick`. */\n  onBayClick?: (payload: DiagramBayClickPayload) => void;\n  /**\n   * Pointer distance (px) within which a drag snaps onto a handle, forwarded to\n   * React Flow's `connectionRadius`. Larger values make small port handles easier\n   * to target; smaller values reduce mis-targeting on densely packed devices.\n   * When omitted, React Flow's default is used.\n   */\n  connectionRadius?: number;\n  /**\n   * Default `interactionWidth` (px) applied to rendered edges, i.e. the width\n   * of each edge's invisible click/hover stroke. React Flow's component\n   * default is 20px, which blankets handles spaced closer than ~10px to a\n   * running edge (stacked splice ports, patch panels) and swallows connection\n   * drops meant for them. Defaults to 8 — still an easy click target while\n   * keeping tightly packed ports reachable. Per-edge `interactionWidth`\n   * overrides this value; pass 0 to disable edge interaction strokes.\n   */\n  edgeInteractionWidth?: number;\n  /**\n   * Fired when a connection attempt is rejected by the diagram's validation\n   * (incompatible endpoints, an occupied non-replaceable handle, a disallowed\n   * fiber pairing, etc.). Lets host apps surface feedback instead of failing\n   * silently. Not called for the continuous hover validation during a drag —\n   * only when an actual connect or reconnect attempt is rejected.\n   */\n  onConnectRejected?: (payload: NetworkConnectionRejection) => void;\n  /**\n   * Library-managed initial camera. Prefer this over demo-level fitView timers.\n   * `'fit'` uses declared top-level node dimensions (v12 SSR model).\n   */\n  initialViewport?: NetworkDiagramInitialViewport;\n  /** Fired once after `initialViewport` fit or restore completes. */\n  onViewportReady?: () => void;\n  /** Hide the canvas until the initial viewport is applied. Defaults to true when fitting. */\n  hideUntilViewportReady?: boolean;\n  /**\n   * What to show inside the canvas when the diagram has no nodes to render.\n   *\n   * Defaults to a small centred, themed \"No nodes to display\" message. Pass\n   * your own node for a custom empty state, or `false` to render nothing (the\n   * bare grid). The built-in overlay is `pointer-events: none`, so\n   * drag-and-drop onto an empty canvas keeps working; keep that in mind if you\n   * supply your own — an interactive empty state must opt its own controls\n   * back in with `pointer-events: auto`.\n   *\n   * The overlay appears only after the node count has been zero for a short\n   * moment (~150ms), so a controlled diagram that renders `nodes={[]}` for a\n   * tick while data loads does not flash the message. While you know you are\n   * loading, pass `emptyState={false}` to suppress it entirely.\n   */\n  emptyState?: React.ReactNode | false;\n  /**\n   * Wrap the React Flow subtree in `DiagramErrorBoundary` so a throwing\n   * custom node type renders a themed fallback inside the canvas instead of\n   * unmounting the host page. Defaults to `true`; pass `false` to opt out and\n   * let errors propagate to your own boundary.\n   *\n   * Because it defaults to `true`, diagram render errors no longer reach an\n   * error boundary you wrap around `NetworkDiagram`, nor any reporting hooked\n   * up to it. Pass {@link NetworkDiagramProps.onError} to keep reporting them,\n   * or `errorBoundary={false}` for the previous propagate-to-you behaviour.\n   */\n  errorBoundary?: boolean;\n  /**\n   * Fallback rendered in place of the diagram when the built-in error boundary\n   * catches an error. A render prop receives the error and a `reset` callback\n   * that clears the error and re-renders the diagram. Ignored when\n   * `errorBoundary` is `false`.\n   */\n  errorFallback?: React.ReactNode | ((error: Error, reset: () => void) => React.ReactNode);\n  /**\n   * Called when the built-in error boundary catches an error. This is the way\n   * to keep existing error reporting (Sentry, etc.) working now that the\n   * boundary is on by default and swallows errors before they reach your own\n   * boundary. Ignored when `errorBoundary` is `false`.\n   */\n  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\n/**\n * One tier of {@link NetworkDiagramProps.highlightGroups} declarative\n * highlighting.\n *\n * @public\n * @remarks\n * A group names some `nodeIds`/`edgeIds` and paints its member edges with\n * `color`/`width` via the built-in per-edge highlight channel. Use one group\n * per severity/category (e.g. error, warning, info) and order the array\n * least-to-most specific, since **later groups win** where members overlap. The\n * optional `label` is reserved for future legend rendering and is not drawn by\n * the diagram today.\n *\n * @example\n * ```tsx\n * const groups: NetworkDiagramHighlightGroup[] = [\n *   { edgeIds: warningEdgeIds, color: \"#d97706\", width: 4, label: \"Warning\" },\n *   { edgeIds: errorEdgeIds, nodeIds: errorNodeIds, color: \"#dc2626\", width: 6, label: \"Error\" },\n * ];\n * <NetworkDiagram nodes={nodes} edges={edges} highlightGroups={groups} dimUnhighlighted />\n * ```\n */\nexport interface NetworkDiagramHighlightGroup {\n  /** Node IDs in this tier. Contributes to the dim complement when dimming. */\n  readonly nodeIds?: readonly string[];\n  /** Edge IDs in this tier. Painted with `color`/`width`; also kept bright when dimming. */\n  readonly edgeIds?: readonly string[];\n  /** Highlight color for member edges. When omitted, edges keep their own color. */\n  readonly color?: string;\n  /** Highlight stroke width for member edges. When omitted, the renderer default is used. */\n  readonly width?: number;\n  /** Human-readable tier name, reserved for future legend rendering. Unused in render today. */\n  readonly label?: string;\n}\n\n/** Initial camera strategy for {@link NetworkDiagram}. @public */\nexport type NetworkDiagramInitialViewport =\n  | {\n      mode: 'fit';\n      padding?: number;\n      minZoom?: number;\n      maxZoom?: number;\n      /**\n       * Refit when top-level node layout changes. Default (unset): refit until\n       * the user moves the camera (pan, zoom, Zoom to edit, or setViewport),\n       * then leave their camera alone. `true` always refits; `false` never does.\n       */\n      refitOnLayoutChange?: boolean;\n    }\n  | { mode: 'restore'; viewport: Viewport }\n  | { mode: 'none' };\n\n/** Payload describing why a connection attempt was rejected. */\nexport interface NetworkConnectionRejection {\n  /** The connection the user attempted to make. */\n  connection: Connection;\n  /** Human-readable reason the connection was rejected. */\n  reason: string;\n  /** Whether the rejection happened while creating a new edge or reconnecting an existing one. */\n  phase: \"connect\" | \"reconnect\";\n}\n\nexport * from \"./netbox\";\n","import type { PortPosition } from '../types';\n\nconst EDGE_OFFSET_LOW_PERCENT = 12;\nconst EDGE_OFFSET_CENTER_LOW_PERCENT = 34;\nconst EDGE_OFFSET_CENTER_PERCENT = 50;\nconst EDGE_OFFSET_CENTER_HIGH_PERCENT = 66;\nconst EDGE_OFFSET_HIGH_PERCENT = 88;\nconst FACE_POSITION_FRONT_PERCENT = 22;\nconst FACE_POSITION_REAR_PERCENT = 78;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst toNumber = (value: unknown): number | undefined => {\n  if (typeof value === 'number' && Number.isFinite(value)) {\n    return value;\n  }\n  if (typeof value === 'string' && value.trim().length > 0) {\n    const parsed = Number(value);\n    if (Number.isFinite(parsed)) {\n      return parsed;\n    }\n  }\n  return undefined;\n};\n\nconst normalizeSemanticPositionTokens = (value: string): string[] =>\n  value\n    .trim()\n    .toLowerCase()\n    .split(/[^a-z0-9]+/)\n    .filter((token) => token.length > 0);\n\nconst canonicalizeSideToken = (token: string): NonNullable<PortPosition['side']> | undefined => {\n  if (token === 'top' || token === 'up') {\n    return 'top';\n  }\n  if (token === 'right') {\n    return 'right';\n  }\n  if (token === 'bottom' || token === 'down') {\n    return 'bottom';\n  }\n  if (token === 'left') {\n    return 'left';\n  }\n  return undefined;\n};\n\nconst hasCenterToken = (tokens: string[]): boolean =>\n  tokens.includes('center') || tokens.includes('centre') || tokens.includes('middle') || tokens.includes('mid');\n\nconst resolveAxisOffsetPercent = (\n  primarySide: NonNullable<PortPosition['side']>,\n  tokens: string[],\n): number | undefined => {\n  const hasLeft = tokens.includes('left');\n  const hasRight = tokens.includes('right');\n  const hasTop = tokens.includes('top') || tokens.includes('up');\n  const hasBottom = tokens.includes('bottom') || tokens.includes('down');\n  const hasCenter = hasCenterToken(tokens);\n\n  if (primarySide === 'top' || primarySide === 'bottom') {\n    if (hasLeft && hasCenter && !hasRight) return EDGE_OFFSET_CENTER_LOW_PERCENT;\n    if (hasRight && hasCenter && !hasLeft) return EDGE_OFFSET_CENTER_HIGH_PERCENT;\n    if (hasLeft && !hasRight) return EDGE_OFFSET_LOW_PERCENT;\n    if (hasRight && !hasLeft) return EDGE_OFFSET_HIGH_PERCENT;\n    if (hasCenter) return EDGE_OFFSET_CENTER_PERCENT;\n    return undefined;\n  }\n\n  if (hasTop && hasCenter && !hasBottom) return EDGE_OFFSET_CENTER_LOW_PERCENT;\n  if (hasBottom && hasCenter && !hasTop) return EDGE_OFFSET_CENTER_HIGH_PERCENT;\n  if (hasTop && !hasBottom) return EDGE_OFFSET_LOW_PERCENT;\n  if (hasBottom && !hasTop) return EDGE_OFFSET_HIGH_PERCENT;\n  if (hasCenter) return EDGE_OFFSET_CENTER_PERCENT;\n  return undefined;\n};\n\nconst resolveSemanticEdgePortPosition = (tokens: string[]): PortPosition | undefined => {\n  const hasFaceTokens = tokens.includes('front') || tokens.includes('rear') || tokens.includes('back');\n  if (hasFaceTokens) {\n    return undefined;\n  }\n\n  const primarySide = tokens\n    .map(canonicalizeSideToken)\n    .find((side): side is NonNullable<PortPosition['side']> => side !== undefined);\n\n  if (!primarySide) {\n    return undefined;\n  }\n\n  const offsetPercent = resolveAxisOffsetPercent(primarySide, tokens);\n  if (offsetPercent === undefined) {\n    return undefined;\n  }\n\n  return {\n    side: primarySide,\n    offsetPercent,\n  };\n};\n\nconst resolveSemanticCompositePortPosition = (tokens: string[]): PortPosition | undefined => {\n  if (tokens.length === 0) {\n    return undefined;\n  }\n\n  const hasLeft = tokens.includes('left');\n  const hasRight = tokens.includes('right');\n  const hasCenter = hasCenterToken(tokens);\n  const hasFront = tokens.includes('front') || tokens.includes('top') || tokens.includes('up');\n  const hasRear = tokens.includes('rear') || tokens.includes('back') || tokens.includes('bottom') || tokens.includes('down');\n\n  let xPercent: number | undefined;\n  if (hasLeft && hasCenter && !hasRight) {\n    xPercent = EDGE_OFFSET_CENTER_LOW_PERCENT;\n  } else if (hasRight && hasCenter && !hasLeft) {\n    xPercent = EDGE_OFFSET_CENTER_HIGH_PERCENT;\n  } else if (hasLeft && !hasRight) {\n    xPercent = EDGE_OFFSET_LOW_PERCENT;\n  } else if (hasRight && !hasLeft) {\n    xPercent = EDGE_OFFSET_HIGH_PERCENT;\n  } else if (hasCenter) {\n    xPercent = EDGE_OFFSET_CENTER_PERCENT;\n  }\n\n  let yPercent: number | undefined;\n  if (hasFront && !hasRear) {\n    yPercent = FACE_POSITION_FRONT_PERCENT;\n  } else if (hasRear && !hasFront) {\n    yPercent = FACE_POSITION_REAR_PERCENT;\n  } else if (hasCenter) {\n    yPercent = EDGE_OFFSET_CENTER_PERCENT;\n  }\n\n  if (xPercent === undefined || yPercent === undefined) {\n    return undefined;\n  }\n\n  return { xPercent, yPercent };\n};\n\nexport const parseSemanticPortPosition = (value: string): PortPosition | undefined => {\n  const tokens = normalizeSemanticPositionTokens(value);\n  if (tokens.length === 0) {\n    return undefined;\n  }\n\n  if (tokens.length === 1) {\n    const side = canonicalizeSideToken(tokens[0]);\n    return side ? { side } : undefined;\n  }\n\n  return resolveSemanticEdgePortPosition(tokens) ?? resolveSemanticCompositePortPosition(tokens);\n};\n\nexport const normalizePortPositionInput = (value: unknown): PortPosition | undefined => {\n  if (typeof value === 'string') {\n    return parseSemanticPortPosition(value);\n  }\n\n  if (!isRecord(value)) {\n    return undefined;\n  }\n\n  const sideRaw = value.side ?? value.port_side ?? value.portSide;\n  const semanticPosition = typeof sideRaw === 'string' ? parseSemanticPortPosition(sideRaw) : undefined;\n  const simpleSide = typeof sideRaw === 'string' ? canonicalizeSideToken(sideRaw.trim().toLowerCase()) : undefined;\n\n  const side = simpleSide ?? semanticPosition?.side;\n  const offsetPercent = toNumber(\n    value.offsetPercent ??\n    value.offset_percent ??\n    value.offsetPct ??\n    value.offset_pct\n  ) ?? semanticPosition?.offsetPercent;\n  const offsetPx = toNumber(\n    value.offsetPx ??\n    value.offset_px ??\n    value.offsetPixels ??\n    value.offset_pixels\n  ) ?? semanticPosition?.offsetPx;\n  const xPercent = toNumber(\n    value.xPercent ??\n    value.x_percent ??\n    value.xPct ??\n    value.x_pct ??\n    value.horizontalPercent ??\n    value.horizontal_percent ??\n    value.x\n  ) ?? semanticPosition?.xPercent;\n  const yPercent = toNumber(\n    value.yPercent ??\n    value.y_percent ??\n    value.yPct ??\n    value.y_pct ??\n    value.verticalPercent ??\n    value.vertical_percent ??\n    value.y\n  ) ?? semanticPosition?.yPercent;\n  const widthPercent = toNumber(\n    value.widthPercent ??\n    value.width_percent ??\n    value.widthPct ??\n    value.width_pct ??\n    value.wPercent ??\n    value.w_percent\n  );\n  const heightPercent = toNumber(\n    value.heightPercent ??\n    value.height_percent ??\n    value.heightPct ??\n    value.height_pct ??\n    value.hPercent ??\n    value.h_percent\n  );\n\n  if (\n    side === undefined &&\n    offsetPercent === undefined &&\n    offsetPx === undefined &&\n    xPercent === undefined &&\n    yPercent === undefined &&\n    widthPercent === undefined &&\n    heightPercent === undefined\n  ) {\n    return undefined;\n  }\n\n  return {\n    side,\n    offsetPercent,\n    offsetPx,\n    xPercent,\n    yPercent,\n    widthPercent,\n    heightPercent,\n  };\n};\n","import type {\n  DevicePort,\n  DeviceFace,\n  NetworkNode,\n  Port,\n  PortBlock,\n  PortPosition,\n} from '../types';\nimport { isPowerPortType } from './handle-ids';\nimport { normalizePortPositionInput } from './port-position';\n\ntype IndexedPort = {\n  index: number;\n  port: DevicePort;\n  position: PortPosition | undefined;\n};\n\ntype GroupedPortBucket = {\n  face: DeviceFace;\n  groupName: string;\n  entries: IndexedPort[];\n};\n\nconst DEFAULT_FRONT_GROUP_NAME = 'Network Ports';\nconst DEFAULT_REAR_GROUP_NAME = 'Rear Network Ports';\nconst DEFAULT_POWER_GROUP_NAME = 'Power Ports';\nconst DISTRIBUTED_OFFSET_MIN_PERCENT = 8;\nconst DISTRIBUTED_OFFSET_MAX_PERCENT = 92;\n\nfunction clampPercent(value: number): number {\n  return Math.max(0, Math.min(100, value));\n}\n\nfunction normalizePortAnchor(value: PortPosition['side'] | undefined): PortPosition['side'] | undefined {\n  if (value === 'top' || value === 'right' || value === 'bottom' || value === 'left') {\n    return value;\n  }\n  return undefined;\n}\n\nfunction getNormalizedPortPosition(position: unknown): PortPosition | undefined {\n  return normalizePortPositionInput(position);\n}\n\nfunction hasExplicitAbsoluteCoordinates(position: PortPosition | undefined): boolean {\n  return (\n    typeof position?.xPercent === 'number' &&\n    typeof position?.yPercent === 'number'\n  );\n}\n\nfunction hasExplicitOffset(position: PortPosition | undefined): boolean {\n  return (\n    typeof position?.offsetPercent === 'number' ||\n    typeof position?.offsetPx === 'number'\n  );\n}\n\nfunction resolveRenderPortFace(port: DevicePort): DeviceFace {\n  if (port.face === 'rear' || port.face === 'back') {\n    return 'rear';\n  }\n  if (port.face === 'front') {\n    return 'front';\n  }\n  return isPowerPortType(port.type) ? 'rear' : 'front';\n}\n\nfunction resolveRenderPortGroupName(port: DevicePort, face: DeviceFace): string {\n  if (typeof port.group === 'string' && port.group.trim().length > 0) {\n    return port.group.trim();\n  }\n  if (isPowerPortType(port.type)) {\n    return DEFAULT_POWER_GROUP_NAME;\n  }\n  return face === 'rear' ? DEFAULT_REAR_GROUP_NAME : DEFAULT_FRONT_GROUP_NAME;\n}\n\nfunction getEntrySortValue(entry: IndexedPort): number {\n  return typeof entry.port.order === 'number' && Number.isFinite(entry.port.order)\n    ? entry.port.order\n    : entry.index;\n}\n\nfunction toDistributedOffsetPercent(index: number, count: number): number {\n  if (count <= 1) {\n    return 50;\n  }\n  return (\n    DISTRIBUTED_OFFSET_MIN_PERCENT +\n    (index * (DISTRIBUTED_OFFSET_MAX_PERCENT - DISTRIBUTED_OFFSET_MIN_PERCENT)) / (count - 1)\n  );\n}\n\nfunction buildDistributedOffsetMap(entries: IndexedPort[]): Map<number, number> {\n  const distributedEntries = entries.filter((entry) => {\n    const anchor = normalizePortAnchor(entry.position?.side);\n    return !!anchor && !hasExplicitAbsoluteCoordinates(entry.position) && !hasExplicitOffset(entry.position);\n  });\n  const offsetPercentByIndex = new Map<number, number>();\n  const entriesByAnchor = new Map<NonNullable<PortPosition['side']>, IndexedPort[]>();\n\n  distributedEntries.forEach((entry) => {\n    const anchor = normalizePortAnchor(entry.position?.side);\n    if (!anchor) {\n      return;\n    }\n    const group = entriesByAnchor.get(anchor);\n    if (group) {\n      group.push(entry);\n      return;\n    }\n    entriesByAnchor.set(anchor, [entry]);\n  });\n\n  entriesByAnchor.forEach((anchorEntries) => {\n    const sortedEntries = [...anchorEntries].sort((left, right) => {\n      const orderDifference = getEntrySortValue(left) - getEntrySortValue(right);\n      if (orderDifference !== 0) {\n        return orderDifference;\n      }\n      return left.index - right.index;\n    });\n    sortedEntries.forEach((entry, index) => {\n      offsetPercentByIndex.set(entry.index, toDistributedOffsetPercent(index, sortedEntries.length));\n    });\n  });\n\n  return offsetPercentByIndex;\n}\n\nfunction toPortPosition(\n  normalizedPosition: PortPosition | undefined,\n  distributedOffsetPercent?: number,\n): PortPosition | undefined {\n  const anchor = normalizePortAnchor(normalizedPosition?.side);\n  if (hasExplicitAbsoluteCoordinates(normalizedPosition)) {\n    const position: PortPosition = {\n      xPercent: clampPercent(normalizedPosition?.xPercent as number),\n      yPercent: clampPercent(normalizedPosition?.yPercent as number),\n    };\n    if (typeof normalizedPosition?.widthPercent === 'number' && Number.isFinite(normalizedPosition.widthPercent)) {\n      position.widthPercent = clampPercent(normalizedPosition.widthPercent);\n    }\n    if (typeof normalizedPosition?.heightPercent === 'number' && Number.isFinite(normalizedPosition.heightPercent)) {\n      position.heightPercent = clampPercent(normalizedPosition.heightPercent);\n    }\n    if (anchor) {\n      position.side = anchor;\n    }\n    return position;\n  }\n\n  if (!anchor) {\n    return undefined;\n  }\n\n  const position: PortPosition = { side: anchor };\n  if (typeof normalizedPosition?.offsetPx === 'number' && Number.isFinite(normalizedPosition.offsetPx)) {\n    position.offsetPx = normalizedPosition.offsetPx;\n    return position;\n  }\n\n  const explicitOffsetPercent =\n    typeof normalizedPosition?.offsetPercent === 'number' && Number.isFinite(normalizedPosition.offsetPercent)\n      ? clampPercent(normalizedPosition.offsetPercent)\n      : undefined;\n  const resolvedOffsetPercent =\n    explicitOffsetPercent ?? (typeof distributedOffsetPercent === 'number'\n      ? clampPercent(distributedOffsetPercent)\n      : undefined);\n\n  if (typeof resolvedOffsetPercent === 'number') {\n    position.offsetPercent = resolvedOffsetPercent;\n  }\n\n  return position;\n}\n\nfunction toPort(entry: IndexedPort, distributedOffsetPercent?: number): Port {\n  const position = toPortPosition(entry.position, distributedOffsetPercent);\n  return {\n    id: entry.port.id,\n    label: entry.port.label,\n    sourceTypeLabel: entry.port.sourceTypeLabel,\n    connected: entry.port.connected ?? false,\n    type: entry.port.type,\n    cableType: entry.port.cableType,\n    connectorType: entry.port.connectorType,\n    compatibility: Array.isArray(entry.port.compatibility) ? [...entry.port.compatibility] : undefined,\n    ...(position ? { position } : {}),\n  };\n}\n\nfunction normalizePortBlockCollection(portBlocks: PortBlock[]): PortBlock[] {\n  const normalizedBlocks = portBlocks.map((block) => ({\n    ...block,\n    ports: block.ports.map((port) => {\n      const position = getNormalizedPortPosition(port.position);\n      return {\n        ...port,\n        ...(position ? { position } : { position: undefined }),\n      };\n    }),\n  }));\n\n  // A side without an offset is an automatic placement request. Distribute it\n  // across the whole face collection rather than restarting inside each block.\n  const portsByUnpositionedSide = new Map<\n    NonNullable<PortPosition['side']>,\n    Port[]\n  >();\n  normalizedBlocks.forEach((block) => {\n    block.ports.forEach((port) => {\n      const position = port.position as PortPosition | undefined;\n      const side = normalizePortAnchor(position?.side);\n      if (\n        !side ||\n        hasExplicitAbsoluteCoordinates(position) ||\n        hasExplicitOffset(position)\n      ) {\n        return;\n      }\n      const ports = portsByUnpositionedSide.get(side);\n      if (ports) {\n        ports.push(port);\n      } else {\n        portsByUnpositionedSide.set(side, [port]);\n      }\n    });\n  });\n\n  // These are owned output ports from the normalization pass above. Fill in\n  // automatic offsets in place instead of cloning every block a second time.\n  portsByUnpositionedSide.forEach((ports) => {\n    ports.forEach((port, index) => {\n      port.position = {\n        ...(port.position as PortPosition),\n        offsetPercent: toDistributedOffsetPercent(index, ports.length),\n      };\n    });\n  });\n\n  return normalizedBlocks;\n}\n\nfunction isGroupedPortBlockArray(value: unknown): value is PortBlock[] {\n  return (\n    Array.isArray(value) &&\n    value.length > 0 &&\n    value.every(\n      (entry) =>\n        entry !== null &&\n        typeof entry === 'object' &&\n        typeof (entry as PortBlock).name === 'string' &&\n        Array.isArray((entry as PortBlock).ports),\n    )\n  );\n}\n\nfunction isFlatDevicePortArray(value: unknown): value is DevicePort[] {\n  return (\n    Array.isArray(value) &&\n    value.length > 0 &&\n    value.every(\n      (entry) =>\n        entry !== null &&\n        typeof entry === 'object' &&\n        typeof (entry as DevicePort).label === 'string' &&\n        !(\n          typeof (entry as { name?: unknown }).name === 'string' &&\n          Array.isArray((entry as { ports?: unknown }).ports)\n        ),\n    )\n  );\n}\n\n/**\n * Normalize flat `DevicePort[]` input into explicit front/rear grouped blocks.\n * This is the bridge between the recommended simple port schema and the\n * lower-level grouped renderer input.\n * @public\n */\nexport function buildPortBlocksFromPorts(\n  devicePorts: DevicePort[],\n): { ports: PortBlock[]; rearPorts: PortBlock[] } {\n  const bucketsByKey = new Map<string, GroupedPortBucket>();\n  // Normalize each accepted port once. The placement, grouping and output\n  // passes share this representation rather than repeatedly parsing aliases.\n  const indexedPorts: IndexedPort[] = [];\n  devicePorts.forEach((port, index) => {\n    if (typeof port.label !== 'string' || port.label.trim().length === 0) return;\n    indexedPorts.push({\n      index,\n      port: { ...port, label: port.label.trim() },\n      position: getNormalizedPortPosition(port.position),\n    });\n  });\n  const distributedOffsetsByFace = {\n    front: buildDistributedOffsetMap(\n      indexedPorts.filter(({ port }) => resolveRenderPortFace(port) === 'front'),\n    ),\n    rear: buildDistributedOffsetMap(\n      indexedPorts.filter(({ port }) => resolveRenderPortFace(port) === 'rear'),\n    ),\n  };\n\n  indexedPorts.forEach((entry) => {\n    const face = resolveRenderPortFace(entry.port);\n    const groupName = resolveRenderPortGroupName(entry.port, face);\n    const key = `${face}:${groupName}`;\n    const existingBucket = bucketsByKey.get(key);\n    if (existingBucket) {\n      existingBucket.entries.push(entry);\n    } else {\n      bucketsByKey.set(key, { face, groupName, entries: [entry] });\n    }\n  });\n\n  const ports: PortBlock[] = [];\n  const rearPorts: PortBlock[] = [];\n\n  bucketsByKey.forEach((bucket) => {\n    const sortedEntries = [...bucket.entries].sort((left, right) => {\n      const orderDifference = getEntrySortValue(left) - getEntrySortValue(right);\n      if (orderDifference !== 0) {\n        return orderDifference;\n      }\n      return left.index - right.index;\n    });\n    const distributedOffsetByIndex = distributedOffsetsByFace[bucket.face];\n    const block: PortBlock = {\n      name: bucket.groupName,\n      ports: sortedEntries.map((entry) =>\n        toPort(entry, distributedOffsetByIndex.get(entry.index)),\n      ),\n    };\n    if (bucket.face === 'rear') {\n      rearPorts.push(block);\n      return;\n    }\n    ports.push(block);\n  });\n\n  return { ports, rearPorts };\n}\n\n/**\n * Node-data subset accepted by {@link getResolvedPortBlocks}: a flat\n * `DevicePort[]` on `ports`, grouped `PortBlock[]` on\n * `ports`/`portGroups`/`rearPortGroups`, or a `rearPorts` block fallback.\n * @public\n */\nexport type ResolvedPortBlockInput = Pick<\n  NetworkNode['data'],\n  'ports' | 'portGroups' | 'rearPortGroups'\n> & {\n  rearPorts?: PortBlock[];\n};\n\n/**\n * Resolve the grouped front/rear port blocks from any supported input form:\n * flat `ports: DevicePort[]`, grouped `PortBlock[]`, or explicit\n * `portGroups` / `rearPortGroups`.\n * @public\n */\nexport function getResolvedPortBlocks(\n  data: ResolvedPortBlockInput | undefined,\n): { ports: PortBlock[]; rearPorts: PortBlock[] } {\n  if (isGroupedPortBlockArray(data?.ports)) {\n    return {\n      ports: normalizePortBlockCollection(data.ports),\n      rearPorts: normalizePortBlockCollection(\n        Array.isArray(data?.rearPortGroups)\n          ? data.rearPortGroups\n          : isGroupedPortBlockArray(data?.rearPorts)\n            ? data.rearPorts\n            : [],\n      ),\n    };\n  }\n\n  if (isFlatDevicePortArray(data?.ports)) {\n    return buildPortBlocksFromPorts(data.ports);\n  }\n\n  return {\n    ports: normalizePortBlockCollection(Array.isArray(data?.portGroups) ? data.portGroups : []),\n    rearPorts: normalizePortBlockCollection(\n      Array.isArray(data?.rearPortGroups)\n        ? data.rearPortGroups\n        : isGroupedPortBlockArray(data?.rearPorts)\n          ? data.rearPorts\n          : [],\n    ),\n  };\n}\n\n/**\n * Resolve the front-facing grouped port blocks for a node/device data object.\n * @public\n */\nexport function getFrontPortBlocks(\n  data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): PortBlock[] {\n  return getResolvedPortBlocks(data).ports;\n}\n\n/**\n * Resolve the rear-facing grouped port blocks for a node/device data object.\n * @public\n */\nexport function getRearPortBlocks(\n  data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): PortBlock[] {\n  return getResolvedPortBlocks(data).rearPorts;\n}\n\nexport function countPortBlocks(portBlocks: PortBlock[] | undefined): number {\n  if (!Array.isArray(portBlocks)) {\n    return 0;\n  }\n\n  return portBlocks.reduce((total, block) => {\n    return total + (Array.isArray(block.ports) ? block.ports.length : 0);\n  }, 0);\n}\n\n/**\n * Determine whether a node/device data object resolves to any front ports.\n * @public\n */\nexport function hasFrontPorts(\n  data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): boolean {\n  return countPortBlocks(getResolvedPortBlocks(data).ports) > 0;\n}\n\n/**\n * Determine whether a node/device data object resolves to any rear ports.\n * @public\n */\nexport function hasRearPorts(\n  data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): boolean {\n  return countPortBlocks(getResolvedPortBlocks(data).rearPorts) > 0;\n}\n\n/**\n * Choose the default device face based on the resolved front/rear port data.\n * @public\n */\nexport function getDefaultDeviceFace(\n  data: Pick<NetworkNode['data'], 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n): DeviceFace {\n  const hasFront = hasFrontPorts(data);\n  const hasRear = hasRearPorts(data);\n  return hasRear && !hasFront ? 'rear' : 'front';\n}\n","import type { NetworkNode, RackMountMode } from '../types';\nimport { getResolvedPortBlocks, countPortBlocks } from './render-ports';\n\ntype MountResolutionNodeData = Pick<\n  NetworkNode['data'],\n  'mountMode' | 'ports' | 'portGroups' | 'rearPortGroups' | 'frontImageUrl' | 'rearImageUrl' | 'face'\n>;\n\nfunction hasImage(url: string | undefined): boolean {\n  return typeof url === 'string' && url.trim().length > 0;\n}\n\nexport function inferRackMountModeFromFaces(\n  nodeData: Pick<MountResolutionNodeData, 'ports' | 'portGroups' | 'rearPortGroups' | 'frontImageUrl' | 'rearImageUrl'>\n): RackMountMode | undefined {\n  const { ports, rearPorts } = getResolvedPortBlocks(nodeData);\n  const hasFront = countPortBlocks(ports) > 0 || hasImage(nodeData.frontImageUrl);\n  const hasRear = countPortBlocks(rearPorts) > 0 || hasImage(nodeData.rearImageUrl);\n\n  if (hasFront && hasRear) {\n    return 'both';\n  }\n  if (hasFront) {\n    return 'front';\n  }\n  if (hasRear) {\n    return 'rear';\n  }\n\n  return undefined;\n}\n\nfunction resolvePreferredFace(nodeData: Pick<MountResolutionNodeData, 'face'>): RackMountMode | undefined {\n  if (nodeData.face === 'front' || nodeData.face === 'rear') {\n    return nodeData.face;\n  }\n  return undefined;\n}\n\nexport function resolveSplitRackMountModeForDrop(\n  nodeData: MountResolutionNodeData,\n  dropRelativeX: number,\n  rackWidthPx: number,\n  rackFaceGapPx: number\n): RackMountMode {\n  const explicitMountMode = nodeData.mountMode;\n  if (\n    explicitMountMode === 'front' ||\n    explicitMountMode === 'rear' ||\n    explicitMountMode === 'both'\n  ) {\n    return explicitMountMode;\n  }\n\n  const inferredMountMode = inferRackMountModeFromFaces(nodeData);\n  if (inferredMountMode) {\n    return inferredMountMode;\n  }\n\n  const preferredFace = resolvePreferredFace(nodeData);\n  if (preferredFace) {\n    return preferredFace;\n  }\n\n  const splitMidpointX = rackWidthPx + rackFaceGapPx / 2;\n  return dropRelativeX >= splitMidpointX ? 'front' : 'rear';\n}\n","import type {\n  DevicePort,\n  DeviceFace,\n  NetworkNode,\n  PortBlock,\n  RackLayoutMode,\n  RackMountMode,\n} from '../types';\nimport { Width } from '../types';\nimport {\n  RACK_HEADER_HEIGHT,\n  RACK_WIDTH_PX,\n  STANDARD_RACK_UNIT_HEIGHT_IN,\n  STANDARD_RACK_WIDTH_IN,\n} from './constants';\nimport { inferRackMountModeFromFaces } from './rack-mount-mode';\n\ninterface RackLayoutData {\n  rackLayout?: RackLayoutMode;\n  rackFaceGapPx?: number;\n  rackWidthPx?: number;\n}\n\ninterface RackMountData {\n  mountMode?: RackMountMode;\n  ports?: DevicePort[] | PortBlock[] | number;\n  portGroups?: PortBlock[];\n  rearPortGroups?: PortBlock[];\n  rearPorts?: PortBlock[];\n  frontImageUrl?: string;\n  rearImageUrl?: string;\n}\n\nexport const DEFAULT_RACK_FACE_GAP_PX = 24;\n\n/**\n * Hard ceiling on a rack's U height. Physical racks top out around 60U; this\n * generous cap keeps a hostile persisted `uHeight` (e.g. `5_000_000`) from\n * driving the O(uHeight x devices) availability scan or the per-U marker\n * render into a DoS. Legitimate values (42/48/60U) are well under the cap.\n */\nexport const MAX_RACK_U = 100;\n\n/** U height used when a rack's `uHeight` is absent or not a positive number. */\nexport const DEFAULT_RACK_U_HEIGHT = 42;\n\n/**\n * The rack U height every layout path agrees on: a positive integer, at most\n * {@link MAX_RACK_U}, falling back to {@link DEFAULT_RACK_U_HEIGHT} for an\n * absent or invalid value (0, negative, NaN, a string). The rack's pixel box,\n * its U markers, U snapping/availability and U-derived device positions all\n * resolve through this so they can never disagree about the rack's height.\n */\nexport function resolveRackUHeight(value: unknown): number {\n  if (typeof value === 'number' && Number.isFinite(value) && value >= 1) {\n    return Math.min(MAX_RACK_U, Math.floor(value));\n  }\n  return DEFAULT_RACK_U_HEIGHT;\n}\nexport const RACK_DEVICE_SINGLE_FACE_GUTTER_PX = 4;\n\nexport function getRackLayoutMode(data: RackLayoutData | undefined): RackLayoutMode {\n  return data?.rackLayout === 'split-face' ? 'split-face' : 'single-face';\n}\n\nexport function isSplitFaceRackLayout(data: RackLayoutData | undefined): boolean {\n  return getRackLayoutMode(data) === 'split-face';\n}\n\nexport function getRackFaceGapPx(data: RackLayoutData | undefined): number {\n  if (typeof data?.rackFaceGapPx === 'number' && Number.isFinite(data.rackFaceGapPx) && data.rackFaceGapPx >= 0) {\n    return data.rackFaceGapPx;\n  }\n  return DEFAULT_RACK_FACE_GAP_PX;\n}\n\nexport function getRackFaceWidthPx(data: RackLayoutData | undefined): number {\n  return data?.rackWidthPx || RACK_WIDTH_PX;\n}\n\nexport function getRackUnitHeightPx(data: RackLayoutData | undefined): number {\n  const faceWidth = getRackFaceWidthPx(data);\n  return (faceWidth * STANDARD_RACK_UNIT_HEIGHT_IN) / STANDARD_RACK_WIDTH_IN;\n}\n\nexport function getRackTotalWidthPx(data: RackLayoutData | undefined): number {\n  const faceWidth = getRackFaceWidthPx(data);\n  if (!isSplitFaceRackLayout(data)) {\n    return faceWidth;\n  }\n  return faceWidth * 2 + getRackFaceGapPx(data);\n}\n\n/**\n * Pixel width/height of the rack node's content box (matches RackNode outer dimensions\n * before border — used to sync React Flow node dimensions with diagram layout math).\n */\nexport function getRackNodePixelDimensions(\n  data: (RackLayoutData & { uHeight?: number }) | undefined\n): { width: number; height: number } {\n  const uHeight = resolveRackUHeight(data?.uHeight);\n  const rackUnitHeightPx = getRackUnitHeightPx(data);\n  return {\n    width: getRackTotalWidthPx(data),\n    height: RACK_HEADER_HEIGHT + uHeight * rackUnitHeightPx,\n  };\n}\n\n/**\n * Forces React Flow width/height on rack nodes from rack data. RF's dimension updates\n * (ResizeObserver) otherwise overwrite split-face racks with a single-face measure.\n */\nexport function applyRackPixelDimensionsToNodes(nodes: NetworkNode[]): NetworkNode[] {\n  return nodes.map((node) => {\n    if (node.type !== 'rack') {\n      return node;\n    }\n    const dims = getRackNodePixelDimensions(node.data);\n    if (node.width === dims.width && node.height === dims.height) {\n      return node;\n    }\n    return { ...node, width: dims.width, height: dims.height };\n  });\n}\n\nexport function getRackMountMode(data: RackMountData | undefined): RackMountMode {\n  if (data?.mountMode === 'front' || data?.mountMode === 'rear' || data?.mountMode === 'both') {\n    return data.mountMode;\n  }\n  const inferredMountMode = inferRackMountModeFromFaces(data ?? {});\n  if (inferredMountMode) {\n    return inferredMountMode;\n  }\n  return 'both';\n}\n\nexport function getVisibleFacesForMountMode(mountMode: RackMountMode): DeviceFace[] {\n  if (mountMode === 'front') {\n    return ['front'];\n  }\n  if (mountMode === 'rear') {\n    return ['rear'];\n  }\n  return ['front', 'rear'];\n}\n\nexport function sharesMountedFace(\n  mountModeA: RackMountMode,\n  mountModeB: RackMountMode\n): boolean {\n  const facesA = new Set(getVisibleFacesForMountMode(mountModeA));\n  const facesB = getVisibleFacesForMountMode(mountModeB);\n  return facesB.some((face) => facesA.has(face));\n}\n\nexport function getRackDeviceXPosition(\n  rackData: RackLayoutData | undefined,\n  deviceData: RackMountData | undefined\n): number {\n  if (!isSplitFaceRackLayout(rackData)) {\n    return 0;\n  }\n  const mountMode = getRackMountMode(deviceData);\n  // Split-face orientation: rear on left, front on right.\n  if (mountMode === 'front') {\n    return getRackFaceWidthPx(rackData) + getRackFaceGapPx(rackData);\n  }\n  return 0;\n}\n\nexport function getRackDeviceImageContainerWidthPx(\n  effectiveRackWidth: number,\n  options: {\n    isRackMounted?: boolean;\n    splitFaceLayout?: boolean;\n    /** Numeric widths (px) are treated like full-width devices. */\n    deviceWidth?: Width | number;\n  }\n): number {\n  if (options.isRackMounted !== true) {\n    return effectiveRackWidth;\n  }\n  if (options.splitFaceLayout) {\n    return effectiveRackWidth;\n  }\n  if (options.deviceWidth === Width.HALF) {\n    return Math.floor(effectiveRackWidth / 2);\n  }\n  return effectiveRackWidth + RACK_DEVICE_SINGLE_FACE_GUTTER_PX;\n}\n\nexport function getRackDeviceBodyWidthPx(\n  rackData: RackLayoutData | undefined,\n  deviceData: RackMountData | undefined\n): number {\n  if (!isSplitFaceRackLayout(rackData)) {\n    return getRackFaceWidthPx(rackData);\n  }\n  const mountMode = getRackMountMode(deviceData);\n  if (mountMode === 'both') {\n    return getRackTotalWidthPx(rackData);\n  }\n  return getRackFaceWidthPx(rackData);\n}\n","import { BreakerWay, NetworkNode } from '../types';\nimport { buildDistributionBoardLayout } from './distribution-board-layout';\nimport { U_HEIGHT_PX, RACK_WIDTH_PX } from './constants';\nimport {\n  getCouplerDimensions,\n  getPanelHeight,\n  getPanelWidth,\n  getTubeDimensions,\n} from './coupler-positioning';\nimport { getRackNodePixelDimensions } from './rack-layout';\nimport { SPLICE_WIDTH_PX, TRAY_WIDTH_PX, getTrayNodeHeight } from './splice-positioning';\n\nconst DEFAULT_DEVICE_WIDTH = RACK_WIDTH_PX;\nconst DEFAULT_DEVICE_HEIGHT = U_HEIGHT_PX;\n\nfunction getCableDimensions(node: NetworkNode): { width: number; height: number } {\n  const fibers = Math.max(\n    1,\n    Math.min(24, Number(node.data?.fiberCount) || Number(node.data?.fibersPerTube) || 12),\n  );\n  const handleSize = 10;\n  const handleSpacing = 2;\n  const padding = 6;\n  const height = 60;\n  const width = Math.max(120, fibers * (handleSize + handleSpacing) + padding * 2);\n  return { width, height };\n}\n\nfunction getMultiTubeCableDimensions(node: NetworkNode): { width: number; height: number } {\n  const tubes = Math.max(1, Math.min(24, Number(node.data?.tubeCount) || 12));\n  const handleWidth = 14;\n  const handleSpacing = 3;\n  const padding = 6;\n  const height = 60;\n  const width = Math.max(120, tubes * (handleWidth + handleSpacing) + padding * 2);\n  return { width, height };\n}\n\nfunction getFibreSplitDimensions(node: NetworkNode): { width: number; height: number } {\n  const handleSize = 9;\n  const handleSpacing = 3;\n  const padding = 3;\n  const minWidth = 50;\n  const minHeight = 30;\n  const inputCount = Math.min(Math.max(Number(node.data?.inputCount) || 1, 1), 2);\n  const outputCount = Math.min(Number(node.data?.outputCount) || 8, 48);\n  const inputHandlesHeight = inputCount * handleSize + (inputCount - 1) * handleSpacing;\n  const outputHandlesHeight = outputCount * handleSize + (outputCount - 1) * handleSpacing;\n  const totalHandlesHeight = Math.max(inputHandlesHeight, outputHandlesHeight);\n  return {\n    width: Math.max(minWidth, 50),\n    height: Math.max(minHeight, totalHandlesHeight + padding * 2),\n  };\n}\n\nfunction getClosureDimensions(node: NetworkNode): { width: number; height: number } {\n  const data = node.data ?? {};\n  const width =\n    (typeof data.closureWidth === 'number' && data.closureWidth) ||\n    (typeof data.panelWidth === 'number' && data.panelWidth) ||\n    400;\n  const height =\n    (typeof data.closureHeight === 'number' && data.closureHeight) ||\n    (typeof data.panelHeight === 'number' && data.panelHeight) ||\n    300;\n  return { width, height };\n}\n\nfunction getDeviceDimensions(node: NetworkNode): { width: number; height: number } {\n  const uHeight = Math.max(1, Number(node.data?.uHeight) || 1);\n  return {\n    width: DEFAULT_DEVICE_WIDTH,\n    height: uHeight * DEFAULT_DEVICE_HEIGHT,\n  };\n}\n\nfunction getDistributionBoardDimensions(node: NetworkNode): { width: number; height: number } {\n  const layout = buildDistributionBoardLayout(\n    node.data?.ways as BreakerWay[] | undefined,\n    node.data?.wayColumns,\n  );\n  return { width: layout.width, height: layout.height };\n}\n\n/**\n * Resolve declared pixel dimensions for bounds / SSR-style layout.\n * Prefers explicit node.width/height, then measured, then type-specific defaults.\n * @public\n */\nexport function resolveNodeDimensions(node: NetworkNode): { width: number; height: number } {\n  const width = node.width ?? node.measured?.width;\n  const height = node.height ?? node.measured?.height;\n  if (typeof width === 'number' && typeof height === 'number') return { width, height };\n  const fallback = resolveDefaultNodeDimensions(node);\n  return {\n    width: typeof width === 'number' ? width : fallback.width,\n    height: typeof height === 'number' ? height : fallback.height,\n  };\n}\n\nfunction resolveDefaultNodeDimensions(node: NetworkNode): { width: number; height: number } {\n  switch (node.type) {\n    case 'rack':\n      return getRackNodePixelDimensions(node.data);\n    case 'patch-panel':\n      return { width: getPanelWidth(node), height: getPanelHeight(node) };\n    case 'closure':\n      return getClosureDimensions(node);\n    case 'splice-tray':\n      return {\n        width: TRAY_WIDTH_PX,\n        height: getTrayNodeHeight(node.data?.mode === 'ribbon'),\n      };\n    case 'coupler':\n      return getCouplerDimensions(node);\n    case 'tube':\n      return getTubeDimensions(node);\n    case 'splice':\n      return {\n        width: SPLICE_WIDTH_PX,\n        height: node.data?.mode === 'ribbon' ? 32 : 16,\n      };\n    case 'cable':\n      return getCableDimensions(node);\n    case 'multi-tube-cable':\n      return getMultiTubeCableDimensions(node);\n    case 'fibre-split':\n      return getFibreSplitDimensions(node);\n    case 'distribution-board':\n      return getDistributionBoardDimensions(node);\n    case 'device':\n    case 'server':\n    case 'switch':\n    case 'router':\n      return getDeviceDimensions(node);\n    default:\n      return { width: 80, height: 30 };\n  }\n}\n\n/**\n * Top-level nodes with declared dimensions for getNodesBounds.\n * @public\n */\nexport function prepareTopLevelNodesForBounds(nodes: readonly NetworkNode[]): NetworkNode[] {\n  return nodes\n    .filter((node) => !node.parentId)\n    .map((node) => {\n      const { width, height } = resolveNodeDimensions(node);\n      return { ...node, width, height };\n    })\n    .filter((node) => (node.width ?? 0) > 0 && (node.height ?? 0) > 0);\n}\n","import { getViewportForBounds, type Viewport } from '@xyflow/react';\nimport { NetworkNode } from '../types';\nimport { prepareTopLevelNodesForBounds } from './resolve-node-dimensions';\n\nexport type ComputeInitialViewportOptions = {\n  padding?: number;\n  minZoom?: number;\n  maxZoom?: number;\n};\n\n/**\n * Compute a viewport that fits top-level nodes using declared dimensions (v12 SSR model).\n * Does not require nodes to be mounted in the DOM.\n * @public\n */\nexport function computeInitialViewport(\n  nodes: readonly NetworkNode[],\n  container: { width: number; height: number },\n  options: ComputeInitialViewportOptions = {},\n): Viewport | null {\n  const { width, height } = container;\n  if (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1 || nodes.length === 0) {\n    return null;\n  }\n\n  const prepared = prepareTopLevelNodesForBounds(nodes);\n  if (prepared.length === 0) {\n    return null;\n  }\n\n  // These are declared, top-level rectangles, not React Flow internal nodes.\n  // Calculate their bounds directly: getNodesBounds requires a nodeLookup in\n  // development even when no child nodes are included.\n  let left = Infinity;\n  let top = Infinity;\n  let right = -Infinity;\n  let bottom = -Infinity;\n  for (const node of prepared) {\n    const nodeWidth = node.width!;\n    const nodeHeight = node.height!;\n    const x = node.position.x - nodeWidth * (node.origin?.[0] ?? 0);\n    const y = node.position.y - nodeHeight * (node.origin?.[1] ?? 0);\n    left = Math.min(left, x);\n    top = Math.min(top, y);\n    right = Math.max(right, x + nodeWidth);\n    bottom = Math.max(bottom, y + nodeHeight);\n  }\n  const bounds = { x: left, y: top, width: right - left, height: bottom - top };\n  if (![bounds.x, bounds.y, bounds.width, bounds.height].every(Number.isFinite)) {\n    return null;\n  }\n  if (bounds.width <= 0 && bounds.height <= 0) {\n    return null;\n  }\n\n  const padding = options.padding ?? 0.2;\n  const minZoom = options.minZoom ?? 0.1;\n  const maxZoom = options.maxZoom ?? 4;\n\n  return getViewportForBounds(bounds, width, height, minZoom, maxZoom, padding);\n}\n","/**\n * Zoom below which the diagram drops its micro-label tier (bay labels,\n * manufacturer, U indicator).\n *\n * Those classes are authored at 8-9 CSS px because they have to fit inside\n * 1U-scale geometry. Below this zoom they render under ~7 device px, where they\n * are illegible noise that hides the shapes underneath.\n *\n * @public\n */\nexport const DIAGRAM_LABEL_LOD_ZOOM = 0.8;\n\n/**\n * Class placed on the diagram wrapper while the viewport is below\n * {@link DIAGRAM_LABEL_LOD_ZOOM}.\n *\n * Visibility is CSS-only (descendant rules in the library stylesheet), so the\n * level-of-detail switch costs one `classList` write per threshold crossing\n * rather than a re-render per node. Consumers can hook their own rules onto the\n * same class.\n *\n * @public\n */\nexport const DIAGRAM_ZOOM_LOD_CLASS = \"rn-zoom-below-detail\";\n\n/**\n * Whether `zoom` is below the micro-label threshold.\n *\n * Non-finite input is treated as \"not below\" so a store that has not published\n * a transform yet renders the full label set rather than a stripped-down one.\n *\n * @public\n */\nexport function isBelowLabelLodZoom(\n  zoom: number,\n  threshold: number = DIAGRAM_LABEL_LOD_ZOOM,\n): boolean {\n  if (!Number.isFinite(zoom)) {\n    return false;\n  }\n  return zoom < threshold;\n}\n\n/**\n * Read the zoom scalar out of a React Flow store transform tuple.\n *\n * @public\n */\nexport function readTransformZoom(transform: unknown): number {\n  if (Array.isArray(transform) && typeof transform[2] === \"number\") {\n    return transform[2];\n  }\n  return 1;\n}\n","import type { FitViewOptions } from \"@xyflow/react\";\nimport { DIAGRAM_LABEL_LOD_ZOOM } from \"./zoom-lod\";\n\n/**\n * Default `fitView` padding applied when the consumer passes no\n * `fitViewOptions.padding`.\n *\n * React Flow's own default is `0.1`, which leaves rack/ODF scenes touching the\n * canvas edges. `0.14` keeps a visible gutter without meaningfully shrinking\n * the content.\n *\n * @public\n */\nexport const DIAGRAM_DEFAULT_FIT_PADDING = 0.14;\n\n/**\n * Smallest authored label size (CSS px) that survives the zoom level-of-detail\n * pass — i.e. the class the readable-zoom floor is computed against.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport const DIAGRAM_SMALLEST_LABEL_PX = 9;\n\n/**\n * Rendered size (CSS px) below which {@link DIAGRAM_SMALLEST_LABEL_PX} text is\n * treated as unreadable.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport const DIAGRAM_MIN_READABLE_LABEL_PX = 9;\n\n/**\n * Hard ceiling on the readable-zoom floor. Nothing zooms past this in the name\n * of readability.\n *\n * Derived from {@link DIAGRAM_LABEL_LOD_ZOOM} rather than set independently: a\n * ceiling *below* the level-of-detail threshold would land a floored scene in\n * the band where the micro-label tier is still hidden and the primary 9px label\n * renders under 9 device px — a lift that buys nothing. Raising the floor to\n * the threshold is the smallest zoom at which the full label set is back.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport const DIAGRAM_MAX_READABLE_ZOOM_FLOOR = DIAGRAM_LABEL_LOD_ZOOM;\n\n/**\n * How far above the natural fit the readable-zoom floor may zoom in.\n *\n * The floor trades framing for legibility, and that trade is only worth making\n * when it is nearly free. A 42U ODF rack fits at ~0.15 zoom: lifting it to 0.8\n * would replace a whole-rack overview with a six-panel keyhole and *still* be a\n * worse view. So the floor is all-or-nothing — if the readable zoom is more\n * than this multiple of the natural fit, the scene keeps its overview and the\n * zoom level-of-detail pass handles legibility instead.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport const DIAGRAM_MAX_READABLE_ZOOM_LIFT = 1.5;\n\n/**\n * Inputs to {@link computeReadableZoomFloor}.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport interface ReadableZoomFloorOptions {\n  /**\n   * The zoom the content would fit at with no floor. **Required for a floor to\n   * apply** — without it the lift cannot be bounded, and an unbounded floor\n   * wrecks the framing of any large scene.\n   */\n  fittedZoom?: number;\n  /** Smallest label class, in authored CSS px. Defaults to {@link DIAGRAM_SMALLEST_LABEL_PX}. */\n  smallestLabelPx?: number;\n  /** Rendered px that class must reach. Defaults to {@link DIAGRAM_MIN_READABLE_LABEL_PX}. */\n  minReadableLabelPx?: number;\n  /** Ceiling for the floor. Defaults to {@link DIAGRAM_MAX_READABLE_ZOOM_FLOOR}. */\n  maxFloorZoom?: number;\n  /** Lift budget over the natural fit. Defaults to {@link DIAGRAM_MAX_READABLE_ZOOM_LIFT}. */\n  maxLift?: number;\n  /** Diagram `minZoom`. A floor at or below it is a no-op. Defaults to `0.1`. */\n  minZoom?: number;\n  /** Diagram `maxZoom`. The floor never exceeds it. Defaults to `4`. */\n  maxZoom?: number;\n}\n\nfunction positiveOr(value: number | undefined, fallback: number): number {\n  return typeof value === \"number\" && Number.isFinite(value) && value > 0\n    ? value\n    : fallback;\n}\n\n/**\n * The zoom below which the smallest surviving label class renders too small to\n * read — or `undefined` when no floor should be applied.\n *\n * Returns `undefined` (leave the fit alone) when:\n * - `fittedZoom` is unknown, so the lift cannot be bounded;\n * - the content already fits at or above the readable zoom;\n * - reaching the readable zoom would cost more than `maxLift` — a scene that\n *   large is better served by its overview plus the label LOD pass;\n * - the readable zoom is not above the diagram's own `minZoom`.\n *\n * Pure: no DOM, no React Flow store. Feeding the result to\n * `fitViewOptions.minZoom` makes React Flow's fit clamp to it while still\n * centring on the content bounds.\n *\n * @deprecated The diagram no longer applies a readable-zoom floor to its fits\n * (since 0.12.0 every fit frames the complete scene and the zoom\n * level-of-detail pass handles legibility). Kept for compatibility; it will be\n * removed in the next major version.\n * @public\n */\nexport function computeReadableZoomFloor(\n  options: ReadableZoomFloorOptions = {},\n): number | undefined {\n  const smallestLabelPx = positiveOr(\n    options.smallestLabelPx,\n    DIAGRAM_SMALLEST_LABEL_PX,\n  );\n  const minReadableLabelPx = positiveOr(\n    options.minReadableLabelPx,\n    DIAGRAM_MIN_READABLE_LABEL_PX,\n  );\n  const maxFloorZoom = positiveOr(\n    options.maxFloorZoom,\n    DIAGRAM_MAX_READABLE_ZOOM_FLOOR,\n  );\n  const maxLift = positiveOr(options.maxLift, DIAGRAM_MAX_READABLE_ZOOM_LIFT);\n  const minZoom = positiveOr(options.minZoom, 0.1);\n  const maxZoom = positiveOr(options.maxZoom, 4);\n\n  const fittedZoom = options.fittedZoom;\n  if (\n    typeof fittedZoom !== \"number\" ||\n    !Number.isFinite(fittedZoom) ||\n    fittedZoom <= 0\n  ) {\n    return undefined;\n  }\n\n  const readableZoom = Math.min(\n    minReadableLabelPx / smallestLabelPx,\n    maxFloorZoom,\n    maxZoom,\n  );\n\n  if (readableZoom <= minZoom) {\n    return undefined;\n  }\n  if (readableZoom <= fittedZoom) {\n    return undefined;\n  }\n  if (readableZoom > fittedZoom * maxLift) {\n    return undefined;\n  }\n\n  return readableZoom;\n}\n\n/**\n * Context for {@link resolveFitViewOptions}.\n *\n * @public\n */\nexport interface FitViewPolicyContext {\n  /** Diagram `minZoom` prop. */\n  minZoom?: number;\n  /** Diagram `maxZoom` prop. */\n  maxZoom?: number;\n  /**\n   * The zoom the content would fit at, when known. Enables the floor.\n   * @deprecated The diagram no longer passes a fitted zoom; see\n   * {@link computeReadableZoomFloor}.\n   */\n  fittedZoom?: number;\n  /**\n   * Pre-computed floor, bypassing {@link computeReadableZoomFloor}.\n   * @deprecated See {@link computeReadableZoomFloor}.\n   */\n  readableZoomFloor?: number;\n}\n\n/**\n * Merge the library's fit-view defaults under the consumer's own options.\n *\n * Anything the consumer set is preserved verbatim — including `padding: 0` and\n * an explicit `minZoom`, which opt out of the padding default and the\n * readable-zoom floor respectively. Only unset keys are filled in, and `minZoom`\n * is only added when a floor actually applies.\n *\n * @public\n */\nexport function resolveFitViewOptions(\n  options: FitViewOptions | undefined,\n  context: FitViewPolicyContext = {},\n): FitViewOptions {\n  const resolved: FitViewOptions = {\n    ...options,\n    padding: options?.padding ?? DIAGRAM_DEFAULT_FIT_PADDING,\n  };\n\n  if (options?.minZoom !== undefined) {\n    return resolved;\n  }\n\n  const candidateReadableZoomFloor =\n    context.readableZoomFloor ??\n    computeReadableZoomFloor({\n      fittedZoom: context.fittedZoom,\n      minZoom: context.minZoom,\n      maxZoom: options?.maxZoom ?? context.maxZoom,\n    });\n  const readableZoomFloor =\n    candidateReadableZoomFloor === undefined\n      ? undefined\n      : Math.min(\n          candidateReadableZoomFloor,\n          positiveOr(options?.maxZoom ?? context.maxZoom, 4),\n        );\n\n  if (readableZoomFloor !== undefined) {\n    resolved.minZoom = readableZoomFloor;\n  }\n\n  return resolved;\n}\n","import React, { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from \"react\";\nimport { getViewportForBounds, type Node, type Viewport, type ReactFlowInstance } from \"@xyflow/react\";\nimport type { NetworkDiagramProps, NetworkDiagramInitialViewport, NetworkNode, NetworkFlowNode } from \"../types\";\nimport { computeInitialViewport } from \"../utils/initial-viewport\";\nimport { resolveNodeDimensions } from \"../utils/resolve-node-dimensions\";\nimport { DIAGRAM_DEFAULT_FIT_PADDING, resolveFitViewOptions } from \"../utils/fit-view-policy\";\n\nconst MIN_FIT_VIEWPORT_PX = 60;\n// How long after the first successful fit the wrapper-resize settle watch\n// stays armed — long enough to outlive post-mount chrome settling (toolbar\n// growth, panel expansion, font swap) without holding a camera intent.\nconst INITIAL_FIT_SETTLE_MS = 1500;\nconst matchesViewport = (a: Viewport, b: Viewport) =>\n  Math.abs(a.x - b.x) < 0.000001 && Math.abs(a.y - b.y) < 0.000001 && Math.abs(a.zoom - b.zoom) < 0.000001;\n// How long the default (measured) init fit waits for the remaining nodes once\n// some have reported a size. Only reached when a mounted node never measures\n// (e.g. a custom node type that renders nothing); a normal scene fits as soon\n// as every node is measured.\nconst DEFAULT_FIT_SETTLE_MS = 400;\ntype Options = Pick<NetworkDiagramProps, \"initialViewport\" | \"fitViewOnInit\" | \"fitViewOptions\" | \"onViewportReady\" | \"onInit\" | \"onError\" | \"onlyRenderVisibleElements\"> & {\n  reactFlowNodes: NetworkFlowNode[];\n  latestNodesRef: RefObject<Node[]>;\n  reactFlowWrapper: RefObject<HTMLDivElement | null>;\n  reactFlowApiRef: RefObject<Pick<ReactFlowInstance, \"setViewport\">>;\n  minZoom: number;\n  maxZoom: number;\n};\n\nconst hasMeasuredSize = (node: Node): boolean =>\n  (node.measured?.width ?? 0) > 0 && (node.measured?.height ?? 0) > 0;\n\n/** Fit policy, initial camera lifecycle, and error-recovery viewport ownership. */\nexport function useDiagramViewport({\n  reactFlowNodes, latestNodesRef, reactFlowWrapper, reactFlowApiRef,\n  initialViewport, fitViewOnInit, fitViewOptions, minZoom, maxZoom,\n  onViewportReady, onInit, onError, onlyRenderVisibleElements,\n}: Options) {\n  // The declared `initialViewport` policies own the camera: 'fit' runs its own\n  // declared-bounds fit and 'restore' must never be overwritten by a later\n  // measured fit. 'none' (and no initialViewport) honour `fitViewOnInit`.\n  const effectiveFitViewOnInit =\n    initialViewport?.mode === \"fit\" || initialViewport?.mode === \"restore\"\n      ? false\n      : fitViewOnInit !== false;\n\n  const [viewportReady, setViewportReady] = useState(\n    !initialViewport || initialViewport.mode === \"none\",\n  );\n  // React Flow's pan/zoom exists (onInit has fired); gates the default fit.\n  const [flowReady, setFlowReady] = useState(false);\n\n  const topLevelLayoutSignature = useMemo(\n    () =>\n      reactFlowNodes\n        .filter((node) => !node.parentId)\n        .map((node) => {\n          const { width, height } = resolveNodeDimensions(node as NetworkNode);\n          return `${node.id}:${node.position?.x ?? 0},${node.position?.y ?? 0},${width},${height}`;\n        })\n        .join(\"|\"),\n    [reactFlowNodes],\n  );\n\n  // Let React Flow fit the measured bounds. A label-readability minimum can\n  // exceed the zoom that fits the scene and silently crop its outer nodes.\n  const fitPaddingDefault =\n    typeof fitViewOptions?.padding === \"number\"\n      ? fitViewOptions.padding\n      : DIAGRAM_DEFAULT_FIT_PADDING;\n  const resolvedFitViewOptions = useMemo(\n    () => resolveFitViewOptions(fitViewOptions),\n    [fitViewOptions],\n  );\n\n  // F20: the refit effect must depend only on the layout signature and a stable\n  // mode — never on the identity of an inline `initialViewport` object literal,\n  // the `onViewportReady` callback, or the reactFlowNodes-derived\n  // `applyComputedViewport`. Those identities changed on every parent render,\n  // selection, and re-measure, which re-ran the effect and forced a refit that\n  // stomped the user's pan/zoom. The live fit parameters are read from a ref so\n  // the effect's dependency set stays minimal.\n  //\n  // Both fit paths share the configured padding, so the\n  // declared-bounds fit ('fit' mode) and React Flow's measured fit share the same\n  // padding and explicit zoom limits. `initialViewport` overrides win.\n  const fitParams = {\n    initialViewport,\n    fitPadding: fitPaddingDefault,\n    explicitMinZoom: fitViewOptions?.minZoom,\n    minZoom,\n    maxZoom: fitViewOptions?.maxZoom ?? maxZoom,\n  };\n  const fitParamsRef = useRef<{\n    initialViewport: NetworkDiagramInitialViewport | undefined;\n    fitPadding: number;\n    explicitMinZoom: number | undefined;\n    minZoom: number;\n    maxZoom: number;\n  }>(fitParams);\n  fitParamsRef.current = fitParams;\n\n  const onViewportReadyRef = useRef(onViewportReady);\n  onViewportReadyRef.current = onViewportReady;\n  const onInitRef = useRef(onInit);\n  onInitRef.current = onInit;\n  const initializedFlowRef = useRef<ReactFlowInstance | null>(null);\n\n  // One-shot guard for the initial fit/restore, plus the signature we last fit\n  // to so subsequent effect runs only refit on a real top-level layout change.\n  const didInitialViewportRef = useRef(false);\n  const lastFitSignatureRef = useRef<string | null>(null);\n  // Timestamp of the first successful declared-bounds fit and the wrapper size\n  // that fit was computed for — the settle watch below re-fits if the wrapper\n  // keeps changing shortly after that first fit (chrome still settling).\n  const firstFitAtRef = useRef<number | null>(null);\n  const lastFitContainerRef = useRef<{ width: number; height: number } | null>(null);\n  const lastAutomaticViewportRef = useRef<Viewport | null>(null);\n  const cameraIntentRef = useRef(0);\n  // Set once the user (or the host, through the instance) moves the camera\n  // away from the last automatic fit. The default refit policy then leaves the\n  // camera alone; only an explicit `refitOnLayoutChange: true` still refits.\n  const userMovedCameraRef = useRef(false);\n  const wasDraggingRef = useRef(false);\n  const nodesDragging = reactFlowNodes.some((node) => node.dragging === true);\n  const restoreInFlightRef = useRef(false);\n  const lastViewportRef = useRef<Viewport | null>(null);\n  const recoveryViewportRef = useRef<Viewport | null>(null);\n  const pauseViewportTrackingRef = useRef(false);\n  const topLevelLayoutSignatureRef = useRef(topLevelLayoutSignature);\n  topLevelLayoutSignatureRef.current = topLevelLayoutSignature;\n  const [viewportLifecycleGeneration, setViewportLifecycleGeneration] =\n    useState(0);\n\n  // The tracker is a tiny child subscription, so viewport changes update one\n  // ref without re-rendering NetworkDiagramInner.\n  const trackViewport = useCallback((viewport: Viewport) => {\n    if (!pauseViewportTrackingRef.current) {\n      lastViewportRef.current = viewport;\n      const automatic = lastAutomaticViewportRef.current;\n      if (automatic && !matchesViewport(viewport, automatic)) {\n        firstFitAtRef.current = null;\n        cameraIntentRef.current += 1;\n        userMovedCameraRef.current = true;\n      }\n    }\n  }, []);\n\n  const handleViewportMoveStart = useCallback((event: MouseEvent | TouchEvent | null) => {\n    if (event) {\n      firstFitAtRef.current = null;\n      cameraIntentRef.current += 1;\n      userMovedCameraRef.current = true;\n    }\n  }, []);\n\n  const applyRestoredViewport = useCallback(\n    (instance: ReactFlowInstance | null) => {\n      const config = fitParamsRef.current.initialViewport;\n      if (\n        !instance ||\n        !config ||\n        config.mode !== \"restore\" ||\n        didInitialViewportRef.current ||\n        restoreInFlightRef.current\n      ) {\n        return;\n      }\n      restoreInFlightRef.current = true;\n      void instance.setViewport(config.viewport).then((applied) => {\n        restoreInFlightRef.current = false;\n        if (!applied || didInitialViewportRef.current) {\n          return;\n        }\n        lastViewportRef.current = config.viewport;\n        didInitialViewportRef.current = true;\n        setViewportReady(true);\n        onViewportReadyRef.current?.();\n      });\n    },\n    [],\n  );\n\n  const handleReactFlowInit = useCallback(\n    (instance: ReactFlowInstance) => {\n      initializedFlowRef.current = instance;\n      setFlowReady(true);\n      onInitRef.current?.(instance);\n\n      const recoveryViewport = recoveryViewportRef.current;\n      if (recoveryViewport) {\n        restoreInFlightRef.current = true;\n        void instance.setViewport(recoveryViewport).then((applied) => {\n          restoreInFlightRef.current = false;\n          recoveryViewportRef.current = null;\n          pauseViewportTrackingRef.current = false;\n          if (applied) {\n            lastViewportRef.current = recoveryViewport;\n            didInitialViewportRef.current = true;\n            lastFitSignatureRef.current = topLevelLayoutSignatureRef.current;\n            setViewportReady(true);\n            return;\n          }\n\n          // A not-yet-ready viewport can reject a set request. Re-run the\n          // declared initialization policy once the new canvas has settled.\n          didInitialViewportRef.current = false;\n          lastFitSignatureRef.current = null;\n          setViewportLifecycleGeneration((generation) => generation + 1);\n          applyRestoredViewport(instance);\n        });\n        return;\n      }\n\n      pauseViewportTrackingRef.current = false;\n      applyRestoredViewport(instance);\n      // The fit effect may have run before ZoomPane created its pan/zoom API.\n      // Initialization retries it even if the wrapper never resizes again.\n      if (fitParamsRef.current.initialViewport?.mode === \"fit\" && !didInitialViewportRef.current) {\n        setViewportLifecycleGeneration((generation) => generation + 1);\n      }\n    },\n    [applyRestoredViewport],\n  );\n\n  const applyComputedViewport = useCallback(() => {\n    const {\n      initialViewport: config,\n      fitPadding,\n      explicitMinZoom,\n      minZoom: paramMinZoom,\n      maxZoom: paramMaxZoom,\n    } = fitParamsRef.current;\n    if (!config || config.mode !== \"fit\" || !reactFlowWrapper.current || !initializedFlowRef.current?.viewportInitialized) {\n      return false;\n    }\n\n    const rect = reactFlowWrapper.current.getBoundingClientRect();\n    // A fit against a degenerate wrapper (display:none, a flex row that hasn't\n    // laid out yet, a backgrounded tab reporting 0×0) computes a garbage zoom\n    // that gets clamped to maxZoom — the camera ends up buried in one node and\n    // nothing ever refits. Refuse to fit until the wrapper has a real size; the\n    // caller retries (frames + resize observer) until this succeeds.\n    if (rect.width < MIN_FIT_VIEWPORT_PX || rect.height < MIN_FIT_VIEWPORT_PX) {\n      return false;\n    }\n    const padding = config.padding ?? fitPadding;\n    const consumerMinZoom = config.minZoom ?? explicitMinZoom;\n    const maxZoomForFit = config.maxZoom ?? paramMaxZoom;\n    const container = { width: rect.width, height: rect.height };\n\n    const viewport = computeInitialViewport(\n      latestNodesRef.current as NetworkNode[],\n      container,\n      {\n        padding,\n        minZoom: consumerMinZoom ?? paramMinZoom,\n        maxZoom: maxZoomForFit,\n      },\n    );\n\n    if (!viewport) {\n      return false;\n    }\n\n    lastAutomaticViewportRef.current = viewport;\n    lastViewportRef.current = viewport;\n    reactFlowApiRef.current.setViewport(viewport);\n    lastFitContainerRef.current = { width: rect.width, height: rect.height };\n    return true;\n  }, [latestNodesRef, reactFlowApiRef, reactFlowWrapper]);\n\n  // A declared-bounds fit can land while the page chrome around the canvas is\n  // still settling (toolbars re-wrapping on font/metrics settle, panels\n  // expanding): the wrapper then resizes *after* the fit and a zoom chosen for\n  // the pre-settle container is permanently wrong — no signature change ever\n  // refits it. For a bounded window after the first successful fit, further\n  // wrapper resizes therefore re-run the same declared-bounds fit on the new\n  // container. This observer lives in its own effect on purpose: the fit\n  // effect below re-runs on every signature/correction commit, and its\n  // cleanup would tear a watch down in the same mount tick it was armed —\n  // milliseconds before the chrome typically finishes settling.\n  useEffect(() => {\n    if (typeof ResizeObserver === \"undefined\") {\n      return;\n    }\n    const wrapper = reactFlowWrapper.current;\n    if (!wrapper) {\n      return;\n    }\n    const observer = new ResizeObserver(() => {\n      const fittedAt = firstFitAtRef.current;\n      if (fittedAt === null || performance.now() - fittedAt > INITIAL_FIT_SETTLE_MS) {\n        return;\n      }\n      const automatic = lastAutomaticViewportRef.current;\n      const current = initializedFlowRef.current?.getViewport();\n      if (\n        latestNodesRef.current.some((node) => node.dragging === true) ||\n        (automatic && current && !matchesViewport(current, automatic))\n      ) {\n        firstFitAtRef.current = null;\n        return;\n      }\n      const rect = wrapper.getBoundingClientRect();\n      if (\n        rect.width < MIN_FIT_VIEWPORT_PX ||\n        rect.height < MIN_FIT_VIEWPORT_PX\n      ) {\n        return;\n      }\n      const last = lastFitContainerRef.current;\n      if (\n        last &&\n        Math.abs(rect.width - last.width) < 1 &&\n        Math.abs(rect.height - last.height) < 1\n      ) {\n        return;\n      }\n      applyComputedViewport();\n    });\n    observer.observe(wrapper);\n    return () => observer.disconnect();\n  }, [applyComputedViewport, reactFlowWrapper, latestNodesRef]);\n\n  const initialViewportMode = initialViewport?.mode;\n\n  useEffect(() => {\n    const config = fitParamsRef.current.initialViewport;\n    if (!config) {\n      return;\n    }\n\n    // The remounted React Flow instance restores this camera in its onInit.\n    // Avoid racing that restore with the declared initial-fit effect.\n    if (recoveryViewportRef.current) {\n      return;\n    }\n\n    // restore / none apply exactly once, then never touch the camera again, so a\n    // later parent render or layout change can't override the user's pan/zoom.\n    if (config.mode === \"restore\") {\n      applyRestoredViewport(initializedFlowRef.current);\n      return;\n    }\n\n    if (config.mode === \"none\") {\n      setViewportReady(true);\n      return;\n    }\n\n    // mode === \"fit\"\n    if (reactFlowNodes.length === 0) {\n      // Nothing to fit — but the canvas still has to become visible, or\n      // `hideUntilViewportReady` would keep an empty diagram (and its empty\n      // state) at `opacity: 0` forever. The fit itself is deliberately not\n      // marked as done, so the first real node still gets fitted.\n      setViewportReady(true);\n      return;\n    }\n\n    const signature = topLevelLayoutSignature;\n\n    // A manual move is not a request to reframe the scene. Consume its layout\n    // signatures through the final drop so the camera stays fixed throughout.\n    if (didInitialViewportRef.current && (nodesDragging || wasDraggingRef.current)) {\n      wasDraggingRef.current = nodesDragging;\n      firstFitAtRef.current = null;\n      cameraIntentRef.current += 1;\n      lastFitSignatureRef.current = signature;\n      return;\n    }\n\n    // (a) Initial fit: exactly once, as soon as the wrapper is sized and the\n    // declared node bounds are computable. Retry across a couple of frames only\n    // while the fit hasn't succeeded — until it does there is no user camera to\n    // stomp, so retrying is safe.\n    if (!didInitialViewportRef.current) {\n      let cancelled = false;\n      let observer: ResizeObserver | null = null;\n      const runFit = () => {\n        if (cancelled || didInitialViewportRef.current) {\n          return;\n        }\n        if (applyComputedViewport()) {\n          didInitialViewportRef.current = true;\n          firstFitAtRef.current = performance.now();\n          lastFitSignatureRef.current = signature;\n          setViewportReady(true);\n          onViewportReadyRef.current?.();\n        }\n      };\n\n      runFit();\n\n      if (!didInitialViewportRef.current) {\n        const rafId = window.requestAnimationFrame(() => {\n          window.requestAnimationFrame(runFit);\n        });\n        // The wrapper may be degenerate for longer than two frames (flex row\n        // still laying out, tab backgrounded, container animated in). Keep\n        // watching until the first successful fit — one-shot semantics are\n        // preserved because runFit no-ops once didInitialViewportRef is set.\n        // Post-success wrapper resizes are handled by the settle watch in the\n        // dedicated effect above (it must outlive this effect's re-runs).\n        if (\n          typeof ResizeObserver !== \"undefined\" &&\n          reactFlowWrapper.current\n        ) {\n          observer = new ResizeObserver(() => runFit());\n          observer.observe(reactFlowWrapper.current);\n        }\n        return () => {\n          cancelled = true;\n          window.cancelAnimationFrame(rafId);\n          observer?.disconnect();\n          observer = null;\n        };\n      }\n      return;\n    }\n\n    // (b) Subsequent refit: only when the top-level layout signature actually\n    // changed AND refitOnLayoutChange !== false. The effect can re-run for other\n    // reasons (e.g. adding a child node changes reactFlowNodes.length without\n    // changing the top-level signature) — those must not move the camera.\n    if (config.refitOnLayoutChange === false) {\n      return;\n    }\n    if (lastFitSignatureRef.current === signature) {\n      return;\n    }\n    // Default policy: keep the scene framed only while the library still owns\n    // the camera. Once the user has panned/zoomed (including \"Zoom to edit\"),\n    // a later layout change — a De-rack, an added node — must not throw that\n    // camera away. `refitOnLayoutChange: true` opts back into always refitting.\n    if (config.refitOnLayoutChange !== true && userMovedCameraRef.current) {\n      lastFitSignatureRef.current = signature;\n      return;\n    }\n    if (applyComputedViewport()) {\n      lastFitSignatureRef.current = signature;\n      return;\n    }\n    // The refit bailed (degenerate wrapper mid-layout, or bounds not yet\n    // computable). The signature is deliberately left unconsumed so a later\n    // effect run still lands the settled fit; retry across a bounded span of\n    // frames plus wrapper resizes — long enough to outlive toolbar/layout\n    // churn, short enough that a long-hidden canvas doesn't hold a stale\n    // camera intent. A superseding signature cancels the wait via cleanup.\n    let cancelled = false;\n    let rafId: number | null = null;\n    let observer: ResizeObserver | null = null;\n    let frames = 0;\n    const cameraIntent = cameraIntentRef.current;\n    const retry = () => {\n      if (cancelled || cameraIntentRef.current !== cameraIntent || lastFitSignatureRef.current === signature) {\n        return;\n      }\n      frames += 1;\n      if (applyComputedViewport()) {\n        lastFitSignatureRef.current = signature;\n        observer?.disconnect();\n        observer = null;\n        return;\n      }\n      if (frames >= 90) {\n        observer?.disconnect();\n        observer = null;\n        return;\n      }\n      rafId = window.requestAnimationFrame(retry);\n    };\n    rafId = window.requestAnimationFrame(retry);\n    if (typeof ResizeObserver !== \"undefined\" && reactFlowWrapper.current) {\n      observer = new ResizeObserver(() => retry());\n      observer.observe(reactFlowWrapper.current);\n    }\n    return () => {\n      cancelled = true;\n      if (rafId !== null) {\n        window.cancelAnimationFrame(rafId);\n      }\n      observer?.disconnect();\n    };\n  }, [topLevelLayoutSignature, reactFlowNodes.length, nodesDragging, applyComputedViewport, applyRestoredViewport, initialViewportMode, viewportLifecycleGeneration, reactFlowWrapper]);\n\n  // Default init fit (no `initialViewport`, `fitViewOnInit` not false).\n  //\n  // React Flow's own `fitView` prop resolves its one-shot fit on the *first*\n  // node-internals update, measured or not. Device nodes refresh their handle\n  // internals in a frame callback that runs before the first ResizeObserver\n  // batch, so the native fit framed whichever single device reported first and\n  // then never ran again. Instead the library requests the fit itself, once,\n  // after the viewport exists and every mounted node has a measured size.\n  const didDefaultFitRef = useRef(false);\n  const resolvedFitViewOptionsRef = useRef(resolvedFitViewOptions);\n  resolvedFitViewOptionsRef.current = resolvedFitViewOptions;\n  // Applied directly (not through the instance's queued `fitView`, which only\n  // resolves on React Flow's next node update) so it lands in this frame and\n  // cannot race a camera action the user starts straight after mount.\n  const applyMeasuredFit = useCallback(\n    (candidateIds: string[]): boolean => {\n      const instance = initializedFlowRef.current;\n      const wrapper = reactFlowWrapper.current;\n      if (!instance || !wrapper) {\n        return false;\n      }\n      const options = resolvedFitViewOptionsRef.current;\n      const { minZoom: paramMinZoom, maxZoom: paramMaxZoom } = fitParamsRef.current;\n      const requested = Array.isArray(options.nodes)\n        ? new Set(options.nodes.map((node) => node.id))\n        : null;\n      const ids = new Set(candidateIds);\n      const targets = instance.getNodes().filter(\n        (node) =>\n          (ids.has(node.id) ||\n            (options.includeHiddenNodes === true && node.hidden === true)) &&\n          (!requested || requested.has(node.id)),\n      );\n      if (targets.length === 0) {\n        return false;\n      }\n      const rect = wrapper.getBoundingClientRect();\n      if (rect.width < MIN_FIT_VIEWPORT_PX || rect.height < MIN_FIT_VIEWPORT_PX) {\n        return false;\n      }\n      const bounds = instance.getNodesBounds(targets);\n      if (\n        ![bounds.x, bounds.y, bounds.width, bounds.height].every(Number.isFinite) ||\n        bounds.width <= 0 ||\n        bounds.height <= 0\n      ) {\n        return false;\n      }\n      const viewport = getViewportForBounds(\n        bounds,\n        rect.width,\n        rect.height,\n        options.minZoom ?? paramMinZoom,\n        options.maxZoom ?? paramMaxZoom,\n        options.padding ?? DIAGRAM_DEFAULT_FIT_PADDING,\n      );\n      lastAutomaticViewportRef.current = viewport;\n      lastViewportRef.current = viewport;\n      void instance.setViewport(\n        viewport,\n        options.duration ? { duration: options.duration } : undefined,\n      );\n      return true;\n    },\n    [reactFlowWrapper],\n  );\n\n  useEffect(() => {\n    if (!effectiveFitViewOnInit || didDefaultFitRef.current || !flowReady) {\n      return;\n    }\n    let candidates = reactFlowNodes.filter((node) => node.hidden !== true);\n    if (onlyRenderVisibleElements) {\n      // Culled nodes never mount, so they never measure; fit what is mounted.\n      const mounted = new Set(\n        Array.from(\n          reactFlowWrapper.current?.querySelectorAll<HTMLElement>(\n            \".react-flow__node[data-id]\",\n          ) ?? [],\n        ).map((element) => element.getAttribute(\"data-id\")),\n      );\n      candidates = candidates.filter((node) => mounted.has(node.id));\n    }\n    if (candidates.length === 0) {\n      return;\n    }\n    const runFit = () => {\n      if (didDefaultFitRef.current) {\n        return;\n      }\n      if (applyMeasuredFit(candidates.map((node) => node.id))) {\n        didDefaultFitRef.current = true;\n      }\n    };\n    if (candidates.every(hasMeasuredSize)) {\n      runFit();\n      return;\n    }\n    if (!candidates.some(hasMeasuredSize)) {\n      return;\n    }\n    const timer = window.setTimeout(runFit, DEFAULT_FIT_SETTLE_MS);\n    return () => window.clearTimeout(timer);\n  }, [effectiveFitViewOnInit, flowReady, reactFlowNodes, onlyRenderVisibleElements, reactFlowWrapper, applyMeasuredFit]);\n\n  // A caught error also has to defeat the hide-until-viewport-ready fade: the\n  // \"ready\" signal comes from React Flow, which is exactly what just failed, so\n  // without this the fallback would sit inside an `opacity: 0` wrapper forever.\n  const [hasCaughtError, setHasCaughtError] = useState(false);\n  const handleBoundaryError = useCallback(\n    (error: Error, errorInfo: React.ErrorInfo) => {\n      pauseViewportTrackingRef.current = true;\n      recoveryViewportRef.current =\n        lastViewportRef.current ??\n        initializedFlowRef.current?.getViewport() ??\n        null;\n      setHasCaughtError(true);\n      onError?.(error, errorInfo);\n    },\n    [onError],\n  );\n  // Retrying clears the flag too, so the diagram gets its normal\n  // hide-until-viewport-ready behaviour back on the re-render.\n  const handleBoundaryReset = useCallback(() => {\n    initializedFlowRef.current = null;\n    restoreInFlightRef.current = false;\n    if (!recoveryViewportRef.current) {\n      didInitialViewportRef.current = false;\n      lastFitSignatureRef.current = null;\n    }\n    setViewportLifecycleGeneration((generation) => generation + 1);\n    setHasCaughtError(false);\n  }, []);\n  return {\n    viewportReady, resolvedFitViewOptions, trackViewport,\n    handleViewportMoveStart, handleReactFlowInit, hasCaughtError, handleBoundaryError, handleBoundaryReset,\n  };\n}\n","/**\n * Node types that represent rack-mounted devices with U-position behavior.\n */\nexport const RACK_DEVICE_NODE_TYPES = ['device', 'server', 'switch', 'router'] as const;\n\n/**\n * Node types that can be assigned to a rack, including 0U side-mounted units.\n */\nexport const RACK_ASSIGNABLE_NODE_TYPES = [...RACK_DEVICE_NODE_TYPES, 'vertical-pdu'] as const;\n\nexport type RackDeviceNodeType = (typeof RACK_DEVICE_NODE_TYPES)[number];\nexport type RackAssignableNodeType = (typeof RACK_ASSIGNABLE_NODE_TYPES)[number];\n\nconst rackDeviceTypeSet = new Set<string>(RACK_DEVICE_NODE_TYPES);\nconst rackAssignableTypeSet = new Set<string>(RACK_ASSIGNABLE_NODE_TYPES);\n\n/**\n * Check if a node type is rack-mountable (U-positioned) device type.\n */\nexport function isRackDeviceNodeType(nodeType: string | undefined): nodeType is RackDeviceNodeType {\n  return typeof nodeType === 'string' && rackDeviceTypeSet.has(nodeType);\n}\n\n/**\n * Check if a node type can be attached to a rack, including 0U vertical PDUs.\n */\nexport function isRackAssignableNodeType(\n  nodeType: string | undefined\n): nodeType is RackAssignableNodeType {\n  return typeof nodeType === 'string' && rackAssignableTypeSet.has(nodeType);\n}\n\n/**\n * Check if a node is a rack-mountable (U-positioned) device.\n */\nexport function isRackDeviceNode(node: { type?: string } | null | undefined): node is { type: RackDeviceNodeType } {\n  return !!node && isRackDeviceNodeType(node.type);\n}\n\n/**\n * Check if a node can be assigned to a rack parent.\n */\nexport function isRackAssignableNode(\n  node: { type?: string } | null | undefined\n): node is { type: RackAssignableNodeType } {\n  return !!node && isRackAssignableNodeType(node.type);\n}\n","/**\n * Single seam for reading a node's rendered pixel size.\n *\n * Rack drop-targeting, nearest-rack hit-testing and in-rack child clamping all\n * need the measured size of the node being dragged. In @xyflow/react v12 the\n * measured values live on `node.measured.*`; top-level `width`/`height` are\n * user-set style hints, present only when the caller provided them. The read\n * order is therefore measured-first with the style hint as fallback (a node\n * with an explicit width renders at that width even before measurement).\n */\nexport interface MeasurableNode {\n  width?: number | null;\n  height?: number | null;\n  measured?: { width?: number | null; height?: number | null };\n}\n\n/** Rendered width in px, or 0 when the node has not been measured yet. */\nexport function getNodeRenderedWidth(node: MeasurableNode): number {\n  return node.measured?.width ?? node.width ?? 0;\n}\n\n/** Rendered height in px, or 0 when the node has not been measured yet. */\nexport function getNodeRenderedHeight(node: MeasurableNode): number {\n  return node.measured?.height ?? node.height ?? 0;\n}\n\n/** Rendered {width, height} in px; each is 0 when the node has not been measured yet. */\nexport function getNodeRenderedSize(node: MeasurableNode): { width: number; height: number } {\n  return { width: getNodeRenderedWidth(node), height: getNodeRenderedHeight(node) };\n}\n","import type { Node } from \"@xyflow/react\";\nimport type { NetworkNode } from \"../types\";\nimport { getDefaultDeviceFace } from \"./render-ports\";\n\nexport function resolveNodeFace(\n  data: NetworkNode[\"data\"] | undefined,\n): \"front\" | \"rear\" {\n  if (data?.face === \"front\" || data?.face === \"rear\") {\n    return data.face;\n  }\n  return getDefaultDeviceFace(data);\n}\n\nexport function setNodeFaceData(\n  data: NetworkNode[\"data\"],\n  face: \"front\" | \"rear\",\n): NetworkNode[\"data\"] {\n  return {\n    ...data,\n    face,\n  };\n}\n\n/** Preserve node and array identities when the selected id already matches. */\nexport function selectNodesById<T extends Node>(nodes: T[], selectedNodeId: string | null): T[] {\n  let changed = false;\n  const nextNodes = nodes.map((node) => {\n    const shouldSelect = selectedNodeId !== null && node.id === selectedNodeId;\n    if (node.selected === shouldSelect) return node;\n    changed = true;\n    return { ...node, selected: shouldSelect };\n  });\n  return changed ? nextNodes : nodes;\n}\n","import { NetworkNode, UNumberingDirection } from '../types';\nimport { U_HEIGHT_PX, RACK_HEADER_HEIGHT } from './constants';\nimport { isRackDeviceNodeType } from './rack-device-types';\nimport {\n  getRackDeviceXPosition,\n  getRackMountMode,\n  getRackUnitHeightPx,\n  isSplitFaceRackLayout,\n  resolveRackUHeight,\n  sharesMountedFace,\n} from './rack-layout';\n\nexport { MAX_RACK_U } from './rack-layout';\n\nconst asPositiveInteger = (value: unknown, fallback: number): number => {\n  if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n    return Math.floor(value);\n  }\n  return fallback;\n};\n\nconst asRackUHeight = resolveRackUHeight;\n\n/**\n * Get the default U numbering direction\n */\nexport const getDefaultUNumberingDirection = (): UNumberingDirection => 'bottom-up';\n\n/**\n * Get the U numbering direction for a rack\n */\nexport const getRackUNumberingDirection = (rack: NetworkNode): UNumberingDirection => {\n  return rack.data.uNumberingDirection || getDefaultUNumberingDirection();\n};\n\n/**\n * Convert U position number based on numbering direction\n * For bottom-up: U position 1 is at the bottom\n * For top-down: U position 1 is at the top\n */\nexport const convertUPositionForDirection = (\n  uPosition: number,\n  rackUHeight: number,\n  direction: UNumberingDirection\n): number => {\n  if (direction === 'bottom-up') {\n    // For bottom-up, U position 1 is at the bottom, so we need to flip the numbering\n    return rackUHeight - uPosition + 1;\n  }\n  // For top-down, U position 1 is at the top, so no conversion needed\n  return uPosition;\n};\n\n/**\n * Convert U position number back to physical position\n */\nexport const convertUPositionFromDirection = (\n  uPosition: number,\n  rackUHeight: number,\n  direction: UNumberingDirection\n): number => {\n  if (direction === 'bottom-up') {\n    // For bottom-up, convert back from the flipped numbering\n    return rackUHeight - uPosition + 1;\n  }\n  // For top-down, no conversion needed\n  return uPosition;\n};\n\n/**\n * Calculate U position from device Y coordinate within a rack\n */\nexport const calculateUPositionFromY = (\n  deviceY: number,\n  rackY: number,\n  rackUHeight: number = 42,\n  direction: UNumberingDirection = 'bottom-up'\n): number => {\n  const startY = RACK_HEADER_HEIGHT; // Start at top of rack body\n  const relativeY = deviceY - rackY - startY;\n  const physicalUPosition = Math.max(1, Math.min(rackUHeight, Math.round(relativeY / U_HEIGHT_PX) + 1));\n  \n  // Convert to the appropriate numbering direction\n  return convertUPositionForDirection(physicalUPosition, rackUHeight, direction);\n};\n\n/**\n * The logical U position (the device's numbered slot, in the rack's numbering\n * direction) whose top edge is nearest to a parent-relative `y`. Not clamped\n * to the rack — callers decide what an out-of-range slot means.\n */\nconst uPositionForTopY = (\n  rack: NetworkNode,\n  deviceUHeight: number,\n  y: number,\n): number => {\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n  const direction = getRackUNumberingDirection(rack);\n  const rackUnitHeightPx = getRackUnitHeightPx(rack.data);\n  const physicalUPosition =\n    Math.round((y - RACK_HEADER_HEIGHT) / rackUnitHeightPx) + 1;\n  const logicalPosition = convertUPositionForDirection(\n    physicalUPosition,\n    rackUHeight,\n    direction,\n  );\n  // Bottom-up numbering names a device by its BOTTOM unit, while y is its top.\n  return direction === 'bottom-up'\n    ? logicalPosition - deviceUHeight + 1\n    : logicalPosition;\n};\n\n/**\n * The U slot another racked device occupies, for availability checks. An\n * explicit `uPosition` wins; a device without one (e.g. hand-placed by\n * position) is located from its y instead of being assumed to sit at U1. A\n * device whose slot cannot be resolved inside the rack occupies nothing.\n */\nconst resolveOccupiedUPosition = (\n  rack: NetworkNode,\n  device: NetworkNode,\n  deviceUHeight: number,\n  rackUHeight: number,\n): number | undefined => {\n  const explicit = device.data.uPosition;\n  if (typeof explicit === 'number' && Number.isFinite(explicit) && explicit >= 1) {\n    return Math.floor(explicit);\n  }\n  const y = device.position?.y;\n  if (typeof y !== 'number' || !Number.isFinite(y)) {\n    return undefined;\n  }\n  const derived = uPositionForTopY(rack, deviceUHeight, y);\n  if (derived < 1 || derived + deviceUHeight - 1 > rackUHeight) {\n    return undefined;\n  }\n  return derived;\n};\n\n/**\n * Snap a device position to the nearest U position within a rack\n */\nexport const snapToUPosition = (\n  device: NetworkNode,\n  rack: NetworkNode,\n  newPosition: { x: number; y: number },\n  allNodes: NetworkNode[]\n): { x: number; y: number; uPosition?: number } => {\n  const deviceParentId = device.parentId;\n  if (!deviceParentId || deviceParentId !== rack.id) {\n    return newPosition;\n  }\n\n  const uHeight = asPositiveInteger(device.data.uHeight, 1);\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n  const direction = getRackUNumberingDirection(rack);\n  const rackUnitHeightPx = getRackUnitHeightPx(rack.data);\n  const startY = RACK_HEADER_HEIGHT; // After header\n\n  // The logical U the device's top edge (parent-relative y) lands on.\n  const calculatedUPosition = uPositionForTopY(rack, uHeight, newPosition.y);\n  \n  // Ensure the device doesn't extend beyond the rack boundaries\n  // For multi-U devices, we need to ensure the entire device fits\n  const maxUPosition = rackUHeight - uHeight + 1;\n  let snappedUPosition = Math.max(1, Math.min(maxUPosition, calculatedUPosition));\n  \n  // For top-down numbering, ensure we can place devices at the top (U1)\n  if (direction === 'top-down' && calculatedUPosition <= 1) {\n    snappedUPosition = 1;\n  }\n  \n  \n  // Check if the calculated position is available, if not find the nearest available position\n  if (!isUPositionAvailable(rack, device, snappedUPosition, allNodes)) {\n    // Try to find the nearest available position (with wrap-around)\n    const availablePosition = findNextAvailableUPosition(rack, device, allNodes, snappedUPosition);\n    if (availablePosition !== undefined) {\n      snappedUPosition = availablePosition;\n    } else {\n      // If no free slot exists, keep the current placement (if valid) rather than forcing overlap.\n      const currentUPosition = asPositiveInteger(device.data.uPosition, 0);\n      if (currentUPosition > 0 && isUPositionAvailable(rack, device, currentUPosition, allNodes)) {\n        snappedUPosition = currentUPosition;\n      } else {\n        return {\n          x: getRackDeviceXPosition(rack.data, device.data),\n          y: device.position?.y ?? newPosition.y,\n          uPosition: currentUPosition > 0 ? currentUPosition : undefined,\n        };\n      }\n    }\n  }\n  \n  // Snap to exact U position - ensure devices are exactly on U boundaries\n  // Account for device border (1px) to align properly with U grid\n  // The Y coordinate should ALWAYS represent the TOP of the device\n  // For bottom-up direction, we need to calculate the Y position of the TOP of the device\n  let snappedY;\n  if (direction === 'bottom-up') {\n    // For bottom-up, the logical U position represents the BOTTOM of the device\n    // So we need to calculate the Y position of the TOP of the device\n    const topLogicalUPosition = snappedUPosition + uHeight - 1;\n    const topPhysicalUPosition = convertUPositionFromDirection(topLogicalUPosition, rackUHeight, direction);\n    snappedY = startY + (topPhysicalUPosition - 1) * rackUnitHeightPx - 1;\n  } else {\n    // For top-down, the logical U position represents the TOP of the device\n    const snappedPhysicalUPosition = convertUPositionFromDirection(snappedUPosition, rackUHeight, direction);\n    snappedY = startY + (snappedPhysicalUPosition - 1) * rackUnitHeightPx - 1;\n  }\n  \n  return {\n    x: getRackDeviceXPosition(rack.data, device.data),\n    y: snappedY,\n    uPosition: snappedUPosition,\n  };\n};\n\n/**\n * Check if a U position is available in a rack\n */\nexport const isUPositionAvailable = (\n  rack: NetworkNode,\n  device: NetworkNode,\n  uPosition: number,\n  allNodes: NetworkNode[]\n): boolean => {\n  const deviceUHeight = asPositiveInteger(device.data.uHeight, 1);\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n\n  // Check bounds\n  if (uPosition < 1 || uPosition + deviceUHeight - 1 > rackUHeight) {\n    return false;\n  }\n  \n  // Check for conflicts with other devices in the same rack\n  const rackDevices = allNodes.filter(node => \n    isRackDeviceNodeType(node.type) &&\n    node.parentId === rack.id &&\n    node.id !== device.id\n  );\n  const splitFaceLayout = isSplitFaceRackLayout(rack.data);\n  const deviceMountMode = getRackMountMode(device.data);\n  \n  for (const otherDevice of rackDevices) {\n    const otherUHeight = asPositiveInteger(otherDevice.data.uHeight, 1);\n    const otherUPosition = resolveOccupiedUPosition(rack, otherDevice, otherUHeight, rackUHeight);\n    if (otherUPosition === undefined) {\n      continue;\n    }\n    \n    // Check if the new device would overlap with existing device\n    const newDeviceStart = uPosition;\n    const newDeviceEnd = uPosition + deviceUHeight - 1;\n    const otherDeviceStart = otherUPosition;\n    const otherDeviceEnd = otherUPosition + otherUHeight - 1;\n    \n    if (newDeviceStart <= otherDeviceEnd && newDeviceEnd >= otherDeviceStart) {\n      if (splitFaceLayout) {\n        const otherMountMode = getRackMountMode(otherDevice.data);\n        if (!sharesMountedFace(deviceMountMode, otherMountMode)) {\n          continue;\n        }\n      }\n      return false; // Overlap detected\n    }\n  }\n  \n  return true;\n};\n\n/**\n * Find the next available U position in a rack\n */\nexport const findNextAvailableUPosition = (\n  rack: NetworkNode,\n  device: NetworkNode,\n  allNodes: NetworkNode[],\n  startFrom: number = 1\n): number | undefined => {\n  const deviceUHeight = asPositiveInteger(device.data.uHeight, 1);\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n  const maxUPosition = rackUHeight - deviceUHeight + 1;\n\n  if (maxUPosition < 1) {\n    return undefined;\n  }\n\n  const normalizedStart = Number.isFinite(startFrom) ? Math.floor(startFrom) : 1;\n  const clampedStart = Math.max(1, Math.min(maxUPosition, normalizedStart));\n\n  for (let u = clampedStart; u <= maxUPosition; u++) {\n    if (isUPositionAvailable(rack, device, u, allNodes)) {\n      return u;\n    }\n  }\n\n  for (let u = 1; u < clampedStart; u++) {\n    if (isUPositionAvailable(rack, device, u, allNodes)) {\n      return u;\n    }\n  }\n\n  return undefined;\n};\n\n/**\n * Calculate device position based on U position within a rack\n */\nexport const calculateDevicePositionFromU = (\n  rack: NetworkNode,\n  uPosition: number,\n  deviceHeight: number = 1,\n  deviceData?: Pick<NetworkNode['data'], 'mountMode'>\n): { x: number; y: number } => {\n  const startY = RACK_HEADER_HEIGHT; // After header\n  const direction = getRackUNumberingDirection(rack);\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n  const rackUnitHeightPx = getRackUnitHeightPx(rack.data);\n  \n  // Convert U position to physical position\n  const physicalUPosition = convertUPositionFromDirection(uPosition, rackUHeight, direction);\n  \n  // Position device so its TOP edge aligns with the U position\n  // For initial positioning, we place the device at the exact U position specified\n  let deviceY;\n  if (direction === 'bottom-up') {\n    // For bottom-up, the logical U position represents the BOTTOM of the device\n    // So we need to calculate the Y position of the TOP of the device\n    const topLogicalUPosition = uPosition + deviceHeight - 1;\n    const topPhysicalUPosition = convertUPositionFromDirection(topLogicalUPosition, rackUHeight, direction);\n    deviceY = startY + (topPhysicalUPosition - 1) * rackUnitHeightPx - 1;\n  } else {\n    // For top-down, the logical U position represents the TOP of the device\n    deviceY = startY + (physicalUPosition - 1) * rackUnitHeightPx - 1;\n  }\n  \n  return {\n    x: getRackDeviceXPosition(rack.data, deviceData), // Align with active rack face (relative to parent)\n    y: deviceY,\n  };\n};\n\n/**\n * Update device U position and recalculate position\n */\nexport const updateDeviceUPosition = (\n  device: NetworkNode<'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu'>,\n  rack: NetworkNode,\n  newUPosition: number\n): NetworkNode<'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu'> => {\n  const deviceHeight = device.data.uHeight || 1;\n  const newPosition = calculateDevicePositionFromU(rack, newUPosition, deviceHeight, device.data);\n  const normalizedPorts = Array.isArray(device.data.ports) ? device.data.ports : undefined;\n  \n  return {\n    ...device,\n    position: newPosition,\n    data: {\n      ...device.data,\n      ports: normalizedPorts,\n      uPosition: newUPosition,\n    },\n  };\n};\n\n/**\n * Validate and snap device to valid U position\n */\nexport const validateAndSnapDevice = (\n  device: NetworkNode<'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu'>,\n  rack: NetworkNode\n): NetworkNode<'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu'> => {\n  const currentUPosition = device.data.uPosition || 1;\n  const deviceUHeight = device.data.uHeight || 1;\n  const rackUHeight = asRackUHeight(rack.data.uHeight);\n  \n  // Ensure U position is within bounds\n  const validUPosition = Math.max(1, Math.min(rackUHeight - deviceUHeight + 1, currentUPosition));\n  \n  if (validUPosition !== currentUPosition) {\n    return updateDeviceUPosition(device, rack, validUPosition);\n  }\n  \n  return device;\n};\n","import React, { useCallback, useContext, useRef, useSyncExternalStore } from \"react\";\n\n/**\n * Instance-scoped store for the rack currently under an ungrouped device being\n * dragged (the drop-target highlight).\n *\n * The value lives outside React state on purpose: React Flow memoises node\n * wrappers, so a rack node only re-renders when its own node object changes.\n * Reading the drop target from diagram state (or a ref) during the rack's\n * render therefore never repainted the highlight. Each rack subscribes here\n * through `useSyncExternalStore` and re-renders only when *its* drop-target\n * status flips — and the diagram orchestrator does not re-render per drag\n * frame at all.\n */\nexport interface RackDropTargetStore {\n  get: () => string | null;\n  set: (rackId: string | null) => void;\n  subscribe: (listener: () => void) => () => void;\n}\n\nexport function createRackDropTargetStore(): RackDropTargetStore {\n  let current: string | null = null;\n  const listeners = new Set<() => void>();\n  return {\n    get: () => current,\n    set(rackId) {\n      if (rackId === current) return;\n      current = rackId;\n      listeners.forEach((listener) => listener());\n    },\n    subscribe(listener) {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n  };\n}\n\nconst RackDropTargetContext = React.createContext<RackDropTargetStore | null>(\n  null,\n);\n\nexport const RackDropTargetProvider = RackDropTargetContext.Provider;\n\n/** Lazily creates one stable store for the lifetime of a diagram instance. */\nexport function useRackDropTargetStore(): RackDropTargetStore {\n  const ref = useRef<RackDropTargetStore | null>(null);\n  if (ref.current === null) {\n    ref.current = createRackDropTargetStore();\n  }\n  return ref.current;\n}\n\nconst noopSubscribe = () => () => {};\n\n/**\n * Whether `rackId` is the current drop target. Outside a diagram (e.g. a rack\n * rendered standalone) there is no store and the answer is always `false`.\n */\nexport function useIsRackDropTarget(rackId: string): boolean {\n  const store = useContext(RackDropTargetContext);\n  const subscribe = store?.subscribe ?? noopSubscribe;\n  const getSnapshot = useCallback(\n    () => (store ? store.get() === rackId : false),\n    [store, rackId],\n  );\n  return useSyncExternalStore(subscribe, getSnapshot, () => false);\n}\n","import { useCallback, type RefObject } from \"react\";\nimport { useReactFlow, type Node } from \"@xyflow/react\";\nimport type { NetworkNode, NetworkFlowNode } from \"../types\";\nimport { RACK_HEADER_HEIGHT } from \"../utils/constants\";\nimport { isRackAssignableNode, isRackDeviceNode } from \"../utils/rack-device-types\";\nimport { getRackFaceWidthPx, getRackFaceGapPx, getRackNodePixelDimensions, isSplitFaceRackLayout } from \"../utils/rack-layout\";\nimport { getNodeRenderedSize } from \"../utils/node-dimensions\";\nimport { resolveSplitRackMountModeForDrop } from \"../utils/rack-mount-mode\";\nimport { resolveNodeFace } from \"../utils/diagram-selection\";\nimport { snapToUPosition, isUPositionAvailable } from \"../utils/u-positioning\";\nimport {\n  useRackDropTargetStore,\n  type RackDropTargetStore,\n} from \"../context/DiagramRackDropTargetContext\";\n\ntype Options = {\n  reAssignable: boolean;\n  latestNodesRef: RefObject<Node[]>;\n  nodesMapRef: RefObject<Map<string, Node>>;\n  commitNodes: (nodes: Node[]) => void;\n  /**\n   * Instance drop-target store the rack nodes subscribe to. Defaults to a\n   * private store (no rack highlight) when the caller provides none.\n   */\n  rackDropTargetStore?: RackDropTargetStore;\n};\n\n/** Rack hit testing, drop preview, and validated rack assignments. */\nexport function useDiagramRackDrag({ reAssignable, latestNodesRef, nodesMapRef, commitNodes, rackDropTargetStore }: Options) {\n  const { getIntersectingNodes, getInternalNode } = useReactFlow();\n  const fallbackStore = useRackDropTargetStore();\n  const setDragOverRackId = (rackDropTargetStore ?? fallbackStore).set;\n  const getNodeById = useCallback((id: string) => {\n    return nodesMapRef.current.get(id);\n  }, [nodesMapRef]);\n\n  const getNodeParentId = useCallback(\n    (node: Node | null | undefined): string | undefined => {\n      return node?.parentId;\n    },\n    [],\n  );\n\n  const getParentRackNode = useCallback(\n    (node: Node | null | undefined): Node | undefined => {\n      const parentId = getNodeParentId(node);\n      if (!parentId) return undefined;\n      const parentNode = getNodeById(parentId);\n      if (!parentNode || parentNode.type !== \"rack\") {\n        return undefined;\n      }\n      return parentNode;\n    },\n    [getNodeById, getNodeParentId],\n  );\n\n  const isSplitFaceRackNode = useCallback(\n    (node: Node | null | undefined): boolean => {\n      if (!node || node.type !== \"rack\") {\n        return false;\n      }\n      return isSplitFaceRackLayout((node as NetworkNode).data);\n    },\n    [],\n  );\n\n  const isSplitFaceRackDeviceNode = useCallback(\n    (node: Node | null | undefined): boolean => {\n      if (!node || !isRackDeviceNode(node as NetworkNode)) {\n        return false;\n      }\n      const parentRack = getParentRackNode(node);\n      if (!parentRack) {\n        return false;\n      }\n      return isSplitFaceRackLayout((parentRack as NetworkNode).data);\n    },\n    [getParentRackNode],\n  );\n\n  const getAbsoluteNodePosition = useCallback(\n    (node: Node): { x: number; y: number } => {\n      // React Flow already maintains absolute positions, including parent\n      // origins and clamping. Read that geometry instead of walking ancestors.\n      const internal = getInternalNode(node.id);\n      if (!internal) return node.position;\n      // A drag callback can precede the controlled store adopting its newest\n      // position. Apply that event's delta to the maintained absolute geometry.\n      return {\n        x: internal.internals.positionAbsolute.x + node.position.x - internal.position.x,\n        y: internal.internals.positionAbsolute.y + node.position.y - internal.position.y,\n      };\n    },\n    [getInternalNode],\n  );\n\n  const getRackDropPoint = useCallback(\n    (node: Node): { x: number; y: number } => {\n      const absolute = getAbsoluteNodePosition(node);\n      const { width, height } = getNodeRenderedSize(node);\n      return {\n        x: absolute.x + width / 2,\n        y: absolute.y + height / 2,\n      };\n    },\n    [getAbsoluteNodePosition],\n  );\n\n  const getNearestRackAtPoint = useCallback(\n    (\n      point: { x: number; y: number },\n      draggingNodeId?: string,\n    ): NetworkNode | null => {\n      const intersectingRacks = getIntersectingNodes(\n        { x: point.x, y: point.y, width: 1, height: 1 },\n        true,\n      ).filter(\n        (candidate) =>\n          candidate.id !== draggingNodeId && candidate.type === \"rack\",\n      ) as NetworkNode[];\n\n      let nearestRack: NetworkNode | null = null;\n      let minDistance = Infinity;\n\n      for (const rack of intersectingRacks) {\n        const rackPosition = getAbsoluteNodePosition(rack);\n        const distance = Math.hypot(\n          point.x - rackPosition.x,\n          point.y - rackPosition.y,\n        );\n        if (distance < minDistance) {\n          minDistance = distance;\n          nearestRack = rack;\n        }\n      }\n\n      return nearestRack;\n    },\n    [getIntersectingNodes, getAbsoluteNodePosition],\n  );\n\n  const getRackConstraintSize = useCallback(\n    (rack: NetworkNode): { width: number; height: number } => {\n      return getRackNodePixelDimensions(rack.data);\n    },\n    [],\n  );\n\n  const clampRackChildPosition = useCallback(\n    (\n      rack: NetworkNode,\n      node: Node,\n      position: { x: number; y: number },\n    ): { x: number; y: number } => {\n      const { width, height } = getRackConstraintSize(rack);\n      const { width: nodeWidth, height: nodeHeight } = getNodeRenderedSize(node);\n      const maxX = Math.max(0, width - nodeWidth);\n      const minY = node.type === \"vertical-pdu\" ? RACK_HEADER_HEIGHT : 0;\n      const maxY = Math.max(minY, height - nodeHeight);\n\n      return {\n        x: Math.max(0, Math.min(position.x, maxX)),\n        y: Math.max(minY, Math.min(position.y, maxY)),\n      };\n    },\n    [getRackConstraintSize],\n  );\n\n  // Handle drag start to track which node is being dragged\n  const handleNodeDragStart = useCallback(\n    (_event: MouseEvent | TouchEvent, _node: NetworkFlowNode) => {\n      // Reset drag over state when starting a new drag\n      setDragOverRackId(null);\n    },\n    [setDragOverRackId],\n  );\n\n  // Handle drag over to detect when an ungrouped rack-assignable node is over a rack\n  const handleNodeDrag = useCallback(\n    (_event: MouseEvent | TouchEvent, node: NetworkFlowNode, _nodes: NetworkFlowNode[]) => {\n      const parentId = getNodeParentId(node);\n      if (\n        !reAssignable ||\n        !isRackAssignableNode(node as NetworkNode) ||\n        parentId\n      ) {\n        setDragOverRackId(null);\n        return;\n      }\n\n      const dropPoint = getRackDropPoint(node);\n      const nearestRack = getNearestRackAtPoint(dropPoint, node.id);\n      const nextRackId = nearestRack?.id ?? null;\n\n      setDragOverRackId(nextRackId);\n    },\n    [reAssignable, getRackDropPoint, getNodeParentId, getNearestRackAtPoint, setDragOverRackId],\n  );\n\n  // Handle drag end to assign ungrouped rack-assignable nodes into racks\n  const handleNodeDragStop = useCallback(\n    (_event: MouseEvent | TouchEvent, node: NetworkFlowNode, _nodes: NetworkFlowNode[]) => {\n      setDragOverRackId(null);\n\n      const parentId = getNodeParentId(node);\n      if (\n        !reAssignable ||\n        !isRackAssignableNode(node as NetworkNode) ||\n        parentId\n      ) {\n        return;\n      }\n\n      const absolutePosition = getAbsoluteNodePosition(node);\n      const dropPoint = getRackDropPoint(node);\n      const nearestRack = getNearestRackAtPoint(dropPoint, node.id);\n      const currentNodes = latestNodesRef.current;\n\n      if (!nearestRack) {\n        // Keep ungrouped as-is when dropped outside any rack.\n        return;\n      }\n\n      const rackPosition = getAbsoluteNodePosition(nearestRack);\n      const relativeToRack = {\n        x: absolutePosition.x - rackPosition.x,\n        y: absolutePosition.y - rackPosition.y,\n      };\n      const clampedPosition = clampRackChildPosition(\n        nearestRack,\n        node,\n        relativeToRack,\n      );\n\n      if (node.type === \"vertical-pdu\") {\n        const updatedNodes = currentNodes.map((currentNode) => {\n          if (currentNode.id !== node.id) {\n            return currentNode;\n          }\n\n          return {\n            ...currentNode,\n            parentId: nearestRack.id,\n            extent: \"parent\",\n            position: clampedPosition,\n            data: {\n              ...currentNode.data,\n              rackLayout: isSplitFaceRackLayout(nearestRack.data)\n                ? \"split-face\"\n                : \"single-face\",\n              rackWidthPx: getRackFaceWidthPx(nearestRack.data),\n              rackFaceGapPx: getRackFaceGapPx(nearestRack.data),\n            },\n          } as Node;\n        });\n\n        commitNodes(updatedNodes);\n        return;\n      }\n\n      const baseNode = (currentNodes.find((n) => n.id === node.id) ||\n        node) as NetworkNode<\"device\" | \"server\" | \"switch\" | \"router\">;\n      const splitFaceRack = isSplitFaceRackLayout(nearestRack.data);\n      const rackWidthPx = getRackFaceWidthPx(nearestRack.data);\n      const rackFaceGapPx = getRackFaceGapPx(nearestRack.data);\n      const nextMountMode = splitFaceRack\n        ? resolveSplitRackMountModeForDrop(\n            baseNode.data,\n            relativeToRack.x,\n            rackWidthPx,\n            rackFaceGapPx,\n          )\n        : \"both\";\n      const initialDeviceX =\n        splitFaceRack && nextMountMode === \"front\"\n          ? rackWidthPx + rackFaceGapPx\n          : 0;\n      const nextView =\n        nextMountMode === \"rear\"\n          ? \"rear\"\n          : nextMountMode === \"front\"\n            ? \"front\"\n            : resolveNodeFace(baseNode.data);\n      const candidateNode: NetworkNode<\n        \"device\" | \"server\" | \"switch\" | \"router\"\n      > = {\n        ...baseNode,\n        parentId: nearestRack.id,\n        extent: \"parent\",\n        position: { x: initialDeviceX, y: relativeToRack.y },\n        data: {\n          ...baseNode.data,\n          rackLayout: splitFaceRack ? \"split-face\" : \"single-face\",\n          rackWidthPx,\n          rackFaceGapPx,\n          mountMode: nextMountMode,\n          face: nextView,\n        },\n      };\n\n      const nodesForSnapping = currentNodes\n        .filter((n) => n.id !== node.id)\n        .concat(candidateNode as unknown as Node) as NetworkNode[];\n      const snapped = snapToUPosition(\n        candidateNode,\n        nearestRack,\n        candidateNode.position,\n        nodesForSnapping,\n      );\n\n      if (\n        snapped.uPosition === undefined ||\n        !isUPositionAvailable(\n          nearestRack,\n          candidateNode,\n          snapped.uPosition,\n          nodesForSnapping,\n        )\n      ) {\n        // Rack has no legal free slot for this device at drop time; keep it ungrouped.\n        return;\n      }\n\n      const updatedNodes = currentNodes.map((currentNode) => {\n        if (currentNode.id !== node.id) {\n          return currentNode;\n        }\n\n        return {\n          ...currentNode,\n          parentId: nearestRack.id,\n          extent: \"parent\",\n          position: { x: snapped.x, y: snapped.y },\n          data: {\n            ...currentNode.data,\n            rackLayout: splitFaceRack ? \"split-face\" : \"single-face\",\n            rackWidthPx,\n            rackFaceGapPx,\n            mountMode: nextMountMode,\n            face: nextView,\n            uPosition: snapped.uPosition ?? currentNode.data.uPosition,\n          },\n        } as Node;\n      });\n\n      commitNodes(updatedNodes);\n    },\n    [getNodeParentId, reAssignable, getAbsoluteNodePosition, getRackDropPoint, getNearestRackAtPoint, latestNodesRef, clampRackChildPosition, commitNodes, setDragOverRackId],\n  );\n\n  return { getNodeById, getNodeParentId, isSplitFaceRackNode, isSplitFaceRackDeviceNode, getAbsoluteNodePosition, clampRackChildPosition, handleNodeDragStart, handleNodeDrag, handleNodeDragStop };\n}\n","import type { NetworkEdge, NetworkNode } from \"../types\";\nimport { getRackNodePixelDimensions } from \"../utils/rack-layout\";\n\n/**\n * Cheap content-equality for the document-emit dirty-check. Compares only the\n * fields {@link networkGraphToDiagramDocument} actually serializes — plus edge\n * topology. Runtime-only churn (selection, dragging, dimensions) leaves these\n * untouched.\n */\nexport function graphContentEqual(\n  a: { nodes: NetworkNode[]; edges: NetworkEdge[] } | null,\n  b: { nodes: NetworkNode[]; edges: NetworkEdge[] },\n): boolean {\n  if (!a) {\n    return false;\n  }\n  if (a.nodes.length !== b.nodes.length || a.edges.length !== b.edges.length) {\n    return false;\n  }\n\n  for (let i = 0; i < b.nodes.length; i += 1) {\n    const prev = a.nodes[i];\n    const next = b.nodes[i];\n    if (\n      prev.id !== next.id ||\n      prev.type !== next.type ||\n      (prev.position?.x ?? 0) !== (next.position?.x ?? 0) ||\n      (prev.position?.y ?? 0) !== (next.position?.y ?? 0) ||\n      prev.extent !== next.extent ||\n      prev.parentId !== next.parentId ||\n      prev.data !== next.data\n    ) {\n      return false;\n    }\n  }\n\n  for (let i = 0; i < b.edges.length; i += 1) {\n    const prev = a.edges[i];\n    const next = b.edges[i];\n    if (\n      prev.id !== next.id ||\n      prev.source !== next.source ||\n      prev.target !== next.target ||\n      prev.sourceHandle !== next.sourceHandle ||\n      prev.targetHandle !== next.targetHandle ||\n      prev.type !== next.type ||\n      (prev.hidden ?? false) !== (next.hidden ?? false) ||\n      prev.deletable !== next.deletable ||\n      prev.interactionWidth !== next.interactionWidth ||\n      prev.data !== next.data\n    ) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n// These are the only fields a document-derived graph may omit while React\n// Flow owns their current values. All other properties belong to the incoming\n// graph, including properties introduced by newer React Flow versions.\nconst NODE_RUNTIME_FIELDS = [\n  \"selected\",\n  \"dragging\",\n  \"resizing\",\n  \"width\",\n  \"height\",\n  \"measured\",\n  \"positionAbsolute\",\n] as const;\nconst EDGE_RUNTIME_FIELDS = [\"selected\"] as const;\n\nfunction restoreOmittedRuntimeFields<T extends object>(\n  incoming: T,\n  previous: T,\n  fields: readonly string[],\n): T {\n  let restored = incoming;\n  const previousFields = previous as Record<string, unknown>;\n  for (const field of fields) {\n    if (\n      !Object.prototype.hasOwnProperty.call(incoming, field) &&\n      Object.prototype.hasOwnProperty.call(previous, field)\n    ) {\n      if (restored === incoming) {\n        restored = { ...incoming };\n      }\n      (restored as Record<string, unknown>)[field] = previousFields[field];\n    }\n  }\n  // An explicit false, zero or undefined from the consumer also takes\n  // precedence; omission is the only request to retain runtime state.\n  return restored;\n}\n\nfunction shallowPropertiesEqual(\n  a: object,\n  b: object,\n  comparePositions = false,\n): boolean {\n  if (a === b) return true;\n  const aKeys = Object.keys(a);\n  const bKeys = Object.keys(b);\n  const aFields = a as Record<string, unknown>;\n  const bFields = b as Record<string, unknown>;\n  return (\n    aKeys.length === bKeys.length &&\n    aKeys.every(\n      (key) =>\n        Object.prototype.hasOwnProperty.call(b, key) &&\n        (Object.is(aFields[key], bFields[key]) ||\n          (comparePositions &&\n            (key === \"position\" || key === \"positionAbsolute\") &&\n            typeof aFields[key] === \"object\" &&\n            aFields[key] !== null &&\n            typeof bFields[key] === \"object\" &&\n            bFields[key] !== null &&\n            shallowPropertiesEqual(aFields[key] as object, bFields[key] as object))),\n    )\n  );\n}\n\n/**\n * Reattach omitted React Flow runtime state to incoming nodes. Controlled\n * properties win, and unchanged nodes retain their identities in input order.\n */\nexport function mergeRuntimeStateIntoNodes(\n  nodes: NetworkNode[],\n  previousNodes: NetworkNode[],\n): NetworkNode[] {\n  const previousNodesById = new Map(\n    previousNodes.map((node) => [node.id, node]),\n  );\n\n  const merged = nodes.map((node) => {\n    const previousNode = previousNodesById.get(node.id);\n    let next = previousNode\n      ? restoreOmittedRuntimeFields(node, previousNode, NODE_RUNTIME_FIELDS)\n      : node;\n\n    if (node.type === \"rack\") {\n      // RackNode renders dimensions from rack data; retain that existing\n      // contract instead of restoring an obsolete single-face measurement.\n      const dimensions = getRackNodePixelDimensions(node.data);\n      if (next.width !== dimensions.width || next.height !== dimensions.height) {\n        next = { ...next, ...dimensions };\n      }\n    }\n\n    // Coordinate objects may be recreated on an otherwise unchanged render.\n    return previousNode && shallowPropertiesEqual(previousNode, next, true)\n      ? previousNode\n      : next;\n  });\n\n  return merged.length === previousNodes.length &&\n    merged.every((node, index) => node === previousNodes[index])\n    ? previousNodes\n    : merged;\n}\n\n/**\n * Reattach omitted React Flow selection to incoming edges, retaining every\n * controlled property and preserving the incoming edge order.\n */\nexport function mergeRuntimeStateIntoEdges(\n  edges: NetworkEdge[],\n  previousEdges: NetworkEdge[],\n): NetworkEdge[] {\n  const previousEdgesById = new Map(\n    previousEdges.map((edge) => [edge.id, edge]),\n  );\n\n  const merged = edges.map((edge) => {\n    const previousEdge = previousEdgesById.get(edge.id);\n    if (!previousEdge) {\n      return edge;\n    }\n    const next = restoreOmittedRuntimeFields(\n      edge,\n      previousEdge,\n      EDGE_RUNTIME_FIELDS,\n    );\n    return shallowPropertiesEqual(previousEdge, next) ? previousEdge : next;\n  });\n\n  return merged.length === previousEdges.length &&\n    merged.every((edge, index) => edge === previousEdges[index])\n    ? previousEdges\n    : merged;\n}\n","import type { NetworkNode } from '../types';\nimport { normalizeHandleId, parseHandleId } from './handle-ids';\n\nexport const isFibreNode = (node?: NetworkNode) =>\n  node?.type === 'tube' ||\n  node?.type === 'splice' ||\n  node?.type === 'coupler' ||\n  node?.type === 'fibre-split' ||\n  node?.type === 'cable' ||\n  node?.type === 'multi-tube-cable';\n\nexport const isFibreConnection = (sourceNode?: NetworkNode, targetNode?: NetworkNode) =>\n  isFibreNode(sourceNode) || isFibreNode(targetNode);\n\n/**\n * Normalize a rendered handle id back to its canonical endpoint id.\n * The `node` parameter is retained for call-site ergonomics and future\n * node-specific normalization; today all node types share one rule.\n */\nexport const normalizeHandle = (_node?: NetworkNode, handle?: string | null) =>\n  handle ? normalizeHandleId(handle) : '';\n\n/** Result shape shared with the store's `ValidationResult`. */\nexport interface ConnectionRuleResult {\n  valid: boolean;\n  reason?: string;\n}\n\n/**\n * Structural classification of a handle that participates in fiber routing.\n * This is the single place that maps a (node, handle) pair to its fiber role;\n * the connection matrix below is expressed purely in terms of these classes.\n */\nexport type FibreEndpointClass =\n  | 'tube-fiber'\n  | 'tube-tube'\n  | 'cable-tube'\n  | 'cable-button'\n  | 'splice-side'\n  | 'coupler-port'\n  | 'splitter-input'\n  | 'splitter-output'\n  | 'other';\n\nexport function classifyFibreEndpoint(\n  node: NetworkNode | undefined,\n  handle: string | null | undefined,\n): FibreEndpointClass {\n  if (!node || !handle) {\n    return 'other';\n  }\n\n  // Structural shape from the single grammar authority; the node type turns\n  // it into semantics (left/right means splice-side on a splice but a port on\n  // a coupler, etc.).\n  const { shape } = parseHandleId(handle);\n  switch (node.type) {\n    case 'tube':\n      if (shape.kind === 'fiber') return 'tube-fiber';\n      if (shape.kind === 'tube') return 'tube-tube';\n      return 'other';\n    case 'cable':\n      if (shape.kind === 'fiber') return 'tube-fiber';\n      if (shape.kind === 'cable-button') return 'cable-button';\n      return 'other';\n    case 'multi-tube-cable':\n      if (shape.kind === 'tube') return 'cable-tube';\n      if (shape.kind === 'cable-button') return 'cable-button';\n      return 'other';\n    case 'splice':\n      return shape.kind === 'splice-side' ? 'splice-side' : 'other';\n    case 'coupler':\n      return shape.kind === 'side-port' ? 'coupler-port' : 'other';\n    case 'fibre-split':\n      if (shape.kind === 'split-io') {\n        return shape.io === 'input' ? 'splitter-input' : 'splitter-output';\n      }\n      return 'other';\n    default:\n      return 'other';\n  }\n}\n\n/**\n * Allowed *directed* `source>target` fiber-endpoint pairs.\n *\n * This list is an exact expansion of the legacy boolean rule set. Directionality\n * is preserved deliberately: e.g. a splitter input may be a connection target\n * (feed in) when paired with a splice, but never a source toward a splice — so\n * `splice-side>splitter-input` is present while `splitter-input>splice-side` is not.\n */\nconst ALLOWED_FIBER_PAIRS: ReadonlySet<string> = new Set([\n  // Tube fiber <-> splice (either direction)\n  'tube-fiber>splice-side',\n  'splice-side>tube-fiber',\n  // Splice <-> coupler (either direction)\n  'splice-side>coupler-port',\n  'coupler-port>splice-side',\n  // Splice -> splitter input (feed in only)\n  'splice-side>splitter-input',\n  // Splice <-> splitter output (either direction)\n  'splice-side>splitter-output',\n  'splitter-output>splice-side',\n  // Coupler <-> coupler\n  'coupler-port>coupler-port',\n  // Coupler <-> splitter (either input or output, either direction)\n  'coupler-port>splitter-input',\n  'splitter-input>coupler-port',\n  'coupler-port>splitter-output',\n  'splitter-output>coupler-port',\n  // Splitter output <-> tube fiber (either direction)\n  'splitter-output>tube-fiber',\n  'tube-fiber>splitter-output',\n  // Cable trunk button <-> cable trunk button\n  'cable-button>cable-button',\n  // Cable tube <-> tube tube (either direction)\n  'cable-tube>tube-tube',\n  'tube-tube>cable-tube',\n]);\n\nfunction isTubeHandleClass(value: FibreEndpointClass): boolean {\n  return value === 'tube-tube' || value === 'cable-tube';\n}\n\n/**\n * Evaluate whether a fiber-domain connection is structurally allowed.\n *\n * This is the single source of truth for fiber connection rules, consumed by\n * both the headless store validator and the interactive `NetworkDiagram`\n * validator. It does not consider handle occupancy (capacity) — fiber handles\n * support edge replacement, so occupancy is resolved by the caller.\n */\nexport function evaluateFibreConnectionRule(\n  sourceNode: NetworkNode | undefined,\n  sourceHandle: string | null | undefined,\n  targetNode: NetworkNode | undefined,\n  targetHandle: string | null | undefined,\n): ConnectionRuleResult {\n  if (sourceNode?.type === 'splice' && targetNode?.type === 'splice') {\n    const sourceIsRibbon = sourceNode.data?.mode === 'ribbon';\n    const targetIsRibbon = targetNode.data?.mode === 'ribbon';\n    if (sourceIsRibbon !== targetIsRibbon) {\n      return { valid: false, reason: 'Ribbon splices can only connect to other ribbon splices' };\n    }\n  }\n\n  const sourceClass = classifyFibreEndpoint(sourceNode, sourceHandle);\n  const targetClass = classifyFibreEndpoint(targetNode, targetHandle);\n\n  if (ALLOWED_FIBER_PAIRS.has(`${sourceClass}>${targetClass}`)) {\n    return { valid: true };\n  }\n\n  if (isTubeHandleClass(sourceClass) && isTubeHandleClass(targetClass)) {\n    return { valid: false, reason: 'Tube handles can only connect between cable and tube nodes' };\n  }\n\n  return { valid: false, reason: 'Connection not allowed by fiber rules' };\n}\n","/**\n * Power connector utilities for consistent styling across devices and PDUs\n */\n\nexport interface PowerConnectorConfig {\n  type: 'C13' | 'C14' | 'C19' | 'C20' | 'AC';\n  color: string;\n  borderColor: string;\n  amperage: number;\n  voltage: number;\n  phases: number;\n}\n\nexport const POWER_CONNECTORS: Record<string, PowerConnectorConfig> = {\n  C13: {\n    type: 'C13',\n    color: '#f59e0b', // Orange\n    borderColor: '#d97706',\n    amperage: 10,\n    voltage: 230,\n    phases: 1\n  },\n  C14: {\n    type: 'C14',\n    color: '#f59e0b', // Orange\n    borderColor: '#d97706',\n    amperage: 10,\n    voltage: 230,\n    phases: 1\n  },\n  C19: {\n    type: 'C19',\n    color: '#fbbf24', // Amber\n    borderColor: '#d97706',\n    amperage: 16,\n    voltage: 230,\n    phases: 1\n  },\n  C20: {\n    type: 'C20',\n    color: '#fbbf24', // Amber\n    borderColor: '#d97706',\n    amperage: 16,\n    voltage: 230,\n    phases: 1\n  },\n  AC: {\n    type: 'AC',\n    color: '#60a5fa', // Blue (generic AC/NEMA)\n    borderColor: '#2563eb',\n    amperage: 15,\n    voltage: 120,\n    phases: 1\n  }\n};\n\nexport interface PowerPortStyle {\n  width: number;\n  height: number;\n  borderRadius?: string;\n  clipPath?: string;\n  color: string;\n  borderColor: string;\n  fontSize: string;\n}\n\n/**\n * Get consistent power port styling for any connector type\n */\nexport function getPowerPortStyle(connectorType: string): PowerPortStyle {\n  const connector = POWER_CONNECTORS[connectorType];\n  if (!connector) {\n    throw new Error(`Unknown connector type: ${connectorType}`);\n  }\n\n  const isHighPower = connector.amperage === 16; // C19/C20 are 16A\n  const isGenericAc = connector.type === 'AC';\n\n  // Size: C13/C14 are intentionally 30% larger while keeping C19/C20 unchanged.\n  const width = isHighPower ? 12 : isGenericAc ? 10 : 8 * 1.3;\n  const height = isHighPower ? 10 : isGenericAc ? 7 : 5 * 1.3;\n\n  // Shape: C13/C14 have chamfered corners, C19/C20 and generic AC use rounded corners.\n  const cornerStyle = isHighPower || isGenericAc\n    ? { borderRadius: isGenericAc ? '2px' : '3px' }\n    : {\n        clipPath: `polygon(2px 0, calc(100% - 2px) 0, 100% 2px, 100% 100%, 0 100%, 0 2px)`,\n        borderRadius: '0px'\n      };\n\n  return {\n    width,\n    height,\n    color: connector.color,\n    borderColor: connector.borderColor,\n    fontSize: isHighPower ? '3px' : isGenericAc ? '3.5px' : '4px',\n    ...cornerStyle\n  };\n}\n\n/**\n * Get connector configuration by type\n */\nexport function getConnectorConfig(connectorType: string): PowerConnectorConfig {\n  const connector = POWER_CONNECTORS[connectorType];\n  if (!connector) {\n    throw new Error(`Unknown connector type: ${connectorType}`);\n  }\n  return connector;\n}\n\n/**\n * Determine if a connector is high power (16A)\n */\nexport function isHighPowerConnector(connectorType: string): boolean {\n  const connector = getConnectorConfig(connectorType);\n  return connector.amperage === 16;\n}\n\n/** IEC 60320 designation embedded in a port `type` such as `power_c13`. */\nconst PORT_TYPE_CONNECTOR = /c(13|14|15|16|19|20|21|22)\\b/i;\n\n/**\n * Recover a connector designation from a port's `type` when the port carries no\n * explicit `connectorType` — `power_c13` becomes `C13`.\n *\n * The single implementation of that inference: the endpoint classifier\n * (`endpoint-metadata`), the validation graph and the renderer all read a\n * typed-only port through here, so a rule can never resolve a different\n * connector than the canvas drew.\n * @public\n */\nexport function inferConnectorFromPortType(portType: unknown): string | undefined {\n  if (typeof portType !== 'string') return undefined;\n  const match = PORT_TYPE_CONNECTOR.exec(portType);\n  return match ? `C${match[1]}` : undefined;\n}\n\n/**\n * Whether a PDU strip's data declares a supply inlet.\n *\n * The three fields the renderer reads before it mounts the inlet handle\n * (`inletConnector` / `inletAmps` / `inletGender`) — `feedType`, `voltage` and\n * `phases` only enrich the tooltip and are not an inlet on their own. Stated\n * once so the renderer, the canvas validator and PWR-A13 agree on when a strip\n * has somewhere for a branch circuit to land.\n * @public\n */\nexport function hasPduInlet(data: unknown): boolean {\n  if (!data || typeof data !== 'object') return false;\n  const { inletConnector, inletAmps, inletGender } = data as {\n    inletConnector?: unknown;\n    inletAmps?: unknown;\n    inletGender?: unknown;\n  };\n  if (typeof inletConnector === 'string' && inletConnector.trim().length > 0) return true;\n  if (typeof inletAmps === 'number' && Number.isFinite(inletAmps)) return true;\n  const gender =\n    typeof inletGender === 'string' ? inletGender.trim().toLowerCase() : undefined;\n  return gender === 'male' || gender === 'female';\n}\n\n/**\n * Get PDU port type based on position in cycle\n * PDU cycle: [8xC13, 2xC19] = 10 ports per cycle\n */\nexport function getPDUPortType(portIndex: number): string {\n  const cyclePosition = portIndex % 10; // 10 ports per cycle\n  return cyclePosition < 8 ? 'C13' : 'C19';\n}\n\n/**\n * Connector designation of a vertical-PDU outlet at a 1-based `position`.\n *\n * Prefers the strip's explicit `data.portTypes` sequence and falls back to the\n * default 8×C13 + 2×C19 cycle — the same precedence the renderer uses, so the\n * connector a rule reasons about is the connector that was drawn.\n * @public\n */\nexport function getPduOutletConnectorType(\n  portTypes: ReadonlyArray<string | undefined> | undefined,\n  position: number\n): string {\n  const index = Math.max(0, Math.floor(position) - 1);\n  const explicit = portTypes?.[index];\n  if (typeof explicit === 'string' && explicit.trim().length > 0) {\n    return explicit.trim().toUpperCase();\n  }\n  return getPDUPortType(index);\n}\n\n/**\n * Zero-based phase index of a PDU outlet at a 1-based `position` on an\n * `phases`-phase strip.\n *\n * Convention: outlets are attributed to phases **round-robin from the top** —\n * `(position - 1) % phases`. Real strips vary (some group outlets in per-phase\n * banks), but round-robin is the convention the consumer application uses, and\n * a phase-balance number is only meaningful if both sides attribute outlets the\n * same way. A non-positive or non-finite `phases` is read as single-phase, for\n * which every outlet is phase 0.\n * @public\n */\nexport function toOutletPhaseIndex(position: number, phases: number): number {\n  const count =\n    Number.isFinite(phases) && phases >= 1 ? Math.floor(phases) : 1;\n  const slot = Number.isFinite(position) ? Math.floor(position) : 1;\n  return (((slot - 1) % count) + count) % count;\n}\n\n/** Phase labels in round-robin order, indexed by {@link toOutletPhaseIndex}. */\nexport const PHASE_LABELS = ['L1', 'L2', 'L3'] as const;\n\n/**\n * Phase label (`L1`/`L2`/`L3`) of a PDU outlet at a 1-based `position`, or\n * `undefined` on a single-phase strip (where there is nothing to attribute) or\n * beyond the three labelled phases.\n * @public\n */\nexport function getPduOutletPhaseLabel(\n  position: number,\n  phases: number\n): (typeof PHASE_LABELS)[number] | undefined {\n  if (!Number.isFinite(phases) || phases < 2) return undefined;\n  return PHASE_LABELS[toOutletPhaseIndex(position, phases)];\n}\n\n/**\n * Get device connector type based on device name\n */\nexport function getDeviceConnectorType(deviceName: string): string {\n  const name = deviceName.toLowerCase();\n  \n  // High power devices use C20\n  if (name.includes('mx304') || name.includes('mx480') || name.includes('mx960')) {\n    return 'C20';\n  }\n  \n  // Standard devices use C14\n  return 'C14';\n}\n","import type {\n  DevicePort,\n  DiagramDocument,\n  DiagramEdge,\n  DiagramEdgeEndpoint,\n  DiagramEndpoint,\n  DiagramExternalRef,\n  DiagramNode,\n  DiagramNodeKind,\n  DiagramNodePlacement,\n  DiagramNodeRender,\n  DiagramUiState,\n  NetworkEdge,\n  NetworkNode,\n  NetworkNodeType,\n  Port,\n  PortBlock,\n  PortPosition,\n  PortPositionInput,\n  PortType,\n} from '../types';\nimport {\n  applyFaceAliasHandleId,\n  buildBreakerWayHandleId,\n  buildPortHandleId,\n  isPowerPortType,\n  normalizeDirectionalHandleId,\n  normalizeHandleId,\n  parseHandleId,\n} from './handle-ids';\nimport { normalizePortPositionInput } from './port-position';\nimport { getPDUPortType } from './power-connectors';\nimport { getResolvedPortBlocks } from './render-ports';\n\nexport interface NetworkGraphSnapshot {\n  nodes: NetworkNode[];\n  edges: NetworkEdge[];\n}\n\n/**\n * Hard ceiling on how many power outlets a PDU may allocate. A physical PDU has\n * at most a few dozen outlets; this generous cap keeps a hostile persisted\n * `powerPorts` (e.g. `2_000_000_000`) from triggering an unbounded\n * `Array.from({ length })` allocation on either the serialize path here or the\n * client render in {@link file://../components/VerticalPDU.tsx}. Both sinks\n * share this single constant.\n */\nexport const MAX_POWER_PORTS = 256;\n\n/**\n * Lenient input accepted by {@link diagramDocumentToNetworkGraph}. Everything is\n * optional so partial / legacy documents degrade gracefully instead of throwing:\n * a missing `version` is treated as legacy v0 (identical to `1`), and missing\n * `nodes`/`edges` default to empty. A full {@link DiagramDocument} is assignable.\n */\nexport type DiagramDocumentInput = {\n  version?: number;\n  nodes?: DiagramNode[];\n  edges?: DiagramEdge[];\n  ui?: DiagramUiState;\n  metadata?: Record<string, unknown>;\n};\n\nconst KNOWN_DIAGRAM_NODE_KINDS: ReadonlySet<DiagramNodeKind> = new Set<DiagramNodeKind>([\n  'rack',\n  'equipment',\n  'pdu',\n  'distribution-board',\n  'fiber',\n  'tube',\n  'cable',\n  'multi-tube-cable',\n  'splice',\n  'coupler',\n  'splitter',\n  'container',\n  'fibre-flow-cable',\n  'fibre-flow-closure',\n]);\n\nconst CONTAINER_SUBTYPES = new Set(['patch-panel', 'closure', 'splice-tray']);\nconst IMAGE_RENDER_KEYS = [\n  'frontImageUrl',\n  'rearImageUrl',\n  'frontImageAttribution',\n  'rearImageAttribution',\n  'imageFit',\n  'imagePosition',\n  'imageRepeat',\n  'stretchToFit',\n] as const;\nconst DIMENSION_RENDER_KEYS = [\n  'panelWidth',\n  'panelHeight',\n  'closureWidth',\n  'closureHeight',\n  'headerHeight',\n] as const;\nconst GENERAL_RENDER_KEYS = [\n  'status',\n  'width',\n  'widthPx',\n  'height',\n  'header',\n  'color',\n  'colorMode',\n  'showUMarkers',\n  'customHeaderText',\n  'powerSide',\n  'rotation',\n  'rackWidthPx',\n  'rackFaceGapPx',\n] as const;\nconst UI_NODE_STATE_KEYS = ['face', 'handleSideMap'] as const;\nconst EXCLUDED_CONFIG_KEYS = new Set<string>([\n  'label',\n  'ports',\n  'portGroups',\n  'rearPortGroups',\n  // `rearPorts` is a documented alias for `rearPortGroups` (resolved by\n  // getResolvedPortBlocks). Exclude it too, else a rearPorts-only node leaks its\n  // whole port array into the opaque config blob.\n  'rearPorts',\n  'pduPortGroups',\n  'connections',\n  'netboxId',\n  'netboxType',\n  'inventoryId',\n  'inventoryType',\n  'uPosition',\n  'mountMode',\n  'rackLayout',\n  'slotPosition',\n  'holderPosition',\n  // Derived on the reverse pass from placement.rack; never re-persist it into\n  // config or round-trips accumulate a stale copy.\n  'isRackMounted',\n  ...IMAGE_RENDER_KEYS,\n  ...DIMENSION_RENDER_KEYS,\n  ...GENERAL_RENDER_KEYS,\n  ...UI_NODE_STATE_KEYS,\n]);\n\n/**\n * Assign an own, enumerable property — safe for keys such as `__proto__` that\n * plain assignment would treat as a prototype mutation.\n */\nfunction setOwn(target: Record<string, unknown>, key: string, value: unknown): void {\n  Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction clonePosition(position: { x?: number; y?: number } | undefined): { x: number; y: number } {\n  // Number.isFinite rejects NaN/Infinity (which typeof reports as 'number'),\n  // so a malformed coordinate degrades to 0 instead of corrupting layout.\n  return {\n    x: Number.isFinite(position?.x) ? (position!.x as number) : 0,\n    y: Number.isFinite(position?.y) ? (position!.y as number) : 0,\n  };\n}\n\nfunction clonePortPosition(position: PortPositionInput | undefined): PortPosition | undefined {\n  const normalized = normalizePortPositionInput(position);\n  if (!normalized) {\n    return undefined;\n  }\n\n  return { ...normalized };\n}\n\nfunction toDiagramNodeKind(node: NetworkNode): { kind: DiagramNodeKind; subtype?: string } {\n  if (node.type === 'vertical-pdu') {\n    return { kind: 'pdu' };\n  }\n\n  if (node.type === 'server' || node.type === 'switch' || node.type === 'router' || node.type === 'device') {\n    // `subtype` carries the runtime node TYPE so the reverse pass restores it\n    // exactly. The free-text `data.deviceType` (often a vendor model, e.g.\n    // `Catalyst 9300`) travels in `config` like any other data field — using it\n    // as the subtype made a `switch` with a model name come back as `device`.\n    return { kind: 'equipment', subtype: node.type };\n  }\n\n  if (node.type === 'patch-panel' || node.type === 'closure' || node.type === 'splice-tray') {\n    return { kind: 'container', subtype: node.type };\n  }\n\n  if (node.type === 'fibre-split') {\n    return { kind: 'splitter' };\n  }\n\n  if (node.type === 'fibre-flow-cable' || node.type === 'fibre-flow-closure') {\n    return { kind: node.type };\n  }\n\n  return { kind: node.type as DiagramNodeKind };\n}\n\nfunction toLegacyNodeType(node: DiagramNode): NetworkNodeType {\n  if (node.kind === 'equipment') {\n    const normalizedSubtype = (node.subtype ?? 'device').toLowerCase().replace(/_/g, '-');\n    if (normalizedSubtype === 'server' || normalizedSubtype === 'switch' || normalizedSubtype === 'router') {\n      return normalizedSubtype;\n    }\n    return 'device';\n  }\n\n  if (node.kind === 'pdu') {\n    return 'vertical-pdu';\n  }\n\n  if (node.kind === 'container') {\n    const subtype = node.subtype ?? 'patch-panel';\n    if (CONTAINER_SUBTYPES.has(subtype)) {\n      return subtype as NetworkNodeType;\n    }\n    return 'patch-panel';\n  }\n\n  if (node.kind === 'splitter') {\n    return 'fibre-split';\n  }\n\n  return node.kind as NetworkNodeType;\n}\n\nfunction toDiagramPlacement(node: NetworkNode): DiagramNodePlacement {\n  const placement: DiagramNodePlacement = {\n    position: clonePosition(node.position),\n  };\n\n  const parentId = node.parentId;\n  if (parentId) {\n    placement.parentId = parentId;\n  }\n  if (node.extent === 'parent') {\n    placement.extent = 'parent';\n  }\n\n  const data = node.data ?? {};\n  if (\n    typeof data.uPosition === 'number' ||\n    data.mountMode === 'front' ||\n    data.mountMode === 'rear' ||\n    data.mountMode === 'both' ||\n    data.rackLayout === 'single-face' ||\n    data.rackLayout === 'split-face'\n  ) {\n    placement.rack = {\n      ...(typeof data.uPosition === 'number' ? { uPosition: data.uPosition } : {}),\n      ...(data.mountMode ? { mountMode: data.mountMode } : {}),\n      ...(data.rackLayout ? { rackLayout: data.rackLayout } : {}),\n      ...(typeof data.rackWidthPx === 'number' ? { rackWidthPx: data.rackWidthPx } : {}),\n      ...(typeof data.rackFaceGapPx === 'number' ? { rackFaceGapPx: data.rackFaceGapPx } : {}),\n    };\n  }\n\n  if (typeof data.slotPosition === 'number' || typeof data.holderPosition === 'number') {\n    placement.container = {\n      ...(typeof data.slotPosition === 'number' ? { slotPosition: data.slotPosition } : {}),\n      ...(typeof data.holderPosition === 'number' ? { holderPosition: data.holderPosition } : {}),\n    };\n  }\n\n  return placement;\n}\n\nfunction toDiagramRender(node: NetworkNode): DiagramNodeRender | undefined {\n  const data = node.data ?? {};\n  const render: DiagramNodeRender = {};\n\n  GENERAL_RENDER_KEYS.forEach((key) => {\n    const value = data[key];\n    if (value !== undefined) {\n      (render as Record<string, unknown>)[key] = value;\n    }\n  });\n\n  const images = IMAGE_RENDER_KEYS.reduce<Record<string, unknown>>((acc, key) => {\n    const value = data[key];\n    if (value !== undefined) {\n      acc[key] = value;\n    }\n    return acc;\n  }, {});\n  if (Object.keys(images).length > 0) {\n    render.images = images as DiagramNodeRender['images'];\n  }\n\n  const dimensions = DIMENSION_RENDER_KEYS.reduce<Record<string, number | undefined>>((acc, key) => {\n    const value = data[key];\n    if (typeof value === 'number') {\n      acc[key] = value;\n    }\n    return acc;\n  }, {});\n  if (Object.keys(dimensions).length > 0) {\n    render.dimensions = dimensions;\n  }\n\n  return Object.keys(render).length > 0 ? render : undefined;\n}\n\nfunction toDiagramExternalRefs(node: NetworkNode): DiagramExternalRef[] | undefined {\n  const refs: DiagramExternalRef[] = [];\n  const data = node.data ?? {};\n\n  if (data.netboxId !== undefined) {\n    refs.push({\n      source: 'netbox',\n      id: data.netboxId,\n      type: data.netboxType,\n    });\n  }\n\n  if (data.inventoryId !== undefined) {\n    refs.push({\n      source: 'inventory',\n      id: data.inventoryId,\n      type: data.inventoryType,\n    });\n  }\n\n  return refs.length > 0 ? refs : undefined;\n}\n\nfunction toDiagramConfig(node: NetworkNode): Record<string, unknown> | undefined {\n  const data = node.data ?? {};\n  const config: Record<string, unknown> = {};\n\n  Object.entries(data).forEach(([key, value]) => {\n    // `ports` is excluded because port ARRAYS serialize as endpoints. A numeric\n    // `ports` (the rack header's port count) has no endpoint form, so it is\n    // kept as plain config instead of being dropped.\n    if (EXCLUDED_CONFIG_KEYS.has(key) && !(key === 'ports' && !Array.isArray(value))) {\n      return;\n    }\n    if ((key === 'rackWidthPx' || key === 'rackFaceGapPx') && node.type !== 'rack') {\n      return;\n    }\n    setOwn(config, key, value);\n  });\n\n  // Equipment without a device type is normalised to `deviceType = node.type`,\n  // the value the reverse pass has always filled in. Writing it here keeps\n  // re-serialisation idempotent (graph → doc → graph → doc is stable).\n  if (\n    (node.type === 'server' || node.type === 'switch' || node.type === 'router' || node.type === 'device') &&\n    !(typeof config.deviceType === 'string' && config.deviceType.trim().length > 0)\n  ) {\n    config.deviceType = node.type;\n  }\n\n  return Object.keys(config).length > 0 ? config : undefined;\n}\n\nfunction extractUiState(nodes: NetworkNode[], previousUi?: DiagramUiState): DiagramUiState | undefined {\n  const nodeState: NonNullable<DiagramUiState['nodeState']> = {};\n\n  nodes.forEach((node) => {\n    const data = node.data ?? {};\n    const entry: NonNullable<DiagramUiState['nodeState']>[string] = {};\n    const resolvedFace =\n      data.face === 'front' || data.face === 'rear'\n        ? data.face\n        : undefined;\n\n    if (resolvedFace) {\n      entry.face = resolvedFace;\n    }\n    if (data.handleSideMap && typeof data.handleSideMap === 'object') {\n      entry.handleSideMap = { ...data.handleSideMap };\n    }\n\n    if (Object.keys(entry).length > 0) {\n      setOwn(nodeState, node.id, entry);\n    }\n  });\n\n  if (Object.keys(nodeState).length === 0 && !previousUi?.metadata) {\n    return undefined;\n  }\n\n  return {\n    ...(Object.keys(nodeState).length > 0 ? { nodeState } : {}),\n    ...(previousUi?.metadata ? { metadata: previousUi.metadata } : {}),\n  };\n}\n\nfunction inferEndpointMedium(portType: string | undefined): DiagramEndpoint['medium'] {\n  const normalizedType = String(portType ?? '').toLowerCase();\n  if (normalizedType.includes('power')) return 'power';\n  if (normalizedType.includes('fiber')) return 'fiber';\n  if (normalizedType.includes('console') || normalizedType.includes('mgmt') || normalizedType.includes('usb')) {\n    return 'generic';\n  }\n  if (normalizedType.includes('ethernet') || normalizedType.includes('rj45')) return 'ethernet';\n  return 'generic';\n}\n\nfunction buildPduPortBlocks(node: NetworkNode): PortBlock[] {\n  const data = node.data ?? {};\n  if (Array.isArray(data.pduPortGroups) && data.pduPortGroups.length > 0) {\n    return data.pduPortGroups;\n  }\n\n  const { ports, rearPorts } = getResolvedPortBlocks(data);\n  if (rearPorts.length > 0) {\n    return rearPorts;\n  }\n  if (ports.length > 0) {\n    return ports;\n  }\n\n  const powerPorts = Math.min(\n    MAX_POWER_PORTS,\n    typeof data.powerPorts === 'number' && Number.isFinite(data.powerPorts) && data.powerPorts > 0\n      ? data.powerPorts\n      : 36\n  );\n\n  return [\n    {\n      name: 'Power',\n      ports: Array.from({ length: powerPorts }, (_, index) => {\n        const connectorType = getPDUPortType(index);\n        return {\n          id: `${index + 1}`,\n          label: `${index + 1}`,\n          connected: false,\n          type: connectorType === 'C19' ? 'power_c19' : 'power_c13',\n          connectorType: connectorType as Port['connectorType'],\n        };\n      }),\n    },\n  ];\n}\n\nfunction toPortBasedEndpoints(node: NetworkNode): DiagramEndpoint[] {\n  // Read the resolved blocks directly. Endpoint construction below already\n  // clones compatibility and normalized layout, so an intermediate deep clone\n  // of every block/port only duplicates work on each document round trip.\n  const blocksByFace: Array<{ face: 'front' | 'rear'; blocks: PortBlock[] }> =\n    node.type === 'vertical-pdu'\n      ? [{ face: 'rear', blocks: buildPduPortBlocks(node) }]\n      : (() => {\n          const { ports, rearPorts } = getResolvedPortBlocks(node.data);\n          return [\n            { face: 'front' as const, blocks: ports },\n            { face: 'rear' as const, blocks: rearPorts },\n          ];\n        })();\n\n  const endpoints: DiagramEndpoint[] = blocksByFace.flatMap(({ face, blocks }) =>\n    blocks.flatMap((block) =>\n      block.ports.map((port) => ({\n        // Mint the endpoint id through the same grammar the renderer mounts\n        // handles with ({@link buildPortHandleId}): an explicit `port.id`\n        // verbatim, a bare label for power ports, else a percent-escaped\n        // `<group>-<label>` join. Edges persist\n        // `endpointId = normalizeHandleId(edge.sourceHandle)` — i.e. this exact\n        // builder output — so a raw `${block.name}-${port.label}` join would\n        // diverge for any label/group containing `-` or `%` (or a label that\n        // collides with a fixed grammar shape, e.g. group `fiber` + label `3`),\n        // and the edge would dangle after a document round-trip. Building the id\n        // the same way keeps persisted endpoint ids and edge endpointIds equal\n        // by construction.\n        id: buildPortHandleId(block.name, port),\n        label: port.label,\n        face,\n        group: block.name,\n        role: isPowerPortType(port.type) ? 'power' : 'network',\n        medium: inferEndpointMedium(port.type),\n        type: port.type,\n        cableType: port.cableType,\n        connector: port.connectorType,\n        sourceTypeLabel: port.sourceTypeLabel,\n        compatibility: Array.isArray(port.compatibility) ? [...port.compatibility] : undefined,\n        layout: clonePortPosition(port.position),\n      }))\n    )\n  );\n\n  // A front and a rear port that resolve to the same handle id (same explicit\n  // `port.id`, or same group + label on both faces) are disambiguated by the\n  // renderer with the split-face alias: the rear handle mounts as\n  // `<id>--rear`. Mint the rear endpoint with that same alias so endpoint ids\n  // stay unique and an edge on the rear port keeps pointing at the rear port\n  // after a round trip (instead of collapsing onto the front one).\n  const frontIds = new Set(\n    endpoints.filter((endpoint) => endpoint.face === 'front').map((endpoint) => endpoint.id)\n  );\n  if (frontIds.size > 0) {\n    for (const endpoint of endpoints) {\n      if (endpoint.face === 'rear' && frontIds.has(endpoint.id)) {\n        endpoint.id = applyFaceAliasHandleId(endpoint.id, 'rear');\n      }\n    }\n  }\n  return endpoints;\n}\n\n/**\n * Whether `endpoint` is the rear twin of a front endpoint, minted by\n * {@link toPortBasedEndpoints} as `<frontId>--rear`. Returns the shared base id\n * (what the port's own `id` is) when it is, else `undefined`.\n */\nfunction rearAliasBase(\n  endpoint: DiagramEndpoint,\n  frontIds: ReadonlySet<string>\n): string | undefined {\n  if (endpoint.face !== 'rear' || !endpoint.id.endsWith('--rear')) return undefined;\n  const base = endpoint.id.slice(0, -'--rear'.length);\n  return frontIds.has(base) ? base : undefined;\n}\n\nfunction collectFrontEndpointIds(endpoints: readonly DiagramEndpoint[]): Set<string> {\n  return new Set(\n    endpoints.filter((endpoint) => endpoint.face !== 'rear').map((endpoint) => endpoint.id)\n  );\n}\n\nfunction createSyntheticEndpoints(node: NetworkNode): DiagramEndpoint[] {\n  const data = node.data ?? {};\n\n  if (node.type === 'distribution-board') {\n    const ways = Array.isArray(data.ways) ? data.ways : [];\n    return ways\n      .filter((way): way is { id: string; label?: string } => !!way && typeof way.id === 'string')\n      .map((way) => ({\n        id: buildBreakerWayHandleId(way.id),\n        label: typeof way.label === 'string' ? way.label : way.id,\n        role: 'power' as const,\n        medium: 'power' as const,\n      }));\n  }\n\n  if (node.type === 'tube') {\n    const fibersPerTube = Math.max(1, Math.min(24, Number(data.fibersPerTube) || 12));\n    const tubeIndex = Math.max(1, Number(data.tubeIndex) || 1);\n    return [\n      ...Array.from({ length: fibersPerTube }, (_, index) => ({\n        id: `fiber-${index + 1}`,\n        label: `${index + 1}`,\n        role: 'fiber' as const,\n        medium: 'fiber' as const,\n      })),\n      {\n        id: `tube-${tubeIndex}`,\n        label: `Tube ${tubeIndex}`,\n        role: 'tube',\n        medium: 'tube',\n      },\n    ];\n  }\n\n  if (node.type === 'cable') {\n    const fiberCount = Math.max(1, Math.min(24, Number(data.fiberCount) || 12));\n    return [\n      ...Array.from({ length: fiberCount }, (_, index) => ({\n        id: `fiber-${index + 1}`,\n        label: `${index + 1}`,\n        role: 'fiber' as const,\n        medium: 'fiber' as const,\n      })),\n      {\n        id: 'cable-button',\n        label: 'Cable',\n        role: 'cable',\n      },\n    ];\n  }\n\n  if (node.type === 'multi-tube-cable') {\n    const tubeCount = Math.max(1, Math.min(24, Number(data.tubeCount) || 12));\n    const tubeStartIndex = Math.max(1, Number(data.tubeStartIndex) || 1);\n    return [\n      ...Array.from({ length: tubeCount }, (_, index) => ({\n        id: `tube-${tubeStartIndex + index}`,\n        label: `${tubeStartIndex + index}`,\n        role: 'tube' as const,\n        medium: 'tube' as const,\n      })),\n      {\n        id: 'cable-button',\n        label: 'Cable',\n        role: 'cable',\n      },\n    ];\n  }\n\n  if (node.type === 'splice') {\n    return [\n      { id: 'left', label: 'Left', role: 'splice', medium: 'fiber' },\n      { id: 'right', label: 'Right', role: 'splice', medium: 'fiber' },\n    ];\n  }\n\n  if (node.type === 'coupler') {\n    const portCount = typeof data.couplerFiberCount === 'number' && [1, 2, 4].includes(data.couplerFiberCount)\n      ? data.couplerFiberCount\n      : 2;\n    return [\n      ...Array.from({ length: portCount }, (_, index) => ({\n        id: `left-${index + 1}`,\n        label: `${index + 1}`,\n        role: 'coupler' as const,\n        medium: 'fiber' as const,\n      })),\n      ...Array.from({ length: portCount }, (_, index) => ({\n        id: `right-${index + 1}`,\n        label: `${index + 1}`,\n        role: 'coupler' as const,\n        medium: 'fiber' as const,\n      })),\n    ];\n  }\n\n  if (node.type === 'fibre-split') {\n    const inputCount = Math.max(1, Math.min(2, Number(data.inputCount) || 1));\n    const outputCount = Math.max(1, Math.min(64, Number(data.outputCount) || 8));\n    return [\n      ...Array.from({ length: inputCount }, (_, index) => ({\n        id: `input-${index + 1}`,\n        label: `Input ${index + 1}`,\n        role: 'splitter' as const,\n        medium: 'fiber' as const,\n      })),\n      ...Array.from({ length: outputCount }, (_, index) => ({\n        id: `output-${index + 1}`,\n        label: `Output ${index + 1}`,\n        role: 'splitter' as const,\n        medium: 'fiber' as const,\n      })),\n    ];\n  }\n\n  return [];\n}\n\nfunction toDiagramEndpoints(node: NetworkNode): DiagramEndpoint[] | undefined {\n  const endpoints = node.type === 'vertical-pdu' || Array.isArray(node.data?.ports) || Array.isArray(node.data?.portGroups) || Array.isArray(node.data?.rearPortGroups) || Array.isArray(node.data?.rearPorts) || Array.isArray(node.data?.pduPortGroups)\n    ? toPortBasedEndpoints(node)\n    : createSyntheticEndpoints(node);\n\n  return endpoints.length > 0 ? endpoints : undefined;\n}\n\n/**\n * `node.data` fields the endpoint derivation reads. The cache fingerprints the\n * identity (and, for arrays, the length) of each, so replacing or growing a\n * port collection — even on the same `data` object — invalidates the entry.\n */\nconst ENDPOINT_SOURCE_KEYS = [\n  'ports',\n  'portGroups',\n  'rearPortGroups',\n  'rearPorts',\n  'pduPortGroups',\n  'powerPorts',\n  'ways',\n  'fibersPerTube',\n  'tubeIndex',\n  'fiberCount',\n  'tubeCount',\n  'tubeStartIndex',\n  'couplerFiberCount',\n  'inputCount',\n  'outputCount',\n] as const;\n\nfunction endpointSourceFingerprint(data: Record<string, unknown>): unknown[] {\n  const fingerprint: unknown[] = [];\n  for (const key of ENDPOINT_SOURCE_KEYS) {\n    const value = data[key];\n    fingerprint.push(value, Array.isArray(value) ? value.length : -1);\n  }\n  return fingerprint;\n}\n\nfunction sameFingerprint(a: readonly unknown[], b: readonly unknown[]): boolean {\n  if (a.length !== b.length) return false;\n  for (let i = 0; i < a.length; i += 1) {\n    if (!Object.is(a[i], b[i])) return false;\n  }\n  return true;\n}\n\n/**\n * One derivation of a node's endpoints, cached against the `node.data` object\n * that produced it. `nodeId`/`nodeType` are stored alongside so a data object\n * shared by two different nodes (a hazard, not the norm) is detected and\n * recomputed rather than aliased, and `fingerprint` catches an in-place\n * replacement of a port collection on the same data object.\n */\ninterface EndpointDerivation {\n  nodeId: string;\n  nodeType: NetworkNodeType;\n  fingerprint: unknown[];\n  endpoints: DiagramEndpoint[] | undefined;\n}\n\n/**\n * Module-scoped memo of {@link toDiagramEndpoints}, keyed on `node.data`\n * identity. A position-only drag reframes `node.position` but keeps `node.data`\n * referentially stable, so the (potentially large) endpoint derivation runs\n * once and later frames only copy the cached records. Keys are consumer\n * `node.data` objects, so entries die with them (no unbounded growth).\n *\n * Limitation: editing a port object's fields in place (e.g. `port.label = …`\n * on the same port object inside the same array) is not detected — replace\n * the port, its array, or `data` (the React immutable-update convention).\n */\nconst endpointDerivationCache = new WeakMap<object, EndpointDerivation>();\n\n/** Detached copy of an endpoint list so callers can never mutate the cache. */\nfunction copyEndpoints(endpoints: DiagramEndpoint[] | undefined): DiagramEndpoint[] | undefined {\n  return endpoints?.map((endpoint) => ({\n    ...endpoint,\n    ...(endpoint.compatibility ? { compatibility: [...endpoint.compatibility] } : {}),\n    ...(endpoint.layout ? { layout: { ...endpoint.layout } } : {}),\n  }));\n}\n\n/**\n * Memoized {@link toDiagramEndpoints}. Recomputes on any data-identity change,\n * on a port-collection change detected by the fingerprint, and when the same\n * data object surfaces under a different node id/type. Always returns a fresh\n * copy, so a consumer mutating an emitted document cannot corrupt later ones.\n * Nodes with no object `data` bypass the cache (nothing stable to key on).\n */\nfunction toDiagramEndpointsCached(node: NetworkNode): DiagramEndpoint[] | undefined {\n  const data: unknown = node.data;\n  if (!data || typeof data !== 'object') {\n    return toDiagramEndpoints(node);\n  }\n\n  const fingerprint = endpointSourceFingerprint(data as Record<string, unknown>);\n  const cached = endpointDerivationCache.get(data);\n  if (\n    cached &&\n    cached.nodeId === node.id &&\n    cached.nodeType === node.type &&\n    sameFingerprint(cached.fingerprint, fingerprint)\n  ) {\n    return copyEndpoints(cached.endpoints);\n  }\n\n  const endpoints = toDiagramEndpoints(node);\n  endpointDerivationCache.set(data, { nodeId: node.id, nodeType: node.type, fingerprint, endpoints });\n  return copyEndpoints(endpoints);\n}\n\nfunction mergeEndpoints(\n  previousEndpoints: DiagramEndpoint[] | undefined,\n  nextEndpoints: DiagramEndpoint[] | undefined\n): DiagramEndpoint[] | undefined {\n  if (!nextEndpoints || nextEndpoints.length === 0) {\n    return nextEndpoints;\n  }\n\n  const previousById = new Map((previousEndpoints ?? []).map((endpoint) => [endpoint.id, endpoint]));\n  return nextEndpoints.map((endpoint) => {\n    const previous = previousById.get(endpoint.id);\n    if (!previous) {\n      return endpoint;\n    }\n\n    return {\n      ...previous,\n      ...endpoint,\n      compatibility: endpoint.compatibility ?? previous.compatibility,\n      layout: endpoint.layout ?? previous.layout,\n    };\n  });\n}\n\nfunction mergeDocumentNode(previousNode: DiagramNode | undefined, nextNode: DiagramNode): DiagramNode {\n  if (!previousNode) {\n    return nextNode;\n  }\n\n  return {\n    ...previousNode,\n    ...nextNode,\n    placement: nextNode.placement,\n    render: nextNode.render,\n    config: nextNode.config,\n    externalRefs: nextNode.externalRefs,\n    metadata: nextNode.metadata ?? previousNode.metadata,\n    endpoints: mergeEndpoints(previousNode.endpoints, nextNode.endpoints),\n  };\n}\n\n/**\n * Convert a runtime {@link NetworkGraphSnapshot} into the canonical, persistable\n * {@link DiagramDocument}. The emitted document always carries `version: 1`.\n *\n * Container kinds (`patch-panel`/`closure`/`splice-tray`) serialize their ports\n * as face-tagged `endpoints`, which {@link diagramDocumentToNetworkGraph}\n * reconstructs back into `portGroups`/`rearPortGroups`.\n */\nexport function networkGraphToDiagramDocument(\n  snapshot: NetworkGraphSnapshot,\n  previousDocument?: DiagramDocument\n): DiagramDocument {\n  const previousNodesById = new Map((previousDocument?.nodes ?? []).map((node) => [node.id, node]));\n\n  const nodes = snapshot.nodes.map((node) =>\n    mergeDocumentNode(previousNodesById.get(node.id), {\n      id: node.id,\n      ...toDiagramNodeKind(node),\n      label: node.data?.label ?? node.id,\n      placement: toDiagramPlacement(node),\n      render: toDiagramRender(node),\n      config: toDiagramConfig(node),\n      externalRefs: toDiagramExternalRefs(node),\n      metadata: previousNodesById.get(node.id)?.metadata,\n      endpoints: toDiagramEndpointsCached(node),\n    })\n  );\n\n  // Per node: the endpoint ids that exist on both faces, i.e. whose rear twin\n  // was minted as `<id>--rear` (see toPortBasedEndpoints).\n  const aliasedIdsByNodeId = new Map<string, Set<string>>();\n  nodes.forEach((node) => {\n    const endpoints = node.endpoints ?? [];\n    const frontIds = collectFrontEndpointIds(endpoints);\n    const aliased = new Set<string>();\n    for (const endpoint of endpoints) {\n      const base = rearAliasBase(endpoint, frontIds);\n      if (base !== undefined) aliased.add(base);\n    }\n    if (aliased.size > 0) aliasedIdsByNodeId.set(node.id, aliased);\n  });\n\n  /**\n   * Endpoint id an edge handle persists as: the handle with its directional\n   * suffix and split-face alias stripped — except a `--rear` alias on a port id\n   * that exists on both faces, which names the rear twin endpoint.\n   */\n  const toEndpointId = (nodeId: string, handle: string | null | undefined): string | undefined => {\n    if (!handle) return undefined;\n    const normalized = normalizeHandleId(handle);\n    const aliased = aliasedIdsByNodeId.get(nodeId);\n    if (aliased?.has(normalized) && parseHandleId(normalizeDirectionalHandleId(handle)).faceAlias === 'rear') {\n      return applyFaceAliasHandleId(normalized, 'rear');\n    }\n    return normalized;\n  };\n\n  const previousEdgesById = new Map((previousDocument?.edges ?? []).map((edge) => [edge.id, edge]));\n  const edges = snapshot.edges.map((edge) => {\n    const nextEdge: DiagramEdge = {\n      id: edge.id,\n      source: {\n        nodeId: edge.source,\n        endpointId: toEndpointId(edge.source, edge.sourceHandle),\n      },\n      target: {\n        nodeId: edge.target,\n        endpointId: toEndpointId(edge.target, edge.targetHandle),\n      },\n      type: edge.type,\n      hidden: edge.hidden === true ? true : undefined,\n      deletable: typeof edge.deletable === 'boolean' ? edge.deletable : undefined,\n      interactionWidth:\n        typeof edge.interactionWidth === 'number' && Number.isFinite(edge.interactionWidth)\n          ? edge.interactionWidth\n          : undefined,\n      data: edge.data ? { ...edge.data } : undefined,\n      metadata: previousEdgesById.get(edge.id)?.metadata,\n    };\n\n    const previous = previousEdgesById.get(edge.id);\n    return previous ? { ...previous, ...nextEdge } : nextEdge;\n  });\n\n  return {\n    version: 1,\n    nodes,\n    edges,\n    ui: extractUiState(snapshot.nodes, previousDocument?.ui),\n    metadata: previousDocument?.metadata,\n  };\n}\n\n/**\n * Port type for a rebuilt port. An endpoint's own `type` wins. Without one,\n * only power endpoints get an inferred type (their connector family drives\n * power semantics); every other untyped endpoint stays untyped — inventing\n * `ethernet` changed the port's icon and compatibility on every load and made\n * re-serialisation non-idempotent.\n */\nfunction endpointToPortType(endpoint: DiagramEndpoint): PortType | undefined {\n  if (endpoint.type) {\n    return endpoint.type;\n  }\n\n  if (endpoint.role === 'power' || endpoint.medium === 'power') {\n    const connector = String(endpoint.connector ?? '').toLowerCase();\n    if (connector.includes('c19') || connector.includes('c20')) return 'power_c19';\n    if (connector.includes('c13') || connector.includes('c14')) return 'power_c13';\n    return 'power_ac';\n  }\n\n  // Legacy hand-authored documents that name a medium but no type.\n  if (endpoint.medium === 'fiber') return 'fiber';\n  if (endpoint.medium === 'ethernet') return 'ethernet';\n  return undefined;\n}\n\n/** Shared per-port fields of a rebuilt {@link DevicePort} / {@link Port}. */\nfunction endpointPortFields(\n  endpoint: DiagramEndpoint,\n  frontIds: ReadonlySet<string>,\n  connectedEndpointIds: ReadonlySet<string>\n) {\n  const type = endpointToPortType(endpoint);\n  return {\n    // The rear twin of a both-faces port id was persisted as `<id>--rear`; the\n    // port itself keeps the shared id (the renderer re-applies the alias).\n    id: rearAliasBase(endpoint, frontIds) ?? endpoint.id,\n    label: endpoint.label,\n    connected: connectedEndpointIds.has(endpoint.id),\n    ...(type ? { type } : {}),\n    cableType: endpoint.cableType,\n    connectorType: endpoint.connector as DevicePort['connectorType'],\n    sourceTypeLabel: endpoint.sourceTypeLabel,\n    compatibility:\n      endpoint.compatibility && endpoint.compatibility.length > 0\n        ? [...endpoint.compatibility]\n        : undefined,\n    position: clonePortPosition(endpoint.layout),\n  };\n}\n\nfunction endpointsToDevicePorts(\n  endpoints: DiagramEndpoint[],\n  frontIds: ReadonlySet<string>,\n  connectedEndpointIds: ReadonlySet<string>\n): DevicePort[] {\n  return endpoints.map((endpoint) => {\n    const fields = endpointPortFields(endpoint, frontIds, connectedEndpointIds);\n    return {\n      ...fields,\n      face: endpoint.face === 'rear' ? 'rear' : endpoint.face === 'front' ? 'front' : undefined,\n      group: endpoint.group,\n    } as DevicePort;\n  });\n}\n\nfunction endpointsToPortBlocks(\n  endpoints: DiagramEndpoint[],\n  frontIds: ReadonlySet<string>,\n  connectedEndpointIds: ReadonlySet<string>\n): PortBlock[] {\n  const grouped = new Map<string, Port[]>();\n\n  endpoints.forEach((endpoint) => {\n    const groupName = endpoint.group ?? 'Ports';\n    const port = endpointPortFields(endpoint, frontIds, connectedEndpointIds) as Port;\n    const existing = grouped.get(groupName);\n    if (existing) {\n      existing.push(port);\n      return;\n    }\n    grouped.set(groupName, [port]);\n  });\n\n  return [...grouped.entries()].map(([name, ports]) => ({ name, ports }));\n}\n\nfunction applyRenderToLegacyData(\n  data: NetworkNode['data'],\n  render: DiagramNodeRender | undefined,\n  uiEntry: NonNullable<DiagramUiState['nodeState']>[string] | undefined\n): NetworkNode['data'] {\n  if (!render && !uiEntry) {\n    return data;\n  }\n\n  const nextData = { ...data };\n  if (render) {\n    GENERAL_RENDER_KEYS.forEach((key) => {\n      const value = render[key];\n      if (value !== undefined) {\n        (nextData as Record<string, unknown>)[key] = value;\n      }\n    });\n\n    if (render.images) {\n      IMAGE_RENDER_KEYS.forEach((key) => {\n        const value = render.images?.[key];\n        if (value !== undefined) {\n          (nextData as Record<string, unknown>)[key] = value;\n        }\n      });\n    }\n\n    if (render.dimensions) {\n      DIMENSION_RENDER_KEYS.forEach((key) => {\n        const value = render.dimensions?.[key];\n        if (typeof value === 'number') {\n          (nextData as Record<string, unknown>)[key] = value;\n        }\n      });\n    }\n  }\n\n  const resolvedFace = uiEntry?.face;\n  if (resolvedFace) {\n    nextData.face = resolvedFace;\n  }\n  if (uiEntry?.handleSideMap) {\n    nextData.handleSideMap = { ...uiEntry.handleSideMap };\n  }\n\n  return nextData;\n}\n\nfunction applyExternalRefsToLegacyData(data: NetworkNode['data'], refs: DiagramExternalRef[] | undefined): NetworkNode['data'] {\n  if (!refs || refs.length === 0) {\n    return data;\n  }\n\n  const nextData = { ...data };\n  refs.forEach((ref) => {\n    if (ref.source === 'netbox') {\n      nextData.netboxId = ref.id;\n      if (ref.type) {\n        nextData.netboxType = ref.type;\n      }\n    }\n    if (ref.source === 'inventory') {\n      nextData.inventoryId = ref.id;\n      if (ref.type) {\n        nextData.inventoryType = ref.type;\n      }\n    }\n  });\n\n  return nextData;\n}\n\n/**\n * Build a per-node `endpointId -> endpoint` index for O(1) resolution. First\n * write wins so the lookup mirrors `Array.prototype.find` (first match) exactly;\n * nodes with no endpoints are omitted. Computing this once before the edge loop\n * turns {@link resolveEndpointHandleId} from an O(edges x endpoints) linear scan\n * into O(edges + endpoints).\n */\nfunction buildEndpointIndex(nodes: DiagramNode[]): Map<string, Map<string, DiagramEndpoint>> {\n  const index = new Map<string, Map<string, DiagramEndpoint>>();\n  for (const node of nodes) {\n    if (!node.endpoints || node.endpoints.length === 0) {\n      continue;\n    }\n    const byId = new Map<string, DiagramEndpoint>();\n    for (const endpoint of node.endpoints) {\n      if (!byId.has(endpoint.id)) {\n        byId.set(endpoint.id, endpoint);\n      }\n    }\n    index.set(node.id, byId);\n  }\n  return index;\n}\n\n/**\n * Resolve an edge's endpoint reference to the endpoint RECORD it names: by raw\n * id first (opaque ids, and the `<id>--rear` twin of a both-faces port), then\n * by the normalized id (a hand-authored edge may carry a directional/face\n * suffix the persisted endpoint id lacks).\n */\nfunction findEndpointRecord(\n  endpointId: string,\n  endpointById: Map<string, DiagramEndpoint> | undefined\n): DiagramEndpoint | undefined {\n  if (!endpointById) return undefined;\n  return endpointById.get(endpointId) ?? endpointById.get(normalizeHandleId(endpointId));\n}\n\n/**\n * The device type the renderer reads for an equipment node: `config.deviceType`\n * (what `DeviceNode` reads from `data.deviceType`), falling back to the subtype\n * for legacy/hand-authored documents that carried the device type there.\n */\nfunction effectiveEquipmentDeviceType(node: DiagramNode): string {\n  const configured = node.config?.deviceType;\n  const deviceType =\n    typeof configured === 'string' && configured.trim().length > 0 ? configured : node.subtype;\n  return (deviceType ?? '').toLowerCase().replace(/_/g, '-');\n}\n\n/**\n * Resolve a document edge's endpoint reference into the React Flow\n * `sourceHandle`/`targetHandle` the reconstructed port actually mounts.\n *\n * The endpoint {@link DiagramEndpoint.id} is treated as an *opaque* key, not a\n * handle-grammar string: the edge is resolved to its endpoint RECORD (via the\n * per-node index — {@link buildEndpointIndex}), and the handle base is the\n * record's own id, which is exactly what `endpointsToDevicePorts` /\n * `endpointsToPortBlocks` stamp onto `port.id` and therefore what\n * {@link buildPortHandleId} mounts (for the `<id>--rear` twin of a both-faces\n * port, the id IS the rear handle's split-face alias). Directional\n * (`-source`/`-target`) decisions are then derived from the record's\n * STRUCTURED fields (`face`/`role`/`medium`) plus the node's rendered device\n * type — never from the id text.\n *\n * This is the single resolution path for both id styles: opaque consumer keys\n * (uuid-like, including ones whose text coincidentally ends in a reserved\n * suffix) and the handle-grammar strings the shipped adapters mint round-trip\n * identically. When an edge references a missing endpoint, the base falls back\n * to the normalized edge id so a dangling legacy reference still resolves\n * byte-for-byte as before.\n */\nfunction resolveEndpointHandleId(\n  node: DiagramNode | undefined,\n  endpointId: string | null | undefined,\n  role: 'source' | 'target',\n  endpointById?: Map<string, DiagramEndpoint>\n): string | undefined {\n  if (!endpointId) {\n    return undefined;\n  }\n\n  const endpoint = endpointById\n    ? findEndpointRecord(endpointId, endpointById)\n    : node?.endpoints?.find(\n        (candidate) =>\n          candidate.id === endpointId || candidate.id === normalizeHandleId(endpointId)\n      );\n\n  // Base = the endpoint's own id (what the rebuilt port mounts its handle with),\n  // NOT the normalized edge string. Falls back to the normalized edge id only\n  // for a dangling reference with no matching endpoint.\n  const base = endpoint ? endpoint.id : normalizeHandleId(endpointId);\n\n  if (!node || node.kind !== 'equipment') {\n    return base;\n  }\n\n  const deviceType = effectiveEquipmentDeviceType(node);\n  const isPowerEndpoint = endpoint?.role === 'power' || endpoint?.medium === 'power';\n\n  if ((deviceType === 'switch' || deviceType === 'router') && role === 'target' && !isPowerEndpoint) {\n    return `${base}-target`;\n  }\n\n  if (deviceType === 'patch-panel') {\n    const face = endpoint?.face === 'rear' ? 'rear' : 'front';\n    if (role === 'source' && face === 'front') {\n      return `${base}-source`;\n    }\n    if (role === 'target' && face === 'rear') {\n      return `${base}-target`;\n    }\n  }\n\n  return base;\n}\n\n/**\n * Resolve a document node's placement defensively. Missing placement or a\n * missing/partial position degrades to `{ x: 0, y: 0 }` (with a warning) instead\n * of throwing on a partial document.\n */\nfunction resolveNodePlacement(node: DiagramNode): DiagramNodePlacement {\n  const placement = isRecord(node.placement) ? (node.placement as DiagramNodePlacement) : undefined;\n  if (!placement) {\n    console.warn(\n      `[react-networks] Document node \"${node.id}\" is missing placement; defaulting position to { x: 0, y: 0 }.`\n    );\n    return { position: { x: 0, y: 0 } };\n  }\n  const position = placement.position;\n  // Number.isFinite (not typeof === 'number') so NaN/Infinity coordinates from a\n  // malformed document are rejected rather than corrupting React Flow layout.\n  if (!position || !Number.isFinite(position.x) || !Number.isFinite(position.y)) {\n    console.warn(\n      `[react-networks] Document node \"${node.id}\" is missing a valid placement.position; defaulting to { x: 0, y: 0 }.`\n    );\n    return { ...placement, position: clonePosition(position) };\n  }\n  return placement;\n}\n\n/**\n * Coerce untrusted document nodes: drop non-record entries and entries without\n * a string id or a known kind (with a warning each), drop malformed endpoint\n * entries, and keep the first node for a duplicated id.\n */\nfunction sanitizeDocumentNodes(rawNodes: unknown): DiagramNode[] {\n  if (rawNodes === undefined || rawNodes === null) return [];\n  if (!Array.isArray(rawNodes)) {\n    console.warn('[react-networks] DiagramDocument `nodes` is not an array; treating it as empty.');\n    return [];\n  }\n  const out: DiagramNode[] = [];\n  const seenIds = new Set<string>();\n  rawNodes.forEach((raw, index) => {\n    if (!isRecord(raw) || typeof raw.id !== 'string' || raw.id.length === 0) {\n      console.warn(`[react-networks] Skipping malformed document node at index ${index} (not an object with a string id).`);\n      return;\n    }\n    const node = raw as unknown as DiagramNode;\n    if (!KNOWN_DIAGRAM_NODE_KINDS.has(node.kind)) {\n      console.warn(\n        `[react-networks] Skipping document node \"${node.id}\" with unknown kind \"${String(node.kind)}\".`\n      );\n      return;\n    }\n    if (seenIds.has(node.id)) {\n      console.warn(`[react-networks] Skipping duplicate document node id \"${node.id}\".`);\n      return;\n    }\n    seenIds.add(node.id);\n    const endpoints = Array.isArray(node.endpoints)\n      ? node.endpoints.filter(\n          (endpoint): endpoint is DiagramEndpoint =>\n            isRecord(endpoint) && typeof endpoint.id === 'string' && endpoint.id.length > 0\n        )\n      : undefined;\n    const label = typeof node.label === 'string' ? node.label : node.id;\n    out.push({\n      ...node,\n      label,\n      config: isRecord(node.config) ? node.config : undefined,\n      endpoints: endpoints && endpoints.length > 0 ? endpoints : undefined,\n      externalRefs: Array.isArray(node.externalRefs)\n        ? node.externalRefs.filter((ref): ref is DiagramExternalRef => isRecord(ref))\n        : undefined,\n    });\n  });\n  return out;\n}\n\nfunction isEdgeEnd(value: unknown): value is DiagramEdgeEndpoint {\n  return isRecord(value) && typeof value.nodeId === 'string' && value.nodeId.length > 0;\n}\n\n/**\n * Convert a persisted {@link DiagramDocument} (or a lenient\n * {@link DiagramDocumentInput}) back into a runtime graph.\n *\n * Untrusted input never throws: malformed nodes/endpoints/edges are skipped\n * with a `console.warn`, and edges whose source or target node is absent from\n * the produced graph (unknown kind, malformed, or simply missing) are dropped\n * instead of being emitted dangling.\n */\nexport function diagramDocumentToNetworkGraph(document: DiagramDocumentInput): NetworkGraphSnapshot {\n  const input: DiagramDocumentInput = isRecord(document) ? document : {};\n  if (typeof input.version === 'number' && input.version > 1) {\n    console.warn(\n      `[react-networks] Unknown DiagramDocument version ${input.version}; parsing best-effort as version 1.`\n    );\n  }\n\n  const documentNodes = sanitizeDocumentNodes(input.nodes);\n  const rawEdges: unknown[] = Array.isArray(input.edges) ? input.edges : [];\n  if (input.edges !== undefined && input.edges !== null && !Array.isArray(input.edges)) {\n    console.warn('[react-networks] DiagramDocument `edges` is not an array; treating it as empty.');\n  }\n  const documentEdges = rawEdges.filter((edge, index): edge is DiagramEdge => {\n    if (isRecord(edge) && typeof edge.id === 'string' && isEdgeEnd(edge.source) && isEdgeEnd(edge.target)) {\n      return true;\n    }\n    console.warn(\n      `[react-networks] Skipping malformed document edge at index ${index} (needs an id and source/target node ids).`\n    );\n    return false;\n  });\n\n  const nodesById = new Map(documentNodes.map((node) => [node.id, node]));\n  const endpointsByNodeId = buildEndpointIndex(documentNodes);\n\n  // Mark the endpoint RECORDS edges land on, so a both-faces port's front and\n  // rear twins are tracked independently.\n  const connectedByNodeId = new Map<string, Set<string>>();\n  const markConnected = (ref: DiagramEdgeEndpoint) => {\n    if (!ref.endpointId) return;\n    const record = findEndpointRecord(ref.endpointId, endpointsByNodeId.get(ref.nodeId));\n    if (!record) return;\n    let set = connectedByNodeId.get(ref.nodeId);\n    if (!set) {\n      set = new Set();\n      connectedByNodeId.set(ref.nodeId, set);\n    }\n    set.add(record.id);\n  };\n  documentEdges.forEach((edge) => {\n    markConnected(edge.source);\n    markConnected(edge.target);\n  });\n\n  const uiNodeState = isRecord(input.ui?.nodeState) ? input.ui!.nodeState! : {};\n  const noConnections: ReadonlySet<string> = new Set();\n\n  const nodes: NetworkNode[] = [];\n  for (const node of documentNodes) {\n    const placement = resolveNodePlacement(node);\n    const legacyType = toLegacyNodeType(node);\n    const connected = connectedByNodeId.get(node.id) ?? noConnections;\n    let data: NetworkNode['data'] = {\n      label: node.label,\n      ...(node.config ?? {}),\n    } as NetworkNode['data'];\n\n    const uiEntry = Object.prototype.hasOwnProperty.call(uiNodeState, node.id)\n      ? uiNodeState[node.id]\n      : undefined;\n    data = applyRenderToLegacyData(data, isRecord(node.render) ? node.render : undefined, isRecord(uiEntry) ? uiEntry : undefined);\n    data = applyExternalRefsToLegacyData(data, node.externalRefs);\n\n    if (legacyType === 'device' || legacyType === 'server' || legacyType === 'switch' || legacyType === 'router') {\n      // `config.deviceType` (already spread above) is authoritative. Legacy\n      // documents carried the device type in `subtype` instead.\n      if (typeof data.deviceType !== 'string' || data.deviceType.trim().length === 0) {\n        data.deviceType = node.subtype ?? (legacyType === 'device' ? 'device' : legacyType);\n      }\n      if (node.endpoints && node.endpoints.length > 0) {\n        data.ports = endpointsToDevicePorts(node.endpoints, collectFrontEndpointIds(node.endpoints), connected);\n      }\n    } else if (legacyType === 'vertical-pdu') {\n      const rearEndpoints = (node.endpoints ?? []).filter((endpoint) => endpoint.face !== 'front');\n      if (rearEndpoints.length > 0) {\n        data.pduPortGroups = endpointsToPortBlocks(rearEndpoints, noConnections, connected);\n        data.powerPorts = rearEndpoints.length;\n      }\n    } else if (legacyType === 'patch-panel' || legacyType === 'closure' || legacyType === 'splice-tray') {\n      // Mirror the forward pass, which serializes container ports as face-tagged\n      // endpoints, by reconstructing front -> portGroups and rear -> rearPortGroups.\n      const endpoints = node.endpoints ?? [];\n      const frontIds = collectFrontEndpointIds(endpoints);\n      const frontEndpoints = endpoints.filter((endpoint) => endpoint.face !== 'rear');\n      const rearEndpoints = endpoints.filter((endpoint) => endpoint.face === 'rear');\n      if (frontEndpoints.length > 0) {\n        data.portGroups = endpointsToPortBlocks(frontEndpoints, frontIds, connected);\n      }\n      if (rearEndpoints.length > 0) {\n        data.rearPortGroups = endpointsToPortBlocks(rearEndpoints, frontIds, connected);\n      }\n    }\n\n    if (placement.rack) {\n      if (placement.rack.rackLayout) {\n        data.rackLayout = placement.rack.rackLayout;\n      }\n      if (typeof placement.rack.rackWidthPx === 'number') {\n        data.rackWidthPx = placement.rack.rackWidthPx;\n      }\n      if (typeof placement.rack.rackFaceGapPx === 'number') {\n        data.rackFaceGapPx = placement.rack.rackFaceGapPx;\n      }\n    }\n\n    if (legacyType !== 'rack' && placement.rack) {\n      data.isRackMounted = true;\n      if (typeof placement.rack.uPosition === 'number') {\n        data.uPosition = placement.rack.uPosition;\n      }\n      if (placement.rack.mountMode) {\n        data.mountMode = placement.rack.mountMode;\n      }\n    }\n\n    if (placement.container) {\n      if (typeof placement.container.slotPosition === 'number') {\n        data.slotPosition = placement.container.slotPosition;\n      }\n      if (typeof placement.container.holderPosition === 'number') {\n        data.holderPosition = placement.container.holderPosition;\n      }\n    }\n\n    nodes.push({\n      id: node.id,\n      type: legacyType,\n      position: clonePosition(placement.position),\n      ...(placement.parentId ? { parentId: placement.parentId } : {}),\n      ...(placement.extent ? { extent: placement.extent } : {}),\n      data,\n    } as NetworkNode);\n  }\n\n  const producedNodeIds = new Set(nodes.map((node) => node.id));\n  const danglingEdgeIds: string[] = [];\n  const edges: NetworkEdge[] = [];\n  for (const edge of documentEdges) {\n    if (!producedNodeIds.has(edge.source.nodeId) || !producedNodeIds.has(edge.target.nodeId)) {\n      danglingEdgeIds.push(edge.id);\n      continue;\n    }\n    edges.push({\n      id: edge.id,\n      source: edge.source.nodeId,\n      target: edge.target.nodeId,\n      ...(edge.source.endpointId ? { sourceHandle: resolveEndpointHandleId(nodesById.get(edge.source.nodeId), edge.source.endpointId, 'source', endpointsByNodeId.get(edge.source.nodeId)) } : {}),\n      ...(edge.target.endpointId ? { targetHandle: resolveEndpointHandleId(nodesById.get(edge.target.nodeId), edge.target.endpointId, 'target', endpointsByNodeId.get(edge.target.nodeId)) } : {}),\n      ...(edge.type ? { type: edge.type } : {}),\n      ...(edge.hidden === true ? { hidden: true } : {}),\n      ...(typeof edge.deletable === 'boolean' ? { deletable: edge.deletable } : {}),\n      ...(typeof edge.interactionWidth === 'number' && Number.isFinite(edge.interactionWidth)\n        ? { interactionWidth: edge.interactionWidth }\n        : {}),\n      ...(isRecord(edge.data) ? { data: { ...edge.data } } : {}),\n    });\n  }\n  if (danglingEdgeIds.length > 0) {\n    console.warn(\n      `[react-networks] Dropped ${danglingEdgeIds.length} document edge(s) whose source or target node is not in the document: ${danglingEdgeIds\n        .slice(0, 5)\n        .map((id) => `\"${id}\"`)\n        .join(', ')}${danglingEdgeIds.length > 5 ? ', …' : ''}.`\n    );\n  }\n\n  return { nodes, edges };\n}\n","import type { DeviceFace, NetworkNode, Port, PortBlock } from '../types';\nimport {\n  buildPortHandleId,\n  isPowerInletHandleId,\n  isPowerPortType,\n  looksLikePowerHandleId,\n  normalizeDirectionalHandleId,\n  parseHandleId,\n} from './handle-ids';\nimport { MAX_POWER_PORTS } from './document-adapter';\nimport {\n  getPduOutletConnectorType,\n  inferConnectorFromPortType,\n} from './power-connectors';\nimport { getResolvedPortBlocks } from './render-ports';\n\nexport type EndpointConnectionKind = 'power' | 'network' | 'unknown';\n\n/**\n * The electrical part a power endpoint plays. Real installations have two\n * different kinds of power connection, and conflating them is the mistake this\n * vocabulary exists to prevent:\n *\n * - a **branch circuit** is fixed wiring from a distribution-board breaker way\n *   (`circuit-source`) to a PDU/board supply inlet (`circuit-sink`);\n * - a **cord** is a flexible IEC 60320 lead from a PDU outlet or device power\n *   outlet (`cord-source`) to a device power inlet (`cord-sink`).\n *\n * A way must never land on a device inlet or a PDU outlet, and a cord must\n * never land on a PDU inlet. Endpoints the model cannot place resolve to\n * `unknown`, and rules skip them rather than guessing.\n * @public\n */\nexport type PowerEndpointRole =\n  | 'circuit-source'\n  | 'circuit-sink'\n  | 'cord-source'\n  | 'cord-sink'\n  | 'unknown';\n\nexport interface EndpointMetadata {\n  normalizedHandle: string;\n  face?: DeviceFace;\n  kind: EndpointConnectionKind;\n  compatibility: string[];\n  /**\n   * The endpoint's electrical part, when it is a power endpoint. `unknown` for\n   * network endpoints and for power endpoints the model cannot place.\n   */\n  powerRole: PowerEndpointRole;\n  /** Resolved port `type` (e.g. `power_c13`, `power_outlet`), when known. */\n  portType?: string;\n  /**\n   * Resolved connector designation (e.g. `C13`, `C14`, `C19`, `C20`), when\n   * known. Resolved in the renderer's own precedence, so a rule always reasons\n   * about the connector that was drawn:\n   *\n   * 1. the port's explicit `connectorType`;\n   * 2. the designation embedded in its `type` (`power_c13` → `C13`), via\n   *    `inferConnectorFromPortType`;\n   * 3. for a vertical-PDU outlet only, the strip's `data.portTypes` at that\n   *    outlet position, falling back to the default 8×C13 + 2×C19 cycle.\n   */\n  connectorType?: string;\n  /**\n   * 1-based outlet position on a vertical PDU: the number a bare outlet handle\n   * names, or the port's index in the strip's declared port groups when the\n   * outlet carries an explicit id (`rear-power-1`). `undefined` on every other\n   * node type and for handles the strip does not declare.\n   */\n  outletPosition?: number;\n}\n\ninterface CachedHandleMetadata {\n  face?: DeviceFace;\n  kind: EndpointConnectionKind;\n  compatibility: string[];\n  portType?: string;\n  connectorType?: string;\n  /** 1-based position within a vertical PDU's declared outlets. */\n  position?: number;\n}\n\nconst handleMetadataCache = new WeakMap<object, Map<string, CachedHandleMetadata>>();\n\nconst warnedDuplicateHandleIds = new Set<string>();\n\n/**\n * Dev warning when two ports on one node resolve to the same handle id. The\n * first port's metadata is kept (see {@link buildHandleMetadataMap}); this\n * surfaces the ambiguity without spamming — at most once per id per process.\n */\nfunction warnDuplicateHandleId(handleId: string): void {\n  if (warnedDuplicateHandleIds.has(handleId)) {\n    return;\n  }\n  warnedDuplicateHandleIds.add(handleId);\n  console.warn(\n    `[react-networks] duplicate handle id ${JSON.stringify(handleId)} on a single node; ` +\n      \"keeping the first port's metadata. Give colliding ports distinct labels or \" +\n      'explicit ids (see buildPortHandleId).',\n  );\n}\n\nfunction normalizeCompatibilityToken(value: unknown): string | undefined {\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n\n  const normalized = value.trim().toLowerCase().replace(/[\\s_]+/g, '-');\n  return normalized.length > 0 ? normalized : undefined;\n}\n\nfunction uniqueTokens(tokens: Array<string | undefined>): string[] {\n  const values = new Set<string>();\n  tokens.forEach((token) => {\n    if (token) {\n      values.add(token);\n    }\n  });\n  return [...values];\n}\n\nfunction inferPowerCompatibilityToken(...values: Array<unknown>): string | undefined {\n  const normalizedValues = values\n    .map((value) => normalizeCompatibilityToken(value))\n    .filter((value): value is string => !!value);\n\n  if (normalizedValues.some((value) => value.includes('c13'))) return 'c13';\n  if (normalizedValues.some((value) => value.includes('c14'))) return 'c14';\n  if (normalizedValues.some((value) => value.includes('c19'))) return 'c19';\n  if (normalizedValues.some((value) => value.includes('c20'))) return 'c20';\n  if (normalizedValues.some((value) => value.includes('ac'))) return 'ac';\n\n  return undefined;\n}\n\nfunction inferNetworkCompatibilityToken(input: {\n  type?: string;\n  cableType?: string;\n  connectorType?: string;\n  handleId?: string;\n}): string | undefined {\n  const candidates = [\n    normalizeCompatibilityToken(input.type),\n    normalizeCompatibilityToken(input.cableType),\n    normalizeCompatibilityToken(input.connectorType),\n    normalizeCompatibilityToken(input.handleId),\n  ].filter((value): value is string => !!value);\n\n  if (candidates.some((value) => value.includes('fiber') || value.includes('smf') || value.includes('mmf') || value.includes('qsfp') || value.includes('sfp') || value.includes('lc') || value.includes('sc'))) {\n    return 'fiber';\n  }\n\n  if (candidates.some((value) => value.includes('ethernet') || value.startsWith('eth') || value.includes('rj45') || value.includes('base-t'))) {\n    return 'ethernet';\n  }\n\n  return undefined;\n}\n\nexport function resolveEndpointCompatibilityKeys(input: {\n  compatibility?: string[];\n  kind?: EndpointConnectionKind;\n  type?: string;\n  cableType?: string;\n  connectorType?: string;\n  handleId?: string;\n}): string[] {\n  if (Array.isArray(input.compatibility) && input.compatibility.length > 0) {\n    return uniqueTokens(input.compatibility.map((value) => normalizeCompatibilityToken(value)));\n  }\n\n  if (input.kind === 'power' || isPowerPortType(input.type)) {\n    const connectorToken = inferPowerCompatibilityToken(input.connectorType, input.type, input.handleId);\n    return connectorToken ? [`power:${connectorToken}`] : ['power'];\n  }\n\n  if (input.kind === 'network') {\n    const mediumToken = inferNetworkCompatibilityToken(input);\n    return mediumToken ? [`network:${mediumToken}`] : ['network'];\n  }\n\n  const fallbackKind =\n    isPowerPortType(input.type)\n      ? 'power'\n      : inferNetworkCompatibilityToken(input)\n        ? 'network'\n        : undefined;\n\n  if (fallbackKind === 'power') {\n    const connectorToken = inferPowerCompatibilityToken(input.connectorType, input.type, input.handleId);\n    return connectorToken ? [`power:${connectorToken}`] : ['power'];\n  }\n\n  if (fallbackKind === 'network') {\n    const mediumToken = inferNetworkCompatibilityToken(input);\n    return mediumToken ? [`network:${mediumToken}`] : ['network'];\n  }\n\n  return [];\n}\n\nfunction getFallbackPduPortBlocks(node: NetworkNode): PortBlock[] {\n  const data = node.data ?? {};\n  const powerPortCount =\n    typeof data.powerPorts === 'number' && Number.isFinite(data.powerPorts) && data.powerPorts > 0\n      ? Math.min(MAX_POWER_PORTS, data.powerPorts)\n      : 36;\n\n  const portTypes = Array.isArray(data.portTypes) ? data.portTypes : undefined;\n\n  return [\n    {\n      name: 'Power',\n      ports: Array.from({ length: powerPortCount }, (_, index) => {\n        const defaultConnector = getPduOutletConnectorType(portTypes, index + 1);\n        return {\n          id: `${index + 1}`,\n          label: `${index + 1}`,\n          connected: false,\n          type: defaultConnector === 'C19' ? 'power_c19' : 'power_c13',\n          connectorType: defaultConnector as Port['connectorType'],\n        };\n      }),\n    },\n  ];\n}\n\nfunction getPduPortBlocks(node: NetworkNode): PortBlock[] {\n  const data = node.data ?? {};\n  if (Array.isArray(data.pduPortGroups) && data.pduPortGroups.length > 0) {\n    return data.pduPortGroups;\n  }\n\n  const { ports, rearPorts } = getResolvedPortBlocks(data);\n  if (rearPorts.length > 0) {\n    return rearPorts;\n  }\n  if (ports.length > 0) {\n    return ports;\n  }\n\n  return getFallbackPduPortBlocks(node);\n}\n\nfunction buildHandleMetadataMap(node?: NetworkNode): Map<string, CachedHandleMetadata> {\n  const data = node?.data;\n  if (!node || !data || typeof data !== 'object') {\n    return new Map();\n  }\n\n  const cached = handleMetadataCache.get(data as object);\n  if (cached) {\n    return cached;\n  }\n\n  const metadata = new Map<string, CachedHandleMetadata>();\n\n  // 1-based counter over a vertical PDU's declared outlets, in render order —\n  // the position the connector cycle and the phase attribution are indexed by.\n  let outletPosition = 0;\n\n  const addBlocks = (face: DeviceFace, blocks: PortBlock[], trackPosition = false) => {\n    blocks.forEach((block) => {\n      block.ports.forEach((port) => {\n        const position = trackPosition ? (outletPosition += 1) : undefined;\n        const handleId = buildPortHandleId(block.name, {\n          id: port.id,\n          label: port.label,\n          type: isPowerPortType(port.type) ? port.type : undefined,\n        });\n\n        // Collision safety: two ports can build the same handle id (e.g. the\n        // same group/label rendered on both faces, or an explicit port.id\n        // reused). Keep the FIRST entry deterministically instead of silently\n        // overwriting, and warn once per id so the ambiguity is diagnosable.\n        if (metadata.has(handleId)) {\n          warnDuplicateHandleId(handleId);\n          return;\n        }\n\n        metadata.set(handleId, {\n          face,\n          kind: isPowerPortType(port.type) ? 'power' : 'network',\n          compatibility: resolveEndpointCompatibilityKeys({\n            compatibility: port.compatibility,\n            kind: isPowerPortType(port.type) ? 'power' : 'network',\n            type: port.type,\n            cableType: port.cableType,\n            connectorType: port.connectorType,\n            handleId,\n          }),\n          portType: typeof port.type === 'string' ? port.type : undefined,\n          // A port that names no connector still names it in its `type`\n          // (`power_c13`), which the renderer honours before it falls back to\n          // the positional cycle — so resolve it here rather than dropping to\n          // the cycle and contradicting the drawn connector.\n          connectorType:\n            typeof port.connectorType === 'string' && port.connectorType.length > 0\n              ? port.connectorType\n              : inferConnectorFromPortType(port.type),\n          ...(position !== undefined ? { position } : {}),\n        });\n      });\n    });\n  };\n\n  if (node.type === 'vertical-pdu') {\n    addBlocks('rear', getPduPortBlocks(node), true);\n  } else {\n    const { ports, rearPorts } = getResolvedPortBlocks(data);\n    addBlocks('front', ports);\n    addBlocks('rear', rearPorts);\n  }\n\n  handleMetadataCache.set(data as object, metadata);\n  return metadata;\n}\n\nfunction inferHandleKind(handleId: string, node?: NetworkNode): EndpointConnectionKind {\n  if (!handleId) {\n    return 'unknown';\n  }\n\n  if (node?.type === 'vertical-pdu' || node?.type === 'distribution-board') {\n    return 'power';\n  }\n\n  if (looksLikePowerHandleId(handleId) || /^\\d+$/.test(handleId)) {\n    return 'power';\n  }\n\n  const lowerHandle = handleId.toLowerCase();\n  const isStandardNetworkHandle =\n    lowerHandle.startsWith('net-') ||\n    lowerHandle.startsWith('port-') ||\n    lowerHandle.startsWith('uplink-') ||\n    lowerHandle.includes('fiber') ||\n    lowerHandle.includes('ports');\n  const isCustomPortName =\n    /^[a-z0-9]+-\\d+$/i.test(handleId) &&\n    !lowerHandle.startsWith('pwr') &&\n    !lowerHandle.startsWith('power');\n\n  if (isStandardNetworkHandle || isCustomPortName) {\n    return 'network';\n  }\n\n  return 'unknown';\n}\n\n/** Bare 1-based outlet id on a vertical PDU (`\"1\"…\"N\"`). */\nconst PDU_OUTLET_HANDLE = /^\\d+$/;\n\n/**\n * The connector a PDU outlet at a 1-based `position` carries, honouring an\n * explicit `portTypes` sequence. Last resort only — a declared port's own\n * `connectorType`/`type` wins (see {@link EndpointMetadata.connectorType}).\n */\nfunction inferPduOutletConnectorType(\n  node: NetworkNode | undefined,\n  position: number\n): string {\n  const portTypes = node?.data?.portTypes;\n  return getPduOutletConnectorType(\n    Array.isArray(portTypes) ? portTypes : undefined,\n    position\n  );\n}\n\n/**\n * 1-based outlet position of a handle on a vertical PDU: the number a bare\n * outlet id names, or the port's index among the strip's declared ports when\n * the outlet carries an explicit id. `undefined` for anything else.\n */\nfunction resolveOutletPosition(\n  node: NetworkNode | undefined,\n  normalizedHandle: string,\n  handleMetadata?: CachedHandleMetadata\n): number | undefined {\n  if (node?.type !== 'vertical-pdu') return undefined;\n  if (handleMetadata?.position !== undefined) return handleMetadata.position;\n  return PDU_OUTLET_HANDLE.test(normalizedHandle) ? Number(normalizedHandle) : undefined;\n}\n\nfunction inferHandleCompatibility(\n  handleId: string,\n  kind: EndpointConnectionKind,\n  node?: NetworkNode\n): string[] {\n  if (!handleId) {\n    return [];\n  }\n\n  if (kind === 'power') {\n    // Breaker ways are hard-wired distribution points: they terminate in a\n    // conductor, not a connector, so they impose NO connector constraint.\n    // An empty compatibility list intersects with everything (see\n    // hasCompatibilityIntersection), which is exactly the intent — a way may\n    // feed a C13 PDU inlet, a C14 device inlet, or a busbar tap alike.\n    if (node?.type === 'distribution-board') {\n      return [];\n    }\n\n    if (node?.type === 'vertical-pdu' && PDU_OUTLET_HANDLE.test(handleId)) {\n      return resolveEndpointCompatibilityKeys({\n        kind,\n        connectorType: inferPduOutletConnectorType(node, Number(handleId)),\n        handleId,\n      });\n    }\n\n    return resolveEndpointCompatibilityKeys({\n      kind,\n      handleId,\n    });\n  }\n\n  if (kind === 'network') {\n    const lowerHandle = handleId.toLowerCase();\n    return resolveEndpointCompatibilityKeys({\n      kind,\n      handleId,\n      type:\n        lowerHandle.includes('fiber') || lowerHandle.includes('qsfp') || lowerHandle.includes('sfp')\n          ? 'fiber'\n          : lowerHandle.startsWith('eth') || lowerHandle.startsWith('net-') || lowerHandle.startsWith('port-')\n            ? 'ethernet'\n            : undefined,\n    });\n  }\n\n  return [];\n}\n\n/**\n * Node types that distribute power: their handles are ways, inlets and outlets\n * rather than equipment power inlets.\n */\nconst POWER_DISTRIBUTION_NODE_TYPES = new Set<string>([\n  'vertical-pdu',\n  'distribution-board',\n]);\n\n/** A device port that *supplies* a cord (NetBox's `power_outlet`). */\nfunction isPowerOutletPortType(portType: string | undefined): boolean {\n  return typeof portType === 'string' && /outlet/i.test(portType);\n}\n\n/**\n * Classify a power endpoint into the part it plays electrically. Pure and\n * total: an endpoint that cannot be placed returns `unknown`, which every\n * caller treats as \"skip\", never as \"invalid\".\n */\nfunction resolvePowerEndpointRole(\n  node: NetworkNode | undefined,\n  normalizedHandle: string,\n  kind: EndpointConnectionKind,\n  handleMetadata?: CachedHandleMetadata\n): PowerEndpointRole {\n  if (!normalizedHandle) return 'unknown';\n\n  // A breaker way is always a circuit source, whatever it is mounted on.\n  if (parseHandleId(normalizedHandle).shape.kind === 'breaker-way') {\n    return 'circuit-source';\n  }\n\n  if (kind !== 'power') return 'unknown';\n\n  const nodeType = node?.type;\n  if (nodeType && POWER_DISTRIBUTION_NODE_TYPES.has(nodeType)) {\n    if (nodeType === 'vertical-pdu') {\n      // Outlets: the bare 1-based ids, plus any port the strip declares\n      // through `pduPortGroups` / `rearPorts`. Anything else on a strip\n      // (an ad-hoc `OUT`, a management handle) stays unplaced.\n      //\n      // A declared port wins over the reserved inlet ids: a strip whose outlet\n      // is labelled `Inlet` mounts an outlet handle there, so classifying it as\n      // the supply inlet would make the canvas refuse the cords it actually\n      // carries. The reserved ids are documented on `VerticalPduNodeData`.\n      if (PDU_OUTLET_HANDLE.test(normalizedHandle)) return 'cord-source';\n      if (handleMetadata) return 'cord-source';\n    }\n    if (isPowerInletHandleId(normalizedHandle)) return 'circuit-sink';\n    return 'unknown';\n  }\n\n  // Equipment. A `power_outlet` port supplies a cord (a device acting as a\n  // power strip); every other power port on equipment is an inlet.\n  if (isPowerOutletPortType(handleMetadata?.portType)) return 'cord-source';\n  if (/(^|[-_/ ])outlet([-_/ ]|\\d|$)/i.test(normalizedHandle)) return 'cord-source';\n  if (isPowerPortType(handleMetadata?.portType)) return 'cord-sink';\n  if (/^pwr[-_]/i.test(normalizedHandle)) return 'cord-sink';\n  if (looksLikePowerHandleId(normalizedHandle)) return 'cord-sink';\n\n  return 'unknown';\n}\n\n/**\n * The electrical part a power endpoint plays — see {@link PowerEndpointRole}.\n *\n * Accepts any node-shaped value (`NetworkNode` and the validation engine's\n * looser `PowerGraphNode` both satisfy it) so the interactive connection\n * validator and the graph rules classify endpoints identically.\n * @public\n */\nexport function getPowerEndpointRole(\n  node: { type?: string; data?: unknown } | undefined,\n  handle: string | null | undefined\n): PowerEndpointRole {\n  return getEndpointMetadata(node as NetworkNode | undefined, handle).powerRole;\n}\n\nexport function getEndpointMetadata(\n  node?: NetworkNode,\n  handle?: string | null\n): EndpointMetadata {\n  const normalizedHandleWithDirection = normalizeDirectionalHandleId(handle);\n  if (!normalizedHandleWithDirection) {\n    return {\n      normalizedHandle: '',\n      kind: 'unknown',\n      compatibility: [],\n      powerRole: 'unknown',\n    };\n  }\n\n  // Face aliases (--rear/--front) carry the rendered face; parseHandleId\n  // strips them and reports which one was present.\n  const parsedHandle = parseHandleId(normalizedHandleWithDirection);\n  const normalizedHandle = parsedHandle.base;\n  const explicitFace: DeviceFace | undefined = parsedHandle.faceAlias;\n\n  const handleMetadata = buildHandleMetadataMap(node).get(normalizedHandle);\n  const data = node?.data ?? {};\n  const handleSideMap = (data as { handleSideMap?: Record<string, DeviceFace> }).handleSideMap;\n  const mappedFace = handleSideMap?.[normalizedHandle];\n\n  let face = mappedFace ?? explicitFace ?? handleMetadata?.face;\n  if (!face) {\n    if (normalizedHandle === 'net-top' || normalizedHandle === 'net-bottom') {\n      face = 'front';\n    } else if (\n      normalizedHandle === 'pwr-left-1' ||\n      normalizedHandle === 'pwr-left-2' ||\n      normalizedHandle === 'net-right-1' ||\n      normalizedHandle === 'net-right-2' ||\n      looksLikePowerHandleId(normalizedHandle)\n    ) {\n      face = 'rear';\n    }\n  }\n\n  const kind = handleMetadata?.kind ?? inferHandleKind(normalizedHandle, node);\n\n  const outletPosition = resolveOutletPosition(node, normalizedHandle, handleMetadata);\n\n  // Precedence: the port's own connectorType, then the designation in its\n  // `type`, then — outlets only — the strip's positional cycle.\n  const connectorType =\n    handleMetadata?.connectorType ??\n    (outletPosition !== undefined\n      ? inferPduOutletConnectorType(node, outletPosition)\n      : undefined);\n\n  return {\n    normalizedHandle,\n    face,\n    kind,\n    compatibility:\n      handleMetadata?.compatibility.length\n        ? handleMetadata.compatibility\n        : inferHandleCompatibility(normalizedHandle, kind, node),\n    powerRole: resolvePowerEndpointRole(node, normalizedHandle, kind, handleMetadata),\n    portType: handleMetadata?.portType,\n    connectorType,\n    outletPosition,\n  };\n}\n\n/**\n * 1-based outlet position of a vertical-PDU handle — see\n * {@link EndpointMetadata.outletPosition}. Accepts any node-shaped value so the\n * validation engine's looser `PowerGraphNode` resolves positions exactly as the\n * canvas does, including for strips whose outlets carry explicit ids.\n *\n * @internal Consumed by PWR-B08's phase attribution; not part of the public\n * entry surface.\n */\nexport function getPduOutletPosition(\n  node: { type?: string; data?: unknown } | undefined,\n  handle: string | null | undefined\n): number | undefined {\n  return getEndpointMetadata(node as NetworkNode | undefined, handle).outletPosition;\n}\n","/**\n * IEC 60320 coupler facts used by the connector rules (PWR-A05 / PWR-A06).\n *\n * Terminology (catalogue schema note): C14/C16/C20/C22 are **inlets** (male,\n * equipment side); C13/C15/C19/C21 are **connectors** (female, cord side). The\n * word \"plug\" is ambiguous and is not used.\n *\n * Why the table lives here and not in `utils/power-connectors.ts`:\n * `POWER_CONNECTORS` is the *rendering* table — it drives port geometry and\n * colour for the connector types the renderer can draw (C13/C14/C19/C20/AC).\n * The validation catalogue needs the high-temperature C15/C16 and C21/C22\n * families, which have no rendering entry yet, plus a directed mate table and\n * a region axis that mean nothing to the renderer. Amperages come from the\n * region table here first, falling back to `POWER_CONNECTORS` for types it\n * does not list, so the two never disagree on shared types.\n */\n\nimport { POWER_CONNECTORS } from \"../utils/power-connectors\";\n\n/** Region a connector's rating is quoted under. @public */\nexport type PowerConnectorRegion = \"IEC\" | \"UL\";\n\n/**\n * Ratings are region-dependent (C13 = 10 A IEC / 15 A UL, C19 = 16 A IEC /\n * 20 A NA), so the rating rules take a `region` option rather than hardcoding\n * one set: `config.defaults.region` for every rule, overridable per rule via\n * `config.rules[id].options.region`. NEMA (5-15P, L5-30P, L6-30P, L21-30P) is a documented follow-up.\n * @public\n */\nexport const IEC_60320_AMPERAGE: Record<\n  string,\n  Partial<Record<PowerConnectorRegion, number>>\n> = {\n  C13: { IEC: 10, UL: 15 },\n  C14: { IEC: 10, UL: 15 },\n  C15: { IEC: 10, UL: 15 },\n  C16: { IEC: 10, UL: 15 },\n  C19: { IEC: 16, UL: 20 },\n  C20: { IEC: 16, UL: 20 },\n  C21: { IEC: 16, UL: 20 },\n  C22: { IEC: 16, UL: 20 },\n};\n\n/** Inlets are the male, equipment-mounted half. @public */\nexport const IEC_60320_INLETS = [\"C14\", \"C16\", \"C20\", \"C22\"] as const;\n/** Connectors are the female, cord-mounted half. @public */\nexport const IEC_60320_CONNECTORS = [\"C13\", \"C15\", \"C19\", \"C21\"] as const;\n\n/**\n * Directed mate table: which cord connectors each inlet accepts.\n *\n * - `C14 ← C13, C15` — the high-temperature C15 is backward-compatible downward.\n * - `C16 ← C15` only; **`C16 ← C13` is rejected** (the C16 keying ridge).\n * - `C20 ← C19, C21` — the high-temperature C21 is backward-compatible\n *   downward, exactly like C15 → C14.\n * - `C22 ← C21` only; **`C22 ← C19` is rejected** (the C22 keying ridge).\n * - Cross-family `C13 ↔ C20` and `C19 ↔ C14` are hard errors: the 10 A / 16 A\n *   envelopes are deliberately keyed apart.\n * @public\n */\nexport const IEC_60320_MATES: Record<string, readonly string[]> = {\n  C14: [\"C13\", \"C15\"],\n  C16: [\"C15\"],\n  C20: [\"C19\", \"C21\"],\n  C22: [\"C21\"],\n};\n\n/** Role of a connector designation, or `unknown` for unmodelled types. @public */\nexport function getConnectorRole(\n  type: string,\n): \"inlet\" | \"connector\" | \"unknown\" {\n  const upper = type.toUpperCase();\n  if ((IEC_60320_INLETS as readonly string[]).includes(upper)) return \"inlet\";\n  if ((IEC_60320_CONNECTORS as readonly string[]).includes(upper))\n    return \"connector\";\n  return \"unknown\";\n}\n\n/**\n * Rated amperage for a connector designation. The region table above is\n * consulted first, because it is the only one that knows the IEC/UL split;\n * the renderer's region-agnostic `POWER_CONNECTORS` entry is the fallback for\n * types the region table does not list. Returns `undefined` for unmodelled\n * types (e.g. `AC` under a non-IEC region) rather than guessing.\n */\nexport function getConnectorAmperage(\n  type: string,\n  region: PowerConnectorRegion = \"IEC\",\n): number | undefined {\n  const upper = type.toUpperCase();\n  const regional = IEC_60320_AMPERAGE[upper]?.[region];\n  if (typeof regional === \"number\") return regional;\n  const rendered = POWER_CONNECTORS[upper];\n  return typeof rendered?.amperage === \"number\" ? rendered.amperage : undefined;\n}\n\n/** Whether an IEC 60320 pairing is mechanically valid. */\nexport function isValidIecMate(inlet: string, connector: string): boolean {\n  const accepted = IEC_60320_MATES[inlet.toUpperCase()];\n  if (!accepted) return true; // unmodelled inlet — do not invent a failure\n  return accepted.includes(connector.toUpperCase());\n}\n","import type { NetworkEdge, NetworkNode } from '../types';\nimport { evaluateFibreConnectionRule, isFibreConnection } from '../utils/connection-rules';\nimport { normalizeDirectionalHandleId } from '../utils/handle-ids';\nimport type { EndpointMetadata, PowerEndpointRole } from '../utils/endpoint-metadata';\nimport { getEndpointMetadata } from '../utils/endpoint-metadata';\nimport { getConnectorRole, isValidIecMate } from '../validation/connectors';\nimport {\n  getRackMountMode,\n  getVisibleFacesForMountMode,\n  isSplitFaceRackLayout,\n} from '../utils/rack-layout';\nimport { getDefaultDeviceFace } from '../utils/render-ports';\nimport { findEndpointOccupants } from './endpoint-occupancy';\n\nexport type ViewFace = 'front' | 'rear';\n\nexport interface NetworkDiagramState {\n  nodes: NetworkNode[];\n  edges: NetworkEdge[];\n  debug?: boolean;\n}\n\nexport interface ValidateConnectionParams {\n  source: string | null;\n  target: string | null;\n  sourceHandle?: string | null;\n  targetHandle?: string | null;\n  /** Optional edge id to ignore during occupancy checks (used for reconnect). */\n  ignoreEdgeId?: string;\n}\n\n/**\n * Structured rejection codes on {@link ValidationResult}. `invalid` covers\n * every non-occupancy failure (kind mismatch, connector incompatibility,\n * power semantics, fibre rules). `occupied` means the only failure is\n * endpoint capacity — the connection plan may then apply documented\n * replacement. Custom validators simply omit `code`; their rejections are\n * treated as `invalid` and are never bypassed to reach replacement.\n * @public\n */\nexport type ConnectionRejectionCode = 'invalid' | 'occupied';\n\nexport interface ValidationResult {\n  valid: boolean;\n  reason?: string;\n  /**\n   * Optional structured code; only `defaultValidateConnection` emits\n   * it today (`occupied` for capacity failures). Absent always reads as\n   * `invalid`.\n   */\n  code?: ConnectionRejectionCode;\n}\n\nexport interface NetworkDiagramOptions {\n  validateConnection?: (params: ValidateConnectionParams, ctx: NetworkDiagramState) => ValidationResult;\n}\n\nfunction hasCompatibilityIntersection(sourceCompatibility: string[], targetCompatibility: string[]): boolean {\n  if (sourceCompatibility.length === 0 || targetCompatibility.length === 0) {\n    return true;\n  }\n\n  const targetSet = new Set(targetCompatibility);\n  return sourceCompatibility.some((token) => targetSet.has(token));\n}\n\n/**\n * Node types that can act as the source side of a power connection. A power\n * edge must touch at least one of these — a device-to-device power edge is\n * meaningless. Extensible alongside `rack-device-types.ts`.\n */\nexport const POWER_SOURCE_NODE_TYPES = ['vertical-pdu', 'distribution-board'] as const;\n\nconst powerSourceNodeTypeSet = new Set<string>(POWER_SOURCE_NODE_TYPES);\n\n/** Whether a node type can source power (PDU or distribution board). */\nexport function isPowerSourceNodeType(nodeType: string | undefined): boolean {\n  return typeof nodeType === 'string' && powerSourceNodeTypeSet.has(nodeType);\n}\n\n/**\n * The message a branch circuit landing in the wrong place gets. Stated once so\n * the interactive validator and PWR-A13 say exactly the same thing.\n * @public\n */\nexport const CIRCUIT_TERMINATION_REASON =\n  'Branch circuits terminate on a PDU inlet — feed devices from PDU outlets';\n\n/**\n * The message a cord landing on a PDU inlet gets. Daisy-chaining strips fails\n * the IEC 60320 mate table anyway (an inlet is not a cord connector), but the\n * mate reason would be cryptic — this names the actual mistake.\n * @public\n */\nexport const PDU_INLET_FEED_REASON =\n  'A PDU inlet takes a branch circuit from a breaker way — power strips are not daisy-chained';\n\n/**\n * The message wiring two breaker ways together gets. Stated once so the\n * interactive validator and PWR-A13 say exactly the same thing.\n * @public\n */\nexport const WAY_TO_WAY_REASON =\n  'Two breaker ways cannot be wired together — a branch circuit runs from a way to a PDU inlet';\n\n/**\n * The message a cord between two outlets gets. Stated once so the interactive\n * validator and PWR-A13 say exactly the same thing.\n * @public\n */\nexport const OUTLET_TO_OUTLET_REASON =\n  'Both ends are power outlets — a cord runs from an outlet to a device inlet';\n\n/**\n * Real-world power connection semantics, applied before the connector\n * compatibility screen so the reason names the wiring mistake rather than a\n * connector mismatch further down the chain.\n *\n * Two connection kinds exist and they do not mix:\n *\n * - **circuit** — `circuit-source` (breaker way) → `circuit-sink` (PDU/board\n *   inlet). Fixed wiring: IEC 60309, hardwired, C20, NEMA locking.\n * - **cord** — `cord-source` (PDU outlet / device power outlet) →\n *   `cord-sink` (device power inlet), under the IEC 60320 mate table.\n *\n * `unknown` endpoints (an unmodelled handle, a legacy ad-hoc id) are never\n * judged here: the function returns `undefined` and the existing kind and\n * compatibility screens decide. That is what keeps every graph authored before\n * the inlet model valid.\n */\nfunction evaluatePowerConnectionSemantics(\n  sourceMetadata: EndpointMetadata,\n  targetMetadata: EndpointMetadata,\n): ValidationResult | undefined {\n  const sourceRole = sourceMetadata.powerRole;\n  const targetRole = targetMetadata.powerRole;\n  if (sourceRole === 'unknown' || targetRole === 'unknown') return undefined;\n\n  const roles: PowerEndpointRole[] = [sourceRole, targetRole];\n  const has = (role: PowerEndpointRole): boolean => roles.includes(role);\n  const other = (role: PowerEndpointRole): PowerEndpointRole =>\n    sourceRole === role ? targetRole : sourceRole;\n\n  if (has('circuit-source')) {\n    const far = other('circuit-source');\n    if (far === 'circuit-sink') return undefined; // way -> PDU/board inlet\n    if (far === 'circuit-source') {\n      return { valid: false, reason: WAY_TO_WAY_REASON };\n    }\n    return { valid: false, reason: CIRCUIT_TERMINATION_REASON };\n  }\n\n  if (has('circuit-sink')) {\n    // Nothing but a way may feed an inlet; the way case already returned.\n    return { valid: false, reason: PDU_INLET_FEED_REASON };\n  }\n\n  if (sourceRole === 'cord-source' && targetRole === 'cord-source') {\n    return { valid: false, reason: OUTLET_TO_OUTLET_REASON };\n  }\n\n  // Two device inlets: already rejected by the power-source-endpoint screen\n  // below, which owns that message. Nothing to add here.\n  if (sourceRole === targetRole) return undefined;\n\n  return evaluateCordMate(\n    sourceRole === 'cord-source' ? sourceMetadata : targetMetadata,\n    sourceRole === 'cord-sink' ? sourceMetadata : targetMetadata,\n  );\n}\n\n/**\n * IEC 60320 mate check for a cord: the outlet supplies the cord connector, the\n * device supplies the inlet. Only judged when both ends resolve to a modelled\n * connector on the expected half of the pairing — an unmodelled connector is\n * skipped rather than guessed at, exactly as PWR-A05 does.\n */\nfunction evaluateCordMate(\n  outlet: EndpointMetadata,\n  deviceInlet: EndpointMetadata,\n): ValidationResult | undefined {\n  const cord = outlet.connectorType?.toUpperCase();\n  const inlet = deviceInlet.connectorType?.toUpperCase();\n  if (!cord || !inlet) return undefined;\n  if (getConnectorRole(cord) !== 'connector' || getConnectorRole(inlet) !== 'inlet') {\n    return undefined;\n  }\n  if (isValidIecMate(inlet, cord)) return undefined;\n  return {\n    valid: false,\n    reason: `${cord} cord will not mate with a ${inlet} inlet — fit the correct IEC 60320 cord`,\n  };\n}\n\nexport function createNodeMap(nodes: NetworkNode[]): Map<string, NetworkNode> {\n  const nodesById = new Map<string, NetworkNode>();\n  for (const node of nodes) {\n    nodesById.set(node.id, node);\n  }\n  return nodesById;\n}\n\nexport function defaultValidateConnection(\n  params: ValidateConnectionParams,\n  ctx: NetworkDiagramState,\n  nodesById = createNodeMap(ctx.nodes),\n): ValidationResult {\n  if (!params.source || !params.target || !params.sourceHandle || !params.targetHandle) return { valid: true };\n\n  const sourceNode = nodesById.get(params.source);\n  const targetNode = nodesById.get(params.target);\n  const fiberConnection = isFibreConnection(sourceNode, targetNode);\n\n  if (!fiberConnection) {\n    const sourceMetadata = getEndpointMetadata(sourceNode, params.sourceHandle);\n    const targetMetadata = getEndpointMetadata(targetNode, params.targetHandle);\n    const sourceKind = sourceMetadata.kind;\n    const targetKind = targetMetadata.kind;\n\n    if (sourceKind !== 'unknown' && targetKind !== 'unknown' && sourceKind !== targetKind) {\n      return { valid: false, reason: `Cannot connect ${sourceKind} handle to ${targetKind} handle` };\n    }\n\n    if (sourceKind === 'power' && targetKind === 'power') {\n      const semantics = evaluatePowerConnectionSemantics(sourceMetadata, targetMetadata);\n      if (semantics) return semantics;\n    }\n\n    if (!hasCompatibilityIntersection(sourceMetadata.compatibility, targetMetadata.compatibility)) {\n      return {\n        valid: false,\n        reason: `Incompatible endpoints: ${params.sourceHandle} does not match ${params.targetHandle}`,\n      };\n    }\n\n    const isPowerConnection = sourceKind === 'power' && targetKind === 'power';\n    const hasPowerSourceEndpoint =\n      isPowerSourceNodeType(sourceNode?.type) || isPowerSourceNodeType(targetNode?.type);\n    if (isPowerConnection && !hasPowerSourceEndpoint) {\n      return {\n        valid: false,\n        reason: 'Power connections must include a PDU or distribution board endpoint',\n      };\n    }\n  }\n\n  const sourceOccupants = findEndpointOccupants(\n    ctx.edges,\n    params.source,\n    sourceNode,\n    params.sourceHandle,\n    params.ignoreEdgeId,\n  );\n  const targetOccupants = findEndpointOccupants(\n    ctx.edges,\n    params.target,\n    targetNode,\n    params.targetHandle,\n    params.ignoreEdgeId,\n  );\n  const sourceHandleAlreadyConnected = sourceOccupants.length > 0;\n  const targetHandleAlreadyConnected = targetOccupants.length > 0;\n\n  if (sourceHandleAlreadyConnected || targetHandleAlreadyConnected) {\n    if (!fiberConnection) {\n      if (sourceHandleAlreadyConnected) {\n        return { valid: false, code: 'occupied', reason: `Source handle ${params.sourceHandle} on node ${params.source} is already connected` };\n      }\n      return { valid: false, code: 'occupied', reason: `Target handle ${params.targetHandle} on node ${params.target} is already connected` };\n    }\n  }\n\n  if (!fiberConnection) {\n    return { valid: true };\n  }\n\n  return evaluateFibreConnectionRule(\n    sourceNode,\n    params.sourceHandle,\n    targetNode,\n    params.targetHandle,\n  );\n}\n\nexport function validateNetworkConnection(\n  params: ValidateConnectionParams,\n  ctx: NetworkDiagramState,\n  options: NetworkDiagramOptions = {},\n): ValidationResult {\n  const fn = options.validateConnection ?? defaultValidateConnection;\n  if (fn === defaultValidateConnection) {\n    return defaultValidateConnection(params, ctx);\n  }\n  return fn(params, ctx);\n}\n\n// Determine which face a given handle belongs to, based on node data\nexport function getHandleSide(handleId: string, node: NetworkNode | undefined): ViewFace | undefined {\n  if (!node) {\n    return undefined;\n  }\n  const face = getEndpointMetadata(node, handleId).face;\n  return face === 'front' || face === 'rear' ? face : undefined;\n}\n\n/** Compute edge z-index from rack face visibility (pure; reads nodesById only). */\nexport function computeEdgeZIndex(\n  edge: NetworkEdge,\n  nodesById: Map<string, NetworkNode>,\n): number {\n  const getVisibleFaces = (node: NetworkNode): ViewFace[] => {\n    const parentId = node.parentId;\n    const parentRack = parentId ? nodesById.get(parentId) : undefined;\n    if (parentRack?.type !== 'rack') {\n      return [(node.data?.face as ViewFace) || getDefaultDeviceFace(node.data)];\n    }\n    if (parentRack && isSplitFaceRackLayout(parentRack.data)) {\n      return getVisibleFacesForMountMode(getRackMountMode(node.data));\n    }\n\n    const deviceView: ViewFace =\n      (node.data?.face as ViewFace) || getDefaultDeviceFace(node.data);\n    return [deviceView];\n  };\n\n  const resolveEndpointVisibility = (nodeId: string, handle: string | null | undefined): number | undefined => {\n    const node = nodesById.get(nodeId);\n    const isRackDeviceNode =\n      !!node &&\n      (node.type === 'device' || node.type === 'server' || node.type === 'switch' || node.type === 'router');\n    if (!isRackDeviceNode || !node) {\n      return undefined;\n    }\n\n    const normalizedDeviceType = node.data?.deviceType?.toLowerCase().replace(/_/g, '-');\n    if (normalizedDeviceType === 'patch-panel') {\n      return undefined;\n    }\n\n    const visibleFaces = getVisibleFaces(node);\n    const side = getHandleSide(handle || '', node);\n    if (side) {\n      return visibleFaces.includes(side) ? 1000 : 500;\n    }\n\n    const normalizedHandle = normalizeDirectionalHandleId(handle);\n    const handleLc = normalizedHandle.toLowerCase();\n    const isPowerHandle = handleLc.startsWith('pwr') || handleLc.startsWith('power');\n    const isStandardNetworkHandle =\n      handleLc.startsWith('net-') ||\n      handleLc.startsWith('port-') ||\n      handleLc.startsWith('uplink-') ||\n      handleLc.includes('fiber') ||\n      handleLc.includes('ports');\n    const isCustomPortName =\n      /^[A-Z0-9]+-\\d+$/i.test(normalizedHandle) &&\n      !handleLc.startsWith('pwr') &&\n      !handleLc.startsWith('power');\n    const isNetworkHandle = isStandardNetworkHandle || isCustomPortName;\n\n    if (isPowerHandle) {\n      return visibleFaces.includes('rear') ? 1000 : 500;\n    }\n    if (isNetworkHandle) {\n      return visibleFaces.includes('front') ? 1000 : 500;\n    }\n\n    return undefined;\n  };\n\n  const targetVisibility = resolveEndpointVisibility(edge.target, edge.targetHandle);\n  const sourceVisibility = resolveEndpointVisibility(edge.source, edge.sourceHandle);\n  // A connection is visible only when both face-scoped endpoints are visible.\n  return Math.min(targetVisibility ?? 1000, sourceVisibility ?? 1000);\n}\n","import type { NetworkNode } from '../types';\nimport { getEndpointMetadata, type EndpointConnectionKind } from './endpoint-metadata';\nimport { isPowerSourceNodeType } from '../core/connection-validation';\n\n/**\n * Classify a handle on a node as power, network, or unknown.\n * Used for validation and connection handling.\n */\nexport function getEndpointConnectionKind(\n  node?: NetworkNode,\n  handle?: string | null\n): EndpointConnectionKind {\n  if (!node || !handle) {\n    return 'unknown';\n  }\n  return getEndpointMetadata(node, handle).kind;\n}\n\n/**\n * Returns true when the handle on the node is a power endpoint.\n * Used for power connection handling and edge replacement.\n */\nexport function isPowerEndpointHandle(\n  node?: NetworkNode,\n  handle?: string | null\n): boolean {\n  return getEndpointConnectionKind(node, handle) === 'power';\n}\n\n/**\n * Whether a resolved endpoint pair forms a power edge: both handles classify\n * as power and at least one node is a power-source type (PDU or distribution\n * board). Connect and reconnect share this so edge typing agrees with the\n * power-source requirement in `defaultValidateConnection` — a board way\n * feeding another board's incomer is power, not fibre.\n */\nexport function isPowerEdgeEndpoints(\n  sourceNode?: NetworkNode,\n  sourceHandle?: string | null,\n  targetNode?: NetworkNode,\n  targetHandle?: string | null\n): boolean {\n  if (\n    !isPowerEndpointHandle(sourceNode, sourceHandle) ||\n    !isPowerEndpointHandle(targetNode, targetHandle)\n  ) {\n    return false;\n  }\n  return (\n    isPowerSourceNodeType(sourceNode?.type) ||\n    isPowerSourceNodeType(targetNode?.type)\n  );\n}\n","/** Spacing contract for image-backed port markers. The caller resolves the\n * rendered image frame and passes the resulting port centres in face pixels. */\nexport interface PortCenter { x: number; y: number }\nexport interface PortDimensions { width: number; height: number }\n\nfunction clampNumber(value: number, min: number, max: number): number {\n  return Math.max(min, Math.min(max, value));\n}\n\nexport function resolvePortSpacingDimensions(\n  centers: readonly PortCenter[],\n  baseDimensions: PortDimensions,\n  imageBacked: boolean,\n): PortDimensions {\n  if (centers.length < 2) {\n    return baseDimensions;\n  }\n\n  let minHorizontalGap = Number.POSITIVE_INFINITY;\n  let minVerticalGap = Number.POSITIVE_INFINITY;\n  const rowTolerance = Math.max(baseDimensions.height * 0.75, 3);\n  const columnTolerance = Math.max(baseDimensions.width * 0.75, 3);\n\n  for (let index = 0; index < centers.length; index += 1) {\n    const current = centers[index];\n    for (\n      let candidateIndex = index + 1;\n      candidateIndex < centers.length;\n      candidateIndex += 1\n    ) {\n      const candidate = centers[candidateIndex];\n      const deltaX = Math.abs(current.x - candidate.x);\n      const deltaY = Math.abs(current.y - candidate.y);\n\n      if (deltaY <= rowTolerance && deltaX > 0) {\n        minHorizontalGap = Math.min(minHorizontalGap, deltaX);\n      }\n\n      if (deltaX <= columnTolerance && deltaY > 0) {\n        minVerticalGap = Math.min(minVerticalGap, deltaY);\n      }\n    }\n  }\n\n  const horizontalPaddingPx = imageBacked\n    ? clampNumber(baseDimensions.width * 0.65, 2.5, 4.5)\n    : 2;\n  const verticalPaddingPx = imageBacked\n    ? clampNumber(baseDimensions.height * 0.95, 5, 7.5)\n    : 2;\n  const maxWidth = imageBacked\n    ? baseDimensions.width * 0.9\n    : baseDimensions.width;\n  const width = Number.isFinite(minHorizontalGap)\n    ? clampNumber(\n        minHorizontalGap - horizontalPaddingPx,\n        imageBacked ? 2.5 : 3,\n        maxWidth,\n      )\n    : maxWidth;\n  const maxHeight = imageBacked\n    ? Math.min(baseDimensions.height * 0.48, width * 0.88)\n    : baseDimensions.height;\n  const height = Number.isFinite(minVerticalGap)\n    ? clampNumber(\n        minVerticalGap - verticalPaddingPx,\n        imageBacked ? 1.5 : 3,\n        maxHeight,\n      )\n    : maxHeight;\n\n  return {\n    width,\n    height,\n  };\n}\n","import type { PortType } from '../types';\n\nexport type PortVisualKind =\n  | 'generic'\n  | 'rj45'\n  | 'sfp'\n  | 'qsfp'\n  | 'osfp'\n  | 'console'\n  | 'usb'\n  | 'vga'\n  | 'serial'\n  | 'power_c13'\n  | 'power_c19'\n  | 'power_ac';\n\nexport interface PortVisualInput {\n  type?: PortType | string;\n  label?: string;\n  id?: string;\n  group?: string;\n  sourceTypeLabel?: string;\n  connectorType?: string;\n}\n\nexport interface PortVisualDimensions {\n  width: number;\n  height: number;\n  borderRadius: string;\n}\n\nconst STANDARD_PORT_VISUAL_DIMENSIONS: Record<Exclude<PortVisualKind, 'generic'>, PortVisualDimensions> = {\n  rj45: { width: 8, height: 4, borderRadius: '2px' },\n  sfp: { width: 8, height: 5, borderRadius: '2px' },\n  qsfp: { width: 12, height: 6, borderRadius: '2px' },\n  osfp: { width: 15, height: 9, borderRadius: '3px' },\n  console: { width: 8, height: 4, borderRadius: '2px' },\n  usb: { width: 7, height: 4, borderRadius: '2px' },\n  vga: { width: 10, height: 5, borderRadius: '1px' },\n  serial: { width: 9, height: 5, borderRadius: '1px' },\n  power_c13: { width: 10, height: 7, borderRadius: '0px' },\n  power_c19: { width: 12, height: 10, borderRadius: '3px' },\n  power_ac: { width: 10, height: 7, borderRadius: '2px' },\n};\n\nfunction buildPortVisualSearchText(input: PortVisualInput): string {\n  return [\n    input.group,\n    input.sourceTypeLabel,\n    input.label,\n    input.id,\n    input.connectorType,\n  ]\n    .filter((value): value is string => typeof value === 'string' && value.trim().length > 0)\n    .join(' ')\n    .toLowerCase();\n}\n\nexport function resolvePortVisualKind(input: PortVisualInput): PortVisualKind {\n  const type = String(input.type ?? '').toLowerCase();\n  const label = String(input.label ?? '').toLowerCase();\n  const text = buildPortVisualSearchText(input);\n\n  if (type === 'power_c19') {\n    return 'power_c19';\n  }\n  if (type === 'power_c13') {\n    return 'power_c13';\n  }\n  if (type === 'power_ac') {\n    return 'power_ac';\n  }\n  if (type === 'usb') {\n    return 'usb';\n  }\n  if (type === 'vga') {\n    return 'vga';\n  }\n  if (type === 'serial') {\n    return 'serial';\n  }\n\n  if (/vga|hd-?15|db-?15|d-sub/.test(text)) {\n    return 'vga';\n  }\n\n  if (/serial|rs-?232|db-?9|de-?9|com\\b/.test(text)) {\n    return 'serial';\n  }\n\n  if (type === 'rj45') {\n    return 'rj45';\n  }\n\n  // Authoritative port type: RJ45-style blocks even when detection left cage-style labels\n  // (e.g. et-/xe- names or \"QSFP\" hints) on a port the user reclassified as Ethernet/management.\n  if (type === 'ethernet' || type === 'mgmt') {\n    return 'rj45';\n  }\n\n  if (/osfp/.test(text)) {\n    return 'osfp';\n  }\n\n  // Speed grades are one visual family: QSFP+/28/100/1000/DD → qsfp, SFP+/28/50/56 → sfp.\n  if (\n    /qsfp(?:\\+|[- ]?dd|\\d{2,4})?|xfp/.test(text) ||\n    /^et-/.test(label) ||\n    /^ethernet\\d+\\/\\d+$/.test(label)\n  ) {\n    return 'qsfp';\n  }\n\n  if (\n    /sfp(?:\\+|\\d{2,4})?|cfp|lc(?:\\s+duplex)?|coupler|fibre\\s*port|fiber\\s*port/.test(text) ||\n    /^(xe|ge|fe|te)-/.test(label)\n  ) {\n    return 'sfp';\n  }\n\n  if (/rj45|8p8c/.test(text)) {\n    return 'rj45';\n  }\n\n  if (type === 'console') {\n    return 'console';\n  }\n\n  if (type === 'fiber') {\n    return 'sfp';\n  }\n\n  return 'generic';\n}\n\nexport function getStandardPortVisualDimensions(\n  inputOrKind: PortVisualInput | PortVisualKind,\n): PortVisualDimensions | undefined {\n  const kind =\n    typeof inputOrKind === 'string'\n      ? inputOrKind\n      : resolvePortVisualKind(inputOrKind);\n\n  if (kind === 'generic') {\n    return undefined;\n  }\n\n  return STANDARD_PORT_VISUAL_DIMENSIONS[kind];\n}\n","import type React from \"react\";\nimport { Position } from \"@xyflow/react\";\nimport type { Port, PortBlock, PortCableType, PortPosition } from \"../types\";\nimport type { DeviceImageFrame } from \"./device-image-layout\";\nimport { getDeviceConnectorType, getPowerPortStyle } from \"./power-connectors\";\nimport { resolvePortSpacingDimensions } from \"./device-port-spacing\";\nimport { getStandardPortVisualDimensions, resolvePortVisualKind } from \"./port-visuals\";\nimport { normalizePortPositionInput } from \"./port-position\";\nimport { buildPortHandleId, isPowerPortType } from \"./handle-ids\";\n\nexport type DeviceFace = \"front\" | \"rear\";\nexport type HandleRole = \"source\" | \"target\";\n\ninterface HandlePlacement {\n  position: Position;\n  style: React.CSSProperties;\n}\n\ntype PortSide = NonNullable<PortPosition[\"side\"]>;\n\ninterface PortHitExpansion {\n  top: number;\n  right: number;\n  bottom: number;\n  left: number;\n}\n\nexport type PortHandleStyle = React.CSSProperties & {\n  \"--rn-hit-expand-top\"?: string;\n  \"--rn-hit-expand-right\"?: string;\n  \"--rn-hit-expand-bottom\"?: string;\n  \"--rn-hit-expand-left\"?: string;\n};\n\nconst DISCONNECTED_PORT_COLOR = \"var(--rn-status-unknown, #6b7280)\";\nexport const DISCONNECTED_PORT_BORDER_COLOR = \"var(--rn-border-subtle, #374151)\";\nconst DEFAULT_CONNECTED_PORT_COLOR = \"var(--rn-status-ok, #10b981)\";\nexport const FILL_BACKGROUND_SIZE = \"100% 100%\";\nconst DEVICE_FACE_BORDER_PX = 1;\n\nconst CONNECTED_CABLE_COLORS: Record<PortCableType, string> = {\n  smf: \"#fbbf24\",\n  cat6: \"#8b5cf6\",\n  om3: \"#06b6d4\",\n  om4: \"#8b5cf6\",\n  om5: \"#84cc16\",\n  cat5e: \"#3b82f6\",\n  cat6a: \"#ef4444\",\n  cat7: \"#f97316\",\n  fiber: \"#3b82f6\",\n  ethernet: \"#10b981\",\n};\n\nexport function isPowerPort(port: Port): boolean {\n  return isPowerPortType(port.type);\n}\n\nexport function resolvePowerConnectorType(port: Port, label: string): string {\n  if (\n    typeof port.connectorType === \"string\" &&\n    port.connectorType.trim().length > 0\n  ) {\n    return port.connectorType;\n  }\n\n  const normalizedType = String(port.type ?? \"\").toLowerCase();\n  if (normalizedType.includes(\"c19\") || normalizedType.includes(\"c20\")) {\n    return \"C19\";\n  }\n  if (normalizedType.includes(\"c13\") || normalizedType.includes(\"c14\")) {\n    return \"C13\";\n  }\n  if (normalizedType.includes(\"power_ac\")) {\n    return \"AC\";\n  }\n\n  return getDeviceConnectorType(label);\n}\n\nexport function getHandleId(portBlockName: string, port: Port): string {\n  return buildPortHandleId(portBlockName, port);\n}\n\nfunction getConnectedPortColor(port: Port): string {\n  if (isPowerPort(port)) {\n    return port.type === \"power_c19\"\n      ? \"#fbbf24\"\n      : \"var(--rn-status-warn, #f59e0b)\";\n  }\n  if (port.type === \"console\" || port.type === \"mgmt\") {\n    return \"#8b5cf6\";\n  }\n  if (port.type === \"usb\") {\n    return DEFAULT_CONNECTED_PORT_COLOR;\n  }\n  if (port.cableType) {\n    return (\n      CONNECTED_CABLE_COLORS[port.cableType] ?? DEFAULT_CONNECTED_PORT_COLOR\n    );\n  }\n  return DEFAULT_CONNECTED_PORT_COLOR;\n}\n\nexport function getPortColor(port: Port): string {\n  if (!port.connected) {\n    return DISCONNECTED_PORT_COLOR;\n  }\n  return getConnectedPortColor(port);\n}\n\n/** Normalize authoring `Port.position` (object or semantic string) before field access. */\nexport function resolvePortPosition(port: Port): PortPosition | undefined {\n  return normalizePortPositionInput(port.position);\n}\n\nexport function getPortBlocksSignature(portBlocks: PortBlock[]): string {\n  return portBlocks\n    .map((block) => {\n      const portsSignature = block.ports\n        .map((port) => {\n          const position = resolvePortPosition(port);\n          const positionSignature = position\n            ? [\n                position.side ?? \"\",\n                position.offsetPercent ?? \"\",\n                position.offsetPx ?? \"\",\n                position.xPercent ?? \"\",\n                position.yPercent ?? \"\",\n              ].join(\":\")\n            : \"\";\n          return [\n            port.id ?? \"\",\n            port.label,\n            port.type ?? \"\",\n            port.connected ? \"1\" : \"0\",\n            port.cableType ?? \"\",\n            port.connectorType ?? \"\",\n            positionSignature,\n          ].join(\":\");\n        })\n        .join(\",\");\n      return `${block.name}[${portsSignature}]`;\n    })\n    .join(\"|\");\n}\n\nexport function getBaseHandleStyle(\n  portWidth: number,\n  portHeight: number,\n  portColor: string,\n  borderColor: string,\n): React.CSSProperties {\n  const enforceMinSize = portWidth <= 3 || portHeight <= 3;\n  const borderWidthPx = enforceMinSize ? 0 : 0.5;\n  return {\n    width: `${portWidth}px`,\n    height: `${portHeight}px`,\n    minWidth: enforceMinSize ? `${portWidth}px` : undefined,\n    minHeight: enforceMinSize ? `${portHeight}px` : undefined,\n    background: portColor,\n    border: `${borderWidthPx}px solid ${borderColor}`,\n    boxSizing: \"border-box\",\n    borderRadius: \"1px\",\n    cursor: \"pointer\",\n    zIndex: 2,\n  };\n}\n\nexport function getGenericPortDimensions(faceRegionWidth: number): {\n  width: number;\n  height: number;\n} {\n  // Keep non-power connector markers visually consistent across 1U/2U/4U devices.\n  // Physical connector size does not scale with chassis height, so size by face width.\n  const width = Math.max(5, Math.min(7, Math.round(faceRegionWidth * 0.02)));\n  const height = Math.max(4, Math.min(5, Math.round(width * 0.7)));\n  return { width, height };\n}\n\nexport function isServicePort(port: Port): boolean {\n  return port.type === \"console\" || port.type === \"usb\";\n}\n\nexport function getServicePortDimensions(baseDimensions: {\n  width: number;\n  height: number;\n}): { width: number; height: number } {\n  const width = Math.max(baseDimensions.width + 1, 7);\n  return {\n    width,\n    height: Math.max(4, Math.min(5, Math.round(width * 0.6))),\n  };\n}\n\nexport function getUsbPortDimensions(baseDimensions: {\n  width: number;\n  height: number;\n}): { width: number; height: number } {\n  const width = Math.max(baseDimensions.width + 1, 7);\n  return {\n    width,\n    height: Math.max(3, Math.min(4, Math.round(width * 0.5))),\n  };\n}\n\nexport function resolvePortVisualKindForBlock(port: Port, portBlockName: string) {\n  return resolvePortVisualKind({\n    type: port.type,\n    label: port.label,\n    id: String(port.id ?? \"\"),\n    group: portBlockName,\n    sourceTypeLabel: port.sourceTypeLabel,\n    connectorType: port.connectorType,\n  });\n}\n\nexport function clampPercent(value: number): number {\n  return Math.max(0, Math.min(100, value));\n}\n\nexport function clampNumber(value: number, min: number, max: number): number {\n  return Math.max(min, Math.min(max, value));\n}\n\nexport function getAbsoluteImagePortDimensions(\n  port: Port,\n  portBlockName: string,\n  baseDimensions: { width: number; height: number },\n  absoluteCoordinateFrame?: DeviceImageFrame,\n): { width: number; height: number } {\n  const position = resolvePortPosition(port);\n  if (\n    absoluteCoordinateFrame &&\n    typeof position?.widthPercent === \"number\" &&\n    Number.isFinite(position.widthPercent) &&\n    position.widthPercent > 0 &&\n    typeof position?.heightPercent === \"number\" &&\n    Number.isFinite(position.heightPercent) &&\n    position.heightPercent > 0\n  ) {\n    const authoredWidth =\n      (absoluteCoordinateFrame.width *\n        clampPercent(position.widthPercent)) /\n      100;\n    const authoredHeight =\n      (absoluteCoordinateFrame.height *\n        clampPercent(position.heightPercent)) /\n      100;\n    return {\n      width: clampNumber(authoredWidth, 2, absoluteCoordinateFrame.width),\n      height: clampNumber(authoredHeight, 1.5, absoluteCoordinateFrame.height),\n    };\n  }\n\n  const standardDimensions = getStandardPortVisualDimensions({\n    type: port.type,\n    label: port.label,\n    id: String(port.id ?? \"\"),\n    group: portBlockName,\n    sourceTypeLabel: port.sourceTypeLabel,\n    connectorType: port.connectorType,\n  });\n  if (standardDimensions) {\n    return {\n      width: standardDimensions.width,\n      height: standardDimensions.height,\n    };\n  }\n\n  if (port.type === \"usb\") {\n    return getUsbPortDimensions(baseDimensions);\n  }\n\n  if (isServicePort(port)) {\n    return getServicePortDimensions(baseDimensions);\n  }\n\n  return baseDimensions;\n}\n\nfunction getSfpHandleHitExpansion(position: Position): PortHitExpansion {\n  switch (position) {\n    case Position.Bottom:\n      return { top: 1, right: 1, bottom: 4, left: 1 };\n    case Position.Left:\n      return { top: 1, right: 1, bottom: 1, left: 4 };\n    case Position.Right:\n      return { top: 1, right: 4, bottom: 1, left: 1 };\n    case Position.Top:\n    default:\n      return { top: 4, right: 1, bottom: 1, left: 1 };\n  }\n}\n\nexport function getPortHandleHitExpansion(\n  port: Port,\n  portBlockName: string,\n  position: Position,\n  absoluteCoordinateFrame?: DeviceImageFrame,\n): PortHitExpansion | undefined {\n  if (!absoluteCoordinateFrame || !hasAbsolutePortCoordinates(port)) {\n    return undefined;\n  }\n\n  if (resolvePortVisualKindForBlock(port, portBlockName) !== \"sfp\") {\n    return undefined;\n  }\n\n  return getSfpHandleHitExpansion(position);\n}\n\nexport function hasAbsolutePortCoordinates(port: Port): boolean {\n  const position = resolvePortPosition(port);\n  return (\n    typeof position?.xPercent === \"number\" &&\n    typeof position?.yPercent === \"number\"\n  );\n}\n\nexport function hasResolvableCustomPortPlacement(port: Port): boolean {\n  const position = resolvePortPosition(port);\n  return hasAbsolutePortCoordinates(port) || position?.side !== undefined;\n}\n\nfunction getAbsolutePortCenter(\n  port: Port,\n  faceRegionWidth: number,\n  deviceHeight: number,\n  absoluteCoordinateFrame?: DeviceImageFrame,\n): { x: number; y: number } | undefined {\n  const position = resolvePortPosition(port);\n  if (\n    typeof position?.xPercent !== \"number\" ||\n    typeof position?.yPercent !== \"number\"\n  ) {\n    return undefined;\n  }\n\n  const xPercent = clampPercent(position.xPercent);\n  const yPercent = clampPercent(position.yPercent);\n  const frameWidth = absoluteCoordinateFrame?.width ?? faceRegionWidth;\n  const frameHeight = absoluteCoordinateFrame?.height ?? deviceHeight;\n  const frameOffsetX = absoluteCoordinateFrame?.offsetX ?? 0;\n  const frameOffsetY = absoluteCoordinateFrame?.offsetY ?? 0;\n\n  return {\n    x: frameOffsetX + (frameWidth * xPercent) / 100,\n    y: frameOffsetY + (frameHeight * yPercent) / 100,\n  };\n}\n\nexport function getResolvedGenericPortDimensions(\n  portBlocks: PortBlock[],\n  faceRegionWidth: number,\n  deviceHeight: number,\n  absoluteCoordinateFrame?: DeviceImageFrame,\n): { width: number; height: number } {\n  const baseDimensions = getGenericPortDimensions(faceRegionWidth);\n  const absolutePortCenters = portBlocks.flatMap((block) =>\n    block.ports\n      .filter((port) => !isPowerPort(port))\n      .map((port) =>\n        getAbsolutePortCenter(\n          port,\n          faceRegionWidth,\n          deviceHeight,\n          absoluteCoordinateFrame,\n        ),\n      )\n      .filter(\n        (center): center is { x: number; y: number } => center !== undefined,\n      ),\n  );\n\n  return resolvePortSpacingDimensions(\n    absolutePortCenters,\n    baseDimensions,\n    Boolean(absoluteCoordinateFrame),\n  );\n}\n\nfunction mapPortSideToPosition(side: PortSide): Position {\n  switch (side) {\n    case \"top\":\n      return Position.Top;\n    case \"right\":\n      return Position.Right;\n    case \"bottom\":\n      return Position.Bottom;\n    case \"left\":\n      return Position.Left;\n    default:\n      return Position.Top;\n  }\n}\n\nfunction inferSideFromAbsolutePercent(\n  xPercent: number,\n  yPercent: number,\n): PortSide {\n  const distances: Array<{ side: PortSide; distance: number }> = [\n    { side: \"left\", distance: xPercent },\n    { side: \"right\", distance: 100 - xPercent },\n    { side: \"top\", distance: yPercent },\n    { side: \"bottom\", distance: 100 - yPercent },\n  ];\n  distances.sort((a, b) => a.distance - b.distance);\n  return distances[0].side;\n}\n\nexport function getCustomHandlePlacement(\n  port: Port,\n  portWidth: number,\n  portHeight: number,\n  faceRegionWidth: number,\n  deviceHeight: number,\n  absoluteCoordinateFrame?: DeviceImageFrame,\n  absoluteCenterYOverridePx?: number,\n  absoluteSideOverride?: PortSide,\n): HandlePlacement | undefined {\n  const position = resolvePortPosition(port);\n  if (!position) {\n    return undefined;\n  }\n\n  const hasAbsoluteCoordinates =\n    typeof position.xPercent === \"number\" &&\n    typeof position.yPercent === \"number\";\n  if (hasAbsoluteCoordinates) {\n    const xPercent = clampPercent(position.xPercent as number);\n    const yPercent = clampPercent(position.yPercent as number);\n    const side =\n      absoluteSideOverride ??\n      position.side ??\n      inferSideFromAbsolutePercent(xPercent, yPercent);\n    const frameWidth = absoluteCoordinateFrame?.width ?? faceRegionWidth;\n    const frameHeight = absoluteCoordinateFrame?.height ?? deviceHeight;\n    const frameOffsetX = absoluteCoordinateFrame?.offsetX ?? 0;\n    const frameOffsetY = absoluteCoordinateFrame?.offsetY ?? 0;\n    const minCenterX = portWidth / 2 + DEVICE_FACE_BORDER_PX;\n    const maxCenterX = Math.max(minCenterX, faceRegionWidth - portWidth / 2);\n    const minCenterY = portHeight / 2 + DEVICE_FACE_BORDER_PX;\n    const maxCenterY = Math.max(minCenterY, deviceHeight - portHeight / 2);\n    const unclampedCenterX = frameOffsetX + (frameWidth * xPercent) / 100;\n    const unclampedCenterY =\n      absoluteCenterYOverridePx ??\n      frameOffsetY + (frameHeight * yPercent) / 100;\n    const centerX = absoluteCoordinateFrame\n      ? unclampedCenterX\n      : clampNumber(unclampedCenterX, minCenterX, maxCenterX);\n    const centerY = absoluteCoordinateFrame\n      ? unclampedCenterY\n      : clampNumber(unclampedCenterY, minCenterY, maxCenterY);\n    return {\n      position: mapPortSideToPosition(side),\n      style: {\n        left: `${centerX - portWidth / 2 - DEVICE_FACE_BORDER_PX}px`,\n        top: `${centerY - portHeight / 2 - DEVICE_FACE_BORDER_PX}px`,\n        right: \"initial\",\n        bottom: \"initial\",\n        transform: \"none\",\n      },\n    };\n  }\n\n  if (!position.side) {\n    return undefined;\n  }\n\n  const side = position.side;\n  const offsetPercent =\n    typeof position.offsetPercent === \"number\"\n      ? clampPercent(position.offsetPercent)\n      : undefined;\n  const offsetPx =\n    typeof position.offsetPx === \"number\"\n      ? position.offsetPx\n      : undefined;\n  const minCenterX = portWidth / 2;\n  const maxCenterX = Math.max(minCenterX, faceRegionWidth - portWidth / 2);\n  const minCenterY = portHeight / 2;\n  const maxCenterY = Math.max(minCenterY, deviceHeight - portHeight / 2);\n\n  switch (side) {\n    case \"top\": {\n      const rawCenterX =\n        offsetPx !== undefined\n          ? offsetPx\n          : (faceRegionWidth * (offsetPercent ?? 50)) / 100;\n      const centerX = clampNumber(rawCenterX, minCenterX, maxCenterX);\n      return {\n        position: Position.Top,\n        style: {\n          top: \"0px\",\n          left: `${centerX}px`,\n          right: undefined,\n          bottom: undefined,\n          transform: \"translate(-50%, 50%)\",\n        },\n      };\n    }\n    case \"bottom\": {\n      const rawCenterX =\n        offsetPx !== undefined\n          ? offsetPx\n          : (faceRegionWidth * (offsetPercent ?? 50)) / 100;\n      const centerX = clampNumber(rawCenterX, minCenterX, maxCenterX);\n      return {\n        position: Position.Bottom,\n        style: {\n          bottom: \"0px\",\n          left: `${centerX}px`,\n          right: undefined,\n          top: undefined,\n          transform: \"translate(-50%, -50%)\",\n        },\n      };\n    }\n    case \"left\": {\n      const rawCenterY =\n        offsetPx !== undefined\n          ? offsetPx\n          : (deviceHeight * (offsetPercent ?? 50)) / 100;\n      const centerY = clampNumber(rawCenterY, minCenterY, maxCenterY);\n      return {\n        position: Position.Left,\n        style: {\n          left: \"0px\",\n          top: `${centerY}px`,\n          right: undefined,\n          bottom: undefined,\n          transform: \"translate(50%, -50%)\",\n        },\n      };\n    }\n    case \"right\": {\n      const rawCenterY =\n        offsetPx !== undefined\n          ? offsetPx\n          : (deviceHeight * (offsetPercent ?? 50)) / 100;\n      const centerY = clampNumber(rawCenterY, minCenterY, maxCenterY);\n      return {\n        position: Position.Right,\n        style: {\n          right: \"0px\",\n          top: `${centerY}px`,\n          left: undefined,\n          bottom: undefined,\n          transform: \"translate(-50%, -50%)\",\n        },\n      };\n    }\n    default:\n      return undefined;\n  }\n}\n\nfunction getDistributedRowCenterX(\n  rowIndex: number,\n  rowCount: number,\n  portWidth: number,\n  faceRegionWidth: number,\n): number {\n  const minCenterX = portWidth / 2;\n  const maxCenterX = Math.max(minCenterX, faceRegionWidth - portWidth / 2);\n  return rowCount === 1\n    ? (minCenterX + maxCenterX) / 2\n    : minCenterX + (rowIndex * (maxCenterX - minCenterX)) / (rowCount - 1);\n}\n\n/**\n * Centre x of a port that is alone in its row. The device label is centred on\n * the face, so a lone port is kept clear of it at the right-hand end of the\n * face (the same slot a single-port device uses) instead of being centred\n * over the label text.\n */\nfunction getLabelClearSinglePortCenterX(\n  portWidth: number,\n  faceRegionWidth: number,\n): number {\n  const rightPadding = Math.max(6, Math.round(faceRegionWidth * 0.04));\n  const leftPx = Math.max(\n    portWidth / 2,\n    faceRegionWidth - rightPadding - portWidth / 2,\n  );\n  return leftPx + portWidth / 2;\n}\n\nfunction getAlternatingTopBottomPlacement(\n  portIndex: number,\n  portCount: number,\n  portWidth: number,\n  faceRegionWidth: number,\n): HandlePlacement {\n  const topRow = portIndex % 2 === 0;\n  const topCount = Math.ceil(portCount / 2);\n  const bottomCount = Math.floor(portCount / 2);\n  const rowCount = Math.max(topRow ? topCount : bottomCount, 1);\n  const rowIndex = topRow\n    ? Math.floor(portIndex / 2)\n    : Math.floor((portIndex - 1) / 2);\n  const centerX =\n    rowCount === 1\n      ? getLabelClearSinglePortCenterX(portWidth, faceRegionWidth)\n      : getDistributedRowCenterX(rowIndex, rowCount, portWidth, faceRegionWidth);\n\n  if (topRow) {\n    return {\n      position: Position.Top,\n      style: {\n        transform: \"translateY(50%)\",\n        left: `${centerX - portWidth / 2}px`,\n      },\n    };\n  }\n\n  return {\n    position: Position.Bottom,\n    style: {\n      transform: \"translateY(-50%)\",\n      left: `${centerX - portWidth / 2}px`,\n    },\n  };\n}\n\nexport function getAlternatingTopStackPlacement(\n  portIndex: number,\n  portCount: number,\n  portWidth: number,\n  portHeight: number,\n  faceRegionWidth: number,\n  deviceHeight: number,\n): HandlePlacement {\n  const topRow = portIndex % 2 === 0;\n  const topCount = Math.ceil(portCount / 2);\n  const bottomCount = Math.floor(portCount / 2);\n  const rowCount = Math.max(topRow ? topCount : bottomCount, 1);\n  const rowIndex = topRow\n    ? Math.floor(portIndex / 2)\n    : Math.floor((portIndex - 1) / 2);\n  const centerX = getDistributedRowCenterX(\n    rowIndex,\n    rowCount,\n    portWidth,\n    faceRegionWidth,\n  );\n\n  const topPaddingPx = 1;\n  const rowGapPx = clampNumber(Math.round(portHeight * 0.75), 1, 4);\n  const maxTopPx = Math.max(topPaddingPx, deviceHeight - portHeight - 1);\n  const secondRowTopPx = clampNumber(\n    topPaddingPx + portHeight + rowGapPx,\n    topPaddingPx,\n    maxTopPx,\n  );\n  const topPx = topRow ? topPaddingPx : secondRowTopPx;\n\n  return {\n    position: Position.Top,\n    style: {\n      transform: \"none\",\n      left: `${centerX - portWidth / 2}px`,\n      top: `${topPx}px`,\n      right: undefined,\n      bottom: undefined,\n    },\n  };\n}\n\nexport function getFrontPowerHandlePlacement(\n  port: Port,\n  portIndex: number,\n  portCount: number,\n  deviceHeight: number,\n  label: string,\n): HandlePlacement {\n  const connectorType = resolvePowerConnectorType(port, label);\n  const powerStyle = getPowerPortStyle(connectorType);\n  const topValue = `${getAutomaticPowerPortTopPx(\n    portIndex,\n    portCount,\n    powerStyle.height,\n    deviceHeight,\n  )}px`;\n\n  return {\n    position: Position.Right,\n    style: {\n      transform: \"translateX(-50%)\",\n      top: topValue,\n      width: `${powerStyle.width}px`,\n      height: `${powerStyle.height}px`,\n      background: powerStyle.color,\n      border: `1px solid ${powerStyle.borderColor}`,\n      zIndex: 10,\n      display: \"flex\",\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      fontSize: powerStyle.fontSize,\n      color: \"var(--rn-text-on-accent, #1a1a1a)\",\n      fontFamily: \"monospace\",\n      fontWeight: \"bold\",\n      borderRadius: powerStyle.borderRadius,\n      clipPath: powerStyle.clipPath,\n    },\n  };\n}\n\n/**\n * Place automatic power connectors by their top edge so the whole connector\n * stays on the face. One- and two-port faces taller than 1U retain their\n * established 25%/75% positions when those positions fit. Denser faces use\n * all available height; connectors may overlap when their combined heights\n * physically cannot fit, but none is pushed outside the device.\n */\nfunction getAutomaticPowerPortTopPx(\n  portIndex: number,\n  portCount: number,\n  connectorHeight: number,\n  deviceHeight: number,\n): number {\n  const lastTop = Math.max(deviceHeight - connectorHeight, 0);\n\n  if (deviceHeight > 20 && portCount <= 2) {\n    const legacyTop = (deviceHeight * (25 + portIndex * 50)) / 100;\n    return clampNumber(legacyTop, 0, lastTop);\n  }\n\n  const paddingPx = Math.min(2, lastTop / 2);\n  const minTop = paddingPx;\n  const maxTop = Math.max(minTop, lastTop - paddingPx);\n  return portCount <= 1\n    ? (minTop + maxTop) / 2\n    : minTop + (portIndex * (maxTop - minTop)) / (portCount - 1);\n}\n\nexport function getFrontHandlePlacement(\n  portIndex: number,\n  portCount: number,\n  portWidth: number,\n  faceRegionWidth: number,\n): HandlePlacement {\n  if (portCount === 1) {\n    const leftPx =\n      getLabelClearSinglePortCenterX(portWidth, faceRegionWidth) -\n      portWidth / 2;\n    return {\n      position: Position.Top,\n      style: {\n        transform: \"translateY(50%)\",\n        left: `${leftPx}px`,\n        right: undefined,\n      },\n    };\n  }\n\n  return getAlternatingTopBottomPlacement(\n    portIndex,\n    portCount,\n    portWidth,\n    faceRegionWidth,\n  );\n}\n\nexport function getRearHandlePlacement(\n  port: Port,\n  portIndex: number,\n  portCount: number,\n  portWidth: number,\n  portHeight: number,\n  faceRegionWidth: number,\n  deviceHeight: number,\n  normalizedDeviceType: string,\n  label: string,\n): HandlePlacement {\n  const isPatchPanel = normalizedDeviceType === \"patch-panel\";\n  const isSwitch = normalizedDeviceType === \"switch\";\n  // Power inlets keep their IEC connector treatment on every device type — the\n  // switch/patch-panel branch would render them as data ports, whose tiny\n  // square handles let the long inlet label (\"pwr-right-1\") spill across the\n  // device body.\n  if ((isPatchPanel || isSwitch) && !isPowerPort(port)) {\n    return getAlternatingTopBottomPlacement(\n      portIndex,\n      portCount,\n      portWidth,\n      faceRegionWidth,\n    );\n  }\n\n  if (isPowerPort(port)) {\n    const connectorType = resolvePowerConnectorType(port, label);\n    const powerStyle = getPowerPortStyle(connectorType);\n    const topValue = `${getAutomaticPowerPortTopPx(\n      portIndex,\n      portCount,\n      powerStyle.height,\n      deviceHeight,\n    )}px`;\n\n    return {\n      position: Position.Left,\n      style: {\n        transform: \"translateX(50%)\",\n        top: topValue,\n        width: `${powerStyle.width}px`,\n        height: `${powerStyle.height}px`,\n        background: powerStyle.color,\n        border: `1px solid ${powerStyle.borderColor}`,\n        zIndex: 10,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        fontSize: powerStyle.fontSize,\n        color: \"var(--rn-text-on-accent, #1a1a1a)\",\n        fontFamily: \"monospace\",\n        fontWeight: \"bold\",\n        borderRadius: powerStyle.borderRadius,\n        clipPath: powerStyle.clipPath,\n      },\n    };\n  }\n\n  return {\n    position: Position.Right,\n    style: {\n      transform: \"translate(-50%, -50%)\",\n      top: `${clampNumber(\n        portCount <= 1\n          ? deviceHeight / 2\n          : portHeight / 2 +\n              (portIndex * Math.max(deviceHeight - portHeight, 0)) /\n                Math.max(portCount - 1, 1),\n        portHeight / 2,\n        Math.max(portHeight / 2, deviceHeight - portHeight / 2),\n      )}px`,\n    },\n  };\n}\n\n/**\n * The handle classes a device port mounts.\n *\n * Every non-power data port mounts a directional pair so it can be either end\n * of a cable: React Flow resolves an edge's `source` endpoint against\n * source-class handles only (even in loose mode), so a port that mounted a\n * single target-class handle could never be an edge source. Generic devices\n * (server, firewall, storage, …) therefore use the switch convention — bare\n * `<base>` is the source, `<base>-target` the target sibling — regardless of\n * `port.connected`, which is display state and must not change the handle\n * grammar. Saved edges naming the bare id keep resolving: as a source directly,\n * and as a target because loose mode also searches source-class handles.\n *\n * `isConnected` is retained for signature compatibility and no longer affects\n * the result.\n */\nexport function getHandleRoles(\n  isPower: boolean,\n  normalizedDeviceType: string,\n  face: DeviceFace,\n  _isConnected?: boolean,\n): { primary: HandleRole; secondary?: HandleRole } {\n  if (isPower) {\n    return { primary: \"target\" };\n  }\n\n  if (normalizedDeviceType === \"patch-panel\") {\n    return face === \"rear\"\n      ? { primary: \"source\", secondary: \"target\" }\n      : { primary: \"target\", secondary: \"source\" };\n  }\n\n  return { primary: \"source\", secondary: \"target\" };\n}\n","import type { DeviceFace, NetworkNode, PortBlock } from '../types';\nimport { getRackMountMode, getVisibleFacesForMountMode, isSplitFaceRackLayout } from './rack-layout';\n\n/** Shared by the renderer and connection model so bare ids name the visible face. */\nexport function getDevicePortVisibleFaces(\n  data: NetworkNode['data'],\n  ports: PortBlock[],\n  rearPorts: PortBlock[],\n): DeviceFace[] {\n  if (isSplitFaceRackLayout(data)) {\n    return getVisibleFacesForMountMode(getRackMountMode(data));\n  }\n  const hasFront = ports.length > 0 || !!data.frontImageUrl;\n  const hasRear = rearPorts.length > 0 || !!data.rearImageUrl;\n  return [data.face || (hasFront ? 'front' : hasRear ? 'rear' : 'front')];\n}\n","import type { Connection } from \"@xyflow/react\";\nimport type { NetworkNode, PortBlock } from \"../types\";\nimport {\n  applyFaceAliasHandleId,\n  normalizeDirectionalHandleId,\n  parseHandleId,\n} from \"./handle-ids\";\nimport { isPowerEndpointHandle } from \"./endpoint-kind\";\nimport { getHandleId, getHandleRoles, isPowerPort } from \"./device-port-layout\";\nimport { getResolvedPortBlocks } from \"./render-ports\";\nimport { getDevicePortVisibleFaces } from \"./device-port-faces\";\n\nexport function isSwitchLikeNode(node?: NetworkNode): boolean {\n  if (!node) {\n    return false;\n  }\n\n  if (node.type === \"switch\") {\n    return true;\n  }\n\n  if (node.type !== \"device\") {\n    return false;\n  }\n\n  const deviceType = node.data?.deviceType;\n  return (\n    typeof deviceType === \"string\" &&\n    deviceType.toLowerCase().replace(/_/g, \"-\") === \"switch\"\n  );\n}\n\n/**\n * In loose mode, dragging from target handles can yield connections where a switch\n * endpoint carries the opposite directional suffix for its side. Canonicalize to:\n * - switch source side => \"<port>\"\n * - switch target side => \"<port>-target\"\n */\nexport function normalizeSwitchPortConnection(\n  connection: Connection,\n  sourceNode?: NetworkNode,\n  targetNode?: NetworkNode,\n): Connection {\n  let sourceHandle = connection.sourceHandle;\n  let targetHandle = connection.targetHandle;\n\n  if (\n    isSwitchLikeNode(sourceNode) &&\n    sourceHandle &&\n    !isPowerEndpointHandle(sourceNode, sourceHandle)\n  ) {\n    sourceHandle = normalizeDirectionalHandleId(sourceHandle);\n  }\n\n  if (\n    isSwitchLikeNode(targetNode) &&\n    targetHandle &&\n    !isPowerEndpointHandle(targetNode, targetHandle)\n  ) {\n    targetHandle = `${normalizeDirectionalHandleId(targetHandle)}-target`;\n  }\n\n  if (\n    sourceHandle === connection.sourceHandle &&\n    targetHandle === connection.targetHandle\n  ) {\n    return connection;\n  }\n\n  return {\n    ...connection,\n    sourceHandle,\n    targetHandle,\n  };\n}\n\nfunction isPatchPanelLikeNode(node?: NetworkNode): boolean {\n  if (!node) {\n    return false;\n  }\n\n  if (node.type === \"patch-panel\") {\n    return true;\n  }\n\n  if (node.type !== \"device\") {\n    return false;\n  }\n\n  const deviceType = node.data?.deviceType;\n  return (\n    typeof deviceType === \"string\" &&\n    deviceType.toLowerCase().replace(/_/g, \"-\") === \"patch-panel\"\n  );\n}\n\ntype MountedHandleClass = \"source\" | \"target\";\n\n/** One mounted handle's entry in a face's mount table. */\ninterface MountedHandle {\n  /** The face's primary id this handle stacks under (itself for primaries). */\n  primary: string;\n  /** The directional class the handle mounts with. */\n  cls: MountedHandleClass;\n}\n\n/** Emitted handle id -> its mount, for one face's port strips. */\ntype PortMountTable = Map<string, MountedHandle>;\n\n/**\n * The handle ids a node's port strips actually mount, mirroring\n * `resolveRenderedHandleId`/`getHandleRoles` in DeviceNode: every port emits\n * either the bare `<base>` or the face-aliased `<base>--<face>` (aliases\n * appear on rear ids colliding with a front id in split view, and on ghost\n * mounts of a hidden face), plus a secondary sibling `<primary>-<cls>` where\n * {@link getHandleRoles} defines one — patch-panel and switch/router strips\n * mount directional siblings; generic and power ports mount a single class.\n */\nexport interface DevicePortMounts {\n  front: PortMountTable;\n  rear: PortMountTable;\n  /** Face whose visible handles keep a bare id when both faces share it. */\n  bareFace: \"front\" | \"rear\";\n}\n\nfunction collectPortMounts(\n  blocks: PortBlock[],\n  face: \"front\" | \"rear\",\n  normalizedDeviceType: string,\n): PortMountTable {\n  const table: PortMountTable = new Map();\n  for (const block of blocks) {\n    for (const port of block.ports) {\n      const roles = getHandleRoles(\n        isPowerPort(port),\n        normalizedDeviceType,\n        face,\n        port.connected,\n      );\n      const base = getHandleId(block.name, port);\n      for (const primary of [base, applyFaceAliasHandleId(base, face)]) {\n        table.set(primary, { primary, cls: roles.primary });\n        if (roles.secondary) {\n          table.set(`${primary}-${roles.secondary}`, {\n            primary,\n            cls: roles.secondary,\n          });\n        }\n      }\n    }\n  }\n  return table;\n}\n\n/**\n * Which node types mount port-strip handles through the `getHandleRoles`\n * grammar — every node type rendered by `DeviceNode`: `device` (any\n * deviceType), `server`, `patch-panel`, `switch`, and `router`. Other kinds\n * (splice, tube, PDU, distribution boards, …) mount handles their own way\n * and are opaque to this model.\n */\nfunction portMountDeviceType(node?: NetworkNode): string | undefined {\n  if (!node) {\n    return undefined;\n  }\n  if (\n    node.type !== \"device\" &&\n    node.type !== \"server\" &&\n    node.type !== \"patch-panel\" &&\n    node.type !== \"switch\" &&\n    node.type !== \"router\"\n  ) {\n    return undefined;\n  }\n  const deviceType = node.data?.deviceType;\n  const raw =\n    typeof deviceType === \"string\" && deviceType.length > 0\n      ? deviceType\n      : node.type;\n  return raw.toLowerCase().replace(/_/g, \"-\");\n}\n\nexport function getDevicePortMounts(\n  node?: NetworkNode,\n): DevicePortMounts | undefined {\n  const deviceType = portMountDeviceType(node);\n  if (!node || !deviceType) {\n    return undefined;\n  }\n  const { ports, rearPorts } = getResolvedPortBlocks(node.data);\n  const visibleFaces = getDevicePortVisibleFaces(node.data, ports, rearPorts);\n  return {\n    front: collectPortMounts(ports, \"front\", deviceType),\n    rear: collectPortMounts(rearPorts, \"rear\", deviceType),\n    bareFace: visibleFaces.includes(\"front\") ? \"front\" : \"rear\",\n  };\n}\n\n/**\n * Which face an emitted id mounts on, or undefined when it is not a mounted\n * port-strip id at all. An id mounted on both faces is a base authored on\n * both — the emitted alias still disambiguates: rear mounts alias `--rear`\n * on collision, front mounts alias `--front` only while ghost-hidden, so an\n * aliased id names its face and a bare ambiguous id is the visible primary\n * mount.\n */\nexport function mountFace(\n  mounts: DevicePortMounts,\n  emitted: string,\n): \"front\" | \"rear\" | undefined {\n  const onFront = mounts.front.has(emitted);\n  const onRear = mounts.rear.has(emitted);\n  if (!onFront && !onRear) {\n    return undefined;\n  }\n  if (onFront && onRear) {\n    return parseHandleId(emitted).faceAlias ?? mounts.bareFace;\n  }\n  return onFront ? \"front\" : \"rear\";\n}\n\n/**\n * The canonical mounted id `emitted` can serve `slot` as, or null when the\n * jack is mounted but cannot serve the class — e.g. a target-only device\n * port or power inlet in the source slot. An emitted id whose own class\n * already matches resolves verbatim; otherwise its directional sibling\n * `<primary>-<cls>` or its primary — whichever mounts the wanted class.\n */\nfunction resolveMountedHandle(\n  mounts: DevicePortMounts,\n  emitted: string,\n  face: \"front\" | \"rear\",\n  slot: \"source\" | \"target\",\n): string | null {\n  const table = face === \"front\" ? mounts.front : mounts.rear;\n  const mounted = table.get(emitted);\n  if (!mounted) {\n    return null;\n  }\n  if (mounted.cls === slot) {\n    return emitted;\n  }\n  const sibling = `${mounted.primary}-${slot}`;\n  if (table.has(sibling)) {\n    return sibling;\n  }\n  return table.get(mounted.primary)?.cls === slot ? mounted.primary : null;\n}\n\n/**\n * The canonical mounted id for `emitted` serving `slot` on `mounts` —\n * resolving an id to whichever face/form of it the node actually mounts —\n * or null when `emitted` is mounted but cannot serve the slot's class.\n * Returns null too for ids that are not mounted port-strip handles at all;\n * callers distinguish that case with {@link isMountedPortHandle}.\n */\nexport function resolvableMountedHandle(\n  mounts: DevicePortMounts | undefined,\n  emitted: string | null | undefined,\n  slot: \"source\" | \"target\",\n): string | null {\n  if (!mounts || !emitted) {\n    return null;\n  }\n  const face = mountFace(mounts, emitted);\n  if (!face) {\n    return null;\n  }\n  return resolveMountedHandle(mounts, emitted, face, slot);\n}\n\n/** Whether `emitted` is a mounted port-strip id on `mounts` at all. */\nexport function isMountedPortHandle(\n  mounts: DevicePortMounts | undefined,\n  emitted: string | null | undefined,\n): boolean {\n  return !!mounts && !!emitted && mountFace(mounts, emitted) !== undefined;\n}\n\nfunction getPatchPanelPortFaces(\n  node?: NetworkNode,\n): DevicePortMounts | undefined {\n  if (!node || !isPatchPanelLikeNode(node)) {\n    return undefined;\n  }\n  return getDevicePortMounts(node);\n}\n\nfunction rewritePatchPanelHandle(\n  handle: string,\n  faces: DevicePortMounts,\n  slot: \"source\" | \"target\",\n): string {\n  const face = mountFace(faces, handle);\n  if (!face) {\n    return handle;\n  }\n  return resolveMountedHandle(faces, handle, face, slot) ?? handle;\n}\n\n/**\n * A patch-panel port strip mounts two directional siblings per port — rear\n * face: source `<base>` over target `<base>-target`; front face: target\n * `<base>` over source `<base>-source` — and only the top (primary) one\n * receives pointer hits. In loose mode the grabbed handle lands in\n * `sourceHandle` whatever its class, so a front-face drag emits the\n * target-class `<base>` — an id React Flow can never resolve as a source\n * endpoint, producing an accepted edge that is persisted but never rendered.\n * Canonicalize to the sibling that resolves for each slot, mirroring\n * {@link normalizeSwitchPortConnection}:\n * - patch-panel source side => `<primary>-source` (front) / `<primary>` (rear)\n * - patch-panel target side => `<primary>` (front) / `<primary>-target` (rear)\n *\n * `<primary>` is the face's mounted primary id — the bare base or its\n * `--front`/`--rear` alias — so explicit `port.id`s and split-face aliases\n * are preserved verbatim in the rewrite.\n */\nexport function normalizePatchPanelPortConnection(\n  connection: Connection,\n  sourceNode?: NetworkNode,\n  targetNode?: NetworkNode,\n): Connection {\n  const sourceFaces = getPatchPanelPortFaces(sourceNode);\n  const targetFaces = getPatchPanelPortFaces(targetNode);\n  if (!sourceFaces && !targetFaces) {\n    return connection;\n  }\n\n  let sourceHandle = connection.sourceHandle;\n  let targetHandle = connection.targetHandle;\n\n  if (sourceFaces && sourceHandle) {\n    sourceHandle = rewritePatchPanelHandle(sourceHandle, sourceFaces, \"source\");\n  }\n\n  if (targetFaces && targetHandle) {\n    targetHandle = rewritePatchPanelHandle(targetHandle, targetFaces, \"target\");\n  }\n\n  if (\n    sourceHandle === connection.sourceHandle &&\n    targetHandle === connection.targetHandle\n  ) {\n    return connection;\n  }\n\n  return {\n    ...connection,\n    sourceHandle,\n    targetHandle,\n  };\n}\n","import type { NetworkNode } from '../types';\nimport { isFibreConnection } from '../utils/connection-rules';\nimport {\n  normalizeDirectionalHandleId,\n  parseHandleId,\n  sanitizeHandleIdForMatching,\n} from '../utils/handle-ids';\nimport { isPowerEdgeEndpoints, isPowerEndpointHandle } from '../utils/endpoint-kind';\nimport {\n  isSwitchLikeNode,\n  getDevicePortMounts,\n  mountFace,\n  type DevicePortMounts,\n} from '../utils/diagram-connection';\n\n/**\n * Endpoint identity and capacity policy — the single authority every\n * connection path shares. Previously the interactive connect handler, the\n * reconnect handler, the headless validator, and the load-time edge filter\n * each implemented occupancy with different handle keys and different\n * multi-tenant rules, so an endpoint could be \"connected\" to one path and\n * \"free\" to another.\n *\n * Identity: an endpoint is `nodeId + occupancyHandleId`, where the handle\n * resolves to the jack's mounted primary id — directional siblings\n * (`port-1-source`/`port-1-target`) are one endpoint, while `port-1--rear`\n * (the alias a rear-face jack mounts when its base id collides with a\n * front-face id) is a distinct jack with its own occupancy.\n * An edge touches the endpoint whichever side (`source`/`target`) stores the\n * node. An edge with an absent handle id has no endpoint identity and never\n * occupies.\n *\n * Capacity: most endpoints host exactly one edge. A small set are\n * multi-tenant by design — the `cable-button` trunk connector, `tube-*`\n * handles, and the `left`/`right` faces of a splice — and are reported here\n * via {@link isUnboundedEndpoint}; occupants on them never conflict.\n */\n\n/** Mount tables keyed by node object — rebuilt only when the node is replaced. */\nconst mountsCache = new WeakMap<NetworkNode, DevicePortMounts | undefined>();\n\nfunction mountsForNode(node: NetworkNode | undefined): DevicePortMounts | undefined {\n  if (!node) return undefined;\n  if (!mountsCache.has(node)) {\n    mountsCache.set(node, getDevicePortMounts(node));\n  }\n  return mountsCache.get(node);\n}\n\n/**\n * Jack identity for one mounted emitted id: `F:`/`R:` + the port's base id.\n * Every port registers both its bare base and a synthetic `--<face>` alias\n * in its face's mount table — those are ONE physical jack, so `SFP+-1` and\n * `SFP+-1--rear` on a rear-only mount share the key `R:SFP+-1` instead of\n * double-booking it. The alias only denotes a distinct jack across faces:\n * `X` and `X--rear` are different keys (`F:X`/`R:X`) because a colliding\n * rear jack carries the alias — the same separation the renderer uses.\n * A bare id mounted on both faces resolves to the visible primary face,\n * using the same face resolution as the renderer and planner.\n */\nfunction canonicalFor(\n  mounts: DevicePortMounts,\n  emitted: string,\n): string | undefined {\n  const onFront = mounts.front.has(emitted);\n  const onRear = mounts.rear.has(emitted);\n  if (!onFront && !onRear) return undefined;\n  const parsed = parseHandleId(emitted);\n  if (parsed.faceAlias === 'rear' && onRear) return `R:${parsed.base}`;\n  if (parsed.faceAlias === 'front' && onFront) return `F:${parsed.base}`;\n  return `${mountFace(mounts, emitted) === 'front' ? 'F' : 'R'}:${emitted}`;\n}\n\n/**\n * Handle identity for occupancy: the mounted jack key (`F:`/`R:` + base) of\n * the port the handle refers to — lossless, so genuinely distinct ports\n * (`SFP+-1` vs `SFP-1`, `port-1` vs `port-1--rear`) keep separate occupancy\n * keys while one jack's bare and aliased emitted ids share one key.\n * The `+`-dropping fold of {@link sanitizeHandleIdForMatching} is applied\n * ONLY to resolve legacy stored handles that were emitted `+`-stripped —\n * and only when that resolution lands on exactly one mounted jack, never\n * as the canonical form itself.\n */\nfunction occupancyHandleId(\n  node: NetworkNode | undefined,\n  handle: string | null | undefined,\n): string {\n  if (!handle) return '';\n  const normalized = normalizeDirectionalHandleId(handle);\n  const mounts = mountsForNode(node);\n  if (!mounts) return normalized;\n\n  const canonical = canonicalFor(mounts, normalized);\n  if (canonical) return canonical;\n\n  // Not a mounted id — a saved edge may carry a legacy `+`-stripped handle.\n  // Resolve it to the one mounted jack it can only mean, deduplicated by\n  // jack (a port's bare + aliased emitted ids are the same candidate); when\n  // it lands on distinct jacks (SFP+-1 and SFP-1 coexisting, or the same\n  // base on both faces) the id stays itself — never collapse two jacks.\n  const wanted = sanitizeHandleIdForMatching(normalized);\n  const candidates = new Set<string>();\n  const faces = [\n    ['front', 'F', mounts.front],\n    ['rear', 'R', mounts.rear],\n  ] as const;\n  for (const [, faceTag, table] of faces) {\n    for (const mount of table.values()) {\n      // Match on the jack's primary (not the emitted key): each port\n      // registers its bare base AND a synthetic `--<face>` alias in this\n      // table, so dedup by (table face, base) — the emitted-id canonical\n      // would fold a bare id that exists on both faces to front and hide\n      // the rear jack's candidacy.\n      if (sanitizeHandleIdForMatching(mount.primary) === wanted) {\n        candidates.add(`${faceTag}:${parseHandleId(mount.primary).base}`);\n      }\n    }\n  }\n  return candidates.size === 1 ? [...candidates][0] : normalized;\n}\n\n/** Canonical key for a node endpoint, or `null` when the handle is absent. */\nexport function endpointKey(\n  nodeId: string,\n  node: NetworkNode | undefined,\n  handle: string | null | undefined,\n): string | null {\n  const normalized = occupancyHandleId(node, handle);\n  return normalized ? `${nodeId}:${normalized}` : null;\n}\n\n/**\n * Whether an endpoint intentionally hosts more than one edge (capacity ∞).\n * Every unbounded shape is node-checked — a device port with an explicit id\n * like `tube-3` or `cable-button` stays single-tenant, only the fibre nodes\n * that actually mount those handles are unbounded:\n * `tube-*` on tube / multi-tube-cable nodes, `cable-button` on cable /\n * multi-tube-cable nodes, and `left`/`right` on splice nodes.\n */\nexport function isUnboundedEndpoint(\n  node: NetworkNode | undefined,\n  handle: string | null | undefined,\n): boolean {\n  if (!handle || !node) return false;\n  const shape = parseHandleId(handle).shape;\n  if (shape.kind === 'tube') {\n    return node.type === 'tube' || node.type === 'multi-tube-cable';\n  }\n  if (shape.kind === 'cable-button') {\n    return node.type === 'cable' || node.type === 'multi-tube-cable';\n  }\n  if (shape.kind === 'splice-side' && node.type === 'splice') return true;\n  return false;\n}\n\n/** Minimal edge shape the occupancy scan needs (both NetworkEdge and Edge satisfy it). */\nexport interface OccupancyEdgeLike {\n  id: string;\n  source: string;\n  target: string;\n  sourceHandle?: string | null;\n  targetHandle?: string | null;\n}\n\n/**\n * Edges occupying an endpoint — matched regardless of which side of the\n * stored edge holds the node, on the normalized handle id. Unbounded\n * endpoints never report occupants; `ignoreEdgeId` lets a reconnect ignore\n * the edge being moved.\n */\nexport function findEndpointOccupants<T extends OccupancyEdgeLike>(\n  edges: readonly T[],\n  nodeId: string | null | undefined,\n  node: NetworkNode | undefined,\n  handle: string | null | undefined,\n  ignoreEdgeId?: string,\n): T[] {\n  if (!nodeId || !handle || isUnboundedEndpoint(node, handle)) {\n    return [];\n  }\n  const normalized = occupancyHandleId(node, handle);\n  if (!normalized) {\n    return [];\n  }\n  const occupants: T[] = [];\n  for (const edge of edges) {\n    if (ignoreEdgeId && edge.id === ignoreEdgeId) continue;\n    if (\n      (edge.source === nodeId &&\n        occupancyHandleId(node, edge.sourceHandle) === normalized) ||\n      (edge.target === nodeId &&\n        occupancyHandleId(node, edge.targetHandle) === normalized)\n    ) {\n      occupants.push(edge);\n    }\n  }\n  return occupants;\n}\n\n/**\n * How a proposed connection pair is classified. Derived once per connect/\n * reconnect so edge typing, occupancy, and replacement policy can never drift\n * apart.\n */\nexport interface ConnectionPairClassification {\n  /** Power edge — both handles are power endpoints and one node sources power (PDU or distribution board). */\n  power: boolean;\n  /** Fibre-family edge — either endpoint node is a fibre node. */\n  fibre: boolean;\n  /** Network-port edge involving a switch (fibre and power handles excluded). */\n  switchNetwork: boolean;\n  /** Tube↔multi-tube-cable pair. */\n  tube: boolean;\n  /** Both endpoints are the reserved `cable-button` trunk connector. */\n  cableButton: boolean;\n  /**\n   * Whether the pair's endpoints intentionally evict their occupants —\n   * documented interactive replacement for fibre, power, and switch-network\n   * connections.\n   */\n  replaceable: boolean;\n}\n\n/** Classify a connection pair (handles should be post-normalization values). */\nexport function classifyConnectionPair(\n  sourceNode: NetworkNode | undefined,\n  sourceHandle: string | null | undefined,\n  targetNode: NetworkNode | undefined,\n  targetHandle: string | null | undefined,\n): ConnectionPairClassification {\n  const power = isPowerEdgeEndpoints(\n    sourceNode,\n    sourceHandle,\n    targetNode,\n    targetHandle,\n  );\n  const fibre = isFibreConnection(sourceNode, targetNode);\n  const tube =\n    (sourceNode?.type === 'tube' && targetNode?.type === 'multi-tube-cable') ||\n    (sourceNode?.type === 'multi-tube-cable' && targetNode?.type === 'tube');\n  const cableButton =\n    sourceHandle === 'cable-button' && targetHandle === 'cable-button';\n\n  const sourceIsNetworkEndpoint =\n    !fibre && !!sourceHandle && !isPowerEndpointHandle(sourceNode, sourceHandle);\n  const targetIsNetworkEndpoint =\n    !fibre && !!targetHandle && !isPowerEndpointHandle(targetNode, targetHandle);\n  const switchNetwork =\n    sourceIsNetworkEndpoint &&\n    targetIsNetworkEndpoint &&\n    (isSwitchLikeNode(sourceNode) || isSwitchLikeNode(targetNode));\n\n  return {\n    power,\n    fibre,\n    switchNetwork,\n    tube,\n    cableButton,\n    replaceable: fibre || power || switchNetwork,\n  };\n}\n","/**\n * Minimal logger for diagram code. Only outputs when enabled (e.g. debug prop).\n * Use this instead of console.* so production builds stay quiet unless debug is on.\n *\n * Enable via:\n * - NetworkDiagram debug={true}\n * - Or set env REACT_NETWORKS_DEBUG=1 (or non-empty) when building/running the app\n */\n\nfunction isDebugEnabled(debugProp: boolean | undefined): boolean {\n  if (debugProp === true) return true;\n  if (typeof process !== 'undefined' && typeof process.env !== 'undefined') {\n    const v = process.env.REACT_NETWORKS_DEBUG;\n    if (v !== undefined && v !== '' && v !== '0' && v.toLowerCase() !== 'false') return true;\n  }\n  return false;\n}\n\nexport type DiagramLogger = {\n  log: (...args: unknown[]) => void;\n  warn: (...args: unknown[]) => void;\n  error: (...args: unknown[]) => void;\n};\n\nconst noop = (..._args: unknown[]) => {};\n\n/**\n * Returns a logger that only writes to console when debug is enabled.\n * Use in NetworkDiagram and other library code to avoid leaking logs in production.\n */\nexport function createDiagramLogger(debug: boolean | undefined): DiagramLogger {\n  const enabled = isDebugEnabled(debug);\n  return {\n    log: enabled ? (...args: unknown[]) => console.log(...args) : noop,\n    warn: enabled ? (...args: unknown[]) => console.warn(...args) : noop,\n    error: enabled ? (...args: unknown[]) => console.error(...args) : noop,\n  };\n}\n\n/** Prefix every dev-only misuse warning carries, so consumers can grep/filter. */\nconst DEV_WARNING_PREFIX = '[react-networks]';\n\n/**\n * True unless we can see a production build.\n *\n * Reads the bare `process.env.NODE_ENV` expression on purpose: that is the\n * exact text bundlers (Vite/Rollup, webpack's DefinePlugin) replace with a\n * literal, so consumer production builds dead-code-eliminate every warning.\n * Guarding with `typeof process === 'undefined'` would defeat that — the\n * bundlers do not define a bare `process` identifier, so the guard would\n * survive and short-circuit to \"dev\" in production. The try/catch covers a\n * genuinely undefined `process` at runtime (unbundled browser usage), where\n * failing *open* is the right default.\n */\nfunction isDevEnvironment(): boolean {\n  try {\n    return process.env.NODE_ENV !== 'production';\n  } catch {\n    return true;\n  }\n}\n\n/**\n * Warn about a *misuse of the public API* (a silent-mode footgun) in\n * development.\n *\n * Unlike {@link createDiagramLogger}, this is intentionally NOT gated on the\n * `debug` prop: the consumer these warnings are aimed at has, by definition,\n * not opted into anything — they passed a prop combination that the component\n * quietly ignores. Gating on `process.env.NODE_ENV` keeps it out of consumer\n * production bundles (the standard guard every bundler dead-code-eliminates).\n *\n * Callers are expected to fire each warning at most once per component\n * instance (track with a ref) so a re-rendering diagram cannot spam the\n * console.\n */\nexport function warnDev(message: string, details?: unknown): void {\n  if (!isDevEnvironment()) return;\n  if (details === undefined) {\n    console.warn(`${DEV_WARNING_PREFIX} ${message}`);\n    return;\n  }\n  console.warn(`${DEV_WARNING_PREFIX} ${message}`, details);\n}\n","import React, { useCallback, useEffect, useRef } from \"react\";\nimport { useNodesState, useEdgesState, type Node, type Edge } from \"@xyflow/react\";\nimport type { NetworkDiagramProps, NetworkNode, NetworkEdge, NetworkFlowNode, NetworkFlowEdge } from \"../types\";\nimport { applyRackPixelDimensionsToNodes } from \"../utils/rack-layout\";\nimport { mergeRuntimeStateIntoNodes, mergeRuntimeStateIntoEdges } from \"../utils/document-runtime-merge\";\nimport { endpointKey, isUnboundedEndpoint } from \"../core/endpoint-occupancy\";\nimport { warnDev } from \"../utils/diagram-logger\";\n\ntype Options = Pick<NetworkDiagramProps, \"nodes\" | \"edges\" | \"initialNodes\" | \"initialEdges\" | \"onNodesChange\" | \"onEdgesChange\" | \"debug\">;\n\n/** Owns graph state, controlled synchronization, and synchronous mutation refs. */\nexport function useDiagramState({ nodes, edges, initialNodes, initialEdges,\n  onNodesChange: externalOnNodesChange, onEdgesChange: externalOnEdgesChange, debug,\n}: Options) {\n  // Validate and filter edges to remove duplicate handle connections.\n  //\n  // Load-time conflict resolution shares the interactive endpoint identity —\n  // `endpointKey` normalizes directional suffixes while keeping the\n  // split-face alias, so `port-1-source` and `port-1` are one endpoint but\n  // `port-1--rear` is a distinct rear jack — and the\n  // shared capacity policy (`isUnboundedEndpoint`) — but the resolution mode\n  // is deliberately different from interactive connect/reconnect: the FIRST\n  // edge on each single-tenant endpoint wins and later duplicates are\n  // dropped. Interactive replacement (evicting an occupant to make room) is\n  // never replayed over a saved graph.\n  const validateAndFilterEdges = useCallback(\n    (\n      edgesToValidate: NetworkEdge[],\n      nodesForValidation: NetworkNode[],\n    ): NetworkEdge[] => {\n      const seenEndpoints = new Map<string, NetworkEdge>(); // endpointKey -> first edge using it\n      const validEdges: NetworkEdge[] = [];\n      const removedEdges: NetworkEdge[] = [];\n      const nodesById = new Map(\n        nodesForValidation.map((node) => [node.id, node]),\n      );\n\n      for (const edge of edgesToValidate) {\n        if (!edge.source || !edge.target) {\n          // Invalid edge, skip it\n          if (debug) {\n            console.warn(\"Invalid edge skipped (missing source/target):\", edge);\n          }\n          continue;\n        }\n\n        const sourceNode = nodesById.get(edge.source);\n        const targetNode = nodesById.get(edge.target);\n\n        // Single-tenant endpoints claim their canonical key; unbounded\n        // endpoints (splice faces, tube handles, cable-button trunks) and\n        // absent handles never conflict.\n        const sourceKey =\n          edge.sourceHandle && !isUnboundedEndpoint(sourceNode, edge.sourceHandle)\n            ? endpointKey(edge.source, sourceNode, edge.sourceHandle)\n            : undefined;\n        const targetKey =\n          edge.targetHandle && !isUnboundedEndpoint(targetNode, edge.targetHandle)\n            ? endpointKey(edge.target, targetNode, edge.targetHandle)\n            : undefined;\n\n        const existingSourceEdge = sourceKey\n          ? seenEndpoints.get(sourceKey)\n          : undefined;\n        const existingTargetEdge = targetKey\n          ? seenEndpoints.get(targetKey)\n          : undefined;\n\n        if (existingSourceEdge) {\n          if (debug) {\n            console.warn(\n              \"Duplicate connection detected on source handle, removing:\",\n              {\n                handle: sourceKey,\n                existingEdge: existingSourceEdge.id,\n                duplicateEdge: edge.id,\n              },\n            );\n          }\n          removedEdges.push(edge);\n          continue;\n        }\n\n        if (existingTargetEdge) {\n          if (debug) {\n            console.warn(\n              \"Duplicate connection detected on target handle, removing:\",\n              {\n                handle: targetKey,\n                existingEdge: existingTargetEdge.id,\n                duplicateEdge: edge.id,\n              },\n            );\n          }\n          removedEdges.push(edge);\n          continue;\n        }\n\n        // Edge is valid, add it and track its endpoints\n        validEdges.push(edge);\n        if (sourceKey) {\n          seenEndpoints.set(sourceKey, edge);\n        }\n        if (targetKey) {\n          seenEndpoints.set(targetKey, edge);\n        }\n      }\n\n      if (debug && removedEdges.length > 0) {\n        console.log(\n          `Filtered ${removedEdges.length} duplicate edge(s) from initial edges`,\n        );\n      }\n\n      return validEdges.length === edgesToValidate.length ? edgesToValidate : validEdges;\n    },\n    [debug],\n  );\n\n  // Decide controlled vs uncontrolled\n  const isControlled = Array.isArray(nodes) && Array.isArray(edges);\n\n  // Dev-only footgun #1: `nodes` without `edges` (or vice versa) does NOT make\n  // the diagram controlled — the array that was passed is silently demoted to\n  // initial state, so the consumer's later updates never land. Warn once per\n  // instance; production builds strip this (see utils/diagram-logger warnDev).\n  const partialControlledWarnedRef = useRef(false);\n  useEffect(() => {\n    if (partialControlledWarnedRef.current) return;\n    const hasNodes = Array.isArray(nodes);\n    const hasEdges = Array.isArray(edges);\n    if (hasNodes === hasEdges) return;\n    partialControlledWarnedRef.current = true;\n    warnDev(\n      `NetworkDiagram received \\`${hasNodes ? \"nodes\" : \"edges\"}\\` without \\`${\n        hasNodes ? \"edges\" : \"nodes\"\n      }\\`. Controlled mode requires BOTH props, so this diagram is running ` +\n        `uncontrolled and the prop you passed is only used as initial state — ` +\n        `later updates to it will be ignored. Pass both \\`nodes\\` and \\`edges\\` ` +\n        `(with onNodesChange/onEdgesChange) to control the diagram, or use ` +\n        `\\`initialNodes\\`/\\`initialEdges\\` if uncontrolled is what you want.`,\n    );\n  }, [nodes, edges]);\n\n  // Validate and filter initial edges to remove duplicates\n  const initialNodesForValidation = React.useMemo(() => {\n    return isControlled ? nodes || [] : initialNodes || nodes || [];\n  }, [isControlled, nodes, initialNodes]);\n\n  const initialEdgesRaw = React.useMemo(() => {\n    return isControlled ? edges || [] : initialEdges || edges || [];\n  }, [isControlled, edges, initialEdges]);\n\n  const validatedInitialEdges = React.useMemo(() => {\n    return validateAndFilterEdges(initialEdgesRaw, initialNodesForValidation);\n  }, [initialEdgesRaw, initialNodesForValidation, validateAndFilterEdges]);\n\n  const seedNodes = React.useMemo(\n    () => applyRackPixelDimensionsToNodes(initialNodesForValidation),\n    // Seed once at mount; controlled updates flow through the sync effect below.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [],\n  );\n\n  const [reactFlowNodes, setNodes] = useNodesState<NetworkFlowNode>(seedNodes);\n  const [reactFlowEdges, setEdges, onEdgesChange] =\n    useEdgesState<NetworkFlowEdge>(validatedInitialEdges);\n  // Maintain the latest nodes in a ref for always-current lookups in callbacks\n  const latestNodesRef = useRef<Node[]>(reactFlowNodes);\n  useEffect(() => {\n    latestNodesRef.current = reactFlowNodes;\n  }, [reactFlowNodes]);\n\n  // Maintain the latest edges in a ref for always-current lookups in callbacks\n  const latestEdgesRef = useRef<Edge[]>(reactFlowEdges);\n  useEffect(() => {\n    latestEdgesRef.current = reactFlowEdges;\n  }, [reactFlowEdges]);\n\n  const edgesByTargetRef = useRef<Map<string, NetworkEdge[]>>(new Map());\n  useEffect(() => {\n    const map = new Map<string, NetworkEdge[]>();\n    for (const edge of reactFlowEdges) {\n      const list = map.get(edge.target) ?? [];\n      list.push(edge as NetworkEdge);\n      map.set(edge.target, list);\n    }\n    edgesByTargetRef.current = map;\n  }, [reactFlowEdges]);\n\n  const nodesMapRef = useRef<Map<string, Node>>(new Map());\n  useEffect(() => {\n    const map = new Map<string, Node>();\n    reactFlowNodes.forEach((node) => {\n      map.set(node.id, node);\n    });\n    nodesMapRef.current = map;\n  }, [reactFlowNodes]);\n\n  // Sync controlled props -> React Flow state\n  // Also validate edges when they're updated in controlled mode\n  useEffect(() => {\n    if (isControlled && edges) {\n      const validated = validatedInitialEdges;\n      if (validated.length !== edges.length) {\n        if (debug) {\n          console.warn(\n            `Controlled edges had ${edges.length - validated.length} duplicate(s) removed`,\n          );\n        }\n        // Update edges to remove duplicates\n        externalOnEdgesChange?.(validated);\n      }\n    }\n  }, [\n    isControlled,\n    edges,\n    validatedInitialEdges,\n    externalOnEdgesChange,\n    debug,\n  ]);\n\n  useEffect(() => {\n    if (!isControlled) return;\n    const merged = mergeRuntimeStateIntoNodes(\n      nodes || [],\n      latestNodesRef.current as NetworkNode[],\n    );\n    if (merged === latestNodesRef.current) return;\n    setNodes(merged);\n  }, [isControlled, nodes, setNodes]);\n  useEffect(() => {\n    if (!isControlled) return;\n    const merged = mergeRuntimeStateIntoEdges(\n      validatedInitialEdges,\n      latestEdgesRef.current as NetworkEdge[],\n    );\n    if (merged === latestEdgesRef.current) return;\n    setEdges(merged);\n  }, [isControlled, validatedInitialEdges, setEdges]);\n\n  const commitNodes = useCallback(\n    (nextNodes: Node[]) => {\n      latestNodesRef.current = nextNodes;\n      const map = new Map<string, Node>();\n      nextNodes.forEach((node) => {\n        map.set(node.id, node);\n      });\n      nodesMapRef.current = map;\n      setNodes(nextNodes as NetworkFlowNode[]);\n\n      if (isControlled) {\n        externalOnNodesChange?.(nextNodes as NetworkNode[]);\n      }\n    },\n    [isControlled, setNodes, externalOnNodesChange],\n  );\n\n  const commitEdges = useCallback(\n    (nextEdges: Edge[]) => {\n      latestEdgesRef.current = nextEdges;\n      setEdges(nextEdges as NetworkFlowEdge[]);\n\n      if (isControlled) {\n        externalOnEdgesChange?.(nextEdges as NetworkEdge[]);\n      }\n    },\n    [isControlled, setEdges, externalOnEdgesChange],\n  );\n\n  return {\n    isControlled, reactFlowNodes, reactFlowEdges, setNodes, setEdges, onEdgesChange,\n    latestNodesRef, latestEdgesRef, nodesMapRef, edgesByTargetRef, commitNodes, commitEdges,\n  };\n}\n","import type { NetworkDiagramToImageOptions } from \"../types\";\n\nconst MAX_TO_IMAGE_WIDTH = 8192;\nconst MAX_TO_IMAGE_HEIGHT = 8192;\nconst MAX_TO_IMAGE_PADDING = 2048;\nconst MAX_TO_IMAGE_PIXEL_RATIO = 4;\n\nfunction clampNumericOption(\n  value: number | undefined,\n  min: number,\n  max: number,\n): number | undefined {\n  if (typeof value !== \"number\" || Number.isNaN(value)) return undefined;\n  return Math.min(max, Math.max(min, value));\n}\n\nexport function normalizeToImageOptions(\n  options?: NetworkDiagramToImageOptions,\n): NetworkDiagramToImageOptions | undefined {\n  if (!options) return undefined;\n\n  return {\n    ...options,\n    width: clampNumericOption(options.width, 1, MAX_TO_IMAGE_WIDTH),\n    height: clampNumericOption(options.height, 1, MAX_TO_IMAGE_HEIGHT),\n    padding: clampNumericOption(options.padding, 0, MAX_TO_IMAGE_PADDING),\n    pixelRatio: clampNumericOption(\n      options.pixelRatio,\n      0.1,\n      MAX_TO_IMAGE_PIXEL_RATIO,\n    ),\n    quality: clampNumericOption(options.quality, 0, 1),\n  };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { useReactFlow, type Edge, type Node } from \"@xyflow/react\";\nimport type { NetworkDiagramProps, NetworkDiagramFlowMethods, NetworkDiagramFlowObject, NetworkDiagramFitViewOptions, NetworkDiagramToImageOptions, NetworkNode, NetworkEdge } from \"../types\";\nimport { normalizeToImageOptions } from \"../utils/diagram-image-options\";\nimport { resolveFitViewOptions } from \"../utils/fit-view-policy\";\nimport { applyRackPixelDimensionsToNodes } from \"../utils/rack-layout\";\nimport { DIAGRAM_ZOOM_LOD_CLASS } from \"../utils/zoom-lod\";\nimport type { ColorMode } from \"../utils/react-flow-theming\";\n\ntype Options = Pick<NetworkDiagramProps, \"onFlowMethods\" | \"fitViewOptions\" | \"debug\"> & {\n  reactFlowWrapper: RefObject<HTMLDivElement | null>;\n  /**\n   * The diagram's commit pipeline (internal state + synchronous refs + one\n   * controlled notification). Programmatic `setNodes`/`setEdges` go through\n   * it rather than React Flow's change round-trip, so they are neither\n   * dropped by the read-only gate nor notified twice in controlled mode.\n   */\n  commitNodes: (nodes: Node[]) => void;\n  commitEdges: (edges: Edge[]) => void;\n  colorMode: ColorMode;\n};\n\n/** Stable imperative flow API and balanced image-capture lifecycle. */\nexport function useDiagramFlowMethods({\n  reactFlowWrapper, commitNodes, commitEdges,\n  onFlowMethods, fitViewOptions, debug, colorMode,\n}: Options) {\n  // Get React Flow methods for save/restore\n  const {\n    toObject,\n    setViewport,\n    getNodes,\n    getEdges,\n    fitView,\n  } = useReactFlow();\n\n  const commitNodesRef = useRef(commitNodes);\n  const commitEdgesRef = useRef(commitEdges);\n  const reactFlowApiRef = useRef({\n    toObject,\n    setViewport,\n    getNodes,\n    getEdges,\n    fitView,\n  });\n\n  const fitViewOptionsRef = useRef(fitViewOptions);\n  fitViewOptionsRef.current = fitViewOptions;\n\n  useEffect(() => {\n    commitNodesRef.current = commitNodes;\n  }, [commitNodes]);\n\n  useEffect(() => {\n    commitEdgesRef.current = commitEdges;\n  }, [commitEdges]);\n\n  useEffect(() => {\n    reactFlowApiRef.current = {\n      toObject,\n      setViewport,\n      getNodes,\n      getEdges,\n      fitView,\n    };\n  }, [\n    toObject,\n    setViewport,\n    getNodes,\n    getEdges,\n    fitView,\n  ]);\n\n  // Export-safe culling (F7): onlyRenderVisibleElements virtualizes the scene so\n  // offscreen nodes/edges are unmounted — which would omit them from a toImage\n  // PNG. While a capture is in flight we force culling off so the DOM holds every\n  // element, capture, then restore. flushSync commits the culling-off render\n  // synchronously (the flush mechanism React exposes), so the wrapper DOM is\n  // fully populated before html-to-image reads it. The same state hides empty\n  // and error chrome for every capture, whether virtualization is enabled or\n  // not. `captureDepth` keeps overlapping exports in capture mode until the\n  // final request settles.\n  const [isCapturingImage, setIsCapturingImage] = useState(false);\n\n  const captureDepthRef = useRef(0);\n  const isCapturingRef = useRef(false);\n\n  // Prepare the wrapper for an image capture, and restore it afterwards.\n  // Balanced pair; nested captures only act on the outermost begin/end.\n  //\n  //  * Capture state: committed with flushSync so culling is off and feedback\n  //    chrome is hidden before html-to-image reads the wrapper.\n  //  * Zoom LOD: `rn-zoom-below-detail` is stripped for the duration. An export\n  //    is not a viewport — the PNG is read at its own scale, and at pixelRatio 2\n  //    the micro tier is perfectly legible — so a capture taken below the LOD\n  //    zoom must not silently drop bay labels, manufacturer and U indicators.\n  //    Written imperatively (like DiagramZoomLod's own write) *after* the\n  //    flushSync, so the culling-off render cannot reinstate it.\n  const beginImageCapture = useCallback(() => {\n    captureDepthRef.current += 1;\n    const outermost = captureDepthRef.current === 1;\n\n    if (outermost && !isCapturingRef.current) {\n      isCapturingRef.current = true;\n      flushSync(() => setIsCapturingImage(true));\n    }\n\n    if (outermost) {\n      reactFlowWrapper.current?.classList.remove(DIAGRAM_ZOOM_LOD_CLASS);\n    }\n  }, [reactFlowWrapper]);\n\n  const endImageCapture = useCallback(() => {\n    captureDepthRef.current = Math.max(0, captureDepthRef.current - 1);\n    if (captureDepthRef.current > 0) {\n      return;\n    }\n    if (isCapturingRef.current) {\n      isCapturingRef.current = false;\n      flushSync(() => setIsCapturingImage(false));\n    }\n  }, []);\n\n  const flowMethodsValue = useMemo<NetworkDiagramFlowMethods>(\n    () => ({\n      toObject: () =>\n        reactFlowApiRef.current.toObject() as NetworkDiagramFlowObject,\n      setViewport: (viewport: Parameters<typeof setViewport>[0]) => {\n        reactFlowApiRef.current.setViewport(viewport);\n      },\n      setNodes: (nodes: NetworkNode[]) => {\n        commitNodesRef.current(applyRackPixelDimensionsToNodes(nodes));\n      },\n      setEdges: (edges: NetworkEdge[]) => {\n        commitEdgesRef.current(edges);\n      },\n      toImage: async (options?: NetworkDiagramToImageOptions) => {\n        const wrapper = reactFlowWrapper.current;\n        if (!wrapper) {\n          throw new Error(\"ReactFlow wrapper not available\");\n        }\n        // Export-safe capture (F7): force culling off so every element is in\n        // the captured DOM, and drop the zoom-LOD class so the micro-label tier\n        // survives an export taken below the LOD zoom. Restored in the finally\n        // regardless of outcome.\n        beginImageCapture();\n        try {\n          // Lazy-load html-to-image (F19): keep it out of the \".\" entry's\n          // import-time cost for consumers who never export.\n          const { toPng } = await import(\"html-to-image\");\n          return await toPng(wrapper, normalizeToImageOptions(options));\n        } finally {\n          endImageCapture();\n        }\n      },\n      getNodes: () => reactFlowApiRef.current.getNodes() as NetworkNode[],\n      getEdges: () => reactFlowApiRef.current.getEdges() as NetworkEdge[],\n      fitView: (options?: NetworkDiagramFitViewOptions) => {\n        const {\n          fitView: currentFitView,\n          getNodes: currentGetNodes,\n        } = reactFlowApiRef.current;\n        const configuredOptions = fitViewOptionsRef.current;\n        const allNodes = currentGetNodes();\n        const requestedIds =\n          options?.nodes && Array.isArray(options.nodes) && options.nodes.length > 0\n            ? options.nodes\n            : undefined;\n        const requestedNodes = requestedIds\n          ?.map((id) => allNodes.find((node) => node.id === id))\n          .filter((node): node is Node => node !== undefined);\n        // Resolve string IDs for the convenience API, then let React Flow\n        // calculate the fit from its current measured and absolute node bounds.\n        // eslint-disable-next-line @typescript-eslint/no-unused-vars\n        const { nodes: _unused, ...callOptions } = options || {};\n        const mergedOptions = { ...configuredOptions, ...callOptions };\n        const resolvedOptions = resolveFitViewOptions(mergedOptions);\n\n        if (requestedNodes) {\n          currentFitView({ ...resolvedOptions, nodes: requestedNodes });\n          return;\n        }\n        currentFitView(resolvedOptions);\n      },\n    }),\n    // Callbacks and the wrapper ref are stable; changing props are read via\n    // refs, so exposing the imperative API does not trigger parent feedback.\n    [beginImageCapture, endImageCapture, reactFlowWrapper],\n  );\n\n  // Expose React Flow methods to parent component\n  useEffect(() => {\n    onFlowMethods?.(flowMethodsValue);\n  }, [onFlowMethods, flowMethodsValue]);\n\n  // Download functionality\n  const [isDownloading, setIsDownloading] = useState(false);\n\n  const onDownload = useCallback(() => {\n    if (isDownloading) {\n      return;\n    }\n\n    const wrapper = reactFlowWrapper.current;\n    if (!wrapper) {\n      if (debug) {\n        console.error(\"ReactFlow wrapper ref is not available\");\n      }\n      return;\n    }\n    setIsDownloading(true);\n\n    // Export-safe capture (F7): mirror toImage — offscreen elements and the\n    // micro-label tier both belong in the download PNG. Restored in finally.\n    beginImageCapture();\n\n    // Lazy-load html-to-image (F19) so it isn't bundled/parsed for consumers\n    // who never trigger a download.\n    import(\"html-to-image\")\n      .then(({ toPng }) =>\n        toPng(wrapper, {\n          filter: (node) =>\n            !(\n              node?.classList?.contains(\"react-flow__minimap\") ||\n              node?.classList?.contains(\"react-flow__controls\") ||\n              node?.classList?.contains(\"react-flow__attribution\") ||\n              node?.classList?.contains(\"react-networks-attribution\") ||\n              node?.classList?.contains(\"download-button\") ||\n              node?.tagName === \"BUTTON\"\n            ),\n          backgroundColor: colorMode === \"light\" ? \"#ffffff\" : \"#1a1a1a\",\n          pixelRatio: 2,\n        }),\n      )\n      .then((dataUrl: string) => {\n        const link = document.createElement(\"a\");\n        link.download = `network-diagram-${new Date().toISOString().split(\"T\")[0]}.png`;\n        link.href = dataUrl;\n        link.click();\n      })\n      .catch((error: unknown) => {\n        if (debug) {\n          console.error(\"Error downloading image:\", error);\n        }\n      })\n      .finally(() => {\n        endImageCapture();\n        setIsDownloading(false);\n      });\n  }, [isDownloading, reactFlowWrapper, beginImageCapture, debug, colorMode, endImageCapture]);\n\n  return { reactFlowApiRef, isCapturingImage, isDownloading, onDownload };\n}\n","import React from 'react';\nimport type { NetworkNode } from '../types';\n\nexport type DiagramMutationContextValue = {\n  /** Patch one node's `data` through the diagram's commit pipeline (controlled-safe). */\n  updateNodeData: (nodeId: string, dataPatch: Record<string, unknown>) => void;\n  /** Map the current node array through the diagram's commit pipeline. */\n  updateNodes: (updater: (nodes: NetworkNode[]) => NetworkNode[]) => void;\n};\n\nconst noop = () => {};\n\nconst defaultValue: DiagramMutationContextValue = {\n  updateNodeData: noop,\n  updateNodes: noop,\n};\n\nconst DiagramMutationContext =\n  React.createContext<DiagramMutationContextValue>(defaultValue);\n\nexport const DiagramMutationProvider = DiagramMutationContext.Provider;\n\nexport function useDiagramMutation(): DiagramMutationContextValue {\n  return React.useContext(DiagramMutationContext);\n}\n","import type { NetworkFlowEdge, NetworkFlowNode } from '../types';\n\n// React Flow replaces these arrays on graph edits. Weak keys let all node\n// subscriptions share one scan per revision without retaining old diagrams.\nconst edgeIndexes = new WeakMap<readonly NetworkFlowEdge[], EdgeIndex>();\nconst childIndexes = new WeakMap<readonly NetworkFlowNode[], Map<string, NetworkFlowNode[]>>();\nconst EMPTY_EDGES: NetworkFlowEdge[] = [];\nconst EMPTY_NODES: NetworkFlowNode[] = [];\n\ninterface EdgeIndex {\n  incident: Map<string, NetworkFlowEdge[]>;\n  incoming: Map<string, NetworkFlowEdge[]>;\n}\n\nfunction append<T>(index: Map<string, T[]>, key: string, value: T): void {\n  const entries = index.get(key);\n  if (entries) entries.push(value);\n  else index.set(key, [value]);\n}\n\nfunction indexEdges(edges: readonly NetworkFlowEdge[]): EdgeIndex {\n  const cached = edgeIndexes.get(edges);\n  if (cached) return cached;\n  const index: EdgeIndex = { incident: new Map(), incoming: new Map() };\n  for (const edge of edges) {\n    append(index.incident, edge.source, edge);\n    // A self-loop occurs once, matching the previous filter semantics.\n    if (edge.target !== edge.source) append(index.incident, edge.target, edge);\n    append(index.incoming, edge.target, edge);\n  }\n  edgeIndexes.set(edges, index);\n  return index;\n}\n\nexport function getIncidentEdges(edges: readonly NetworkFlowEdge[], nodeId: string): NetworkFlowEdge[] {\n  return indexEdges(edges).incident.get(nodeId) ?? EMPTY_EDGES;\n}\n\nexport function getIncomingEdges(edges: readonly NetworkFlowEdge[], nodeId: string): NetworkFlowEdge[] {\n  return indexEdges(edges).incoming.get(nodeId) ?? EMPTY_EDGES;\n}\n\nexport function getChildNodes(nodes: readonly NetworkFlowNode[], parentId: string): NetworkFlowNode[] {\n  let index = childIndexes.get(nodes);\n  if (!index) {\n    index = new Map();\n    for (const node of nodes) {\n      if (node.parentId !== undefined) append(index, node.parentId, node);\n    }\n    childIndexes.set(nodes, index);\n  }\n  return index.get(parentId) ?? EMPTY_NODES;\n}\n","import { useCallback } from 'react';\nimport { useStore } from '@xyflow/react';\nimport type { NetworkFlowNode } from '../types';\nimport { getChildNodes } from '../utils/graph-index';\n\nfunction childNodesShallowEqual(a: NetworkFlowNode[], b: NetworkFlowNode[]): boolean {\n  if (a === b) return true;\n  if (a.length !== b.length) return false;\n  for (let i = 0; i < a.length; i++) {\n    if (a[i] !== b[i]) return false;\n  }\n  return true;\n}\n\n/**\n * Subscribe to child nodes of a parent. All parents share one index per node\n * array revision; unrelated store updates only perform a map lookup.\n */\nexport function useChildNodes(parentId: string): NetworkFlowNode[] {\n  return useStore(\n    useCallback(\n      (state: { nodes: unknown[] }) =>\n        getChildNodes(state.nodes as NetworkFlowNode[], parentId),\n      [parentId],\n    ),\n    childNodesShallowEqual,\n  );\n}\n","import { DeviceStatus } from '../types';\n\n/**\n * Raw hex colour for a device status.\n *\n * Use this when a CSS custom property cannot be resolved — canvas/SVG paint,\n * image export, or anywhere the value is parsed rather than handed to CSS.\n * For DOM styling prefer {@link getStatusColor}, which is theme-aware.\n *\n * @param status - The device status\n * @returns Hex colour code for the status\n * @public\n */\nexport function getStatusColorHex(status: DeviceStatus | string): string {\n  switch (status) {\n    case 'active':\n      return '#10b981';\n    case 'inactive':\n      return '#ef4444';\n    case 'maintenance':\n      return '#f59e0b';\n    default:\n      return '#6b7280';\n  }\n}\n\n/**\n * Get the colour for a device status as a CSS value.\n *\n * Returns a `var(--rn-status-*, <hex>)` reference so the colour follows the\n * active theme (`.react-flow-light` / `.react-flow-dark`, or the OS preference\n * when neither class is present). The hex fallback is the historical value, so\n * the string still renders correctly outside the library stylesheet.\n *\n * @param status - The device status\n * @returns CSS colour value for the status\n * @public\n */\nexport function getStatusColor(status: DeviceStatus | string): string {\n  switch (status) {\n    case 'active':\n      return 'var(--rn-status-ok, #10b981)';\n    case 'inactive':\n      return 'var(--rn-status-error, #ef4444)';\n    case 'maintenance':\n      return 'var(--rn-status-warn, #f59e0b)';\n    default:\n      return 'var(--rn-status-unknown, #6b7280)';\n  }\n}\n","import React, { useState } from 'react';\nimport { NodeProps } from '@xyflow/react';\nimport { NetworkNode, UNumberingDirection, NetworkFlowNode } from '../types';\nimport { useDiagramMutation } from '../context/DiagramMutationContext';\nimport { useChildNodes } from './use-child-nodes';\nimport { RACK_HEADER_HEIGHT } from '../utils/constants';\nimport { getRackUNumberingDirection, convertUPositionForDirection } from '../utils/u-positioning';\nimport { getStatusColor } from '../utils/status-colors';\nimport {\n  getRackFaceGapPx,\n  getRackFaceWidthPx,\n  getRackNodePixelDimensions,\n  getRackTotalWidthPx,\n  getRackUnitHeightPx,\n  isSplitFaceRackLayout,\n  resolveRackUHeight,\n} from '../utils/rack-layout';\n\nexport interface RackNodeProps extends NodeProps<NetworkFlowNode> {\n  onFaceChange?: (nodeId: string, face: 'front' | 'rear') => void;\n  onRackFaceChange?: (rackId: string, face: 'front' | 'rear') => void;\n  isDragOver?: boolean;\n}\n\n/**\n * Rack visualization component with U positioning support.\n * \n * Displays a 19\" rack with configurable height, front/rear face switching,\n * and support for mounting devices at specific U positions.\n * \n * @example\n * ```tsx\n * {\n *   id: 'rack-1',\n *   type: 'rack',\n *   position: { x: 100, y: 100 },\n *   data: {\n *     label: 'Main Rack',\n *     uHeight: 42,\n *     status: 'active'\n *   }\n * }\n * ```\n * \n * @public\n */\nconst RackNodeComponent: React.FC<RackNodeProps> = ({ data, selected, id, onFaceChange, onRackFaceChange, isDragOver = false }) => {\n  const { \n    label, \n    ports = 0, \n    status = 'active',\n    uHeight = 42, // Standard rack height\n    face = 'front',\n    customHeaderText,\n  } = data;\n\n  // Resolve at the render boundary with the same rule as the rack box, snapping\n  // and placement (invalid -> 42, capped at MAX_RACK_U), so a hostile persisted\n  // `uHeight` can't drive the per-U marker loop into an unbounded allocation and\n  // the markers always match the rack's pixel height.\n  const safeUHeight = resolveRackUHeight(uHeight);\n\n  const splitFaceLayout = isSplitFaceRackLayout(data);\n  const rackUnitHeightPx = getRackUnitHeightPx(data);\n  const effectiveWidth = getRackTotalWidthPx(data);\n  const rackDimensions = getRackNodePixelDimensions(data);\n  const faceWidth = getRackFaceWidthPx(data);\n  const faceGap = getRackFaceGapPx(data);\n  const splitRackFaceStyle: React.CSSProperties = {\n    width: `${faceWidth}px`,\n    height: '100%',\n    boxSizing: 'border-box',\n    border: '1px solid rgba(156,163,175,0.35)',\n    borderRadius: 'var(--rn-ui-radius, 1px)',\n    flexShrink: 0,\n    display: 'flex',\n    alignItems: 'flex-start',\n    justifyContent: 'center',\n    paddingTop: '4px',\n    fontSize: '8px',\n    color: 'var(--rn-text-muted, #9ca3af)',\n    fontWeight: 'bold',\n    textTransform: 'uppercase',\n    letterSpacing: '0.06em',\n  };\n  \n  const { updateNodeData, updateNodes } = useDiagramMutation();\n  const childNodes = useChildNodes(id);\n  // `data.showUMarkers` seeds the toggle (default true for backward\n  // compatibility) and stays authoritative: when the host changes it, the\n  // local toggle follows. The header button still flips it locally between\n  // host updates.\n  const dataShowUMarkers = data.showUMarkers;\n  const [showUMarkers, setShowUMarkers] = useState(dataShowUMarkers !== undefined ? dataShowUMarkers : true);\n  const [syncedShowUMarkers, setSyncedShowUMarkers] = useState(dataShowUMarkers);\n  if (syncedShowUMarkers !== dataShowUMarkers) {\n    setSyncedShowUMarkers(dataShowUMarkers);\n    setShowUMarkers(dataShowUMarkers !== undefined ? dataShowUMarkers : true);\n  }\n  \n  const toggleUNumberingDirection = () => {\n    const currentDirection = getRackUNumberingDirection({ data } as NetworkNode);\n    const newDirection: UNumberingDirection = currentDirection === 'bottom-up' ? 'top-down' : 'bottom-up';\n    updateNodeData(id, { uNumberingDirection: newDirection });\n  };\n\n  \n  const toggleFace = () => {\n    if (splitFaceLayout) {\n      return;\n    }\n\n    const next = face === 'front' ? 'rear' : 'front';\n    \n    if (onRackFaceChange) {\n      onRackFaceChange(id, next);\n    } else if (onFaceChange) {\n      childNodes.forEach((child) => {\n        onFaceChange(child.id, next);\n      });\n    } else {\n      updateNodes((nodes) =>\n        nodes.map((node) =>\n          node.id === id || node.parentId === id\n            ? { ...node, data: { ...node.data, face: next } }\n            : node,\n        ) as NetworkNode[],\n      );\n    }\n  };\n\n  // Generate U position markers aligned to rack origin\n  const renderUPositions = () => {\n    const positions = [];\n    const startY = RACK_HEADER_HEIGHT; // Start after header from rack origin\n    const direction = getRackUNumberingDirection({ data } as NetworkNode);\n    \n    for (let i = 1; i <= safeUHeight; i++) {\n      const yPos = startY + (i - 1) * rackUnitHeightPx - 4; // Align with device positioning, shifted up 3px\n      // Convert the physical position to the display number based on direction\n      const displayNumber = convertUPositionForDirection(i, safeUHeight, direction);\n      \n      positions.push(\n        <div\n          key={i}\n          style={{\n            position: 'absolute',\n            left: '-35px',\n            top: `${yPos}px`,\n            background: 'var(--rn-rack-rail, #374151)',\n            color: 'var(--rn-text-muted, #9ca3af)',\n            padding: '2px 4px',\n            borderRadius: 'var(--rn-ui-radius, 1px)',\n            fontSize: '8px',\n            fontWeight: 'bold',\n            width: '28px',\n            textAlign: 'center',\n            border: '1px solid var(--rn-border, #4b5563)',\n            zIndex: 10,\n            height: `${rackUnitHeightPx}px`,\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'center',\n            lineHeight: 1,\n          }}\n        >\n          {displayNumber}\n        </div>\n      );\n    }\n    return positions;\n  };\n\n\n  return (\n    <div\n      data-testid=\"rack-node\"\n      className={`rack-node ${selected ? 'selected' : ''} ${selected && !isDragOver ? 'rn-node--selected' : ''} ${isDragOver ? 'drag-over' : ''}`}\n      style={{\n        background: 'transparent',\n        border: `2px solid ${getStatusColor(status)}`,\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        padding: 0,\n        width: `${effectiveWidth}px`, // Custom or default width\n        height: `${rackDimensions.height}px`, // Header + exact U area\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '12px',\n        // Selection ring comes from the shared `.rn-node--selected` class so\n        // every node type uses one width and colour. The drag-over glow still\n        // wins here: the class is only applied when not dragging over.\n        boxShadow: isDragOver\n          ? '0 0 20px var(--rn-accent, #3b82f6), 0 0 40px var(--rn-accent, #3b82f6), 0 0 60px var(--rn-accent, #3b82f6)'\n          : '0 2px 4px rgba(0,0,0,0.1)',\n        position: 'relative',\n        display: 'flex',\n        flexDirection: 'column',\n        transition: 'box-shadow 0.2s ease-in-out',\n      }}\n    >\n      {/* Rack Header */}\n      <div style={{ \n        textAlign: 'center', \n        marginBottom: 0, \n        zIndex: 2, \n        background: 'var(--rn-rack-body, #1f2937)',\n        padding: '8px',\n        position: 'relative',\n        height: `${RACK_HEADER_HEIGHT}px`, // Fixed header height\n        display: 'flex',\n        flexDirection: 'column',\n        justifyContent: 'center',\n      }}>\n        <div style={{ fontWeight: 'bold', marginBottom: '4px' }}>{label}</div>\n        <div style={{ fontSize: '10px', color: 'var(--rn-text-muted, #9ca3af)' }}>\n          {customHeaderText ? (\n            <span style={{ color: 'var(--rn-rack-accent, #fbbf24)', fontWeight: 'bold' }}>{customHeaderText}</span>\n          ) : (\n            <span>{typeof ports === 'number' ? ports : 0} ports</span>\n          )} • {safeUHeight}U • {splitFaceLayout ? 'rear|front' : face}\n        </div>\n        <div style={{ display: 'flex', gap: '4px', position: 'absolute', top: '4px', right: '4px' }}>\n          <button\n            className=\"rn-node-button\"\n            onPointerDown={(e) => { e.stopPropagation(); }}\n            onMouseDown={(e) => { e.stopPropagation(); }}\n            onClick={(e) => { e.stopPropagation(); e.preventDefault(); setShowUMarkers(!showUMarkers); }}\n            style={{\n              background: showUMarkers\n                ? 'var(--rn-status-ok, #10b981)'\n                : 'var(--rn-rack-rail, #374151)',\n              color: showUMarkers\n                ? 'var(--rn-text-strong, #ffffff)'\n                : 'var(--rn-text-muted, #9ca3af)',\n              border: '1px solid var(--rn-border, #4b5563)',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              padding: '2px 6px',\n              fontSize: '8px',\n              cursor: 'pointer',\n              fontFamily: 'monospace',\n            }}\n            type=\"button\"\n            aria-pressed={showUMarkers}\n            aria-label={`${showUMarkers ? 'Hide' : 'Show'} U markers`}\n            title={`${showUMarkers ? 'Hide' : 'Show'} U markers`}\n          >\n            U\n          </button>\n          <button\n            className=\"rn-node-button\"\n            onPointerDown={(e) => { e.stopPropagation(); }}\n            onMouseDown={(e) => { e.stopPropagation(); }}\n            onClick={(e) => { e.stopPropagation(); e.preventDefault(); toggleUNumberingDirection(); }}\n            style={{\n              background: 'var(--rn-rack-rail, #374151)',\n              color: 'var(--rn-text-muted, #9ca3af)',\n              border: '1px solid var(--rn-border, #4b5563)',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              padding: '2px 6px',\n              fontSize: '8px',\n              cursor: 'pointer',\n              fontFamily: 'monospace',\n            }}\n            type=\"button\"\n            aria-label={`Switch U numbering to ${getRackUNumberingDirection({ data } as NetworkNode) === 'bottom-up' ? 'top-down' : 'bottom-up'}`}\n            title={`Switch U numbering to ${getRackUNumberingDirection({ data } as NetworkNode) === 'bottom-up' ? 'top-down' : 'bottom-up'}`}\n          >\n            {getRackUNumberingDirection({ data } as NetworkNode) === 'bottom-up' ? '↑↓' : '↓↑'}\n          </button>\n          {!splitFaceLayout && (\n            <button\n              className=\"rn-node-button\"\n              onPointerDown={(e) => { e.stopPropagation(); }}\n              onMouseDown={(e) => { e.stopPropagation(); }}\n              onClick={(e) => { e.stopPropagation(); e.preventDefault(); toggleFace(); }}\n              style={{\n                background: 'var(--rn-rack-rail, #374151)',\n                color: 'var(--rn-text-muted, #9ca3af)',\n                border: '1px solid var(--rn-border, #4b5563)',\n                borderRadius: 'var(--rn-ui-radius, 1px)',\n                padding: '2px 6px',\n                fontSize: '8px',\n                cursor: 'pointer',\n                fontFamily: 'monospace',\n              }}\n              type=\"button\"\n              aria-label={`Switch to ${face === 'front' ? 'rear' : 'front'} face`}\n              title={`Switch to ${face === 'front' ? 'rear' : 'front'} face`}\n            >\n              {face === 'front' ? '⇄ Rear' : '⇄ Front'}\n            </button>\n          )}\n        </div>\n      </div>\n      \n      {/* Rack Body - transparent background; devices are positioned relative to rack origin */}\n      <div\n        style={{\n          flex: 1,\n          background: 'transparent',\n          // no borders to avoid pixel offsets\n          position: 'relative',\n          minHeight: `${safeUHeight * rackUnitHeightPx}px`,\n          overflow: 'hidden',\n        }}\n      >\n        {splitFaceLayout && (\n          <div\n            style={{\n              position: 'absolute',\n              inset: 0,\n              pointerEvents: 'none',\n              display: 'flex',\n              alignItems: 'stretch',\n            }}\n          >\n            <div\n              data-testid=\"rack-face-rear\"\n              style={{\n                ...splitRackFaceStyle,\n                background: 'linear-gradient(180deg, rgba(17,24,39,0.22) 0%, rgba(17,24,39,0.12) 100%)',\n              }}\n            >\n              Rear\n            </div>\n            <div\n              data-testid=\"rack-face-gap\"\n              style={{\n                width: `${faceGap}px`,\n                display: 'flex',\n                justifyContent: 'center',\n                alignItems: 'stretch',\n                flexShrink: 0,\n              }}\n            >\n              <div\n                data-testid=\"rack-face-divider\"\n                style={{\n                  width: '1px',\n                  height: '100%',\n                  background: 'rgba(156,163,175,0.22)',\n                  borderRadius: 'var(--rn-ui-radius, 1px)',\n                }}\n              />\n            </div>\n            <div\n              data-testid=\"rack-face-front\"\n              style={{\n                ...splitRackFaceStyle,\n                background: 'linear-gradient(180deg, rgba(17,24,39,0.32) 0%, rgba(17,24,39,0.18) 100%)',\n              }}\n            >\n              Front\n            </div>\n          </div>\n        )}\n      </div>\n\n      {/* Overlay for U markers aligned to rack origin */}\n      {showUMarkers && (\n        <div\n          style={{\n            position: 'absolute',\n            top: 0,\n            left: 0,\n            right: 0,\n            bottom: 0,\n            pointerEvents: 'none',\n          }}\n        >\n          {renderUPositions()}\n        </div>\n      )}\n      \n    </div>\n  );\n};\n\n/**\n * Memoized; uses useStoreApi (not useReactFlow) so rack chrome does not re-render on every node move.\n *\n * The comparator intentionally omits the `onFaceChange` / `onRackFaceChange` function\n * props: this is only safe because `NetworkDiagram` passes them as ref-backed stable\n * callbacks. If a caller ever renders `RackNode` with unstable face-change handlers,\n * add them here or the node will keep a stale callback.\n */\nexport const RackNode = React.memo(\n  RackNodeComponent,\n  (previous, next) =>\n    previous.id === next.id &&\n    previous.selected === next.selected &&\n    previous.data === next.data &&\n    previous.isDragOver === next.isDragOver,\n);\n","/**\n * Accessibility helpers shared by the node renderers.\n *\n * Two concerns live here:\n *\n * 1. `buildPortAriaLabel` — the single builder for the accessible name of every\n *    port, bay, tube and fibre element. Node renderers already compose a `title`\n *    string from the same data; routing both through one builder keeps the\n *    announced name and the tooltip in step.\n * 2. `activateOnEnterOrSpace` — the keyboard half of a mouse-only click handler.\n *    An element given `role=\"button\"` must fire on Enter and Space, so a click\n *    handler on a non-native control is always paired with this.\n *\n * Internal module: nothing here is exported from the package entry points.\n */\n\nimport type React from \"react\";\n\n/** Inputs to {@link buildPortAriaLabel}. Everything but `kind` is optional. */\nexport interface PortAriaLabelParts {\n  /** Noun for the element type, e.g. `\"Port\"`, `\"Bay\"`, `\"Tube\"`, `\"Fibre\"`. */\n  kind: string;\n  /** Owning group/block, e.g. a device port-block name or a cable label. */\n  group?: string;\n  /** The element's own label, e.g. `\"Ethernet 1/1\"` or `\"Drive Bay 0\"`. */\n  label?: string | number;\n  /**\n   * Status words appended after the name, in order, e.g. `[\"connected\"]`.\n   * Falsy entries are dropped so callers can inline conditionals.\n   */\n  status?: Array<string | number | false | null | undefined>;\n}\n\nfunction toText(value: string | number | false | null | undefined): string {\n  if (value === undefined || value === null || value === false) return \"\";\n  return String(value).trim();\n}\n\n/**\n * Build the accessible name for an interactive diagram element.\n *\n * `buildPortAriaLabel({ kind: \"Port\", group: \"Ethernet\", label: \"1/1\",\n * status: [\"connected\"] })` → `\"Port Ethernet 1/1, connected\"`.\n */\nexport function buildPortAriaLabel({\n  kind,\n  group,\n  label,\n  status,\n}: PortAriaLabelParts): string {\n  const name = [toText(kind), toText(group), toText(label)]\n    .filter(Boolean)\n    .join(\" \");\n  const suffixes = (status ?? []).map(toText).filter(Boolean);\n  return suffixes.length > 0 ? `${name}, ${suffixes.join(\", \")}` : name;\n}\n\n/** True for the two keys that must activate an element with `role=\"button\"`. */\nexport function isActivationKey(key: string): boolean {\n  // \"Spacebar\" is the legacy IE/Edge key name; jsdom and every current browser\n  // report \" \", but matching both costs nothing.\n  return key === \"Enter\" || key === \" \" || key === \"Spacebar\";\n}\n\n/**\n * Wrap an activation callback as an `onKeyDown` handler that fires on Enter and\n * Space and swallows the event, so the keypress neither scrolls the page (Space)\n * nor bubbles to React Flow's node-level key handling.\n */\nexport function activateOnEnterOrSpace<T extends Element>(\n  activate: (event: React.KeyboardEvent<T>) => void,\n): React.KeyboardEventHandler<T> {\n  return (event) => {\n    if (!isActivationKey(event.key)) return;\n    event.preventDefault();\n    event.stopPropagation();\n    activate(event);\n  };\n}\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode } from '../types';\nimport { getStatusColor } from '../utils/status-colors';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nconst FibreNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected }) => {\n  const { label, status = 'active' } = data;\n\n  // Each handle has an id so a connection names the side it was drawn to; an\n  // edge saved without handle ids still resolves to the first handle of its\n  // type (top for targets, bottom for sources) exactly as before.\n  return (\n    <div\n      className={`fiber-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        background: 'var(--rn-surface, #1f2937)',\n        border: `2px solid ${getStatusColor(status)}`,\n        borderRadius: '50%',\n        width: '60px',\n        height: '60px',\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '10px',\n        fontWeight: 'bold',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.1)',\n        position: 'relative',\n      }}\n    >\n      <Handle\n        id=\"top\"\n        type=\"target\"\n        position={Position.Top}\n        aria-label={buildPortAriaLabel({ kind: 'Connection', group: label, status: ['top', 'input'] })}\n        role=\"img\"\n      />\n      <Handle\n        id=\"bottom\"\n        type=\"source\"\n        position={Position.Bottom}\n        aria-label={buildPortAriaLabel({ kind: 'Connection', group: label, status: ['bottom', 'output'] })}\n        role=\"img\"\n      />\n      <Handle\n        id=\"left\"\n        type=\"target\"\n        position={Position.Left}\n        aria-label={buildPortAriaLabel({ kind: 'Connection', group: label, status: ['left', 'input'] })}\n        role=\"img\"\n      />\n      <Handle\n        id=\"right\"\n        type=\"source\"\n        position={Position.Right}\n        aria-label={buildPortAriaLabel({ kind: 'Connection', group: label, status: ['right', 'output'] })}\n        role=\"img\"\n      />\n      \n      <div style={{ textAlign: 'center' }}>\n        <div style={{ fontSize: '8px', lineHeight: '1.2' }}>{label}</div>\n      </div>\n      \n      <div\n        style={{\n          position: 'absolute',\n          bottom: '4px',\n          left: '50%',\n          transform: 'translateX(-50%)',\n          width: '8px',\n          height: '8px',\n          background: getStatusColor(status),\n          borderRadius: '50%',\n        }}\n      />\n    </div>\n  );\n};\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const FibreNode = React.memo(FibreNodeInner);\n","import type { DeviceImageFit } from '../types';\n\nexport type ResolvedDeviceImageBackgroundSize = '100% 100%' | 'auto' | 'auto 100%' | 'contain' | 'cover';\n\nexport interface DeviceImageNaturalSize {\n  width: number;\n  height: number;\n}\n\nexport interface DeviceImageFrame {\n  width: number;\n  height: number;\n  offsetX: number;\n  offsetY: number;\n}\n\nfunction clampNumber(value: number, min: number, max: number): number {\n  return Math.max(min, Math.min(max, value));\n}\n\nfunction normalizePositionToken(\n  rawToken: string | undefined\n): { mode: 'percent'; value: number } | { mode: 'px'; value: number } {\n  const token = (rawToken ?? 'center').trim().toLowerCase();\n\n  if (token.endsWith('%')) {\n    const value = Number.parseFloat(token.slice(0, -1));\n    if (Number.isFinite(value)) {\n      return { mode: 'percent', value: clampNumber(value / 100, 0, 1) };\n    }\n  }\n\n  if (token.endsWith('px')) {\n    const value = Number.parseFloat(token.slice(0, -2));\n    if (Number.isFinite(value)) {\n      return { mode: 'px', value };\n    }\n  }\n\n  if (token === 'left' || token === 'top') {\n    return { mode: 'percent', value: 0 };\n  }\n  if (token === 'right' || token === 'bottom') {\n    return { mode: 'percent', value: 1 };\n  }\n  if (token === 'center') {\n    return { mode: 'percent', value: 0.5 };\n  }\n\n  const numericValue = Number.parseFloat(token);\n  if (Number.isFinite(numericValue)) {\n    return { mode: 'px', value: numericValue };\n  }\n\n  return { mode: 'percent', value: 0.5 };\n}\n\nfunction normalizeBackgroundPosition(position: string | undefined): [string | undefined, string | undefined] {\n  const tokens = (position ?? 'center').trim().split(/\\s+/).filter(Boolean);\n  if (tokens.length === 0) {\n    return ['center', 'center'];\n  }\n  if (tokens.length === 1) {\n    const token = tokens[0].toLowerCase();\n    if (token === 'top' || token === 'bottom') {\n      return ['center', token];\n    }\n    if (token === 'left' || token === 'right') {\n      return [token, 'center'];\n    }\n    return [token, 'center'];\n  }\n  return [tokens[0], tokens[1]];\n}\n\nfunction resolveAxisOffset(\n  containerSize: number,\n  renderedSize: number,\n  token: ReturnType<typeof normalizePositionToken>\n): number {\n  if (token.mode === 'px') {\n    return token.value;\n  }\n  return (containerSize - renderedSize) * token.value;\n}\n\nexport function resolveDeviceImageBackgroundSize(\n  imageFit?: DeviceImageFit,\n  stretchToFit?: boolean\n): ResolvedDeviceImageBackgroundSize {\n  if (stretchToFit === true || imageFit === 'fill') {\n    return '100% 100%';\n  }\n\n  if (imageFit === 'none') {\n    return 'auto';\n  }\n  if (imageFit === 'contain-height') {\n    return 'auto 100%';\n  }\n  if (imageFit === 'scale-down') {\n    return 'contain';\n  }\n\n  return imageFit ?? 'cover';\n}\n\nexport function resolveDeviceImageFrame(\n  containerWidth: number,\n  containerHeight: number,\n  naturalSize: DeviceImageNaturalSize | undefined,\n  backgroundSize: ResolvedDeviceImageBackgroundSize,\n  backgroundPosition?: string\n): DeviceImageFrame | undefined {\n  if (!naturalSize || naturalSize.width <= 0 || naturalSize.height <= 0) {\n    return undefined;\n  }\n\n  let width = naturalSize.width;\n  let height = naturalSize.height;\n\n  if (backgroundSize === '100% 100%') {\n    width = containerWidth;\n    height = containerHeight;\n  } else if (backgroundSize === 'auto 100%') {\n    const scale = containerHeight / naturalSize.height;\n    width = naturalSize.width * scale;\n    height = naturalSize.height * scale;\n  } else if (backgroundSize === 'cover' || backgroundSize === 'contain') {\n    const scale = backgroundSize === 'cover'\n      ? Math.max(containerWidth / naturalSize.width, containerHeight / naturalSize.height)\n      : Math.min(containerWidth / naturalSize.width, containerHeight / naturalSize.height);\n    width = naturalSize.width * scale;\n    height = naturalSize.height * scale;\n  }\n\n  const [xToken, yToken] = normalizeBackgroundPosition(backgroundPosition);\n\n  return {\n    width,\n    height,\n    offsetX: resolveAxisOffset(containerWidth, width, normalizePositionToken(xToken)),\n    offsetY: resolveAxisOffset(containerHeight, height, normalizePositionToken(yToken)),\n  };\n}\n","import React, { useEffect, useMemo, useRef } from 'react';\nimport type {\n  DiagramBayClickPayload,\n  DiagramBayRef,\n  DiagramPortClickPayload,\n  DiagramPortHandleRef,\n} from '../types';\n\nexport type {\n  DiagramBayClickPayload,\n  DiagramBayRef,\n  DiagramPortClickPayload,\n  DiagramPortHandleRef,\n};\n\nexport type DiagramPortContextValue = {\n  onPortClick?: (payload: DiagramPortClickPayload) => void;\n  portClickEnabled: boolean;\n  /** Bay click handler — the bay analogue of `onPortClick`. */\n  onBayClick?: (payload: DiagramBayClickPayload) => void;\n  /**\n   * Whether bay clicks are wired (drives clickable styling + pointer events).\n   * Optional: `undefined` reads as falsy, so this stays purely additive to the\n   * existing context value shape.\n   */\n  bayClickEnabled?: boolean;\n};\n// NOTE: port highlighting flows exclusively through the instance-scoped\n// highlight store (useDiagramPortHandleHighlighted / DiagramPortHighlightSync).\n// `highlightedPortHandles` used to also live on this interaction value, which\n// re-rendered every DeviceNode whenever any highlight changed. It was redundant\n// (nothing reads it off the context) and was removed.\n\nconst defaultValue: DiagramPortContextValue = {\n  portClickEnabled: false,\n};\n\nconst DiagramPortContext = React.createContext<DiagramPortContextValue>(defaultValue);\n\nexport const DiagramPortProvider = DiagramPortContext.Provider;\n\nexport function useDiagramPortInteraction(): DiagramPortContextValue {\n  return React.useContext(DiagramPortContext);\n}\n\nexport function isDiagramPortHandleHighlighted(\n  highlightedPortHandles: readonly DiagramPortHandleRef[] | undefined,\n  nodeId: string,\n  handleId: string,\n): boolean {\n  if (!highlightedPortHandles?.length) {\n    return false;\n  }\n\n  return highlightedPortHandles.some(\n    (entry) => entry.nodeId === nodeId && entry.handleId === handleId,\n  );\n}\n\n/**\n * Per-handle highlight subscriptions (DeviceNode perf).\n * @internal\n */\nexport type HighlightStore = {\n  readonly keys: ReadonlySet<string>;\n  subscribe: (listener: () => void) => () => void;\n  /** Internal: replace the highlighted keys and notify subscribers. */\n  setKeys: (keys: ReadonlySet<string>) => void;\n};\n\nconst EMPTY_KEYS: ReadonlySet<string> = new Set<string>();\n\n/**\n * Factory for an instance-scoped highlight store. Exported for unit-testing the\n * `setKeys` set-equality short-circuit directly (notification behavior); NOT\n * re-exported from the package entry, so it is not part of the public API.\n * @internal\n */\nexport function createHighlightStore(): HighlightStore {\n  let keys: ReadonlySet<string> = EMPTY_KEYS;\n  const listeners = new Set<() => void>();\n  return {\n    get keys() {\n      return keys;\n    },\n    subscribe(listener) {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n    setKeys(next) {\n      // Short-circuit when the key SET is unchanged (e.g. a fresh array prop\n      // with identical contents): skip notifying so subscribers don't re-render\n      // unless a membership actually flipped. Applies to ports and bays alike.\n      if (next.size === keys.size) {\n        let identical = true;\n        for (const key of next) {\n          if (!keys.has(key)) {\n            identical = false;\n            break;\n          }\n        }\n        if (identical) {\n          return;\n        }\n      }\n      keys = next;\n      listeners.forEach((listener) => listener());\n    },\n  };\n}\n\n/**\n * Each diagram owns its own highlight store instance, provided through this\n * context. This avoids a module-level singleton that two diagrams on one page\n * would otherwise stomp (mounting one clearing the other's port highlights).\n */\nconst DiagramPortHighlightStoreContext =\n  React.createContext<HighlightStore | null>(null);\n\n/**\n * Bays get their own instance-scoped store, separate from ports: each store's\n * `setKeys` REPLACES the whole key set, so sharing one would make the port and\n * bay syncs clobber each other. Same isolation guarantee, one per capability.\n */\nconst DiagramBayHighlightStoreContext =\n  React.createContext<HighlightStore | null>(null);\n\n/** Lazily creates a stable highlight store for the lifetime of a diagram. */\nfunction useStableHighlightStore(): HighlightStore {\n  const ref = useRef<HighlightStore | null>(null);\n  if (ref.current === null) {\n    ref.current = createHighlightStore();\n  }\n  return ref.current;\n}\n\n/**\n * Provides instance-scoped highlight stores (ports + bays) to a single\n * diagram's subtree.\n */\nexport function DiagramPortHighlightStoreProvider({\n  children,\n}: {\n  children?: React.ReactNode;\n}) {\n  const portStore = useStableHighlightStore();\n  const bayStore = useStableHighlightStore();\n  return (\n    <DiagramPortHighlightStoreContext.Provider value={portStore}>\n      <DiagramBayHighlightStoreContext.Provider value={bayStore}>\n        {children}\n      </DiagramBayHighlightStoreContext.Provider>\n    </DiagramPortHighlightStoreContext.Provider>\n  );\n}\n\nfunction highlightKey(nodeId: string, handleId: string): string {\n  return `${nodeId}\\0${handleId}`;\n}\n\nexport function useDiagramPortHandleHighlighted(\n  nodeId: string,\n  handleId: string,\n): boolean {\n  const store = React.useContext(DiagramPortHighlightStoreContext);\n  const subscribe = React.useCallback(\n    (listener: () => void) => store?.subscribe(listener) ?? (() => {}),\n    [store],\n  );\n  const getSnapshot = React.useCallback(() => {\n    if (!store) return false;\n    return store.keys.has(highlightKey(nodeId, handleId));\n  }, [store, nodeId, handleId]);\n\n  return React.useSyncExternalStore(subscribe, getSnapshot, () => false);\n}\n\n/** Keeps this diagram's per-handle highlight store in sync with diagram props. */\nexport function DiagramPortHighlightSync({\n  highlightedPortHandles,\n}: {\n  highlightedPortHandles?: readonly DiagramPortHandleRef[];\n}) {\n  const store = React.useContext(DiagramPortHighlightStoreContext);\n  const keys = useMemo(() => {\n    const next = new Set<string>();\n    for (const entry of highlightedPortHandles ?? []) {\n      next.add(highlightKey(entry.nodeId, entry.handleId));\n    }\n    return next;\n  }, [highlightedPortHandles]);\n\n  useEffect(() => {\n    if (!store) return;\n    store.setKeys(keys);\n  }, [store, keys]);\n\n  return null;\n}\n\n/**\n * Whether a given bay is highlighted in this diagram — the bay analogue of\n * {@link useDiagramPortHandleHighlighted}. Returns false outside a store\n * provider (e.g. an isolated DeviceNode test), so bay markers degrade cleanly.\n */\nexport function useDiagramBayHighlighted(nodeId: string, bayId: string): boolean {\n  const store = React.useContext(DiagramBayHighlightStoreContext);\n  const subscribe = React.useCallback(\n    (listener: () => void) => store?.subscribe(listener) ?? (() => {}),\n    [store],\n  );\n  const getSnapshot = React.useCallback(() => {\n    if (!store) return false;\n    return store.keys.has(highlightKey(nodeId, bayId));\n  }, [store, nodeId, bayId]);\n\n  return React.useSyncExternalStore(subscribe, getSnapshot, () => false);\n}\n\n/** Keeps this diagram's per-bay highlight store in sync with diagram props. */\nexport function DiagramBayHighlightSync({\n  highlightedBays,\n}: {\n  highlightedBays?: readonly DiagramBayRef[];\n}) {\n  const store = React.useContext(DiagramBayHighlightStoreContext);\n  const keys = useMemo(() => {\n    const next = new Set<string>();\n    for (const entry of highlightedBays ?? []) {\n      next.add(highlightKey(entry.nodeId, entry.bayId));\n    }\n    return next;\n  }, [highlightedBays]);\n\n  useEffect(() => {\n    if (!store) return;\n    store.setKeys(keys);\n  }, [store, keys]);\n\n  return null;\n}\n","import {\n  DeviceFace,\n  HandleRole,\n  PortHandleStyle,\n  DISCONNECTED_PORT_BORDER_COLOR,\n  FILL_BACKGROUND_SIZE,\n  isPowerPort,\n  resolvePowerConnectorType,\n  getHandleId,\n  getPortColor,\n  resolvePortPosition,\n  getPortBlocksSignature,\n  getBaseHandleStyle,\n  getGenericPortDimensions,\n  isServicePort,\n  getServicePortDimensions,\n  getUsbPortDimensions,\n  resolvePortVisualKindForBlock,\n  clampPercent,\n  clampNumber,\n  getAbsoluteImagePortDimensions,\n  getPortHandleHitExpansion,\n  hasAbsolutePortCoordinates,\n  hasResolvableCustomPortPlacement,\n  getResolvedGenericPortDimensions,\n  getCustomHandlePlacement,\n  getAlternatingTopStackPlacement,\n  getFrontPowerHandlePlacement,\n  getFrontHandlePlacement,\n  getRearHandlePlacement,\n  getHandleRoles\n} from \"../utils/device-port-layout\";\nimport React from \"react\";\nimport {\n  Handle,\n  Position,\n  NodeProps,\n  useStore,\n  useUpdateNodeInternals,\n} from \"@xyflow/react\";\nimport {\n  Width,\n  PortBlock,\n  PortPosition,\n  NetworkFlowNode,\n  type DeviceBay,\n  type DeviceBayInstanceState,\n} from \"../types\";\nimport { U_HEIGHT_PX, RACK_WIDTH_PX } from \"../utils/constants\";\nimport {\n  resolveDeviceImageBackgroundSize,\n  resolveDeviceImageFrame,\n  type DeviceImageFrame,\n  type DeviceImageNaturalSize,\n} from \"../utils/device-image-layout\";\nimport {\n  getPowerPortStyle,\n} from \"../utils/power-connectors\";\nimport { getStatusColor } from \"../utils/status-colors\";\nimport {\n  applyFaceAliasHandleId,\n} from \"../utils/handle-ids\";\nimport {\n  getRackDeviceBodyWidthPx,\n  getRackDeviceImageContainerWidthPx,\n  getRackFaceGapPx,\n  getRackFaceWidthPx,\n  getRackMountMode,\n  getRackUnitHeightPx,\n  isSplitFaceRackLayout,\n} from \"../utils/rack-layout\";\nimport { getDevicePortVisibleFaces } from \"../utils/device-port-faces\";\nimport { getResolvedPortBlocks } from \"../utils/render-ports\";\nimport { activateOnEnterOrSpace, buildPortAriaLabel } from \"../utils/a11y\";\nimport {\n  useDiagramBayHighlighted,\n  useDiagramPortHandleHighlighted,\n  useDiagramPortInteraction,\n} from \"../context/DiagramPortContext\";\nimport type {\n  DiagramBayClickPayload,\n  DiagramPortClickPayload,\n} from \"../context/DiagramPortContext\";\n\ntype DeviceNodeCssVars = React.CSSProperties & {\n  \"--rn-device-bg-image\"?: string;\n  \"--rn-device-bg-position\"?: string;\n  \"--rn-device-bg-repeat\"?: string;\n  \"--rn-device-bg-size\"?: string;\n  \"--rn-device-bg-color\"?: string;\n  \"--rn-device-border-color\"?: string;\n  \"--rn-device-text-color\"?: string;\n  \"--rn-device-box-shadow\"?: string;\n  \"--rn-device-height\"?: string;\n  \"--rn-device-left\"?: string;\n  \"--rn-device-status-color\"?: string;\n  \"--rn-device-translate-y\"?: string;\n  \"--rn-device-width\"?: string;\n};\n\ntype DevicePrimaryPortHandleProps = {\n  nodeId: string;\n  renderedHandleId: string;\n  ghost: boolean;\n  portClickEnabled: boolean;\n  handleClassName?: string;\n  handleStyle: PortHandleStyle;\n  ghostHandleStyle: PortHandleStyle;\n  type: HandleRole;\n  position: Position;\n  title: string;\n  /** Accessible name, built by `buildPortAriaLabel` at the call site. */\n  ariaLabel: string;\n  portIndex?: number;\n  isPower: boolean;\n  onPortClick?: (payload: DiagramPortClickPayload) => void;\n  groupName: string;\n  portLabel: string;\n  face: DeviceFace;\n};\n\nconst DevicePrimaryPortHandle = React.memo(function DevicePrimaryPortHandle({\n  nodeId,\n  renderedHandleId,\n  ghost,\n  portClickEnabled,\n  handleClassName,\n  handleStyle,\n  ghostHandleStyle,\n  type,\n  position,\n  title,\n  ariaLabel,\n  portIndex,\n  isPower,\n  onPortClick,\n  groupName,\n  portLabel,\n  face,\n}: DevicePrimaryPortHandleProps) {\n  const isHighlighted = useDiagramPortHandleHighlighted(nodeId, renderedHandleId);\n  const highlighted = !ghost && isHighlighted;\n  const combinedHandleClassName = [\n    handleClassName,\n    ghost ? \"rn-port-handle--ghost\" : undefined,\n    !ghost && portClickEnabled ? \"rn-port-handle--clickable\" : undefined,\n    highlighted ? \"rn-port-handle--highlighted\" : undefined,\n  ]\n    .filter(Boolean)\n    .join(\" \");\n  // Prefer the authored label (e.g. \"1\"/\"2\") so a non-power sibling earlier in the\n  // block does not push IEC inlets to \"2\"/\"3\".\n  const powerDisplayLabel = (() => {\n    if (!isPower || ghost) {\n      return undefined;\n    }\n    const trimmed = portLabel.trim();\n    if (trimmed) {\n      return trimmed;\n    }\n    return portIndex;\n  })();\n\n  // A clickable port is a custom control: it needs the button role, a tab stop\n  // and Enter/Space activation, or it is mouse-only. Non-clickable ports stay\n  // out of the tab order (React Flow already makes the node itself focusable).\n  const interactive = !ghost && portClickEnabled && Boolean(onPortClick);\n  const activate = React.useCallback(() => {\n    onPortClick?.({\n      nodeId,\n      handleId: renderedHandleId,\n      groupName,\n      portLabel,\n      face,\n    });\n  }, [onPortClick, nodeId, renderedHandleId, groupName, portLabel, face]);\n\n  // A new handler identity on every render would defeat the React.memo above,\n  // so the wrapper is memoised on the same inputs the callback depends on.\n  const onKeyDown = React.useMemo(\n    () => (interactive ? activateOnEnterOrSpace(activate) : undefined),\n    [interactive, activate],\n  );\n\n  return (\n    <Handle\n      id={renderedHandleId}\n      type={type}\n      position={position}\n      isConnectable={ghost ? false : undefined}\n      className={combinedHandleClassName || undefined}\n      style={{ ...handleStyle, ...ghostHandleStyle }}\n      title={title}\n      // Ghost handles are the hidden-face duplicates (opacity 0, no pointer\n      // events); they must not be announced or focusable.\n      aria-hidden={ghost ? true : undefined}\n      aria-label={ghost ? undefined : ariaLabel}\n      // A React Flow handle is a plain <div> (role=generic), and a generic\n      // element exposes no accessible name. `role=\"button\"` gives the clickable\n      // ports one; a visible but non-interactive port needs a naming role too,\n      // and `img` is the least-surprising of them for a graphic port marker.\n      role={interactive ? \"button\" : ghost ? undefined : \"img\"}\n      tabIndex={interactive ? 0 : undefined}\n      onKeyDown={onKeyDown}\n      onClick={\n        interactive\n          ? (event) => {\n              event.stopPropagation();\n              activate();\n            }\n          : undefined\n      }\n    >\n      {powerDisplayLabel}\n    </Handle>\n  );\n});\n\n// ---------------------------------------------------------------------------\n// Bays — containable-only elements. Drawn as outlines + labels; they mount NO\n// React Flow handle (that's the whole point: a bay contains, a port cables).\n// Cages, which do both, stay ports and are handled in the port path above.\n// ---------------------------------------------------------------------------\n\n/** A resolved bay/slot rectangle in device-local pixels (top-left origin). */\ntype DeviceBayRect = {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n};\n\n/** The face frame a bay is placed within (falls back to the whole face). */\ntype DeviceBayFrame = {\n  width: number;\n  height: number;\n  offsetX: number;\n  offsetY: number;\n};\n\nconst DEFAULT_BAY_WIDTH_PX = 16;\nconst DEFAULT_BAY_HEIGHT_PX = 12;\nconst MIN_BAY_SIZE_PX = 3;\n\nfunction resolveBayFace(bay: DeviceBay): DeviceFace {\n  return bay.face === \"rear\" || bay.face === \"back\" ? \"rear\" : \"front\";\n}\n\nfunction resolveBayFrame(\n  frame: DeviceImageFrame | undefined,\n  faceRegionWidth: number,\n  deviceHeight: number,\n): DeviceBayFrame {\n  return {\n    width: frame?.width ?? faceRegionWidth,\n    height: frame?.height ?? deviceHeight,\n    offsetX: frame?.offsetX ?? 0,\n    offsetY: frame?.offsetY ?? 0,\n  };\n}\n\n/**\n * Resolve a bay/slot bounding box from its percent geometry. Mirrors the port\n * absolute-coordinate math: `xPercent`/`yPercent` are the element CENTRE,\n * `widthPercent`/`heightPercent` its box, all relative to the face frame. Falls\n * back to a small default box when only a centre is authored; returns undefined\n * (skip) when there is no resolvable centre.\n */\nfunction resolveBayRect(\n  position: PortPosition | undefined,\n  frame: DeviceBayFrame,\n  faceOffsetPx: number,\n): DeviceBayRect | undefined {\n  if (\n    !position ||\n    typeof position.xPercent !== \"number\" ||\n    !Number.isFinite(position.xPercent) ||\n    typeof position.yPercent !== \"number\" ||\n    !Number.isFinite(position.yPercent)\n  ) {\n    return undefined;\n  }\n\n  const centerX = frame.offsetX + (frame.width * clampPercent(position.xPercent)) / 100;\n  const centerY = frame.offsetY + (frame.height * clampPercent(position.yPercent)) / 100;\n\n  const hasWidth =\n    typeof position.widthPercent === \"number\" &&\n    Number.isFinite(position.widthPercent) &&\n    position.widthPercent > 0;\n  const hasHeight =\n    typeof position.heightPercent === \"number\" &&\n    Number.isFinite(position.heightPercent) &&\n    position.heightPercent > 0;\n\n  const width = clampNumber(\n    hasWidth ? (frame.width * clampPercent(position.widthPercent as number)) / 100 : DEFAULT_BAY_WIDTH_PX,\n    MIN_BAY_SIZE_PX,\n    frame.width,\n  );\n  const height = clampNumber(\n    hasHeight ? (frame.height * clampPercent(position.heightPercent as number)) / 100 : DEFAULT_BAY_HEIGHT_PX,\n    MIN_BAY_SIZE_PX,\n    frame.height,\n  );\n\n  return {\n    left: centerX - width / 2 + faceOffsetPx,\n    top: centerY - height / 2,\n    width,\n    height,\n  };\n}\n\ntype DeviceBayMarkerProps = {\n  nodeId: string;\n  bay: DeviceBay;\n  face: DeviceFace;\n  rect: DeviceBayRect;\n  slotRects: Array<{ id: string; rect: DeviceBayRect }>;\n  state?: DeviceBayInstanceState;\n  bayClickEnabled: boolean;\n  onBayClick?: (payload: DiagramBayClickPayload) => void;\n};\n\nconst DeviceBayMarker = React.memo(function DeviceBayMarker({\n  nodeId,\n  bay,\n  face,\n  rect,\n  slotRects,\n  state,\n  bayClickEnabled,\n  onBayClick,\n}: DeviceBayMarkerProps) {\n  const isHighlighted = useDiagramBayHighlighted(nodeId, bay.id);\n  const status = state?.status;\n  const clickable = bayClickEnabled && Boolean(onBayClick);\n\n  const className = [\n    \"device-node__bay\",\n    state?.occupied ? \"device-node__bay--occupied\" : \"device-node__bay--empty\",\n    status ? `device-node__bay--${status}` : undefined,\n    isHighlighted ? \"device-node__bay--highlighted\" : undefined,\n    // `nodrag` stops React Flow from starting a node drag on a click that moves\n    // a few px, so bay clicks land instead of dragging the device.\n    clickable ? \"device-node__bay--clickable nodrag\" : undefined,\n  ]\n    .filter(Boolean)\n    .join(\" \");\n\n  const style: React.CSSProperties = {\n    position: \"absolute\",\n    left: `${rect.left}px`,\n    top: `${rect.top}px`,\n    width: `${rect.width}px`,\n    height: `${rect.height}px`,\n    pointerEvents: clickable ? \"auto\" : \"none\",\n  };\n\n  const title = `${bay.label}${state?.occupied ? \" (occupied)\" : \"\"}${\n    status ? ` [${status}]` : \"\"\n  }`;\n\n  const ariaLabel = buildPortAriaLabel({\n    kind: \"Bay\",\n    label: bay.label,\n    status: [state?.occupied ? \"occupied\" : \"empty\", status],\n  });\n\n  const activate = React.useCallback(() => {\n    onBayClick?.({\n      nodeId,\n      bayId: bay.id,\n      bayKind: bay.bayKind,\n      bayLabel: bay.label,\n      face,\n    });\n  }, [onBayClick, nodeId, bay.id, bay.bayKind, bay.label, face]);\n\n  // Memoised so the handler identity is stable across renders and the\n  // React.memo wrapper above keeps working.\n  const onKeyDown = React.useMemo(\n    () => (clickable ? activateOnEnterOrSpace(activate) : undefined),\n    [clickable, activate],\n  );\n\n  return (\n    <div\n      className={className}\n      style={style}\n      title={title}\n      data-bay-id={bay.id}\n      data-bay-kind={bay.bayKind}\n      // Only a clickable bay becomes a control. A decorative outline stays out\n      // of the tab order and the accessibility tree's interactive surface.\n      aria-label={clickable ? ariaLabel : undefined}\n      role={clickable ? \"button\" : undefined}\n      tabIndex={clickable ? 0 : undefined}\n      onKeyDown={onKeyDown}\n      onClick={\n        clickable\n          ? (event) => {\n              event.stopPropagation();\n              activate();\n            }\n          : undefined\n      }\n    >\n      {slotRects.map((slot) => (\n        <div\n          key={slot.id}\n          className=\"device-node__bay-slot\"\n          style={{\n            position: \"absolute\",\n            left: `${slot.rect.left - rect.left}px`,\n            top: `${slot.rect.top - rect.top}px`,\n            width: `${slot.rect.width}px`,\n            height: `${slot.rect.height}px`,\n            pointerEvents: \"none\",\n          }}\n        />\n      ))}\n      <span className=\"device-node__bay-label\">{bay.label}</span>\n    </div>\n  );\n});\n\n/**\n * Hidden-face (\"ghost\") anchors stay mounted so React Flow can resolve the\n * endpoints of the face's saved edges (no error #008), but those edges are\n * hidden while the face is (see `computeEdgeZIndex`). React Flow's\n * closest-handle snapping measures every mounted handle — connectable or\n * not — so a ghost left on the face could win a near-miss drop and turn it\n * into a rejected connection. Park them far outside the node with no size:\n * still registered, never within any connection radius.\n *\n * Patch panels are the exception: their hidden-face trunk links stay visible,\n * so their ghosts must keep their on-face anchor positions.\n */\nconst GHOST_PARK_OFFSET_PX = -100_000;\nconst GHOST_PARKED_HANDLE_STYLE: PortHandleStyle = {\n  opacity: 0,\n  pointerEvents: \"none\",\n  left: `${GHOST_PARK_OFFSET_PX}px`,\n  top: `${GHOST_PARK_OFFSET_PX}px`,\n  right: \"auto\",\n  bottom: \"auto\",\n  transform: \"none\",\n  width: 0,\n  height: 0,\n  minWidth: 0,\n  minHeight: 0,\n  border: \"none\",\n};\n\nconst URL_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\nconst SAFE_DATA_IMAGE_URL_PATTERN = /^data:image\\//i;\nconst useIsomorphicLayoutEffect =\n  typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\nconst imageNaturalSizeCache = new Map<\n  string,\n  Promise<DeviceImageNaturalSize | undefined>\n>();\n\nfunction normalizeDeviceType(deviceType: string | undefined): string {\n  return (deviceType ?? \"\").toLowerCase().replace(/_/g, \"-\");\n}\n\nfunction isRelativeUrl(url: string): boolean {\n  return !URL_SCHEME_PATTERN.test(url) && !url.startsWith(\"//\");\n}\n\nfunction sanitizeDeviceImageUrl(url: string | undefined): string | undefined {\n  if (!url) return undefined;\n  const trimmed = url.trim();\n  if (!trimmed) return undefined;\n\n  if (isRelativeUrl(trimmed)) {\n    return trimmed;\n  }\n\n  try {\n    const parsedUrl = new URL(trimmed);\n    const protocol = parsedUrl.protocol.toLowerCase();\n    const hostname = parsedUrl.hostname.toLowerCase();\n    const isLocalHttp =\n      protocol === \"http:\" &&\n      (hostname === \"127.0.0.1\" || hostname === \"localhost\");\n\n    if (protocol === \"https:\" || protocol === \"blob:\" || isLocalHttp) {\n      return parsedUrl.href;\n    }\n\n    if (protocol === \"data:\" && SAFE_DATA_IMAGE_URL_PATTERN.test(trimmed)) {\n      return trimmed;\n    }\n\n    return undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction toCssUrlValue(url: string): string {\n  return `url(${JSON.stringify(url)})`;\n}\n\nfunction resolveDeviceImageObjectFit(\n  backgroundSize: ReturnType<typeof resolveDeviceImageBackgroundSize>,\n): React.CSSProperties[\"objectFit\"] {\n  switch (backgroundSize) {\n    case \"100% 100%\":\n      return \"fill\";\n    case \"auto\":\n      return \"none\";\n    case \"auto 100%\":\n      return \"contain\";\n    case \"contain\":\n      return \"contain\";\n    case \"cover\":\n    default:\n      return \"cover\";\n  }\n}\n\n/** @internal Exported for tests only. */\nexport function loadImageNaturalSize(\n  url: string,\n): Promise<DeviceImageNaturalSize | undefined> {\n  const cached = imageNaturalSizeCache.get(url);\n  if (cached) {\n    return cached;\n  }\n\n  const pending = new Promise<DeviceImageNaturalSize | undefined>((resolve) => {\n    if (typeof Image === \"undefined\") {\n      resolve(undefined);\n      return;\n    }\n\n    const image = new Image();\n    image.decoding = \"async\";\n    image.onload = () => {\n      if (image.naturalWidth > 0 && image.naturalHeight > 0) {\n        resolve({ width: image.naturalWidth, height: image.naturalHeight });\n        return;\n      }\n      resolve(undefined);\n    };\n    image.onerror = () => resolve(undefined);\n    image.src = url;\n  });\n\n  imageNaturalSizeCache.set(url, pending);\n  // Only successful measurements are cached for the session. A failed load\n  // (transient network error, an asset published after first render) is\n  // forgotten so the next mount of a node with that URL retries it instead of\n  // keeping the fallback frame forever.\n  void pending.then((size) => {\n    if (size === undefined && imageNaturalSizeCache.get(url) === pending) {\n      imageNaturalSizeCache.delete(url);\n    }\n  });\n  return pending;\n}\n\nfunction useImageNaturalSize(\n  url: string | undefined,\n): DeviceImageNaturalSize | undefined {\n  const [naturalSize, setNaturalSize] = React.useState<\n    DeviceImageNaturalSize | undefined\n  >(undefined);\n\n  React.useEffect(() => {\n    let cancelled = false;\n\n    if (!url) {\n      setNaturalSize(undefined);\n      return () => {\n        cancelled = true;\n      };\n    }\n\n    loadImageNaturalSize(url).then((resolvedSize) => {\n      if (!cancelled) {\n        setNaturalSize(resolvedSize);\n      }\n    });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [url]);\n\n  return naturalSize;\n}\n\n/**\n * Device visualization component with port management.\n *\n * Represents a device that can be placed in a rack. Supports port configuration,\n * U positioning, front/rear views, and various device types (servers, switches, routers, etc.).\n *\n * @example\n * ```tsx\n * {\n *   id: 'server-1',\n *   type: 'device',\n *   position: { x: 120, y: 150 },\n *   parentId: 'rack-1',\n *   data: {\n *     label: 'Web Server',\n *     deviceType: 'server',\n *     uPosition: 1,\n *     ports: [\n *       { id: 'eth0', label: 'eth0', group: 'Network Ports', connected: true, type: 'ethernet' }\n *     ]\n *   }\n * }\n * ```\n *\n * @public\n */\nconst DeviceNodeComponent: React.FC<NodeProps<NetworkFlowNode>> = ({\n  id,\n  data,\n  selected,\n  parentId,\n}) => {\n  const updateNodeInternals = useUpdateNodeInternals();\n  // A device parented to a rack is positioned on that rack's U pitch by the\n  // diagram, whether or not its data carries `isRackMounted` (only the rack\n  // builders and the document adapter set that flag). Read the parent rack's\n  // data so a hand-authored `parentId` + `uPosition` device is drawn at the\n  // same pitch it is placed on. The selector returns the rack's data object,\n  // which is referentially stable across drags, so this does not re-render\n  // the device as nodes move.\n  const parentRackData = useStore(\n    React.useCallback(\n      (state: { nodeLookup?: Map<string, { type?: string; data?: unknown }> }) => {\n        if (!parentId) return undefined;\n        const parent = state.nodeLookup?.get(parentId);\n        return parent?.type === \"rack\"\n          ? (parent.data as NetworkFlowNode[\"data\"] | undefined)\n          : undefined;\n      },\n      [parentId],\n    ),\n  );\n  const isRackMounted =\n    data.isRackMounted === true || parentRackData !== undefined;\n  // Geometry source: the device's own (adapter-populated) data when it says it\n  // is rack-mounted, otherwise the parent rack the diagram positions it in.\n  const rackGeometryData =\n    data.isRackMounted === true ? data : parentRackData ?? data;\n  const { onPortClick, portClickEnabled, onBayClick, bayClickEnabled } =\n    useDiagramPortInteraction();\n  const {\n    label,\n    status = \"active\",\n    deviceType = \"server\",\n    manufacturer,\n    uHeight = 1,\n    width = Width.FULL,\n    rackWidthPx,\n    frontImageUrl,\n    rearImageUrl,\n    imageFit,\n    imagePosition,\n    imageRepeat,\n    stretchToFit,\n    showDeviceImages = true,\n  } = data;\n  const { ports, rearPorts } = React.useMemo(\n    () => getResolvedPortBlocks(data),\n    [data],\n  );\n\n  const normalizedDeviceType = normalizeDeviceType(deviceType);\n  const effectiveRackWidth =\n    rackWidthPx ??\n    (parentRackData !== undefined\n      ? getRackFaceWidthPx(parentRackData)\n      : RACK_WIDTH_PX);\n  const resolvedBackgroundSize = React.useMemo(\n    () =>\n      resolveDeviceImageBackgroundSize(\n        imageFit ?? (isRackMounted ? \"contain\" : undefined),\n        stretchToFit,\n      ),\n    [imageFit, isRackMounted, stretchToFit],\n  );\n  const safeFrontImageUrl = React.useMemo(\n    () => sanitizeDeviceImageUrl(frontImageUrl),\n    [frontImageUrl],\n  );\n  const safeRearImageUrl = React.useMemo(\n    () => sanitizeDeviceImageUrl(rearImageUrl),\n    [rearImageUrl],\n  );\n  const frontImageNaturalSize = useImageNaturalSize(safeFrontImageUrl);\n  const rearImageNaturalSize = useImageNaturalSize(safeRearImageUrl);\n  const splitFaceLayout = isSplitFaceRackLayout(data);\n  const mountMode = getRackMountMode(data);\n  const visibleFaces = React.useMemo(\n    () => getDevicePortVisibleFaces(data, ports, rearPorts),\n    [data, ports, rearPorts],\n  );\n  const currentFace = visibleFaces[0] ?? \"front\";\n  const showSplitBothFaces = splitFaceLayout && mountMode === \"both\";\n  const rackUnitHeightPx = isRackMounted\n    ? getRackUnitHeightPx(rackGeometryData)\n    : U_HEIGHT_PX;\n  const deviceHeight = uHeight * rackUnitHeightPx;\n  const rackFaceGapPx = getRackFaceGapPx(data);\n  const rightFaceOffsetPx = effectiveRackWidth + rackFaceGapPx;\n  const imageContainerWidth = React.useMemo(\n    () =>\n      getRackDeviceImageContainerWidthPx(effectiveRackWidth, {\n        isRackMounted,\n        splitFaceLayout,\n        deviceWidth: width,\n      }),\n    [isRackMounted, effectiveRackWidth, splitFaceLayout, width],\n  );\n  const frontImageFrame = React.useMemo(\n    () =>\n      resolveDeviceImageFrame(\n        imageContainerWidth,\n        deviceHeight,\n        frontImageNaturalSize,\n        resolvedBackgroundSize,\n        imagePosition ?? \"center\",\n      ),\n    [\n      imageContainerWidth,\n      deviceHeight,\n      frontImageNaturalSize,\n      resolvedBackgroundSize,\n      imagePosition,\n    ],\n  );\n  const rearImageFrame = React.useMemo(\n    () =>\n      resolveDeviceImageFrame(\n        imageContainerWidth,\n        deviceHeight,\n        rearImageNaturalSize,\n        resolvedBackgroundSize,\n        imagePosition ?? \"center\",\n      ),\n    [\n      imageContainerWidth,\n      deviceHeight,\n      rearImageNaturalSize,\n      resolvedBackgroundSize,\n      imagePosition,\n    ],\n  );\n  const activeImageFrame = React.useMemo(\n    () => (currentFace === \"rear\" ? rearImageFrame : frontImageFrame),\n    [currentFace, frontImageFrame, rearImageFrame],\n  );\n  const hasVisibleFaceImage =\n    showDeviceImages &&\n    (currentFace === \"rear\"\n      ? Boolean(safeRearImageUrl)\n      : Boolean(safeFrontImageUrl));\n  const useRackImageLabelBadge =\n    isRackMounted && hasVisibleFaceImage && !showSplitBothFaces;\n  const activeFacePortBlocks = currentFace === \"rear\" ? rearPorts : ports;\n  const activeFaceNonPowerPorts = React.useMemo(\n    () =>\n      activeFacePortBlocks\n        .flatMap((block) => block.ports)\n        .filter((port) => !isPowerPort(port)),\n    [activeFacePortBlocks],\n  );\n  const shouldHideRackImageLabelBadge = React.useMemo(() => {\n    if (!useRackImageLabelBadge) {\n      return false;\n    }\n\n    const isDenseImageBackedNetworkFace =\n      (normalizedDeviceType === \"switch\" ||\n        normalizedDeviceType === \"patch-panel\" ||\n        normalizedDeviceType === \"router\") &&\n      activeFaceNonPowerPorts.length >= 24;\n    if (!isDenseImageBackedNetworkFace) {\n      return false;\n    }\n\n    return activeFaceNonPowerPorts.every((port) =>\n      hasAbsolutePortCoordinates(port),\n    );\n  }, [activeFaceNonPowerPorts, normalizedDeviceType, useRackImageLabelBadge]);\n\n  const handleLayoutSignature = React.useMemo(() => {\n    return [\n      splitFaceLayout ? \"split\" : \"single\",\n      mountMode,\n      visibleFaces.join(\",\"),\n      currentFace,\n      normalizedDeviceType,\n      label,\n      resolvedBackgroundSize,\n      imagePosition ?? \"center\",\n      frontImageFrame\n        ? `${frontImageFrame.width}:${frontImageFrame.height}:${frontImageFrame.offsetX}:${frontImageFrame.offsetY}`\n        : \"\",\n      rearImageFrame\n        ? `${rearImageFrame.width}:${rearImageFrame.height}:${rearImageFrame.offsetX}:${rearImageFrame.offsetY}`\n        : \"\",\n      deviceHeight.toString(),\n      imageContainerWidth.toString(),\n      rightFaceOffsetPx.toString(),\n      getPortBlocksSignature(ports),\n      getPortBlocksSignature(rearPorts),\n    ].join(\"|\");\n  }, [\n    splitFaceLayout,\n    mountMode,\n    visibleFaces,\n    currentFace,\n    normalizedDeviceType,\n    label,\n    resolvedBackgroundSize,\n    imagePosition,\n    frontImageFrame,\n    rearImageFrame,\n    deviceHeight,\n    imageContainerWidth,\n    rightFaceOffsetPx,\n    ports,\n    rearPorts,\n  ]);\n\n  useIsomorphicLayoutEffect(() => {\n    updateNodeInternals(id);\n  }, [handleLayoutSignature, id, updateNodeInternals]);\n\n  const resolveFaceBackgroundStyle = React.useCallback(\n    (face: DeviceFace): React.CSSProperties => {\n      const safeBackgroundImageUrl =\n        face === \"rear\" ? safeRearImageUrl : safeFrontImageUrl;\n      if (!safeBackgroundImageUrl) {\n        return {};\n      }\n\n      return {\n        backgroundImage: toCssUrlValue(safeBackgroundImageUrl),\n        backgroundSize:\n          resolvedBackgroundSize === \"100% 100%\"\n            ? FILL_BACKGROUND_SIZE\n            : resolvedBackgroundSize,\n        backgroundPosition: imagePosition ?? \"center\",\n        backgroundRepeat: imageRepeat ?? \"no-repeat\",\n      };\n    },\n    [\n      safeRearImageUrl,\n      safeFrontImageUrl,\n      resolvedBackgroundSize,\n      imagePosition,\n      imageRepeat,\n    ],\n  );\n\n  const frontBackgroundStyle = React.useMemo(\n    () => resolveFaceBackgroundStyle(\"front\"),\n    [resolveFaceBackgroundStyle],\n  );\n  const rearBackgroundStyle = React.useMemo(\n    () => resolveFaceBackgroundStyle(\"rear\"),\n    [resolveFaceBackgroundStyle],\n  );\n  const activeBackgroundStyle =\n    currentFace === \"rear\" ? rearBackgroundStyle : frontBackgroundStyle;\n  const resolvedImageObjectFit = React.useMemo(\n    () => resolveDeviceImageObjectFit(resolvedBackgroundSize),\n    [resolvedBackgroundSize],\n  );\n\n  let rackDeviceWidth: string;\n  let rackDeviceLeft: string | undefined;\n  if (splitFaceLayout) {\n    const bodyWidth = getRackDeviceBodyWidthPx(data, data);\n    rackDeviceWidth = `${bodyWidth + 4}px`;\n    // In split-face racks, React Flow child node position already carries face offset.\n    // Applying an internal left offset here would shift the device twice.\n    rackDeviceLeft = undefined;\n  } else {\n    const fullWidthPx = effectiveRackWidth + 4;\n    const halfWidthPx = Math.floor(effectiveRackWidth / 2);\n    rackDeviceWidth =\n      width === Width.HALF ? `${halfWidthPx}px` : `${fullWidthPx}px`;\n    rackDeviceLeft = width === Width.HALF ? `${halfWidthPx}px` : undefined;\n  }\n\n  const isOneUServer = normalizedDeviceType === \"server\" && uHeight === 1;\n  const statusColor = getStatusColor(status);\n  const deviceContainerStyle: DeviceNodeCssVars = {\n    \"--rn-device-bg-image\":\n      showDeviceImages && !showSplitBothFaces\n        ? String(activeBackgroundStyle.backgroundImage ?? \"none\")\n        : \"none\",\n    \"--rn-device-bg-position\": String(\n      activeBackgroundStyle.backgroundPosition ?? \"center\",\n    ),\n    \"--rn-device-bg-repeat\": String(\n      activeBackgroundStyle.backgroundRepeat ?? \"no-repeat\",\n    ),\n    \"--rn-device-bg-size\": String(\n      activeBackgroundStyle.backgroundSize ?? \"cover\",\n    ),\n    \"--rn-device-border-color\": statusColor,\n    // Selection ring is the shared 2px `.rn-node--selected` ring (this node\n    // used to draw a 1px one, out of step with every other node type).\n    \"--rn-device-box-shadow\": \"0 1px 2px rgba(0,0,0,0.1)\",\n    \"--rn-device-height\": `${deviceHeight}px`,\n    \"--rn-device-left\": rackDeviceLeft ?? \"auto\",\n    \"--rn-device-status-color\": statusColor,\n    \"--rn-device-translate-y\": isOneUServer ? \"-1px\" : \"0px\",\n    \"--rn-device-width\": rackDeviceWidth,\n  };\n  const imageBadgeContentStyle = React.useMemo<\n    React.CSSProperties | undefined\n  >(() => {\n    if (!useRackImageLabelBadge) {\n      return undefined;\n    }\n\n    const baseStyle: React.CSSProperties = {\n      position: \"absolute\",\n      top: \"2px\",\n      zIndex: 2,\n      padding: 0,\n      justifyContent: \"flex-start\",\n      pointerEvents: \"none\",\n    };\n    const indicatorReservePx = uHeight > 1 ? 24 : 8;\n\n    if (!activeImageFrame) {\n      if (shouldHideRackImageLabelBadge) {\n        return {\n          ...baseStyle,\n          display: \"none\",\n        };\n      }\n\n      return {\n        ...baseStyle,\n        left: \"4px\",\n        right: `${indicatorReservePx}px`,\n        alignItems: \"flex-start\",\n      };\n    }\n\n    const labelInsetPx = 4;\n    const leftGutterPx = Math.max(\n      0,\n      Math.floor(activeImageFrame.offsetX) - labelInsetPx,\n    );\n    const rightGutterPx = Math.max(\n      0,\n      Math.floor(\n        imageContainerWidth -\n          (activeImageFrame.offsetX + activeImageFrame.width),\n      ) - labelInsetPx,\n    );\n\n    if (Math.max(leftGutterPx, rightGutterPx) >= 56) {\n      const placeOnRight = rightGutterPx > leftGutterPx;\n      return {\n        ...baseStyle,\n        left: placeOnRight ? \"auto\" : `${labelInsetPx}px`,\n        right: placeOnRight ? `${labelInsetPx}px` : \"auto\",\n        width: `${Math.max(48, Math.max(leftGutterPx, rightGutterPx))}px`,\n        alignItems: placeOnRight ? \"flex-end\" : \"flex-start\",\n      };\n    }\n\n    if (shouldHideRackImageLabelBadge) {\n      return {\n        ...baseStyle,\n        display: \"none\",\n      };\n    }\n\n    return {\n      ...baseStyle,\n      left: \"4px\",\n      right: `${indicatorReservePx}px`,\n      alignItems: \"flex-start\",\n    };\n  }, [\n    activeImageFrame,\n    imageContainerWidth,\n    shouldHideRackImageLabelBadge,\n    uHeight,\n    useRackImageLabelBadge,\n  ]);\n\n  const frontHandleIds = React.useMemo(() => {\n    const ids = new Set<string>();\n    for (const block of ports) {\n      for (const port of block.ports) {\n        ids.add(getHandleId(block.name, port));\n      }\n    }\n    return ids;\n  }, [ports]);\n\n  const rearHandleIds = React.useMemo(() => {\n    const ids = new Set<string>();\n    for (const block of rearPorts) {\n      for (const port of block.ports) {\n        ids.add(getHandleId(block.name, port));\n      }\n    }\n    return ids;\n  }, [rearPorts]);\n\n  const resolveRenderedHandleId = React.useCallback(\n    (handleId: string, face: DeviceFace, ghost = false): string => {\n      if (\n        face === \"rear\" &&\n        (showSplitBothFaces || ghost) &&\n        frontHandleIds.has(handleId)\n      ) {\n        return applyFaceAliasHandleId(handleId, \"rear\");\n      }\n      if (face === \"front\" && ghost && rearHandleIds.has(handleId)) {\n        return applyFaceAliasHandleId(handleId, \"front\");\n      }\n      return handleId;\n    },\n    [frontHandleIds, rearHandleIds, showSplitBothFaces],\n  );\n\n  const renderHandlesForPortBlocks = React.useCallback(\n    (\n      portBlocks: PortBlock[],\n      face: DeviceFace,\n      faceOffsetPx: number = 0,\n      faceRegionWidth: number = imageContainerWidth,\n      ghost: boolean = false,\n    ) => {\n      const handles: React.ReactElement[] = [];\n      if (portBlocks.length === 0) {\n        return handles;\n      }\n      const parkGhostHandles = normalizedDeviceType !== \"patch-panel\";\n\n      // Keep non-power ports at a stable visual size across chassis heights.\n      const absoluteCoordinateFrame =\n        face === \"rear\" ? rearImageFrame : frontImageFrame;\n      const standardPortDimensions = getGenericPortDimensions(faceRegionWidth);\n      const hasImageBackedAbsolutePorts = Boolean(\n        absoluteCoordinateFrame &&\n        portBlocks.some((block) =>\n          block.ports.some(\n            (port) => !isPowerPort(port) && hasAbsolutePortCoordinates(port),\n          ),\n        ),\n      );\n      const { width: portWidth, height: portHeight } =\n        normalizedDeviceType === \"switch\" ||\n        normalizedDeviceType === \"patch-panel\"\n          ? hasImageBackedAbsolutePorts\n            ? getResolvedGenericPortDimensions(\n                portBlocks,\n                faceRegionWidth,\n                deviceHeight,\n                absoluteCoordinateFrame,\n              )\n            : getGenericPortDimensions(faceRegionWidth)\n          : getResolvedGenericPortDimensions(\n              portBlocks,\n              faceRegionWidth,\n              deviceHeight,\n              absoluteCoordinateFrame,\n            );\n      const totalPortsOnFace = portBlocks.reduce(\n        (total, block) => total + block.ports.length,\n        0,\n      );\n      const shouldLineUpDenseSwitchPortsAgainstTop =\n        (normalizedDeviceType === \"switch\" ||\n          normalizedDeviceType === \"patch-panel\") &&\n        totalPortsOnFace >= 24;\n      // The visual groups do not define independent coordinate systems. Keep\n      // one fallback sequence per face/category so the first port in every\n      // block does not receive the same automatic position.\n      const automaticallyPlacedPorts = portBlocks\n        .flatMap((block) => block.ports)\n        .filter((port) => !hasResolvableCustomPortPlacement(port));\n      const automaticDataPortCount = automaticallyPlacedPorts.filter(\n        (port) => !isPowerPort(port),\n      ).length;\n      const automaticPowerPortCount =\n        automaticallyPlacedPorts.length - automaticDataPortCount;\n      let automaticDataPortIndex = 0;\n      let automaticPowerPortIndex = 0;\n\n      for (const portBlock of portBlocks) {\n        for (\n          let portIndex = 0;\n          portIndex < portBlock.ports.length;\n          portIndex += 1\n        ) {\n          const port = portBlock.ports[portIndex];\n          const handleId = getHandleId(portBlock.name, port);\n          const isPower = isPowerPort(port);\n          const hasCustomPlacement = hasResolvableCustomPortPlacement(port);\n          const automaticPortIndex = isPower\n            ? automaticPowerPortIndex\n            : automaticDataPortIndex;\n          const automaticPortCount = Math.max(\n            isPower ? automaticPowerPortCount : automaticDataPortCount,\n            1,\n          );\n          const visualKind = resolvePortVisualKindForBlock(\n            port,\n            portBlock.name,\n          );\n\n          // Resolve actual port dimensions — power ports use connector-specific\n          // sizes from getPowerPortStyle which differ from the generic dimensions.\n          // Using the correct size here ensures clamp bounds and center-to-corner\n          // offsets in getCustomHandlePlacement are accurate.\n          let actualPortWidth = portWidth;\n          let actualPortHeight = portHeight;\n          if (isPower) {\n            const connectorType = resolvePowerConnectorType(port, label);\n            const powerStyle = getPowerPortStyle(connectorType);\n            actualPortWidth = powerStyle.width;\n            actualPortHeight = powerStyle.height;\n          } else if (port.type === \"usb\") {\n            const usbPortDimensions = getUsbPortDimensions(\n              standardPortDimensions,\n            );\n            actualPortWidth = usbPortDimensions.width;\n            actualPortHeight = usbPortDimensions.height;\n          } else if (\n            visualKind === \"sfp\" ||\n            visualKind === \"qsfp\" ||\n            visualKind === \"osfp\"\n          ) {\n            const authoredPortDimensions = getAbsoluteImagePortDimensions(\n              port,\n              portBlock.name,\n              standardPortDimensions,\n              absoluteCoordinateFrame,\n            );\n            actualPortWidth = authoredPortDimensions.width;\n            actualPortHeight = authoredPortDimensions.height;\n          } else if (visualKind === \"rj45\" || visualKind === \"console\") {\n            const authoredPortDimensions = getAbsoluteImagePortDimensions(\n              port,\n              portBlock.name,\n              standardPortDimensions,\n              absoluteCoordinateFrame,\n            );\n            actualPortWidth = authoredPortDimensions.width;\n            actualPortHeight = authoredPortDimensions.height;\n          } else if (isServicePort(port)) {\n            const servicePortDimensions = getServicePortDimensions(\n              standardPortDimensions,\n            );\n            actualPortWidth = servicePortDimensions.width;\n            actualPortHeight = servicePortDimensions.height;\n          }\n\n          const position = resolvePortPosition(port);\n          if (\n            hasImageBackedAbsolutePorts &&\n            totalPortsOnFace >= 24 &&\n            !isPower &&\n            hasAbsolutePortCoordinates(port) &&\n            !(typeof position?.widthPercent === \"number\" &&\n              Number.isFinite(position.widthPercent) && position.widthPercent > 0)\n          ) {\n            // Inferred connector sizes must fit the image's port spacing,\n            // including switches and RJ45 ports. Explicit authored sizes win.\n            actualPortWidth = Math.min(actualPortWidth, portWidth);\n          }\n\n          const portColor = getPortColor(port);\n          const borderColor = port.connected\n            ? portColor\n            : DISCONNECTED_PORT_BORDER_COLOR;\n          const baseStyle = getBaseHandleStyle(\n            actualPortWidth,\n            actualPortHeight,\n            portColor,\n            borderColor,\n          );\n          const defaultPlacement =\n            face === \"front\"\n              ? isPower\n                ? getFrontPowerHandlePlacement(\n                    port,\n                    automaticPortIndex,\n                    automaticPortCount,\n                    deviceHeight,\n                    label,\n                  )\n                : shouldLineUpDenseSwitchPortsAgainstTop\n                  ? getAlternatingTopStackPlacement(\n                      automaticPortIndex,\n                      automaticPortCount,\n                      actualPortWidth,\n                      actualPortHeight,\n                      faceRegionWidth,\n                      deviceHeight,\n                    )\n                  : getFrontHandlePlacement(\n                      automaticPortIndex,\n                      automaticPortCount,\n                      actualPortWidth,\n                      faceRegionWidth,\n                    )\n              : !isPower && shouldLineUpDenseSwitchPortsAgainstTop\n                ? getAlternatingTopStackPlacement(\n                    automaticPortIndex,\n                    automaticPortCount,\n                    actualPortWidth,\n                    actualPortHeight,\n                    faceRegionWidth,\n                    deviceHeight,\n                  )\n                : getRearHandlePlacement(\n                    port,\n                    automaticPortIndex,\n                    automaticPortCount,\n                    actualPortWidth,\n                    actualPortHeight,\n                    faceRegionWidth,\n                    deviceHeight,\n                    normalizedDeviceType,\n                    label,\n                  );\n          if (!hasCustomPlacement) {\n            if (isPower) {\n              automaticPowerPortIndex += 1;\n            } else {\n              automaticDataPortIndex += 1;\n            }\n          }\n          const customPlacement = getCustomHandlePlacement(\n            port,\n            actualPortWidth,\n            actualPortHeight,\n            faceRegionWidth,\n            deviceHeight,\n            absoluteCoordinateFrame,\n          );\n          const placement = customPlacement\n            ? isPower\n              ? {\n                  position: customPlacement.position,\n                  style: {\n                    ...defaultPlacement.style,\n                    ...customPlacement.style,\n                  },\n                }\n              : customPlacement\n            : defaultPlacement;\n\n          const hitExpansion = getPortHandleHitExpansion(\n            port,\n            portBlock.name,\n            placement.position,\n            absoluteCoordinateFrame,\n          );\n          const hitAreaStyle: PortHandleStyle = hitExpansion\n            ? {\n                \"--rn-hit-expand-top\": `${hitExpansion.top}px`,\n                \"--rn-hit-expand-right\": `${hitExpansion.right}px`,\n                \"--rn-hit-expand-bottom\": `${hitExpansion.bottom}px`,\n                \"--rn-hit-expand-left\": `${hitExpansion.left}px`,\n              }\n            : {};\n          const handleClassName = hitExpansion ? \"rn-port-hitbox\" : undefined;\n\n          const handleStyle: PortHandleStyle = {\n            ...baseStyle,\n            ...placement.style,\n            ...hitAreaStyle,\n          };\n          const translatedHandleStyle: PortHandleStyle = { ...handleStyle };\n          if (translatedHandleStyle.left !== undefined && faceOffsetPx !== 0) {\n            if (typeof translatedHandleStyle.left === \"number\") {\n              translatedHandleStyle.left = `${translatedHandleStyle.left + faceOffsetPx}px`;\n            } else {\n              translatedHandleStyle.left = `calc(${translatedHandleStyle.left} + ${faceOffsetPx}px)`;\n            }\n          }\n\n          if (showSplitBothFaces && translatedHandleStyle.left === undefined) {\n            if (placement.position === Position.Left) {\n              translatedHandleStyle.left = `${faceOffsetPx}px`;\n              translatedHandleStyle.right = undefined;\n            } else if (placement.position === Position.Right) {\n              translatedHandleStyle.left = `${faceOffsetPx + faceRegionWidth}px`;\n              translatedHandleStyle.right = undefined;\n            }\n          }\n\n          const renderedHandleId = resolveRenderedHandleId(\n            handleId,\n            face,\n            ghost,\n          );\n          const ghostHandleStyle: PortHandleStyle = ghost\n            ? parkGhostHandles\n              ? GHOST_PARKED_HANDLE_STYLE\n              : { opacity: 0, pointerEvents: \"none\" }\n            : {};\n          const roles = getHandleRoles(isPower, normalizedDeviceType, face);\n          if (roles.secondary) {\n            // Only patch panels draw both directional siblings; every other\n            // device stacks an invisible secondary under the visible primary.\n            const isPatchPanelStrip = normalizedDeviceType === \"patch-panel\";\n            const secondaryHandleStyle: PortHandleStyle =\n              !isPatchPanelStrip\n                ? {\n                    ...translatedHandleStyle,\n                    background: \"transparent\",\n                    border: \"none\",\n                    opacity: 0,\n                  }\n                : translatedHandleStyle;\n            const secondaryTitle =\n              !isPatchPanelStrip\n                ? `${portBlock.name} - ${port.label} (input) ${port.connected ? \"(connected)\" : \"(disconnected)\"}`\n                : `${portBlock.name} - ${port.label} (${roles.secondary}) ${port.connected ? \"(connected)\" : \"(disconnected)\"}`;\n            handles.push(\n              <Handle\n                key={`${renderedHandleId}-${roles.secondary}`}\n                id={`${renderedHandleId}-${roles.secondary}`}\n                type={roles.secondary}\n                isConnectable={ghost ? false : undefined}\n                position={placement.position}\n                className={\n                  [handleClassName, ghost ? \"rn-port-handle--ghost\" : undefined]\n                    .filter(Boolean)\n                    .join(\" \") || undefined\n                }\n                style={{ ...secondaryHandleStyle, ...ghostHandleStyle }}\n                title={\n                  ghost ? `${secondaryTitle} (hidden face)` : secondaryTitle\n                }\n                // The secondary handle is a second connection endpoint stacked\n                // on the primary one for the same physical port — invisible on\n                // switches/routers, exactly coincident elsewhere. The primary\n                // handle already carries the port's accessible name, so this one\n                // is hidden and unnamed: a 48-port switch announces 48 ports,\n                // not 96.\n                aria-hidden\n              />,\n            );\n          }\n\n          handles.push(\n            <DevicePrimaryPortHandle\n              key={renderedHandleId}\n              nodeId={id}\n              renderedHandleId={renderedHandleId}\n              ghost={ghost}\n              portClickEnabled={portClickEnabled}\n              handleClassName={handleClassName}\n              handleStyle={translatedHandleStyle}\n              ghostHandleStyle={ghostHandleStyle}\n              type={roles.primary}\n              position={placement.position}\n              title={`${portBlock.name} - ${port.label} ${port.connected ? \"(connected)\" : \"(disconnected)\"}${ghost ? \" (hidden face)\" : \"\"}`}\n              ariaLabel={buildPortAriaLabel({\n                kind: \"Port\",\n                group: portBlock.name,\n                label: port.label,\n                status: [port.connected ? \"connected\" : \"disconnected\"],\n              })}\n              portIndex={portIndex + 1}\n              isPower={isPower}\n              onPortClick={onPortClick}\n              groupName={portBlock.name}\n              portLabel={port.label}\n              face={face}\n            />,\n          );\n        }\n      }\n\n      return handles;\n    },\n    [\n      deviceHeight,\n      imageContainerWidth,\n      frontImageFrame,\n      id,\n      label,\n      normalizedDeviceType,\n      onPortClick,\n      portClickEnabled,\n      rearImageFrame,\n      resolveRenderedHandleId,\n      showSplitBothFaces,\n    ],\n  );\n\n  const renderedHandles = React.useMemo(() => {\n    const handles: React.ReactElement[] = [];\n    for (const face of visibleFaces) {\n      if (face === \"front\" && ports.length > 0) {\n        const faceOffsetPx = showSplitBothFaces ? rightFaceOffsetPx : 0;\n        handles.push(\n          ...renderHandlesForPortBlocks(\n            ports,\n            \"front\",\n            faceOffsetPx,\n            imageContainerWidth,\n          ),\n        );\n      }\n      if (face === \"rear\" && rearPorts.length > 0) {\n        const faceOffsetPx = 0;\n        handles.push(\n          ...renderHandlesForPortBlocks(\n            rearPorts,\n            \"rear\",\n            faceOffsetPx,\n            imageContainerWidth,\n          ),\n        );\n      }\n    }\n    // React Flow resolves endpoints even for hidden edges. Keep invisible,\n    // non-interactive anchors for inactive faces so saved connections retain\n    // their endpoints without emitting error #008 on every store update.\n    const visibleFaceSet = new Set(visibleFaces);\n    if (!visibleFaceSet.has(\"front\") && ports.length > 0) {\n      handles.push(\n        ...renderHandlesForPortBlocks(\n          ports,\n          \"front\",\n          0,\n          imageContainerWidth,\n          true,\n        ),\n      );\n    }\n    if (!visibleFaceSet.has(\"rear\") && rearPorts.length > 0) {\n      handles.push(\n        ...renderHandlesForPortBlocks(\n          rearPorts,\n          \"rear\",\n          0,\n          imageContainerWidth,\n          true,\n        ),\n      );\n    }\n    return handles;\n  }, [\n    visibleFaces,\n    ports,\n    rearPorts,\n    renderHandlesForPortBlocks,\n    imageContainerWidth,\n    showSplitBothFaces,\n    rightFaceOffsetPx,\n  ]);\n\n  // Containable-only bays: outlines + labels only, no React Flow handles. Placed\n  // with the same face-frame percent math as absolute ports, per visible face.\n  const nodeBays: DeviceBay[] = React.useMemo(\n    () => (Array.isArray(data.bays) ? (data.bays as DeviceBay[]) : []),\n    [data.bays],\n  );\n  const bayState = data.bayState;\n  const renderedBays = React.useMemo(() => {\n    if (nodeBays.length === 0) {\n      return [] as React.ReactElement[];\n    }\n    const markers: React.ReactElement[] = [];\n    for (const face of visibleFaces) {\n      const faceOffsetPx = face === \"front\" && showSplitBothFaces ? rightFaceOffsetPx : 0;\n      const frame = resolveBayFrame(\n        face === \"rear\" ? rearImageFrame : frontImageFrame,\n        imageContainerWidth,\n        deviceHeight,\n      );\n      for (const bay of nodeBays) {\n        if (resolveBayFace(bay) !== face) {\n          continue;\n        }\n        const rect = resolveBayRect(bay.position, frame, faceOffsetPx);\n        if (!rect) {\n          continue;\n        }\n        const slotRects = (bay.slots ?? []).flatMap((slot) => {\n          const slotRect = resolveBayRect(slot.position, frame, faceOffsetPx);\n          return slotRect ? [{ id: slot.id, rect: slotRect }] : [];\n        });\n        markers.push(\n          <DeviceBayMarker\n            key={`bay-${face}-${bay.id}`}\n            nodeId={id}\n            bay={bay}\n            face={face}\n            rect={rect}\n            slotRects={slotRects}\n            state={bayState?.[bay.id]}\n            bayClickEnabled={Boolean(bayClickEnabled)}\n            onBayClick={onBayClick}\n          />,\n        );\n      }\n    }\n    return markers;\n  }, [\n    nodeBays,\n    visibleFaces,\n    showSplitBothFaces,\n    rightFaceOffsetPx,\n    rearImageFrame,\n    frontImageFrame,\n    imageContainerWidth,\n    deviceHeight,\n    id,\n    bayState,\n    bayClickEnabled,\n    onBayClick,\n  ]);\n\n  return (\n    <div\n      className={`device-node ${selected ? \"selected rn-node--selected\" : \"\"} ${showSplitBothFaces ? \"device-node--split-both\" : \"\"}`}\n      style={deviceContainerStyle}\n    >\n      {showDeviceImages && !showSplitBothFaces && hasVisibleFaceImage && (\n        <div className=\"device-node__image-layer\" aria-hidden=\"true\">\n          <img\n            className=\"device-node__image\"\n            src={currentFace === \"rear\" ? safeRearImageUrl : safeFrontImageUrl}\n            alt=\"\"\n            draggable={false}\n            style={{\n              objectFit: resolvedImageObjectFit,\n              objectPosition: imagePosition ?? \"center\",\n            }}\n          />\n        </div>\n      )}\n\n      {showDeviceImages && showSplitBothFaces && (\n        <div\n          className=\"device-node__split-face-layer\"\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            display: \"flex\",\n            pointerEvents: \"none\",\n            zIndex: 0,\n          }}\n        >\n          <div\n            className=\"device-node__split-face device-node__split-face--rear\"\n            style={{\n              width: `${effectiveRackWidth}px`,\n              backgroundImage: String(\n                rearBackgroundStyle.backgroundImage ?? \"none\",\n              ),\n              backgroundPosition: String(\n                rearBackgroundStyle.backgroundPosition ?? \"center\",\n              ),\n              backgroundRepeat: String(\n                rearBackgroundStyle.backgroundRepeat ?? \"no-repeat\",\n              ),\n              backgroundSize: String(\n                rearBackgroundStyle.backgroundSize ?? \"cover\",\n              ),\n            }}\n          >\n            {safeRearImageUrl ? (\n              <img\n                className=\"device-node__image\"\n                src={safeRearImageUrl}\n                alt=\"\"\n                draggable={false}\n                style={{\n                  objectFit: resolvedImageObjectFit,\n                  objectPosition: imagePosition ?? \"center\",\n                }}\n              />\n            ) : null}\n          </div>\n          <div\n            className=\"device-node__face-bridge\"\n            data-testid=\"device-face-bridge\"\n            style={{ width: `${rackFaceGapPx}px` }}\n          >\n            <div className=\"device-node__face-bridge-line\" />\n          </div>\n          <div\n            className=\"device-node__split-face device-node__split-face--front\"\n            style={{\n              width: `${effectiveRackWidth}px`,\n              backgroundImage: String(\n                frontBackgroundStyle.backgroundImage ?? \"none\",\n              ),\n              backgroundPosition: String(\n                frontBackgroundStyle.backgroundPosition ?? \"center\",\n              ),\n              backgroundRepeat: String(\n                frontBackgroundStyle.backgroundRepeat ?? \"no-repeat\",\n              ),\n              backgroundSize: String(\n                frontBackgroundStyle.backgroundSize ?? \"cover\",\n              ),\n            }}\n          >\n            {safeFrontImageUrl ? (\n              <img\n                className=\"device-node__image\"\n                src={safeFrontImageUrl}\n                alt=\"\"\n                draggable={false}\n                style={{\n                  objectFit: resolvedImageObjectFit,\n                  objectPosition: imagePosition ?? \"center\",\n                }}\n              />\n            ) : null}\n          </div>\n        </div>\n      )}\n\n      {renderedBays.length > 0 && (\n        <div className=\"device-node__bay-layer\" data-testid=\"device-bay-layer\">\n          {renderedBays}\n        </div>\n      )}\n\n      <div\n        className={`device-node__content ${useRackImageLabelBadge ? \"device-node__content--image-badge\" : \"\"}`}\n        style={imageBadgeContentStyle ?? { position: \"relative\", zIndex: 1 }}\n      >\n        <div\n          className={`device-node__label ${useRackImageLabelBadge ? \"device-node__label--badge\" : \"\"}`}\n        >\n          {label}\n        </div>\n        {!useRackImageLabelBadge && uHeight > 1 && (\n          <div className=\"device-node__manufacturer\">{manufacturer}</div>\n        )}\n      </div>\n\n      {uHeight > 1 && (\n        <div className=\"device-node__u-indicator\">{uHeight}U</div>\n      )}\n\n      <div className=\"device-node__status-indicator\" />\n\n      {renderedHandles.length > 0 ? (\n        renderedHandles\n      ) : (\n        // Label-only fallback: with no resolvable ports there would be zero\n        // connection handles, and React Flow silently drops every edge whose\n        // endpoint has none. Exactly ONE target (left) and ONE source (right):\n        // id-less edges resolve to the FIRST handle of the required type, so a\n        // multi-handle fallback would anchor a linear path's in- and out-edges\n        // on the same point. A single pair gives deterministic left-in /\n        // right-out anchoring for horizontal trace layouts.\n        // These anchors carry no port identity of their own — they exist only\n        // so edges have somewhere to attach — so they are hidden rather than\n        // named; the node itself is what a screen reader should announce.\n        <>\n          <Handle type=\"target\" position={Position.Left} aria-hidden />\n          <Handle type=\"source\" position={Position.Right} aria-hidden />\n        </>\n      )}\n    </div>\n  );\n};\n\n/** Memoized to avoid re-rendering rack devices when unrelated nodes move (React Flow perf guide). */\nexport const DeviceNode = React.memo(\n  DeviceNodeComponent,\n  (previous, next) =>\n    previous.id === next.id &&\n    previous.selected === next.selected &&\n    previous.dragging === next.dragging &&\n    previous.data === next.data,\n);\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkNode, Port, PortBlock, PortPosition, NetworkFlowNode } from '../types';\nimport { getStatusColor } from '../utils/status-colors';\nimport { getPowerPortStyle, getPDUPortType, hasPduInlet } from '../utils/power-connectors';\nimport { buildPortHandleId, PDU_INLET_HANDLE_ID } from '../utils/handle-ids';\nimport { normalizePortPositionInput } from '../utils/port-position';\nimport { getFrontPortBlocks, getRearPortBlocks } from '../utils/render-ports';\nimport { MAX_POWER_PORTS } from '../utils/document-adapter';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nexport enum MgmtUnitPosition {\n  TOP = 'top',\n  MIDDLE = 'middle',\n  BOTTOM = 'bottom'\n}\n\ninterface RenderablePDUPort {\n  id: string;\n  label: string;\n  connectorType: 'C13' | 'C19' | 'AC';\n  groupName: string;\n  position?: PortPosition;\n}\n\ninterface PortRenderPlacement {\n  position: Position;\n  leftPx: number;\n  topPx: number;\n}\n\ninterface AutoLayoutResult {\n  portCenters: number[];\n  mgmtCenterY?: number;\n}\n\ntype LayoutStackItem =\n  | {\n      kind: 'port';\n      portIndex: number;\n      height: number;\n      groupName: string;\n    }\n  | {\n      kind: 'mgmt';\n      height: number;\n    };\n\nconst PDU_HEADER_HEIGHT = 20;\nconst PDU_BOTTOM_PADDING = 3;\nconst PDU_TOP_PADDING = 2;\nconst PDU_BORDER_WIDTH = 1;\nconst INTER_PORT_GAP_PX = 2;\nconst INTER_GROUP_GAP_PX = 6;\nconst MGMT_UNIT_HEIGHT_PX = 12;\n\nfunction clampPercent(value: number): number {\n  return Math.max(0, Math.min(100, value));\n}\n\nfunction clampNumber(value: number, min: number, max: number): number {\n  return Math.max(min, Math.min(max, value));\n}\n\nfunction inferClosestSideFromPercent(xPercent: number, yPercent: number): NonNullable<PortPosition['side']> {\n  const distances: Array<{ side: NonNullable<PortPosition['side']>; distance: number }> = [\n    { side: 'left', distance: xPercent },\n    { side: 'right', distance: 100 - xPercent },\n    { side: 'top', distance: yPercent },\n    { side: 'bottom', distance: 100 - yPercent },\n  ];\n  distances.sort((a, b) => a.distance - b.distance);\n  return distances[0].side;\n}\n\nfunction normalizeConnectorType(value: unknown): 'C13' | 'C19' | 'AC' | undefined {\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n  const normalized = value.trim().toUpperCase();\n  if (normalized === 'C13') return 'C13';\n  if (normalized === 'C19') return 'C19';\n  if (normalized === 'AC') return 'AC';\n  return undefined;\n}\n\nfunction resolvePortConnectorType(\n  port: Port,\n  fallback: 'C13' | 'C19' | 'AC'\n): 'C13' | 'C19' | 'AC' {\n  const fromConnector = normalizeConnectorType(port.connectorType);\n  if (fromConnector) {\n    return fromConnector;\n  }\n\n  const type = String(port.type ?? '').toLowerCase();\n  if (type.includes('c19')) return 'C19';\n  if (type.includes('c13')) return 'C13';\n  if (type.includes('power_ac')) return 'AC';\n  return fallback;\n}\n\nfunction toPduPortGroups(data: NetworkNode['data']): PortBlock[] {\n  if (Array.isArray(data.pduPortGroups) && data.pduPortGroups.length > 0) {\n    return data.pduPortGroups;\n  }\n  const rearPorts = getRearPortBlocks(data);\n  if (rearPorts.length > 0) {\n    return rearPorts;\n  }\n  const frontPorts = getFrontPortBlocks(data);\n  if (frontPorts.length > 0) {\n    return frontPorts;\n  }\n  return [];\n}\n\nfunction getConnectorSequence(data: NetworkNode['data'], count: number): Array<'C13' | 'C19' | 'AC'> {\n  if (!Array.isArray(data.portTypes) || data.portTypes.length === 0) {\n    return Array.from({ length: count }, (_, index) => normalizeConnectorType(getPDUPortType(index)) ?? 'C13');\n  }\n\n  return Array.from({ length: count }, (_, index) => {\n    const explicit = normalizeConnectorType(data.portTypes?.[index]);\n    if (explicit) {\n      return explicit;\n    }\n    return normalizeConnectorType(getPDUPortType(index)) ?? 'C13';\n  });\n}\n\nfunction buildRenderablePorts(data: NetworkNode['data'], powerPorts: number): RenderablePDUPort[] {\n  // Defence in depth: clamp again here so the synthetic allocation is bounded\n  // regardless of caller, sharing the same ceiling as the serialize path.\n  const boundedPowerPorts = Math.min(MAX_POWER_PORTS, powerPorts);\n  const customGroups = toPduPortGroups(data);\n  const connectorSequence = getConnectorSequence(data, boundedPowerPorts);\n  if (customGroups.length === 0) {\n    return Array.from({ length: boundedPowerPorts }, (_, index) => ({\n      id: `${index + 1}`,\n      label: `${index + 1}`,\n      connectorType: connectorSequence[index] ?? 'C13',\n      groupName: 'Power',\n    }));\n  }\n\n  const ports: RenderablePDUPort[] = [];\n  let flatIndex = 0;\n  customGroups.forEach((group) => {\n    group.ports.forEach((port) => {\n      const fallbackType = connectorSequence[flatIndex] ?? 'C13';\n      ports.push({\n        id: buildPortHandleId(group.name, port),\n        label: port.label,\n        connectorType: resolvePortConnectorType(port, fallbackType),\n        groupName: group.name,\n        position: normalizePortPositionInput(port.position),\n      });\n      flatIndex += 1;\n    });\n  });\n  return ports;\n}\n\nfunction getPortRenderHeightPx(port: RenderablePDUPort): number {\n  return getPowerPortStyle(port.connectorType).height;\n}\n\nfunction getMgmtInsertionSlot(mgmtUnit: NetworkNode['data']['mgmt_unit'], totalPorts: number): number {\n  if (!mgmtUnit || totalPorts <= 0) {\n    return -1;\n  }\n  if (mgmtUnit === MgmtUnitPosition.TOP) {\n    return 0;\n  }\n  if (mgmtUnit === MgmtUnitPosition.BOTTOM) {\n    return totalPorts;\n  }\n  return Math.floor(totalPorts / 2);\n}\n\nfunction buildLayoutStackItems(ports: RenderablePDUPort[], mgmtInsertionSlot: number): LayoutStackItem[] {\n  const items: LayoutStackItem[] = [];\n  const includeMgmt = mgmtInsertionSlot >= 0;\n  const slot = includeMgmt ? clampNumber(mgmtInsertionSlot, 0, ports.length) : -1;\n\n  for (let index = 0; index <= ports.length; index += 1) {\n    if (includeMgmt && index === slot) {\n      items.push({\n        kind: 'mgmt',\n        height: MGMT_UNIT_HEIGHT_PX,\n      });\n    }\n    if (index < ports.length) {\n      items.push({\n        kind: 'port',\n        portIndex: index,\n        height: getPortRenderHeightPx(ports[index]),\n        groupName: ports[index].groupName,\n      });\n    }\n  }\n\n  return items;\n}\n\nfunction getGapBetweenItems(current: LayoutStackItem, next: LayoutStackItem): number {\n  if (current.kind === 'port' && next.kind === 'port') {\n    return current.groupName === next.groupName ? INTER_PORT_GAP_PX : INTER_GROUP_GAP_PX;\n  }\n  return INTER_PORT_GAP_PX;\n}\n\nfunction getRequiredBodyHeightPx(ports: RenderablePDUPort[], mgmtInsertionSlot: number): number {\n  const items = buildLayoutStackItems(ports, mgmtInsertionSlot);\n  if (items.length === 0) {\n    return Math.max(PDU_TOP_PADDING + PDU_BOTTOM_PADDING + 1, 1);\n  }\n\n  let stackHeight = 0;\n  for (let index = 0; index < items.length; index += 1) {\n    stackHeight += items[index].height;\n    if (index < items.length - 1) {\n      stackHeight += getGapBetweenItems(items[index], items[index + 1]);\n    }\n  }\n\n  return PDU_TOP_PADDING + stackHeight + PDU_BOTTOM_PADDING;\n}\n\nfunction buildAutoLayout(ports: RenderablePDUPort[], bodyHeight: number, mgmtInsertionSlot: number): AutoLayoutResult {\n  const items = buildLayoutStackItems(ports, mgmtInsertionSlot);\n  if (items.length === 0) {\n    return { portCenters: [] };\n  }\n\n  const baseGaps: number[] = [];\n  for (let index = 0; index < items.length - 1; index += 1) {\n    baseGaps.push(getGapBetweenItems(items[index], items[index + 1]));\n  }\n\n  const totalItemHeights = items.reduce((total, item) => total + item.height, 0);\n  const totalBaseGaps = baseGaps.reduce((total, gap) => total + gap, 0);\n  const requiredStackHeight = totalItemHeights + totalBaseGaps;\n  const drawableHeight = Math.max(bodyHeight - PDU_TOP_PADDING - PDU_BOTTOM_PADDING, 1);\n\n  let startOffset = 0;\n  let adjustedGaps = baseGaps;\n  const extraHeight = Math.max(0, drawableHeight - requiredStackHeight);\n  if (extraHeight > 0) {\n    if (baseGaps.length === 0) {\n      startOffset = extraHeight / 2;\n    } else {\n      const weights = baseGaps.map((gap) => (gap === INTER_GROUP_GAP_PX ? 1.2 : 1));\n      const totalWeight = weights.reduce((total, weight) => total + weight, 0);\n      adjustedGaps = baseGaps.map((gap, index) => {\n        if (totalWeight <= 0) {\n          return gap;\n        }\n        return gap + (extraHeight * weights[index]) / totalWeight;\n      });\n    }\n  }\n\n  const portCenters: number[] = [];\n  let mgmtCenterY: number | undefined;\n  let cursor = PDU_TOP_PADDING + startOffset;\n\n  for (let index = 0; index < items.length; index += 1) {\n    const item = items[index];\n    const centerY = cursor + item.height / 2;\n\n    if (item.kind === 'mgmt') {\n      mgmtCenterY = centerY;\n    } else {\n      portCenters[item.portIndex] = centerY;\n    }\n\n    cursor += item.height;\n    if (index < items.length - 1) {\n      cursor += adjustedGaps[index] ?? INTER_PORT_GAP_PX;\n    }\n  }\n\n  return {\n    portCenters,\n    mgmtCenterY,\n  };\n}\n\nfunction resolveCustomPlacement(\n  position: PortPosition | undefined,\n  fallbackCenterY: number,\n  bodyHeight: number,\n  widthPx: number,\n  handleWidth: number,\n  handleHeight: number\n): PortRenderPlacement {\n  if (!position) {\n    return {\n      position: Position.Right,\n      leftPx: Math.round((widthPx - handleWidth) / 2) - 1,\n      topPx: fallbackCenterY - (handleHeight / 2),\n    };\n  }\n\n  const side = position.side;\n  const sourcePosition =\n    side === 'left'\n      ? Position.Left\n      : side === 'top'\n        ? Position.Top\n        : side === 'bottom'\n          ? Position.Bottom\n          : Position.Right;\n\n  const offsetPercent = typeof position.offsetPercent === 'number' ? clampPercent(position.offsetPercent) : undefined;\n  const offsetPx = typeof position.offsetPx === 'number' ? position.offsetPx : undefined;\n  const xPercent = typeof position.xPercent === 'number' ? clampPercent(position.xPercent) : undefined;\n  const yPercent = typeof position.yPercent === 'number' ? clampPercent(position.yPercent) : undefined;\n\n  if (xPercent !== undefined && yPercent !== undefined) {\n    const resolvedSide = side ?? inferClosestSideFromPercent(xPercent, yPercent);\n    const resolvedPosition =\n      resolvedSide === 'left'\n        ? Position.Left\n        : resolvedSide === 'top'\n          ? Position.Top\n          : resolvedSide === 'bottom'\n            ? Position.Bottom\n            : Position.Right;\n    const drawableHeight = Math.max(bodyHeight - PDU_BOTTOM_PADDING, 1);\n    const centerX = (widthPx * xPercent) / 100;\n    const centerY = clampNumber(\n      (drawableHeight * yPercent) / 100,\n      handleHeight / 2,\n      Math.max(handleHeight / 2, bodyHeight - handleHeight / 2)\n    );\n\n    return {\n      position: resolvedPosition,\n      leftPx: centerX - (handleWidth / 2),\n      topPx: centerY - (handleHeight / 2),\n    };\n  }\n\n  let centerX = xPercent !== undefined ? (widthPx * xPercent) / 100 : widthPx - (handleWidth / 2) - 1;\n  let centerY =\n    yPercent !== undefined\n      ? ((Math.max(bodyHeight - PDU_BOTTOM_PADDING, 1) * yPercent) / 100)\n      : fallbackCenterY;\n\n  if (side === 'top' || side === 'bottom') {\n    if (offsetPx !== undefined) {\n      centerX = offsetPx;\n    } else if (offsetPercent !== undefined) {\n      centerX = (widthPx * offsetPercent) / 100;\n    }\n    centerY = side === 'top'\n      ? (handleHeight / 2)\n      : bodyHeight - (handleHeight / 2);\n  } else {\n    if (offsetPx !== undefined) {\n      centerY = offsetPx;\n    } else if (offsetPercent !== undefined) {\n      centerY = ((Math.max(bodyHeight - PDU_BOTTOM_PADDING, 1) * offsetPercent) / 100);\n    }\n    if (side === 'left') {\n      centerX = handleWidth / 2;\n    } else if (side === 'right') {\n      centerX = widthPx - (handleWidth / 2) - 1;\n    }\n  }\n\n  centerY = clampNumber(\n    centerY,\n    handleHeight / 2,\n    Math.max(handleHeight / 2, bodyHeight - handleHeight / 2)\n  );\n\n  return {\n    position: sourcePosition,\n    leftPx: centerX - (handleWidth / 2),\n    topPx: centerY - (handleHeight / 2),\n  };\n}\n\n/**\n * The inlet's geometry: it spans the full width of the strip and is taller\n * than a C13 outlet, so the one fixed-wiring termination reads as visually\n * distinct from the row of cord outlets below it.\n */\nconst INLET_HANDLE_HEIGHT_PX = 9;\nconst INLET_HANDLE_MIN_WIDTH_PX = 12;\n\n/** Resolved inlet description, or `undefined` when the strip declares none. */\ninterface PduInlet {\n  connector?: string;\n  amps?: number;\n  gender?: 'male' | 'female';\n  feedType?: 'primary' | 'secondary';\n  voltage?: number;\n  phases?: number;\n  title: string;\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n  if (typeof value !== 'string') return undefined;\n  const trimmed = value.trim();\n  return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readOptionalNumber(value: unknown): number | undefined {\n  return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * Resolve the strip's supply inlet from node data.\n *\n * Back-compat contract: returns `undefined` unless the strip actually\n * describes an inlet (`inletConnector` / `inletAmps` / `inletGender`), so a PDU\n * authored before the inlet model mounts no extra handle and renders\n * pixel-identically. `feedType`, `voltage` and `phases` only enrich the\n * tooltip — on their own they are not an inlet.\n */\nfunction resolvePduInlet(data: NetworkNode['data']): PduInlet | undefined {\n  if (!hasPduInlet(data)) {\n    return undefined;\n  }\n  const connector = readOptionalString((data as { inletConnector?: unknown }).inletConnector);\n  const amps = readOptionalNumber((data as { inletAmps?: unknown }).inletAmps);\n  const genderRaw = readOptionalString((data as { inletGender?: unknown }).inletGender)?.toLowerCase();\n  const gender = genderRaw === 'male' || genderRaw === 'female' ? genderRaw : undefined;\n\n  const feedRaw = readOptionalString((data as { feedType?: unknown }).feedType)?.toLowerCase();\n  const feedType = feedRaw === 'primary' || feedRaw === 'secondary' ? feedRaw : undefined;\n  const voltage = readOptionalNumber((data as { voltage?: unknown }).voltage);\n  const phases = readOptionalNumber((data as { phases?: unknown }).phases);\n\n  const title = [\n    'Inlet',\n    connector,\n    amps !== undefined ? `${amps} A` : undefined,\n    voltage !== undefined ? `${voltage} V` : undefined,\n    phases !== undefined ? `${phases}-phase` : undefined,\n    gender,\n    feedType ? `${feedType} feed` : undefined,\n  ]\n    .filter(Boolean)\n    .join(' · ');\n\n  return { connector, amps, gender, feedType, voltage, phases, title };\n}\n\n/**\n * The strip's single supply inlet: where a branch circuit from a\n * distribution-board breaker way lands.\n *\n * Drawn at the very top of the frame, inside the header band and above the\n * label, and absolutely positioned so adding it never changes the node's\n * height — `resolveNodeDimensions` and the rack layout keep agreeing with the\n * renderer.\n *\n * It sits fully within the node box (`top: 0`, and the header is 20 px against\n * the handle's 9 px) rather than straddling the top edge: a rack-mounted strip\n * that overhung its frame would draw over the U above it and steal that U's\n * pointer events.\n */\nfunction renderInletHandle(inlet: PduInlet, widthPx: number): React.ReactElement {\n  const handleWidth = Math.max(INLET_HANDLE_MIN_WIDTH_PX, widthPx);\n  return (\n    <Handle\n      key=\"pdu-inlet\"\n      type=\"target\"\n      position={Position.Top}\n      id={PDU_INLET_HANDLE_ID}\n      className=\"vertical-pdu-node__inlet-handle\"\n      data-feed-type={inlet.feedType}\n      // Named like every other port marker; the title already spells out the\n      // inlet's connector/rating/feed, so the accessible name reuses it.\n      aria-label={inlet.title.split(' · ').join(', ')}\n      role=\"img\"\n      style={{\n        position: 'absolute',\n        left: `${Math.max(0, (widthPx - handleWidth) / 2)}px`,\n        top: '0px',\n        right: 'initial',\n        bottom: 'initial',\n        width: `${handleWidth}px`,\n        height: `${INLET_HANDLE_HEIGHT_PX}px`,\n        background: inlet.feedType === 'secondary' ? '#38bdf8' : '#94a3b8',\n        border: `1px solid ${inlet.feedType === 'secondary' ? '#0284c7' : '#475569'}`,\n        borderRadius: '2px',\n        boxSizing: 'border-box',\n        transform: 'none',\n        cursor: 'pointer',\n        zIndex: 12,\n      }}\n      title={inlet.title}\n    />\n  );\n}\n\nfunction renderMgmtUnit(yCenter: number, widthPx: number): React.ReactElement {\n  const mgmtWidth = 16;\n  const mgmtHeight = 12;\n  return (\n    <div\n      key=\"mgmt-unit\"\n      style={{\n        position: 'absolute',\n        left: `${(widthPx - mgmtWidth) / 2}px`,\n        top: `${yCenter - (mgmtHeight / 2)}px`,\n        width: `${mgmtWidth}px`,\n        height: `${mgmtHeight}px`,\n        background: 'var(--rn-pdu-body, #1a1a1a)',\n        border: '1px solid var(--rn-border-subtle, #374151)',\n        borderRadius: '1px',\n        display: 'flex',\n        flexDirection: 'column',\n        padding: '1px',\n        zIndex: 10,\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          left: '-10px',\n          top: '1px',\n          background: 'var(--rn-pdu-outlet, #ffffff)',\n          border: '0.5px solid var(--rn-pdu-outlet-border, #6b7280)',\n          borderRadius: '0.5px',\n          width: '8px',\n          height: '10px',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          fontSize: '3px',\n          color: 'var(--rn-status-ok, #10b981)',\n          fontFamily: 'monospace',\n          fontWeight: 'bold',\n          transform: 'rotate(90deg)',\n          transformOrigin: 'center',\n        }}\n      >\n        APC\n      </div>\n      <Handle\n        key=\"mgmt-net-1\"\n        type=\"source\"\n        position={Position.Right}\n        id=\"mgmt-net-1\"\n        aria-label={buildPortAriaLabel({ kind: 'Port', group: 'Management', label: 'network 1' })}\n        role=\"img\"\n        style={{\n          width: '70%',\n          height: '6px',\n          background: 'var(--rn-status-ok, #10b981)',\n          border: '1px solid var(--rn-status-ok-border, #059669)',\n          borderRadius: '1px',\n          cursor: 'pointer',\n          zIndex: 11,\n          margin: '0 auto',\n        }}\n        title=\"Management Network Port\"\n      />\n    </div>\n  );\n}\n\nconst VerticalPDUComponent: React.FC<NodeProps<NetworkFlowNode>> = ({\n  data,\n  selected,\n}) => {\n  const {\n    label,\n    status = 'active',\n    heightCm = 100,\n    powerPorts = 36,\n    widthPx = 15,\n    mgmt_unit,\n  } = data;\n\n  const safePowerPorts = Math.max(0, Math.min(MAX_POWER_PORTS, powerPorts));\n  const ports = buildRenderablePorts(data, safePowerPorts);\n  const mgmtInsertionSlot = getMgmtInsertionSlot(mgmt_unit, ports.length);\n  const requestedHeightPx = heightCm * 4.2;\n  const minimumBodyHeightPx = getRequiredBodyHeightPx(ports, mgmtInsertionSlot);\n  const bodyHeight = Math.max(requestedHeightPx - PDU_HEADER_HEIGHT, minimumBodyHeightPx);\n  const bodyWidth = Math.max(widthPx - (PDU_BORDER_WIDTH * 2), 1);\n  const heightPx = PDU_HEADER_HEIGHT + bodyHeight;\n  const autoLayout = buildAutoLayout(ports, bodyHeight, mgmtInsertionSlot);\n  const inlet = resolvePduInlet(data);\n\n  return (\n    <div\n      className={`vertical-pdu-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        background:\n          'linear-gradient(90deg, var(--rn-pdu-chassis-mid, #2d3748) 0%, var(--rn-pdu-chassis-shadow, #1a202c) 50%, var(--rn-pdu-chassis-mid, #2d3748) 100%)',\n        border: `1px solid ${getStatusColor(status)}`,\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        padding: 0,\n        width: `${widthPx}px`,\n        height: `${heightPx}px`,\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '12px',\n        boxShadow: '0 1px 3px rgba(0,0,0,0.3)',\n        boxSizing: 'border-box',\n        position: 'relative',\n        display: 'flex',\n        flexDirection: 'column',\n      }}\n    >\n      <div style={{\n        textAlign: 'center',\n        marginBottom: 0,\n        zIndex: 2,\n        background:\n          'linear-gradient(90deg, var(--rn-pdu-chassis-highlight, #4a5568) 0%, var(--rn-pdu-chassis-mid, #2d3748) 50%, var(--rn-pdu-chassis-highlight, #4a5568) 100%)',\n        padding: '2px',\n        position: 'relative',\n        height: `${PDU_HEADER_HEIGHT}px`,\n        display: 'flex',\n        flexDirection: 'column',\n        justifyContent: 'center',\n        borderBottom: '1px solid var(--rn-pdu-chassis-highlight, #4a5568)',\n      }}>\n        {inlet ? renderInletHandle(inlet, bodyWidth) : null}\n        <div style={{ fontWeight: 'bold', fontSize: '6px', color: 'var(--rn-pdu-header-text, #e2e8f0)' }}>{label}</div>\n      </div>\n\n      <div\n        style={{\n          flex: 1,\n          background:\n            'linear-gradient(180deg, var(--rn-pdu-chassis-mid, #2d3748) 0%, var(--rn-pdu-chassis-shadow, #1a202c) 50%, var(--rn-pdu-chassis-mid, #2d3748) 100%)',\n          position: 'relative',\n          minHeight: `${heightPx - PDU_HEADER_HEIGHT}px`,\n          overflow: 'hidden',\n        }}\n      >\n        {typeof autoLayout.mgmtCenterY === 'number' ? renderMgmtUnit(autoLayout.mgmtCenterY, bodyWidth) : null}\n        {ports.map((port, index) => {\n          const fallbackCenterY = autoLayout.portCenters[index] ?? (bodyHeight / 2);\n          const style = getPowerPortStyle(port.connectorType);\n          const placement = resolveCustomPlacement(\n            port.position,\n            fallbackCenterY,\n            bodyHeight,\n            bodyWidth,\n            style.width,\n            style.height\n          );\n\n          return (\n            <Handle\n              key={`handle-${port.id}`}\n              type=\"source\"\n              position={placement.position}\n              id={port.id}\n              style={{\n                position: 'absolute',\n                left: `${placement.leftPx}px`,\n                top: `${placement.topPx}px`,\n                right: 'initial',\n                bottom: 'initial',\n                width: `${style.width}px`,\n                height: `${style.height}px`,\n                background: style.color,\n                border: `1px solid ${style.borderColor}`,\n                boxSizing: 'border-box',\n                transform: 'none',\n                cursor: 'pointer',\n                zIndex: 10,\n                display: 'flex',\n                alignItems: 'center',\n                justifyContent: 'center',\n                fontSize: style.fontSize,\n                color: 'var(--rn-text-on-accent, #1a1a1a)',\n                fontFamily: 'monospace',\n                fontWeight: 'bold',\n                borderRadius: style.borderRadius,\n                clipPath: style.clipPath,\n              }}\n              title={`${port.groupName} - ${port.label} (${port.connectorType})`}\n              aria-label={buildPortAriaLabel({\n                kind: 'Outlet',\n                group: port.groupName,\n                label: port.label,\n                status: [port.connectorType],\n              })}\n              role=\"img\"\n            >\n              {port.label}\n            </Handle>\n          );\n        })}\n      </div>\n    </div>\n  );\n};\n\nexport const VerticalPDU = React.memo(\n  VerticalPDUComponent,\n  (previous, next) =>\n    previous.id === next.id &&\n    previous.selected === next.selected &&\n    previous.dragging === next.dragging &&\n    previous.data === next.data,\n);\n","import React from 'react';\nimport {\n  Handle,\n  Position,\n  NodeProps,\n  useUpdateNodeInternals,\n} from '@xyflow/react';\nimport type {\n  BreakerWay,\n  BreakerWayInstanceState,\n  BreakerSwitchState,\n  NetworkFlowNode,\n} from '../types';\nimport { getStatusColor } from '../utils/status-colors';\nimport { buildBreakerWayHandleId } from '../utils/handle-ids';\nimport {\n  BOARD_HEADER_HEIGHT_PX,\n  BOARD_WAY_GAP_PX,\n  buildDistributionBoardLayout,\n  type BreakerWayRect,\n} from '../utils/distribution-board-layout';\nimport { useDiagramBayHighlighted } from '../context/DiagramPortContext';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nconst useIsomorphicLayoutEffect =\n  typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;\n\n/**\n * The breaker toggle glyph: a slot with a lever in it, drawn as two crisp\n * rectangles and nothing else. The lever sits high (closed), low (open) or\n * stopped between the two (tripped) — the read a real MCB gives you from\n * across the room. Portrait on purpose: the rack/PDU vocabulary is round\n * status dots and square outlet blocks, so nothing else in the library is\n * shaped like this.\n *\n * Sized so the three positions are separable at demo zoom: a 15px slot holds a\n * 5px lever with 4px between each stop. The 7x12 glyph this replaced had 3px\n * of travel and read as a dot that jittered.\n */\nconst WAY_MARKER_WIDTH_PX = 9;\nconst WAY_MARKER_HEIGHT_PX = 15;\nconst HANDLE_SIZE_PX = 8;\n\n/**\n * Board-type badge text. A board is a `DB` unless its data names a more\n * specific distribution asset, in which case the asset's own vocabulary wins\n * (an RPP and a busway tap-off are not \"DB\"s to the people who maintain them).\n */\nconst BOARD_BADGE_BY_TYPE: Record<string, string> = {\n  db: 'DB',\n  pdu: 'PDU',\n  rpp: 'RPP',\n  panel: 'PANEL',\n  busway: 'BUSWAY',\n};\n\nfunction resolveBoardBadge(boardType: unknown): string {\n  if (typeof boardType !== 'string') return 'DB';\n  return BOARD_BADGE_BY_TYPE[boardType.trim().toLowerCase()] ?? 'DB';\n}\n\n/**\n * Where the lever sits for each switch state. Exposed as `data-lever-position`\n * so the visual state is assertable without reading computed CSS.\n */\nconst LEVER_POSITION_BY_STATE: Record<BreakerSwitchState, 'up' | 'down' | 'mid'> = {\n  closed: 'up',\n  open: 'down',\n  tripped: 'mid',\n};\n\nfunction normalizeSwitchState(value: unknown): BreakerSwitchState {\n  return value === 'open' || value === 'tripped' ? value : 'closed';\n}\n\ntype BreakerWayRowProps = {\n  nodeId: string;\n  rect: BreakerWayRect;\n  state?: BreakerWayInstanceState;\n};\n\n/**\n * One breaker way: a breaker module clipped onto a DIN rail — toggle lever,\n * way label, rating, and the way's power handle.\n *\n * Way highlighting rides the diagram's instance-scoped BAY highlight store\n * (`highlightedBays: [{ nodeId, bayId: wayId }]`) — ways are the board's\n * analogue of a device bay, so they share the store rather than adding a third\n * one. Outside a store provider the hook returns false, so isolated renders\n * still work.\n */\nconst BreakerWayRow = React.memo(function BreakerWayRow({\n  nodeId,\n  rect,\n  state,\n}: BreakerWayRowProps) {\n  const way: BreakerWay = rect.way;\n  const isHighlighted = useDiagramBayHighlighted(nodeId, way.id);\n  const switchState = normalizeSwitchState(state?.switchState);\n  const status = state?.status;\n  const handleId = buildBreakerWayHandleId(way.id);\n\n  const className = [\n    'distribution-board-node__way',\n    `distribution-board-node__way--${switchState}`,\n    status ? `distribution-board-node__way--${status}` : undefined,\n    isHighlighted ? 'distribution-board-node__way--highlighted' : undefined,\n  ]\n    .filter(Boolean)\n    .join(' ');\n\n  const markerClassName = [\n    'distribution-board-node__way-marker',\n    `distribution-board-node__way-marker--${switchState}`,\n  ].join(' ');\n\n  // Three tokens per row and no more: the way number, the rating printed\n  // beside the lever the way it is on a real breaker module, and the phase as\n  // a muted right-hand annotation. Pole count is real but rarely the thing you\n  // are scanning for, so it lives in the row's title instead of on the face.\n  const rating = typeof way.ratingA === 'number' ? `${way.ratingA}A` : undefined;\n  const detail = typeof way.phase === 'string' ? way.phase : undefined;\n  const titleDetail = [way.phase, rating, typeof way.poles === 'number' ? `${way.poles}P` : undefined]\n    .filter(Boolean)\n    .join(' ');\n\n  return (\n    <div\n      className={className}\n      data-way-id={way.id}\n      data-switch-state={switchState}\n      title={`${way.label}${titleDetail ? ` (${titleDetail})` : ''} [${switchState}]`}\n      style={{\n        position: 'absolute',\n        left: `${rect.left}px`,\n        top: `${rect.top}px`,\n        width: `${rect.width}px`,\n        height: `${rect.height}px`,\n      }}\n    >\n      <span\n        className={markerClassName}\n        data-lever-position={LEVER_POSITION_BY_STATE[switchState]}\n        style={{ width: `${WAY_MARKER_WIDTH_PX}px`, height: `${WAY_MARKER_HEIGHT_PX}px` }}\n        aria-hidden=\"true\"\n      >\n        <span className=\"distribution-board-node__way-lever\" />\n      </span>\n      <span className=\"distribution-board-node__way-label\">{way.label}</span>\n      {rating ? (\n        <span className=\"distribution-board-node__way-rating\">{rating}</span>\n      ) : null}\n      {detail ? (\n        <span className=\"distribution-board-node__way-detail\">{detail}</span>\n      ) : null}\n      <Handle\n        key={handleId}\n        type=\"source\"\n        position={Position.Right}\n        id={handleId}\n        className=\"distribution-board-node__way-handle\"\n        aria-label={buildPortAriaLabel({\n          kind: 'Breaker way',\n          label: way.label,\n          status: [rating, switchState, status],\n        })}\n        role=\"img\"\n        style={{\n          position: 'absolute',\n          right: `-${HANDLE_SIZE_PX / 2}px`,\n          // Centred on the breaker module, not on the whole row: the bottom\n          // BOARD_WAY_GAP_PX of a row is the gap to the module below it.\n          top: `${(rect.height - BOARD_WAY_GAP_PX - HANDLE_SIZE_PX) / 2}px`,\n          left: 'initial',\n          bottom: 'initial',\n          width: `${HANDLE_SIZE_PX}px`,\n          height: `${HANDLE_SIZE_PX}px`,\n          transform: 'none',\n          zIndex: 10,\n        }}\n        title={`${way.label} power`}\n      />\n    </div>\n  );\n});\n\nconst DistributionBoardNodeComponent: React.FC<NodeProps<NetworkFlowNode>> = ({\n  id,\n  data,\n  selected,\n}) => {\n  const updateNodeInternals = useUpdateNodeInternals();\n  const { label, status = 'active' } = data;\n  const ways = React.useMemo(\n    () => (Array.isArray(data.ways) ? data.ways : []) as BreakerWay[],\n    [data.ways],\n  );\n  const wayState = (data.wayState ?? {}) as Record<string, BreakerWayInstanceState>;\n  const layout = React.useMemo(\n    () => buildDistributionBoardLayout(ways, data.wayColumns),\n    [ways, data.wayColumns],\n  );\n\n  // React Flow caches each node's handle geometry. Ways are handles, so adding,\n  // removing or reflowing them moves handles React Flow still believes are at\n  // their old coordinates — edges would render to stale anchors until the next\n  // unrelated re-measure. Same pattern as DeviceNode.\n  const handleLayoutSignature = React.useMemo(\n    () =>\n      [\n        layout.width,\n        layout.height,\n        layout.columns,\n        layout.rows,\n        ways.map((way) => way?.id ?? '').join('|'),\n      ].join(':'),\n    [layout.width, layout.height, layout.columns, layout.rows, ways],\n  );\n\n  useIsomorphicLayoutEffect(() => {\n    updateNodeInternals(id);\n  }, [handleLayoutSignature, id, updateNodeInternals]);\n\n  const badge = resolveBoardBadge(data.boardType);\n  const incomerRating =\n    typeof data.incomerRatingA === 'number' && Number.isFinite(data.incomerRatingA)\n      ? `${data.incomerRatingA}A`\n      : undefined;\n\n  return (\n    <div\n      className={`distribution-board-node ${selected ? 'selected rn-node--selected' : ''}`}\n      data-node-type=\"distribution-board\"\n      data-board-badge={badge}\n      style={{\n        width: `${layout.width}px`,\n        height: `${layout.height}px`,\n        border: `1px solid ${getStatusColor(status)}`,\n      }}\n    >\n      {/*\n       * No enclosure chrome. Earlier revisions painted on a hinge band with\n       * knuckles and four corner fixing screws; at diagram zoom they read as\n       * clip-art, and none of them carried information. What separates a board\n       * from a rack is the flat light face, the square corners and the DB chip.\n       */}\n      <div\n        className=\"distribution-board-node__header\"\n        style={{ height: `${BOARD_HEADER_HEIGHT_PX}px` }}\n      >\n        <span className=\"distribution-board-node__badge\">{badge}</span>\n        <span className=\"distribution-board-node__label\">{label}</span>\n        <span className=\"distribution-board-node__header-meta\">\n          {incomerRating ? (\n            <span className=\"distribution-board-node__incomer-rating\">{incomerRating}</span>\n          ) : null}\n          {typeof data.voltageV === 'number' ? (\n            <span className=\"distribution-board-node__voltage\">{data.voltageV}V</span>\n          ) : null}\n        </span>\n        {/*\n         * The board's supply inlet. Ways are the board's *source* handles; this\n         * is the one target handle, so an upstream incomer (utility main switch,\n         * upstream board, UPS) can land on the board. `computeEnergization`\n         * already treats every non-way handle as an incomer.\n         */}\n        <Handle\n          type=\"target\"\n          position={Position.Left}\n          id=\"incomer\"\n          className=\"distribution-board-node__incomer-handle\"\n          aria-label={buildPortAriaLabel({\n            kind: 'Incomer',\n            group: typeof label === 'string' ? label : undefined,\n            status: [incomerRating],\n          })}\n          role=\"img\"\n          style={{\n            position: 'absolute',\n            left: `-${HANDLE_SIZE_PX / 2}px`,\n            top: `${(BOARD_HEADER_HEIGHT_PX - HANDLE_SIZE_PX) / 2}px`,\n            width: `${HANDLE_SIZE_PX}px`,\n            height: `${HANDLE_SIZE_PX}px`,\n            transform: 'none',\n            zIndex: 10,\n          }}\n          title=\"Board incomer\"\n        />\n      </div>\n      <div\n        className=\"distribution-board-node__body\"\n        style={{ height: `${layout.bodyHeight}px` }}\n      >\n        {layout.wayRects.map((rect) => (\n          <BreakerWayRow\n            key={rect.way.id}\n            nodeId={id}\n            rect={rect}\n            state={wayState[rect.way.id]}\n          />\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport const DistributionBoardNode = React.memo(\n  DistributionBoardNodeComponent,\n  (previous, next) =>\n    previous.id === next.id &&\n    previous.selected === next.selected &&\n    previous.dragging === next.dragging &&\n    previous.data === next.data,\n);\n","import type { CSSProperties } from 'react';\n\n/**\n * TIA-598-C standard fiber optic color codes (12 base colors)\n * Colors 1-12: Blue, Orange, Green, Brown, Slate, White, Red, Black, Yellow, Violet, Rose, Aqua\n * @public\n */\nexport const FIBRE_COLORS_12 = [\n  '#1f77b4', /* Blue */\n  '#ff7f0e', /* Orange */\n  '#2ca02c', /* Green */\n  '#8c564b', /* Brown */\n  '#708090', /* Slate */\n  '#ffffff', /* White */\n  '#d62728', /* Red */\n  '#000000', /* Black */\n  '#ffdf00', /* Yellow */\n  '#8a2be2', /* Violet */\n  '#ff69b4', /* Rose */\n  '#00ffff'  /* Aqua */\n] as const;\n\n/**\n * Fiber color ID type (1-24)\n * @public\n */\nexport type FibreColorId = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24;\n\n/**\n * Fiber paint metadata used by handle and edge rendering.\n * @public\n */\nexport interface FibrePaint {\n  base: string;\n  striped: boolean;\n  backgroundImage?: string;\n}\n\n/**\n * Get the base color for a fiber color ID\n * Colors 13-24 use the same base colors as 1-12\n * @param id - Fiber color ID (1-24)\n * @returns Hex color code\n * @public\n */\nexport function baseColorFor(id: FibreColorId | number): string {\n  const normalizedId = ((id - 1) % 12);\n  return FIBRE_COLORS_12[normalizedId];\n}\n\n/**\n * Check if a fiber color ID uses a striped pattern (13-24)\n * @param id - Fiber color ID (1-24)\n * @returns True if the color should be striped\n * @public\n */\nexport function isStriped(id: FibreColorId | number): boolean {\n  return id > 12;\n}\n\nconst BLACK_BASE_COLORS = new Set(['#000', '#000000', 'black']);\n\n/**\n * Tracer (stripe/dash) colour drawn over a striped fibre of the given base\n * colour. TIA-598-C marks positions 13-24 with a black tracer — except 20,\n * whose base is itself black, which carries a yellow tracer. A black tracer on\n * black would make fibre 20 indistinguishable from fibre 8.\n */\nexport function fibreTracerColorFor(base: string | undefined): string {\n  return base && BLACK_BASE_COLORS.has(base.trim().toLowerCase())\n    ? FIBRE_COLORS_12[8]\n    : '#000';\n}\n\n/**\n * Resolve the visual paint for a fiber (base + optional stripe pattern).\n * By default this follows TIA-598 logic: 1-12 solid, 13-24 striped.\n * @param id - Fiber color ID (1-24)\n * @param overrides - Optional visual overrides\n * @returns Paint metadata\n * @public\n */\nexport function getFibrePaint(\n  id: FibreColorId | number,\n  overrides?: { base?: string; striped?: boolean }\n): FibrePaint {\n  const base = overrides?.base ?? baseColorFor(id);\n  const striped = overrides?.striped ?? isStriped(id);\n  const backgroundImage = striped\n    ? `repeating-linear-gradient(45deg, ${base} 0 6px, ${fibreTracerColorFor(base)} 6px 9px)`\n    : undefined;\n\n  return { base, striped, backgroundImage };\n}\n\n/**\n * Get CSS properties for a fiber color (solid or striped)\n * Colors 13-24 use a tracer stripe over the base color (black; yellow on 20)\n * @param id - Fiber color ID (1-24)\n * @returns CSS properties for the color\n * @public\n */\nexport function fibreSolidOrStriped(id: FibreColorId | number): CSSProperties {\n  const paint = getFibrePaint(id);\n  if (paint.backgroundImage) {\n    return {\n      background: paint.base,\n      backgroundImage: paint.backgroundImage,\n    };\n  }\n  return { background: paint.base };\n}\n\n/**\n * Get the hex color code for a fiber color ID\n * @param id - Fiber color ID (1-24)\n * @returns Hex color code\n * @public\n */\nexport function getFibreColor(id: FibreColorId | number): string {\n  return baseColorFor(id);\n}\n\n/**\n * Get color metadata for a ribbon fiber group\n * @param ids - Array of fiber IDs (6-12 fibers, each 1-24)\n * @returns Array of color metadata objects with base color and striped flag\n * @public\n */\nexport function getRibbonColors(ids: number[]): Array<{ base: string; striped: boolean }> {\n  return ids.map(id => ({\n    base: baseColorFor(id),\n    striped: isStriped(id),\n  }));\n}\n\n/**\n * Validate ribbon fiber IDs array\n * @param ids - Array of fiber IDs to validate\n * @returns Object with isValid flag and optional error message\n * @public\n */\nexport function validateRibbonFibreIds(ids: number[]): { isValid: boolean; error?: string } {\n  if (!Array.isArray(ids)) {\n    return { isValid: false, error: 'Ribbon fiber IDs must be an array' };\n  }\n  \n  if (ids.length < 6 || ids.length > 12) {\n    return { isValid: false, error: `Ribbon must have 6-12 fibers, got ${ids.length}` };\n  }\n  \n  for (const id of ids) {\n    if (typeof id !== 'number' || id < 1 || id > 24) {\n      return { isValid: false, error: `Fiber ID must be between 1 and 24, got ${id}` };\n    }\n  }\n  \n  // Check for duplicates\n  const uniqueIds = new Set(ids);\n  if (uniqueIds.size !== ids.length) {\n    return { isValid: false, error: 'Ribbon fiber IDs must be unique' };\n  }\n  \n  return { isValid: true };\n}\n","import { useCallback } from 'react';\nimport { useStore } from '@xyflow/react';\nimport type { NetworkFlowEdge } from '../types';\nimport { getIncidentEdges } from '../utils/graph-index';\n\n/**\n * Shallow array equality on element identity. Edge updates in this library are\n * immutable (changed edges are new objects), so identity equality is exactly\n * \"did any of this node's edges change\".\n */\nexport function edgesShallowEqual(a: NetworkFlowEdge[], b: NetworkFlowEdge[]): boolean {\n  if (a === b) return true;\n  if (a.length !== b.length) return false;\n  for (let i = 0; i < a.length; i++) {\n    if (a[i] !== b[i]) return false;\n  }\n  return true;\n}\n\n/**\n * Subscribe to just the edges that touch one node.\n *\n * `useEdges()` subscribes to the whole edge array, so every splice, coupler,\n * tube, and splitter re-rendered on any edge change anywhere in the diagram —\n * O(nodes x edges) render work per interaction. This selector re-renders the\n * calling node only when one of ITS edges is added, removed, or replaced.\n */\nexport function useNodeEdges(nodeId: string): NetworkFlowEdge[] {\n  return useStore(\n    useCallback(\n      (state: { edges: unknown[] }) =>\n        getIncidentEdges(state.edges as NetworkFlowEdge[], nodeId),\n      [nodeId],\n    ),\n    edgesShallowEqual,\n  );\n}\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode, NetworkFlowEdge } from '../types';\nimport { getRibbonColors } from '../utils/fibre-colors';\nimport {\n  SPLICE_HEIGHT_PX_RIBBON,\n  SPLICE_HEIGHT_PX_SINGLE,\n  SPLICE_WIDTH_PX,\n} from '../utils/splice-positioning';\nimport { useNodeEdges } from './use-node-edges';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nconst SpliceNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { lossDb, mode = 'single', ribbonFiberIds } = data || {};\n  // Subscribe to just this node's edges (colors derive from connected edges).\n  const edges = useNodeEdges(id);\n  \n  // Get colors from connected edges, not from node data. Both sides mount a\n  // single source-class handle and loose-mode connections may name the splice\n  // as either end, so each side matches an edge in either direction.\n  const findSideEdge = (side: 'left' | 'right') =>\n    (edges.find((e: NetworkFlowEdge) => e.target === id && e.targetHandle === side) ??\n      edges.find((e: NetworkFlowEdge) => e.source === id && e.sourceHandle === side)) as\n      | NetworkFlowEdge\n      | undefined;\n  const leftEdge = findSideEdge('left');\n  const rightEdge = findSideEdge('right');\n  \n  // Get color from edge data, or use default gray if no edge\n  const leftColor = leftEdge?.data?.color || '#555';\n  const rightColor = rightEdge?.data?.color || '#555';\n  \n  // For ribbon mode, get colors from ribbonFiberIds or from connected edges\n  const isRibbonMode = mode === 'ribbon';\n  const leftRibbonIds = leftEdge?.data?.ribbonFiberIds || ribbonFiberIds;\n  const rightRibbonIds = rightEdge?.data?.ribbonFiberIds || ribbonFiberIds;\n  const leftRibbonColors = isRibbonMode && leftRibbonIds ? getRibbonColors(leftRibbonIds) : null;\n  const rightRibbonColors = isRibbonMode && rightRibbonIds ? getRibbonColors(rightRibbonIds) : null;\n  \n  // Only show glow when both handles are connected\n  const bothConnected = !!leftEdge && !!rightEdge;\n\n  // Dimensions come from the published geometry constants that tray layout\n  // (`calculateSplicePositionFromNumber`) centres splices with, so a ribbon\n  // splice sits centred in its taller holder instead of 6px high of it.\n  const spliceWidth = SPLICE_WIDTH_PX;\n  const spliceHeight = isRibbonMode ? SPLICE_HEIGHT_PX_RIBBON : SPLICE_HEIGHT_PX_SINGLE;\n\n  return (\n    <div\n      className={`splice-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        width: spliceWidth,\n        height: spliceHeight,\n        borderRadius: 8,\n        border: 'none',\n        outline: 'none',\n        position: 'relative',\n        background: 'transparent',\n        boxShadow: 'none',\n      }}\n    >\n      {/* Background showing colors - ribbon mode shows vertical color bands (top-to-bottom), single mode shows split */}\n      {isRibbonMode && leftRibbonColors && rightRibbonColors ? (\n        <div\n          style={{\n            position: 'absolute',\n            inset: 0,\n            borderRadius: 8,\n            overflow: 'hidden',\n            opacity: 0.75,\n            pointerEvents: 'none',\n            display: 'grid',\n            gridTemplateColumns: '1fr 1fr',\n          }}\n        >\n          {/* Left side ribbon - vertical color bands (top-to-bottom) */}\n          <div\n            style={{\n              display: 'flex',\n              flexDirection: 'column',\n              width: '100%',\n              height: '100%',\n            }}\n          >\n            {leftRibbonColors.map((colorInfo, index) => (\n              <div\n                key={`left-${index}`}\n                style={{\n                  flex: 1,\n                  background: colorInfo.base,\n                  opacity: colorInfo.striped ? 0.9 : 1,\n                }}\n              />\n            ))}\n          </div>\n          {/* Right side ribbon - vertical color bands (top-to-bottom) */}\n          <div\n            style={{\n              display: 'flex',\n              flexDirection: 'column',\n              width: '100%',\n              height: '100%',\n            }}\n          >\n            {rightRibbonColors.map((colorInfo, index) => (\n              <div\n                key={`right-${index}`}\n                style={{\n                  flex: 1,\n                  background: colorInfo.base,\n                  opacity: colorInfo.striped ? 0.9 : 1,\n                }}\n              />\n            ))}\n          </div>\n        </div>\n      ) : (\n        <div\n          style={{\n            position: 'absolute',\n            inset: 0,\n            display: 'grid',\n            gridTemplateColumns: '1fr 1fr',\n            borderRadius: 8,\n            overflow: 'hidden',\n            opacity: 0.75,\n            pointerEvents: 'none', // Don't block handles\n          }}\n        >\n          <div style={{ background: leftColor }} />\n          <div style={{ background: rightColor }} />\n        </div>\n      )}\n      \n      {/* Overlay with blur and opacity to make text clearer - respects dark/light mode */}\n      <div\n        className=\"splice-text-overlay\"\n        style={{\n          position: 'absolute',\n          inset: 0,\n          borderRadius: 8,\n          pointerEvents: 'none',\n          zIndex: 1,\n          background: 'transparent', // Ensure overlay is transparent\n        }}\n      />\n      \n      {/* Thin glow line at the weld junction - only when both handles are connected */}\n      {bothConnected && (\n        <div\n          className=\"splice-glow\"\n          style={{\n            position: 'absolute',\n            left: '50%',\n            top: 0,\n            bottom: 0,\n            width: '1px',\n            background: 'rgba(255, 255, 255, 0.6)',\n            boxShadow: '0 0 2px rgba(255, 255, 255, 0.8), 0 0 4px rgba(255, 255, 255, 0.4)',\n            pointerEvents: 'none',\n            zIndex: 2,\n          }}\n        />\n      )}\n      \n      {/* Loss value label - only show when both handles are connected */}\n      {bothConnected && typeof lossDb === 'number' && (\n        <div\n          className=\"splice-text-label animate-in\"\n          style={{\n            position: 'absolute',\n            inset: 0,\n            display: 'flex',\n            flexDirection: 'column',\n            alignItems: 'center',\n            justifyContent: 'center',\n            fontSize: isRibbonMode ? 8 : 9,\n            fontFamily: 'monospace',\n            pointerEvents: 'none',\n            zIndex: 3,\n            fontWeight: 'bold',\n            gap: '2px',\n          }}\n        >\n          <div>{`${lossDb.toFixed(2)} dB`}</div>\n          {isRibbonMode && ribbonFiberIds && (\n            <div style={{ fontSize: '7px', opacity: 0.8 }}>\n              {ribbonFiberIds.length}F\n            </div>\n          )}\n        </div>\n      )}\n      \n      {/* Invisible linear-flow anchors: id-less edges resolve to the FIRST\n          handle of the needed type, so registering an invisible source-right\n          first and providing the only target-type handle (left) gives\n          deterministic left-in / right-out anchoring for trace-style linear\n          paths without touching the id'd handles or interactive connecting. */}\n      <Handle\n        type=\"source\"\n        position={Position.Right}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', right: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n      <Handle\n        type=\"target\"\n        position={Position.Left}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', left: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n      {/* Left handle - only one connection allowed */}\n      <Handle\n        type=\"source\"\n        position={Position.Left}\n        id=\"left\"\n        aria-label={buildPortAriaLabel({\n          kind: 'Splice',\n          label: 'left',\n          status: [leftEdge ? 'connected' : 'disconnected'],\n        })}\n        role=\"img\"\n        isConnectable={true}\n        isConnectableStart={true}\n        isConnectableEnd={true}\n        style={{\n          width: isRibbonMode && leftRibbonColors ? 12 : 12, // Keep width same\n          height: isRibbonMode && leftRibbonColors ? 20 : 12, // Taller for ribbon mode to show vertical stripes\n          borderRadius: isRibbonMode && leftRibbonColors ? '6px' : '50%', // Rounded rectangle for ribbon\n          border: 'none',\n          background: isRibbonMode && leftRibbonColors \n            ? `linear-gradient(to bottom, ${leftRibbonColors.map(c => c.base).join(', ')})`\n            : (leftColor || '#555'),\n          zIndex: 100,\n          cursor: 'crosshair',\n          left: isRibbonMode && leftRibbonColors ? -6 : -6,\n          top: '50%',\n          transform: 'translateY(-50%)',\n        }}\n      />\n      \n      {/* Right handle - only one connection allowed */}\n      <Handle\n        type=\"source\"\n        position={Position.Right}\n        id=\"right\"\n        aria-label={buildPortAriaLabel({\n          kind: 'Splice',\n          label: 'right',\n          status: [rightEdge ? 'connected' : 'disconnected'],\n        })}\n        role=\"img\"\n        isConnectable={true}\n        isConnectableStart={true}\n        isConnectableEnd={true}\n        style={{\n          width: isRibbonMode && rightRibbonColors ? 12 : 12, // Keep width same\n          height: isRibbonMode && rightRibbonColors ? 20 : 12, // Taller for ribbon mode to show vertical stripes\n          borderRadius: isRibbonMode && rightRibbonColors ? '6px' : '50%', // Rounded rectangle for ribbon\n          border: 'none',\n          background: isRibbonMode && rightRibbonColors\n            ? `linear-gradient(to bottom, ${rightRibbonColors.map(c => c.base).join(', ')})`\n            : (rightColor || '#555'),\n          zIndex: 100,\n          cursor: 'crosshair',\n          right: isRibbonMode && rightRibbonColors ? -6 : -6,\n          top: '50%',\n          transform: 'translateY(-50%)',\n        }}\n      />\n    </div>\n  );\n};\n\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const SpliceNode = React.memo(SpliceNodeInner);\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { NodeProps, useUpdateNodeInternals } from '@xyflow/react';\nimport { NetworkFlowNode } from '../types';\nimport {\n  TRAY_HEADER_HEIGHT,\n  HOLDER_HEIGHT_PX_RIBBON,\n  HOLDER_HEIGHT_PX_SINGLE,\n  HOLDERS_PER_TRAY,\n  TRAY_WIDTH_PX,\n  getTrayNodeHeight,\n} from '../utils/splice-positioning';\nimport { useChildNodes } from './use-child-nodes';\n\nconst SpliceTrayNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { label = 'Splice Tray' } = data || {};\n  const [showHolderMarkers, setShowHolderMarkers] = useState(false);\n  const updateNodeInternals = useUpdateNodeInternals();\n  const childNodes = useChildNodes(id);\n  const lastHeightRef = useRef<number | null>(null);\n\n  const isRibbonTray =\n    data?.mode === 'ribbon' ||\n    childNodes.some((node) => node.type === 'splice' && node.data?.mode === 'ribbon');\n\n  const trayHeight = useMemo(\n    () => getTrayNodeHeight(isRibbonTray),\n    [isRibbonTray],\n  );\n\n  useEffect(() => {\n    const previous = lastHeightRef.current;\n    if (previous !== null && Math.abs(previous - trayHeight) <= 1) {\n      return;\n    }\n    lastHeightRef.current = trayHeight;\n    updateNodeInternals(id);\n  }, [id, trayHeight, updateNodeInternals]);\n\n  // Generate holder position markers (like U markers in racks)\n  const renderHolderPositions = () => {\n    if (!showHolderMarkers) return null;\n    const positions = [];\n    const startY = TRAY_HEADER_HEIGHT;\n    // Ribbon trays use taller holders; markers follow the same pitch the\n    // splices are laid out on.\n    const holderHeightPx = isRibbonTray ? HOLDER_HEIGHT_PX_RIBBON : HOLDER_HEIGHT_PX_SINGLE;\n\n    for (let i = 1; i <= HOLDERS_PER_TRAY; i++) {\n      const yPos = startY + (i - 1) * holderHeightPx;\n\n      positions.push(\n        <div\n          key={i}\n          style={{\n            position: 'absolute',\n            left: '-35px',\n            top: `${yPos}px`,\n            background: 'var(--rn-surface-raised, #374151)',\n            color: 'var(--rn-text-muted, #9ca3af)',\n            padding: '2px 4px',\n            borderRadius: 'var(--rn-ui-radius, 1px)',\n            fontSize: '8px',\n            fontWeight: 'bold',\n            width: '28px',\n            textAlign: 'center',\n            border: '1px solid var(--rn-border, #4b5563)',\n            zIndex: 10,\n            height: `${holderHeightPx}px`,\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: 'center',\n            lineHeight: 1,\n          }}\n        >\n          H{i}\n        </div>\n      );\n    }\n    return positions;\n  };\n\n  return (\n    <div\n      className={`splice-tray-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        background: 'transparent',\n        border: 'none',\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        padding: 0,\n        width: `${TRAY_WIDTH_PX}px`,\n        height: `${trayHeight}px`,\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '12px',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.1)',\n        position: 'relative',\n        display: 'flex',\n        flexDirection: 'column',\n      }}\n    >\n      {/* Tray Header */}\n      <div\n        style={{\n          textAlign: 'center',\n          marginBottom: 0,\n          zIndex: 2,\n          background: 'var(--rn-surface, #1f2937)',\n          padding: '2px',\n          position: 'relative',\n          height: `${TRAY_HEADER_HEIGHT}px`,\n          display: 'flex',\n          flexDirection: 'row',\n          alignItems: 'center',\n          justifyContent: 'center',\n          gap: '6px',\n          borderRadius: 'var(--rn-ui-radius, 1px)',\n        }}\n      >\n        <div style={{ fontWeight: 'bold', fontSize: '10px' }}>{label}</div>\n        <button\n          className=\"rn-node-button\"\n          onPointerDown={(e) => {\n            e.stopPropagation();\n          }}\n          onMouseDown={(e) => {\n            e.stopPropagation();\n          }}\n          onClick={(e) => {\n            e.stopPropagation();\n            e.preventDefault();\n            setShowHolderMarkers(!showHolderMarkers);\n          }}\n          style={{\n            background: showHolderMarkers\n              ? 'var(--rn-status-ok, #10b981)'\n              : 'var(--rn-surface-raised, #374151)',\n            color: showHolderMarkers\n              ? 'var(--rn-text-strong, #ffffff)'\n              : 'var(--rn-text-muted, #9ca3af)',\n            border: '1px solid var(--rn-border, #4b5563)',\n            borderRadius: 'var(--rn-ui-radius, 1px)',\n            padding: '2px 6px',\n            fontSize: '8px',\n            cursor: 'pointer',\n            fontFamily: 'monospace',\n          }}\n          type=\"button\"\n          aria-pressed={showHolderMarkers}\n          aria-label={`${showHolderMarkers ? 'Hide' : 'Show'} holder markers`}\n          title={`${showHolderMarkers ? 'Hide' : 'Show'} holder markers`}\n        >\n          H\n        </button>\n      </div>\n\n      {renderHolderPositions()}\n\n      <div\n        style={{\n          position: 'absolute',\n          top: TRAY_HEADER_HEIGHT,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          pointerEvents: 'none',\n        }}\n      />\n    </div>\n  );\n};\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const SpliceTrayNode = React.memo(SpliceTrayNodeInner);\n","import React, { useMemo } from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode, NetworkFlowEdge } from '../types';\nimport { getFibrePaint, baseColorFor } from '../utils/fibre-colors';\nimport { buildFibreHandleId, buildTubeHandleId } from '../utils/handle-ids';\nimport { useNodeEdges } from './use-node-edges';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\n/**\n * TubeNode\n * - Rectangular node representing a buffer tube (horizontal orientation, rotated 180 degrees)\n * - Renders 12 or 24 fiber handles on the top side with TIA-598 colors\n * - Tube border tinted by tube index color (1..12 cycling)\n * - startOn parameter controls handle positioning:\n *   - 'left-center' (default): handles centered on top, tube handle on bottom\n *   - 'left': handles on left side, tube handle on left\n *   - 'right': handles on right side with reversed colors, tube handle on right\n */\nconst TubeNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { fibersPerTube = 12, tubeIndex = 1, startOn = 'left-center' } = data || {};\n  const isRibbonTube = data?.mode === 'ribbon' || (data as { ribbon?: boolean } | undefined)?.ribbon === true;\n  \n  // Use useEdges hook to detect connected tube and update border color dynamically\n  const edges = useNodeEdges(id);\n  const tubeConnectionHandleId = buildTubeHandleId(tubeIndex);\n  \n  // Find edge connected to this tube's tube handle (tube-* handle)\n  const connectedTubeEdge = edges.find((e: NetworkFlowEdge) => \n    (e.source === id && e.sourceHandle === tubeConnectionHandleId) ||\n    (e.target === id && e.targetHandle === tubeConnectionHandleId)\n  ) as NetworkFlowEdge | undefined;\n  \n  // Get color from connected edge if available, otherwise use default tube color\n  const connectedTubeColor = connectedTubeEdge?.data?.color;\n  const defaultTubeBorderColor = baseColorFor(((tubeIndex - 1) % 12) + 1);\n  // Use connected tube color if available, otherwise fall back to default\n  const tubeBorderColor = connectedTubeColor || defaultTubeBorderColor;\n\n  // Calculate dimensions - rectangle that fits all fibers horizontally (rotated 90 degrees to be flat)\n  const fibers = Math.max(1, Math.min(24, Number(fibersPerTube) || 12));\n  const handleSize = 14; // Handle size\n  const handleSpacing = 4;\n  const padding = 1; // Reduced padding to make tube thinner\n  const borderWidth = 2; // Thinner border\n  // Height must accommodate: border (2px * 2) + padding (1px * 2) + handle (14px) = 20px\n  const height = 20; // Thinner tube but still fits handles inside and centered\n  const totalHandlesWidth = (fibers * handleSize) + ((fibers - 1) * handleSpacing);\n  const width = Math.max(60, totalHandlesWidth + (padding * 2) + (borderWidth * 2));\n\n  const tubeConnectionPosition = startOn === 'left'\n    ? Position.Left\n    : startOn === 'right'\n      ? Position.Right\n      : Position.Bottom;\n  const isSideHandle = tubeConnectionPosition !== Position.Bottom;\n  const tubeHandleWidth = isSideHandle ? 14 : 20;\n  const tubeHandleHeight = isSideHandle ? 8 : 20;\n  const sideInsetShadow = isSideHandle\n    ? `inset ${tubeConnectionPosition === Position.Left ? '-4px' : '4px'} 0 0 rgba(0, 0, 0, 0.35)`\n    : 'none';\n  const tubeHandleBackground = connectedTubeEdge\n    ? tubeBorderColor\n    : isSideHandle\n      ? `linear-gradient(${tubeConnectionPosition === Position.Left ? 'to right' : 'to left'}, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0)), ${tubeBorderColor}`\n      : `radial-gradient(circle, ${tubeBorderColor} 40%, transparent 70%)`;\n  // Show different style when connected vs not connected\n  const connectionHandleStyle: React.CSSProperties & { '--rn-fibre-bg'?: string } = {\n    width: tubeHandleWidth,\n    height: tubeHandleHeight,\n    borderRadius: isSideHandle ? 6 : '50%',\n    opacity: connectedTubeEdge ? 0.3 : 0.9, // More visible when not connected\n    pointerEvents: 'auto',\n    background: tubeHandleBackground,\n    backgroundColor: tubeBorderColor,\n    border: connectedTubeEdge ? `2px solid ${tubeBorderColor}` : `3px dashed ${tubeBorderColor}`,\n    cursor: 'crosshair',\n    '--rn-fibre-bg': tubeBorderColor,\n    boxShadow: connectedTubeEdge\n      ? sideInsetShadow\n      : `${sideInsetShadow}${sideInsetShadow !== 'none' ? ', ' : ''}0 0 8px ${tubeBorderColor}`,\n    transition: 'opacity 0.2s, box-shadow 0.2s',\n    ...(tubeConnectionPosition === Position.Left\n      ? { left: -(tubeHandleWidth - 4) }\n      : tubeConnectionPosition === Position.Right\n        ? { right: -(tubeHandleWidth - 4) }\n        : {}),\n  };\n\n  // Render handles based on startOn mode - memoized to prevent unnecessary re-renders\n  const fiberHandles = useMemo(() => {\n    const handles: React.ReactElement[] = [];\n    const innerWidth = width - (borderWidth * 2) - (padding * 2);\n    \n    let startLeft: number;\n    let handlePosition: Position;\n    let reverseColors = false;\n    \n    if (startOn === 'left') {\n      // Handles start from left (not centered), positioned on top\n      startLeft = padding;\n      handlePosition = Position.Top;\n    } else if (startOn === 'right') {\n      // Handles start from right, positioned on top, colors reversed\n      startLeft = padding + innerWidth - totalHandlesWidth;\n      handlePosition = Position.Top;\n      reverseColors = true;\n    } else {\n      // Default 'left-center': handles centered on top\n      startLeft = padding + (innerWidth - totalHandlesWidth) / 2;\n      handlePosition = Position.Top;\n    }\n    \n    for (let i = 0; i < fibers; i++) {\n      // Calculate fiber index - reverse if startOn is 'right'\n      const fiberIdx = reverseColors ? (fibers - i) : (i + 1);\n      \n      const fiberHandleId = buildFibreHandleId(fiberIdx);\n      \n      // Get color from connected edge if available (for overrides)\n      const connectedEdge = edges.find((e: NetworkFlowEdge) => {\n        return (e.source === id && e.sourceHandle === fiberHandleId) ||\n               (e.target === id && e.targetHandle === fiberHandleId);\n      }) as NetworkFlowEdge | undefined;\n\n      const connectedIsRibbon =\n        !!connectedEdge &&\n        (connectedEdge.data?.isRibbon ||\n          (connectedEdge.data?.ribbonFiberIds && Array.isArray(connectedEdge.data.ribbonFiberIds)));\n\n      const edgeColor = connectedEdge?.data?.color;\n      const edgeStriped =\n        typeof connectedEdge?.data?.striped === 'boolean' ? connectedEdge.data.striped : undefined;\n\n      let fiberPaint = getFibrePaint(fiberIdx);\n      if (isRibbonTube || connectedIsRibbon) {\n        fiberPaint = getFibrePaint(fiberIdx, { base: '#808080', striped: false });\n      } else if (edgeColor || edgeStriped !== undefined) {\n        fiberPaint = getFibrePaint(fiberIdx, {\n          ...(edgeColor ? { base: edgeColor } : {}),\n          ...(edgeStriped !== undefined ? { striped: edgeStriped } : {}),\n        });\n      }\n      \n      // Position handles on top, horizontally arranged\n      const left = startLeft + (i * (handleSize + handleSpacing));\n      const top = padding;\n      \n      // Build handle style - same as normal fibers\n      const handleStyle: React.CSSProperties & { '--rn-fibre-bg'?: string; '--rn-fibre-bg-image'?: string } = {\n        position: 'absolute',\n        left: `${left}px`,\n        top: `${top}px`,\n        width: handleSize,\n        height: handleSize,\n        borderRadius: '50%',\n        border: 'none',\n        zIndex: 10,\n        cursor: 'pointer',\n        '--rn-fibre-bg': fiberPaint.base,\n        backgroundColor: fiberPaint.base,\n      };\n      \n      if (fiberPaint.backgroundImage) {\n        handleStyle['--rn-fibre-bg-image'] = fiberPaint.backgroundImage;\n      }\n\n      handles.push(\n        <Handle\n          key={`fiber-${fiberIdx}`}\n          id={fiberHandleId}\n          type=\"source\"\n          position={handlePosition}\n          aria-label={buildPortAriaLabel({\n            kind: 'Fibre',\n            group: `tube ${tubeIndex}`,\n            label: fiberIdx,\n          })}\n          role=\"img\"\n          className=\"tube-fiber-handle\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n          data-fiber-idx={fiberIdx}\n          data-fiber-color={fiberPaint.base}\n        />\n      );\n    }\n    \n    return handles;\n  }, [fibers, startOn, width, edges, id, handleSize, handleSpacing, padding, borderWidth, totalHandlesWidth, isRibbonTube, tubeIndex]);\n\n  return (\n    <div\n      className={selected ? 'rn-node--selected' : undefined}\n      style={{\n        width: width,\n        height: height,\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        background: 'var(--rn-surface-sunken, #111827)',\n        border: `${borderWidth}px solid ${selected ? 'var(--rn-selection-ring, #3b82f6)' : tubeBorderColor}`,\n        position: 'relative',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\n        padding: padding,\n        boxSizing: 'border-box',\n        overflow: 'visible',\n      }}\n      >\n      {/* Render fiber handles */}\n      {fiberHandles}\n      {/* Tube connection handle for cable-to-tube connections - always visible for reconnection */}\n      <Handle\n        id={tubeConnectionHandleId}\n        type=\"source\"\n        position={tubeConnectionPosition}\n        aria-label={buildPortAriaLabel({\n          kind: 'Tube',\n          label: tubeIndex,\n          status: [connectedTubeEdge ? 'connected' : 'disconnected'],\n        })}\n        role=\"img\"\n        isConnectable={true}\n        isConnectableStart={true}\n        isConnectableEnd={true}\n        className=\"tube-connection-handle\"\n        style={connectionHandleStyle}\n        data-tube-idx={tubeIndex}\n        data-tube-color={tubeBorderColor}\n        title={connectedTubeEdge ? 'Connected - drag to reconnect' : 'Drag to connect cable tube'}\n      />\n    </div>\n  );\n};\n\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const TubeNode = React.memo(TubeNodeInner);\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode } from '../types';\nimport { getFibrePaint, baseColorFor } from '../utils/fibre-colors';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\n/**\n * CableNode\n * - Represents a multi-fiber cable (1-24 fibers)\n * - Each colored handle corresponds to a single fiber\n * - Displays cable summary text (fiber count + cable identifier)\n */\nconst CableNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected }) => {\n  const {\n    fiberCount: providedFibreCount,\n    // No fabricated default: an unnamed cable shows no ID line (line ~42 guards).\n    cableId = '',\n    label,\n    color, // Cable color prop - used for border and button handle\n    rotation,\n  } = data || {};\n  \n  const containerRef = React.useRef<HTMLDivElement>(null);\n  \n  // Apply rotation via ref to ensure it's not overridden\n  React.useEffect(() => {\n    if (containerRef.current) {\n      const rotationDegrees = typeof rotation === 'number' ? rotation : 0;\n      containerRef.current.style.transform = `rotate(${rotationDegrees}deg)`;\n      containerRef.current.style.transformOrigin = 'center center';\n    }\n  }, [rotation]);\n\n  const fibers = Math.max(1, Math.min(24, Number(providedFibreCount) || 12));\n  const handleSize = 10;\n  const handleSpacing = 2;\n  const padding = 6;\n  const innerPaddingTop = padding + handleSize + 4;\n  const height = 60;\n  const width = Math.max(120, (fibers * (handleSize + handleSpacing)) + (padding * 2));\n  const totalFibres = fibers;\n\n  const primaryText = label ?? `${totalFibres}f Cable`;\n  const secondaryText = cableId ? `Cable ID ${cableId}` : undefined;\n  const defaultCableColor = baseColorFor(1);\n  \n  // Use provided color or fall back to default fiber color\n  const cableColor = color || defaultCableColor;\n  const borderColor = selected ? 'var(--rn-selection-ring, #3b82f6)' : cableColor;\n\n  const rotationDegrees = typeof rotation === 'number' ? rotation : 0;\n\n  // Calculate handle position based on rotation\n  // Original position is Top, so we map rotation to the appropriate Position\n  const getHandlePosition = (rotationDeg: number): Position => {\n    // Normalize rotation to 0-360 range\n    const normalized = ((rotationDeg % 360) + 360) % 360;\n    \n    // Map rotation to Position enum\n    // Top: 0°, Right: 90°, Bottom: 180°, Left: 270°\n    if (normalized >= 315 || normalized < 45) {\n      return Position.Top;\n    } else if (normalized >= 45 && normalized < 135) {\n      return Position.Right;\n    } else if (normalized >= 135 && normalized < 225) {\n      return Position.Bottom;\n    } else {\n      return Position.Left;\n    }\n  };\n\n  const renderHandles = () => {\n    const handles: React.ReactElement[] = [];\n    const handlePosition = getHandlePosition(rotationDegrees);\n\n    for (let i = 0; i < fibers; i++) {\n      const fiberIdx = i + 1;\n      const fiberPaint = getFibrePaint(fiberIdx);\n      const baseColor = fiberPaint.base;\n\n      const left = padding + (i * (handleSize + handleSpacing));\n      const top = -(handleSize / 2);\n\n      const handleBackground = fiberPaint.backgroundImage ? fiberPaint.backgroundImage : baseColor;\n\n      const handleStyle: React.CSSProperties & { '--rn-fibre-bg'?: string; '--rn-fibre-bg-image'?: string } = {\n        position: 'absolute',\n        left: `${left}px`,\n        top: `${top}px`,\n        width: handleSize,\n        height: handleSize,\n        borderRadius: '50%',\n        border: 'none',\n        background: handleBackground,\n        backgroundColor: baseColor,\n        zIndex: 10,\n        cursor: 'pointer',\n        '--rn-fibre-bg': baseColor,\n      };\n\n      if (fiberPaint.backgroundImage) {\n        handleStyle['--rn-fibre-bg-image'] = fiberPaint.backgroundImage;\n      }\n\n      const fiberHandleId = `fiber-${fiberIdx}`;\n\n      handles.push(\n        <Handle\n          key={`fiber-${fiberIdx}`}\n          id={fiberHandleId}\n          type=\"source\"\n          position={handlePosition}\n          aria-label={buildPortAriaLabel({\n            kind: 'Fibre',\n            group: label,\n            label: fiberIdx,\n          })}\n          role=\"img\"\n          className=\"tube-fiber-handle\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n          data-fiber-idx={fiberIdx}\n          data-fiber-color={baseColor}\n        />\n      );\n    }\n\n    return handles;\n  };\n\n  // Button handle on edge for cable-to-cable connections (max 1 connection)\n  const buttonHandleId = 'cable-button';\n  const buttonHandleSize = 8; // Smaller handle\n  const buttonHandleStyle: React.CSSProperties = {\n    width: buttonHandleSize,\n    height: buttonHandleSize,\n    borderRadius: '50%',\n    background: cableColor,\n    backgroundColor: cableColor,\n    border: `1px solid ${cableColor}`,\n    cursor: 'pointer',\n    zIndex: 10,\n  };\n\n  const borderWidth = 2;\n  \n  // Calculate button handle position based on rotation (opposite side of fiber handles)\n  const getButtonHandlePosition = (rotationDeg: number): Position => {\n    const normalized = ((rotationDeg % 360) + 360) % 360;\n    // Button is on Bottom when unrotated, so map accordingly\n    if (normalized >= 315 || normalized < 45) {\n      return Position.Bottom;\n    } else if (normalized >= 45 && normalized < 135) {\n      return Position.Left;\n    } else if (normalized >= 135 && normalized < 225) {\n      return Position.Top;\n    } else {\n      return Position.Right;\n    }\n  };\n  \n  const buttonHandlePosition = getButtonHandlePosition(rotationDegrees);\n\n  // Main container with rotation applied directly\n  const containerStyle: React.CSSProperties = {\n    width,\n    height,\n    position: 'relative',\n    transform: `rotate(${rotationDegrees}deg)`,\n    transformOrigin: 'center center',\n  };\n\n  // Content div with styling\n  const contentStyle: React.CSSProperties = {\n    width: '100%',\n    height: '100%',\n    borderRadius: 'var(--rn-ui-radius, 1px)',\n    background: 'var(--rn-surface-sunken, #111827)',\n    border: `${borderWidth}px solid ${borderColor}`,\n    boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\n    padding: padding,\n    paddingTop: innerPaddingTop,\n    boxSizing: 'border-box',\n    display: 'flex',\n    flexDirection: 'column',\n    alignItems: 'center',\n    justifyContent: 'center',\n    color: 'var(--rn-text, #e5e7eb)',\n    fontFamily: 'monospace',\n    textAlign: 'center',\n    gap: 2,\n  };\n\n  return (\n    <div ref={containerRef} style={containerStyle}>\n      <div style={contentStyle} className={selected ? 'rn-node--selected' : undefined}>\n      {/* Invisible linear-flow anchors: id-less edges resolve to the FIRST\n          handle of the needed type, so registering an invisible source-right\n          first and providing the only target-type handle (left) gives\n          deterministic left-in / right-out anchoring for trace-style linear\n          paths without touching the id'd handles or interactive connecting. */}\n      <Handle\n        type=\"source\"\n        position={Position.Right}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', right: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n      <Handle\n        type=\"target\"\n        position={Position.Left}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', left: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n        {renderHandles()}\n        {/* Button handle - position depends on rotation */}\n        <Handle\n          id={buttonHandleId}\n          type=\"source\"\n          position={buttonHandlePosition}\n          aria-label={buildPortAriaLabel({ kind: 'Cable', group: label, status: ['connection point'] })}\n          role=\"img\"\n          className=\"cable-button-handle\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={buttonHandleStyle}\n          data-handle-type=\"cable-button\"\n          data-cable-color={cableColor}\n        />\n        <div\n          style={{\n            display: 'flex',\n            flexDirection: 'column',\n            alignItems: 'center',\n            justifyContent: 'center',\n            gap: 2,\n            transform: 'translateX(2px)',\n          }}\n        >\n          <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>\n            {primaryText}\n          </div>\n          {secondaryText && (\n            <div style={{ fontSize: 10, opacity: 0.8 }}>\n              {secondaryText}\n            </div>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n};\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const CableNode = React.memo(CableNodeInner);\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode } from '../types';\nimport { getFibrePaint, baseColorFor } from '../utils/fibre-colors';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\n/**\n * MultiTubeCableNode\n * - Represents a multi-tube cable (1-24 tubes)\n * - Each colored handle corresponds to a tube within the cable\n * - Displays cable summary text (fiber count + cable identifier)\n */\nconst MultiTubeCableNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected }) => {\n  const {\n    tubeCount = 12,\n    fibersPerTube = 12,\n    fiberCount: providedFibreCount,\n    // No fabricated default: an unnamed cable shows no ID line (matches CableNode).\n    cableId = '',\n    label,\n    tubeStartIndex: rawTubeStartIndex = 1,\n    color, // Cable color prop - used for border and button handle\n  } = data || {};\n\n  const tubes = Math.max(1, Math.min(24, Number(tubeCount) || 12));\n  const tubeStartIndex = Math.max(1, Number(rawTubeStartIndex) || 1);\n  const handleWidth = 14;\n  const handleHeight = 10;\n  const handleSpacing = 3;\n  const padding = 6;\n  const innerPaddingTop = padding + handleHeight + 4;\n  const height = 60;\n  const width = Math.max(120, (tubes * (handleWidth + handleSpacing)) + (padding * 2));\n  const totalFibres = providedFibreCount ?? tubes * (Number(fibersPerTube) || 12);\n\n  const primaryText = label ?? `${totalFibres}f Cable`;\n  const secondaryText = cableId ? `Cable ID ${cableId}` : undefined;\n  const firstTubeColor = baseColorFor(tubeStartIndex);\n  \n  // Use provided color or fall back to first tube color\n  const cableColor = color || firstTubeColor;\n  const borderColor = selected ? 'var(--rn-selection-ring, #3b82f6)' : cableColor;\n\n  const renderHandles = () => {\n    const handles: React.ReactElement[] = [];\n\n    for (let i = 0; i < tubes; i++) {\n      const tubeIdx = tubeStartIndex + i;\n      const tubePaint = getFibrePaint(tubeIdx);\n      const baseColor = tubePaint.base;\n\n      const left = padding + (i * (handleWidth + handleSpacing));\n      const top = -(handleHeight / 2);\n\n      const handleBackground = tubePaint.backgroundImage ? tubePaint.backgroundImage : baseColor;\n\n      const handleStyle: React.CSSProperties & { '--rn-fibre-bg'?: string; '--rn-fibre-bg-image'?: string } = {\n        position: 'absolute',\n        left: `${left}px`,\n        top: `${top}px`,\n        width: handleWidth,\n        height: handleHeight,\n        borderRadius: 6,\n        border: 'none',\n        background: handleBackground,\n        backgroundColor: baseColor,\n        zIndex: 10,\n        cursor: 'pointer',\n        '--rn-fibre-bg': baseColor,\n      };\n\n      if (tubePaint.backgroundImage) {\n        handleStyle['--rn-fibre-bg-image'] = tubePaint.backgroundImage;\n      }\n\n      const tubeHandleId = `tube-${tubeIdx}`;\n\n      handles.push(\n        <Handle\n          key={`tube-${tubeIdx}`}\n          id={tubeHandleId}\n          type=\"source\"\n          position={Position.Top}\n          aria-label={buildPortAriaLabel({\n            kind: 'Tube',\n            group: primaryText,\n            label: tubeIdx,\n          })}\n          role=\"img\"\n          className=\"tube-fiber-handle\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n          data-fiber-idx={tubeIdx}\n          data-tube-idx={tubeIdx}\n          data-tube-color={baseColor}\n        />\n      );\n    }\n\n    return handles;\n  };\n\n  // Button handle on edge for cable-to-cable connections (max 1 connection)\n  const buttonHandleId = 'cable-button';\n  const buttonHandleSize = 8; // Smaller handle\n  const buttonHandleStyle: React.CSSProperties = {\n    width: buttonHandleSize,\n    height: buttonHandleSize,\n    borderRadius: '50%',\n    background: cableColor,\n    backgroundColor: cableColor,\n    border: `1px solid ${cableColor}`,\n    cursor: 'pointer',\n    zIndex: 10,\n  };\n\n  const borderWidth = 2;\n  \n  return (\n    <div\n      className={selected ? 'rn-node--selected' : undefined}\n      style={{\n        width,\n        height,\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        background: 'var(--rn-surface-sunken, #111827)',\n        border: `${borderWidth}px solid ${borderColor}`,\n        position: 'relative',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\n        padding: padding,\n        paddingTop: innerPaddingTop,\n        boxSizing: 'border-box',\n        display: 'flex',\n        flexDirection: 'column',\n        alignItems: 'center',\n        justifyContent: 'center',\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        textAlign: 'center',\n        gap: 2,\n      }}\n    >\n      {renderHandles()}\n      {/* Button handle on bottom edge */}\n      <Handle\n        id={buttonHandleId}\n        type=\"source\"\n        position={Position.Bottom}\n        aria-label={buildPortAriaLabel({ kind: 'Cable', group: primaryText, status: ['connection point'] })}\n        role=\"img\"\n        className=\"cable-button-handle\"\n        isConnectable={true}\n        isConnectableStart={true}\n        isConnectableEnd={true}\n        style={buttonHandleStyle}\n        data-handle-type=\"cable-button\"\n        data-cable-color={cableColor}\n      />\n      <div\n        style={{\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          gap: 2,\n        }}\n      >\n        <div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>\n          {primaryText}\n        </div>\n        {secondaryText && (\n          <div style={{ fontSize: 10, opacity: 0.8 }}>\n            {secondaryText}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n};\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const MultiTubeCableNode = React.memo(MultiTubeCableNodeInner);\n","import { baseColorFor, getFibreColor } from './fibre-colors';\n\n/** Default patch-side coupler output color (TIA yellow, fiber 9). */\nexport const DEFAULT_COUPLER_OUTPUT_COLOR = getFibreColor(9);\n\nexport type FibreEdgePaintLookup = {\n  id?: string;\n  source: string;\n  target: string;\n  sourceHandle?: string | null;\n  targetHandle?: string | null;\n  data?: { color?: string; striped?: boolean };\n};\n\nfunction normalizeEndpointHandle(handle: string): string {\n  return handle.replace(/-(source|target)$/i, '');\n}\n\nfunction edgeTouchesHandle(\n  edge: FibreEdgePaintLookup,\n  nodeId: string,\n  handleId: string,\n): boolean {\n  const normalized = normalizeEndpointHandle(handleId);\n  return (\n    (edge.source === nodeId &&\n      normalizeEndpointHandle(edge.sourceHandle ?? '') === normalized) ||\n    (edge.target === nodeId &&\n      normalizeEndpointHandle(edge.targetHandle ?? '') === normalized)\n  );\n}\n\nexport function isCouplerRightHandle(handle: string | null | undefined): boolean {\n  if (!handle) {\n    return false;\n  }\n  return normalizeEndpointHandle(handle).startsWith('right-');\n}\n\nexport function isCouplerLeftHandle(handle: string | null | undefined): boolean {\n  if (!handle) {\n    return false;\n  }\n  return normalizeEndpointHandle(handle).startsWith('left-');\n}\n\n/** Coupler left-N and right-N are the same fiber path. */\nexport function getCouplerSiblingHandle(handle: string): string | undefined {\n  const normalized = normalizeEndpointHandle(handle);\n  const match = normalized.match(/^(left|right)-(\\d+)$/);\n  if (!match) {\n    return undefined;\n  }\n  return `${match[1] === 'left' ? 'right' : 'left'}-${match[2]}`;\n}\n\n/** Splice left/right are opposite ends of the same fiber path. */\nexport function getOppositeSpliceHandle(handle: string): string | undefined {\n  const normalized = normalizeEndpointHandle(handle);\n  if (normalized === 'left') {\n    return 'right';\n  }\n  if (normalized === 'right') {\n    return 'left';\n  }\n  return undefined;\n}\n\n/**\n * Derive TIA fiber / tube paint from a node endpoint handle.\n * Returns undefined for non-fiber endpoints (e.g. splice left/right).\n */\nexport function colorFromFibreEndpoint(\n  nodeType: string | undefined,\n  handle: string | null | undefined,\n): { color: string; striped?: boolean } | undefined {\n  if (!nodeType || !handle) {\n    return undefined;\n  }\n\n  const normalizedHandle = normalizeEndpointHandle(handle);\n\n  if (\n    (nodeType === 'tube' || nodeType === 'cable') &&\n    normalizedHandle.startsWith('fiber-')\n  ) {\n    const fiberNum = parseInt(normalizedHandle.replace('fiber-', ''), 10);\n    if (fiberNum >= 1 && fiberNum <= 24) {\n      return { color: getFibreColor(fiberNum), striped: fiberNum > 12 };\n    }\n  }\n\n  if (\n    (nodeType === 'multi-tube-cable' || nodeType === 'tube') &&\n    normalizedHandle.startsWith('tube-')\n  ) {\n    const tubeNum = parseInt(normalizedHandle.replace('tube-', ''), 10);\n    if (tubeNum >= 1 && tubeNum <= 24) {\n      return { color: baseColorFor(tubeNum), striped: tubeNum > 12 };\n    }\n  }\n\n  return undefined;\n}\n\n/** Pick fiber paint from whichever endpoint carries a tube/cable handle. */\nexport function deriveFibreColorForConnection(params: {\n  sourceType?: string;\n  sourceHandle?: string | null;\n  targetType?: string;\n  targetHandle?: string | null;\n}): { color: string; striped?: boolean } | undefined {\n  const { sourceType, sourceHandle, targetType, targetHandle } = params;\n\n  if (sourceType === 'tube' && targetType === 'multi-tube-cable') {\n    return colorFromFibreEndpoint(targetType, targetHandle);\n  }\n\n  return (\n    colorFromFibreEndpoint(sourceType, sourceHandle) ??\n    colorFromFibreEndpoint(targetType, targetHandle)\n  );\n}\n\n/** Inherit paint from the opposite side of a splice (tube feed on the other side). */\nexport function colorFromSpliceOppositeEdge(params: {\n  nodeId: string;\n  nodeType?: string;\n  handle: string | null | undefined;\n  edges: readonly FibreEdgePaintLookup[];\n  excludeEdgeId?: string;\n}): { color: string; striped?: boolean } | undefined {\n  const { nodeId, nodeType, handle, edges, excludeEdgeId } = params;\n  if (!handle || nodeType !== 'splice') {\n    return undefined;\n  }\n\n  const oppositeHandle = getOppositeSpliceHandle(handle);\n  if (!oppositeHandle) {\n    return undefined;\n  }\n\n  const edge = edges.find(\n    (candidate) =>\n      (!excludeEdgeId || candidate.id !== excludeEdgeId) &&\n      edgeTouchesHandle(candidate, nodeId, oppositeHandle),\n  );\n  if (edge?.data?.color) {\n    return { color: edge.data.color, striped: edge.data.striped };\n  }\n\n  return undefined;\n}\n\n/**\n * Coupler right ports default to yellow patch-side output.\n * With passThroughColor, they inherit the left port's connected fiber color.\n */\nexport function colorFromCouplerPort(params: {\n  nodeId: string;\n  nodeType?: string;\n  handle: string | null | undefined;\n  passThroughColor?: boolean;\n  edges: readonly FibreEdgePaintLookup[];\n  excludeEdgeId?: string;\n}): { color: string; striped?: boolean } | undefined {\n  const { nodeId, nodeType, handle, passThroughColor, edges, excludeEdgeId } =\n    params;\n  if (!handle || nodeType !== 'coupler') {\n    return undefined;\n  }\n\n  if (isCouplerRightHandle(handle)) {\n    if (passThroughColor) {\n      const leftHandle = getCouplerSiblingHandle(handle);\n      if (leftHandle) {\n        const leftEdge = edges.find(\n          (candidate) =>\n            (!excludeEdgeId || candidate.id !== excludeEdgeId) &&\n            edgeTouchesHandle(candidate, nodeId, leftHandle),\n        );\n        if (leftEdge?.data?.color) {\n          return { color: leftEdge.data.color, striped: leftEdge.data.striped };\n        }\n      }\n    }\n    return { color: DEFAULT_COUPLER_OUTPUT_COLOR, striped: false };\n  }\n\n  return undefined;\n}\n\n/** Resolve edge paint for connect/reconnect across tubes, splices, and couplers. */\nexport function resolveFibreEdgePaint(params: {\n  sourceId: string;\n  sourceType?: string;\n  sourceHandle?: string | null;\n  sourcePassThroughColor?: boolean;\n  targetId: string;\n  targetType?: string;\n  targetHandle?: string | null;\n  targetPassThroughColor?: boolean;\n  edges: readonly FibreEdgePaintLookup[];\n  excludeEdgeId?: string;\n}): { color: string; striped?: boolean } | undefined {\n  const fromEndpoints = deriveFibreColorForConnection({\n    sourceType: params.sourceType,\n    sourceHandle: params.sourceHandle,\n    targetType: params.targetType,\n    targetHandle: params.targetHandle,\n  });\n  if (fromEndpoints) {\n    return fromEndpoints;\n  }\n\n  const fromCouplerSource = colorFromCouplerPort({\n    nodeId: params.sourceId,\n    nodeType: params.sourceType,\n    handle: params.sourceHandle,\n    passThroughColor: params.sourcePassThroughColor,\n    edges: params.edges,\n    excludeEdgeId: params.excludeEdgeId,\n  });\n  if (fromCouplerSource) {\n    return fromCouplerSource;\n  }\n\n  const fromCouplerTarget = colorFromCouplerPort({\n    nodeId: params.targetId,\n    nodeType: params.targetType,\n    handle: params.targetHandle,\n    passThroughColor: params.targetPassThroughColor,\n    edges: params.edges,\n    excludeEdgeId: params.excludeEdgeId,\n  });\n  if (fromCouplerTarget) {\n    return fromCouplerTarget;\n  }\n\n  return (\n    colorFromSpliceOppositeEdge({\n      nodeId: params.sourceId,\n      nodeType: params.sourceType,\n      handle: params.sourceHandle,\n      edges: params.edges,\n      excludeEdgeId: params.excludeEdgeId,\n    }) ??\n    colorFromSpliceOppositeEdge({\n      nodeId: params.targetId,\n      nodeType: params.targetType,\n      handle: params.targetHandle,\n      edges: params.edges,\n      excludeEdgeId: params.excludeEdgeId,\n    })\n  );\n}\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode, NetworkFlowEdge } from '../types';\nimport { buildCouplerHandleId } from '../utils/handle-ids';\nimport { DEFAULT_COUPLER_OUTPUT_COLOR } from '../utils/fibre-edge-color';\nimport { useNodeEdges } from './use-node-edges';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nconst CouplerNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const {\n    connector = 'LC',\n    couplerFiberCount,\n    label,\n    portLabelsLeft,\n    portLabelsRight,\n    passThroughColor = false,\n  } = data || {};\n  const edges = useNodeEdges(id);\n  \n  // Calculate dimensions based on fiber count\n  const normalizedFibreCount = typeof couplerFiberCount === 'number' && [1, 2, 4].includes(couplerFiberCount)\n    ? couplerFiberCount\n    : 2;\n  const portCount = Math.max(1, Math.min(4, normalizedFibreCount));\n  const handleSize = 9;\n  const handleSpacing = 3;\n  const padding = 3;\n  const minWidth = 60;\n  const minHeight = 30;\n  \n  // Calculate height based on number of ports\n  const totalHandlesHeight = (portCount * handleSize) + ((portCount - 1) * handleSpacing);\n  const height = Math.max(minHeight, totalHandlesHeight + (padding * 2));\n  const width = Math.max(minWidth, 80);\n  \n  // Get colors from connected edges for each port\n  const getPortColor = (side: 'left' | 'right', portIndex: number): string => {\n    const handleId = buildCouplerHandleId(side, portIndex + 1);\n    const edge = edges.find((e: NetworkFlowEdge) =>\n      (e.source === id && e.sourceHandle === handleId) ||\n      (e.target === id && e.targetHandle === handleId)\n    ) as NetworkFlowEdge | undefined;\n    if (edge?.data?.color) {\n      return edge.data.color;\n    }\n\n    if (side === 'right') {\n      if (passThroughColor) {\n        const leftHandleId = buildCouplerHandleId('left', portIndex + 1);\n        const leftEdge = edges.find((e: NetworkFlowEdge) =>\n          (e.source === id && e.sourceHandle === leftHandleId) ||\n          (e.target === id && e.targetHandle === leftHandleId)\n        ) as NetworkFlowEdge | undefined;\n        if (leftEdge?.data?.color) {\n          return leftEdge.data.color;\n        }\n      }\n      return DEFAULT_COUPLER_OUTPUT_COLOR;\n    }\n\n    return '#555';\n  };\n  \n  // Render handles for one side\n  const renderHandles = (side: 'left' | 'right') => {\n    const handles: React.ReactElement[] = [];\n    const position = side === 'left' ? Position.Left : Position.Right;\n    const labels = side === 'left' ? portLabelsLeft : portLabelsRight;\n    \n    for (let i = 0; i < portCount; i++) {\n      const portIndex = i + 1;\n      const handleId = buildCouplerHandleId(side, portIndex);\n      const portColor = getPortColor(side, i);\n      \n      // Calculate vertical position - center the ports vertically\n      const totalHeight = totalHandlesHeight;\n      const startTop = padding + (height - totalHeight) / 2;\n      const top = startTop + (i * (handleSize + handleSpacing));\n      \n      const handleStyle: React.CSSProperties = {\n        position: 'absolute',\n        left: side === 'left' ? -5 : undefined,\n        right: side === 'right' ? -5 : undefined,\n        top: `${top}px`,\n        width: handleSize,\n        height: handleSize,\n        borderRadius: '50%',\n        border: 'none',\n        background: portColor,\n        zIndex: 100,\n        cursor: 'crosshair',\n        transform: 'translateY(-50%)',\n      };\n      \n      handles.push(\n        <Handle\n          key={handleId}\n          id={handleId}\n          type=\"source\"\n          position={position}\n          aria-label={buildPortAriaLabel({\n            kind: 'Coupler port',\n            group: label,\n            label: (labels && labels[i]) || `${side} ${portIndex}`,\n          })}\n          role=\"img\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n        />\n      );\n      \n      // Add port label if provided\n      if (labels && labels[i]) {\n        handles.push(\n          <div\n            key={`${handleId}-label`}\n            style={{\n              position: 'absolute',\n              left: side === 'left' ? -30 : undefined,\n              right: side === 'right' ? -30 : undefined,\n              top: `${top}px`,\n              transform: 'translateY(-50%)',\n              fontSize: '8px',\n              color: 'var(--rn-text-muted, #9ca3af)',\n              fontFamily: 'monospace',\n              pointerEvents: 'none',\n              textAlign: side === 'left' ? 'right' : 'left',\n              width: '25px',\n            }}\n          >\n            {labels[i]}\n          </div>\n        );\n      }\n    }\n    \n    return handles;\n  };\n  \n  const displayLabel = label || `${connector}-${portCount}f`;\n  \n  return (\n    <div\n      className={`coupler-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        width,\n        height,\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        border: `2px solid ${selected ? 'var(--rn-selection-ring, #3b82f6)' : 'var(--rn-border, #4b5563)'}`,\n        background: 'var(--rn-surface, #1f2937)',\n        position: 'relative',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '10px',\n        padding: `${padding}px`,\n        boxSizing: 'border-box',\n      }}\n    >\n      {/* Left side handles */}\n      {/* Invisible linear-flow anchors: id-less edges resolve to the FIRST\n          handle of the needed type, so registering an invisible source-right\n          first and providing the only target-type handle (left) gives\n          deterministic left-in / right-out anchoring for trace-style linear\n          paths without touching the id'd handles or interactive connecting. */}\n      <Handle\n        type=\"source\"\n        position={Position.Right}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', right: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n      <Handle\n        type=\"target\"\n        position={Position.Left}\n        aria-hidden\n        style={{ opacity: 0, pointerEvents: 'none', left: -4, top: '50%', transform: 'translateY(-50%)' }}\n      />\n      {renderHandles('left')}\n      \n      {/* Center label */}\n      <div\n        style={{\n          position: 'absolute',\n          top: '50%',\n          left: '50%',\n          transform: 'translate(-50%, -50%)',\n          textAlign: 'center',\n          pointerEvents: 'none',\n          zIndex: 1,\n        }}\n      >\n        <div style={{ fontWeight: 'bold', fontSize: '9px' }}>{displayLabel}</div>\n      </div>\n      \n      {/* Right side handles */}\n      {renderHandles('right')}\n    </div>\n  );\n};\n\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const CouplerNode = React.memo(CouplerNodeInner);\n","import React, { useEffect, useMemo, useRef } from 'react';\nimport { Handle, NodeProps, Position, useUpdateNodeInternals } from '@xyflow/react';\nimport { NetworkNode, NetworkFlowNode } from '../types';\nimport {\n  PANEL_HEADER_HEIGHT,\n  calculatePanelDimensions,\n} from '../utils/coupler-positioning';\nimport { useChildNodes } from './use-child-nodes';\n\nconst PatchPanelNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { label = 'Patch Panel', header, panelWidth: dataWidth, panelHeight: dataHeight } = data || {};\n  const updateNodeInternals = useUpdateNodeInternals();\n  const childNodes = useChildNodes(id);\n  const lastDimensionsRef = useRef<{ width: number; height: number } | null>(null);\n\n  const panelNode: NetworkNode = useMemo(() => ({\n    id,\n    type: 'patch-panel',\n    position: { x: 0, y: 0 },\n    data: { ...data, panelWidth: dataWidth, panelHeight: dataHeight },\n  }), [id, data, dataWidth, dataHeight]);\n\n  const dynamicDimensions = useMemo(() => {\n    if (dataWidth && dataHeight) {\n      return { width: dataWidth, height: dataHeight };\n    }\n\n    if (childNodes.length === 0) {\n      return {\n        width: dataWidth || 400,\n        height: dataHeight || 300,\n      };\n    }\n\n    return calculatePanelDimensions(panelNode, childNodes as NetworkNode[]);\n  }, [childNodes, panelNode, dataWidth, dataHeight]);\n\n  // Let React Flow measure the rendered DOM instead of writing panelWidth/Height\n  // back into node data (that write-back loop was freezing /odf).\n  useEffect(() => {\n    if (dataWidth && dataHeight) {\n      return;\n    }\n\n    const previous = lastDimensionsRef.current;\n    if (\n      previous &&\n      Math.abs(previous.width - dynamicDimensions.width) <= 1 &&\n      Math.abs(previous.height - dynamicDimensions.height) <= 1\n    ) {\n      return;\n    }\n\n    lastDimensionsRef.current = dynamicDimensions;\n    updateNodeInternals(id);\n  // dynamicDimensions identity churns each render; width/height are the signal.\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [id, dynamicDimensions.width, dynamicDimensions.height, dataWidth, dataHeight, updateNodeInternals]);\n\n  const panelWidth = dynamicDimensions.width;\n  const panelHeight = dynamicDimensions.height;\n  const headerHeight = PANEL_HEADER_HEIGHT;\n  const displayHeader = header || label;\n\n  return (\n    <div\n      className={`patch-panel-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        background: 'transparent',\n        border: 'none',\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        padding: 0,\n        width: `${panelWidth}px`,\n        height: `${panelHeight}px`,\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '12px',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.1)',\n        position: 'relative',\n        display: 'flex',\n        flexDirection: 'column',\n        backgroundColor: 'var(--rn-surface, #1f2937)',\n        boxSizing: 'border-box',\n      }}\n    >\n      <div\n        style={{\n          textAlign: 'center',\n          marginBottom: 0,\n          zIndex: 2,\n          background: 'var(--rn-surface-raised, #374151)',\n          padding: '6px 12px',\n          position: 'relative',\n          height: `${headerHeight}px`,\n          display: 'flex',\n          flexDirection: 'row',\n          alignItems: 'center',\n          justifyContent: 'center',\n          gap: '6px',\n          borderRadius: 'var(--rn-ui-radius, 1px) var(--rn-ui-radius, 1px) 0 0',\n          borderBottom: '1px solid var(--rn-border, #4b5563)',\n        }}\n      >\n        <div style={{ fontWeight: 'bold', fontSize: '11px' }}>{displayHeader}</div>\n      </div>\n\n      <div\n        style={{\n          position: 'absolute',\n          top: headerHeight,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          pointerEvents: 'none',\n          overflow: 'visible',\n        }}\n      />\n\n      {childNodes.length === 0 ? (\n        // Label-only fallback: a panel with no child coupler nodes renders no\n        // handles at all, so React Flow drops every edge touching it. Exactly\n        // ONE target (left) and ONE source (right) — id-less edges resolve to\n        // the first handle of the required type, so a single pair gives\n        // deterministic left-in / right-out anchoring on linear trace paths\n        // instead of stacking both edges on one point.\n        <>\n          {/* Structural anchors with no port identity of their own: hidden\n              rather than named, so the panel is announced once. */}\n          <Handle type=\"target\" position={Position.Left} aria-hidden />\n          <Handle type=\"source\" position={Position.Right} aria-hidden />\n        </>\n      ) : null}\n    </div>\n  );\n};\n\nexport const PatchPanelNode = React.memo(PatchPanelNodeInner);\n","import React, { useEffect, useMemo, useRef } from 'react';\nimport { NodeProps, useUpdateNodeInternals } from '@xyflow/react';\nimport { NetworkNode, NetworkFlowNode } from '../types';\nimport {\n  PANEL_HEADER_HEIGHT,\n  calculateClosureDimensions,\n} from '../utils/coupler-positioning';\nimport { useChildNodes } from './use-child-nodes';\n\nconst ClosureNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { label = 'Closure', header, closureWidth: dataWidth, closureHeight: dataHeight, panelWidth, panelHeight } = data || {};\n  const updateNodeInternals = useUpdateNodeInternals();\n  const childNodes = useChildNodes(id);\n  const lastDimensionsRef = useRef<{ width: number; height: number } | null>(null);\n\n  const closureNode: NetworkNode = useMemo(() => ({\n    id,\n    type: 'closure',\n    position: { x: 0, y: 0 },\n    data: { ...data, closureWidth: dataWidth || panelWidth, closureHeight: dataHeight || panelHeight },\n  }), [id, data, dataWidth, dataHeight, panelWidth, panelHeight]);\n\n  const dynamicDimensions = useMemo(() => {\n    if (dataWidth && dataHeight) {\n      return { width: dataWidth, height: dataHeight };\n    }\n\n    if (panelWidth && panelHeight) {\n      return { width: panelWidth, height: panelHeight };\n    }\n\n    if (childNodes.length === 0) {\n      return {\n        width: dataWidth || panelWidth || 400,\n        height: dataHeight || panelHeight || 300,\n      };\n    }\n\n    return calculateClosureDimensions(closureNode, childNodes as NetworkNode[]);\n  }, [childNodes, closureNode, dataWidth, dataHeight, panelWidth, panelHeight]);\n\n  useEffect(() => {\n    if ((dataWidth && dataHeight) || (panelWidth && panelHeight)) {\n      return;\n    }\n\n    const previous = lastDimensionsRef.current;\n    if (\n      previous &&\n      Math.abs(previous.width - dynamicDimensions.width) <= 1 &&\n      Math.abs(previous.height - dynamicDimensions.height) <= 1\n    ) {\n      return;\n    }\n\n    lastDimensionsRef.current = dynamicDimensions;\n    updateNodeInternals(id);\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [\n    id,\n    dynamicDimensions.width,\n    dynamicDimensions.height,\n    dataWidth,\n    dataHeight,\n    panelWidth,\n    panelHeight,\n    updateNodeInternals,\n  ]);\n\n  const closureWidth = dynamicDimensions.width;\n  const closureHeight = dynamicDimensions.height;\n  const headerHeight = PANEL_HEADER_HEIGHT;\n  const displayHeader = header || label;\n  const semicircleRadius = closureWidth / 2;\n\n  return (\n    <div\n      className={`closure-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        background: 'transparent',\n        border: 'none',\n        borderRadius: `${semicircleRadius}px ${semicircleRadius}px var(--rn-ui-radius, 1px) var(--rn-ui-radius, 1px)`,\n        padding: 0,\n        width: `${closureWidth}px`,\n        height: `${closureHeight}px`,\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '12px',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.1)',\n        position: 'relative',\n        display: 'flex',\n        flexDirection: 'column',\n        backgroundColor: 'var(--rn-surface, #1f2937)',\n        boxSizing: 'border-box',\n        overflow: 'hidden',\n      }}\n    >\n      <div\n        style={{\n          textAlign: 'center',\n          marginBottom: 0,\n          zIndex: 2,\n          background: 'var(--rn-surface-raised, #374151)',\n          padding: '6px 12px',\n          position: 'relative',\n          height: `${headerHeight}px`,\n          display: 'flex',\n          flexDirection: 'row',\n          alignItems: 'center',\n          justifyContent: 'center',\n          gap: '6px',\n          borderRadius: `${semicircleRadius}px ${semicircleRadius}px 0 0`,\n          borderBottom: '1px solid var(--rn-border, #4b5563)',\n        }}\n      >\n        <div style={{ fontWeight: 'bold', fontSize: '11px' }}>{displayHeader}</div>\n      </div>\n\n      <div\n        style={{\n          position: 'absolute',\n          top: headerHeight,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          pointerEvents: 'none',\n          overflow: 'visible',\n        }}\n      />\n    </div>\n  );\n};\n\nexport const ClosureNode = React.memo(ClosureNodeInner);\n","import React from 'react';\nimport { Handle, Position, NodeProps } from '@xyflow/react';\nimport { NetworkFlowNode, NetworkFlowEdge } from '../types';\nimport { normalizeHandleId } from '../utils/handle-ids';\nimport { useNodeEdges } from './use-node-edges';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\n/**\n * FibreSplitNode component for rendering fiber splitters and WDM devices\n * Uses a trapezoid shape pointing right (narrow input on left, wider outputs on right)\n * \n * @public\n */\nconst FibreSplitNodeInner: React.FC<NodeProps<NetworkFlowNode>> = ({ data, selected, id }) => {\n  const { \n    label, \n    splitterType = 'splitter',\n    inputCount = 1,\n    outputCount = 8,\n  } = data || {};\n  const edges = useNodeEdges(id);\n\n  const displayLabel = label || (splitterType === 'WDM' ? 'WDM' : 'Splitter');\n  \n  // Handle dimensions matching CouplerNode\n  const handleSize = 9;\n  const handleSpacing = 3;\n  const padding = 3;\n  const minWidth = 50;\n  const minHeight = 30;\n  \n  // Input handles: 1 or 2, positioned closer together\n  const inputCountNum = Math.min(Math.max(inputCount || 1, 1), 2);\n  const inputHandlesHeight = (inputCountNum * handleSize) + ((inputCountNum - 1) * handleSpacing);\n  \n  // Output handles\n  const outputCountNum = Math.min(outputCount || 8, 48);\n  const outputHandlesHeight = (outputCountNum * handleSize) + ((outputCountNum - 1) * handleSpacing);\n  \n  // Calculate height based on outputs (larger side)\n  const totalHandlesHeight = Math.max(inputHandlesHeight, outputHandlesHeight);\n  const height = Math.max(minHeight, totalHandlesHeight + (padding * 2));\n  const width = Math.max(minWidth, 50);\n  \n  // Get color from connected edge for a handle\n  const getHandleColor = (handleId: string): string => {\n    const normalizedHandleId = normalizeHandleId(handleId);\n    const edge = edges.find((e: NetworkFlowEdge) => \n      (e.source === id && normalizeHandleId(e.sourceHandle) === normalizedHandleId) ||\n      (e.target === id && normalizeHandleId(e.targetHandle) === normalizedHandleId)\n    ) as NetworkFlowEdge | undefined;\n    return edge?.data?.color || '#555';\n  };\n  \n  // Render input handles on the left - 1 or 2, closer together\n  const renderInputHandles = () => {\n    const handles: React.ReactElement[] = [];\n    const startTop = padding + (height - inputHandlesHeight) / 2;\n    \n    for (let i = 0; i < inputCountNum; i++) {\n      const handleBaseId = `input-${i + 1}`;\n      const top = startTop + (i * (handleSize + handleSpacing));\n      const portColor = getHandleColor(handleBaseId);\n      \n      const handleStyle: React.CSSProperties = {\n        position: 'absolute',\n        left: -5,\n        top: `${top}px`,\n        width: handleSize,\n        height: handleSize,\n        borderRadius: '50%',\n        border: 'none',\n        background: portColor,\n        zIndex: 100,\n        cursor: 'crosshair',\n        transform: 'translateY(-50%)',\n      };\n      \n      handles.push(\n        <Handle\n          key={handleBaseId}\n          id={handleBaseId}\n          type=\"source\"\n          position={Position.Left}\n          aria-label={buildPortAriaLabel({\n            kind: 'Input',\n            group: displayLabel,\n            label: i + 1,\n          })}\n          role=\"img\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n        />\n      );\n    }\n    \n    return handles;\n  };\n  \n  // Render output handles on the right\n  const renderOutputHandles = () => {\n    const handles: React.ReactElement[] = [];\n    const startTop = padding + (height - outputHandlesHeight) / 2;\n    \n    for (let i = 0; i < outputCountNum; i++) {\n      const handleBaseId = `output-${i + 1}`;\n      const top = startTop + (i * (handleSize + handleSpacing));\n      const portColor = getHandleColor(handleBaseId);\n      \n      const handleStyle: React.CSSProperties = {\n        position: 'absolute',\n        right: -5,\n        top: `${top}px`,\n        width: handleSize,\n        height: handleSize,\n        borderRadius: '50%',\n        border: 'none',\n        background: portColor,\n        zIndex: 100,\n        cursor: 'crosshair',\n        transform: 'translateY(-50%)',\n      };\n      \n      handles.push(\n        <Handle\n          key={handleBaseId}\n          id={handleBaseId}\n          type=\"source\"\n          position={Position.Right}\n          aria-label={buildPortAriaLabel({\n            kind: 'Output',\n            group: displayLabel,\n            label: i + 1,\n          })}\n          role=\"img\"\n          isConnectable={true}\n          isConnectableStart={true}\n          isConnectableEnd={true}\n          style={handleStyle}\n        />\n      );\n    }\n    \n    return handles;\n  };\n  \n  // Trapezoid shape (narrower on left, wider on right)\n  // Left edge height based on input handles - increased for taller flat edge\n  const leftEdgeHeight = Math.max(inputHandlesHeight + (padding * 4), minHeight * 0.6); // Minimum 60% of minHeight, with extra padding\n  const leftTop = (height - leftEdgeHeight) / 2;\n  const leftBottom = leftTop + leftEdgeHeight;\n  const rightTop = 0;\n  const rightBottom = height;\n  \n  const borderColor = selected\n    ? 'var(--rn-selection-ring, #3b82f6)'\n    : 'var(--rn-border, #4b5563)';\n  \n  return (\n    <div\n      className={`trapezoid-node ${selected ? 'selected rn-node--selected' : ''}`}\n      style={{\n        width,\n        height,\n        position: 'relative',\n        background: 'transparent',\n        boxShadow: '0 2px 4px rgba(0,0,0,0.2)',\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        color: 'var(--rn-text, #e5e7eb)',\n        fontFamily: 'monospace',\n        fontSize: '10px',\n        padding: `${padding}px`,\n        boxSizing: 'border-box',\n      }}\n    >\n      {/* Trapezoid border (no fill) */}\n      <svg\n        width={width}\n        height={height}\n        viewBox={`0 0 ${width} ${height}`}\n        style={{\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: `${width}px`,\n          height: `${height}px`,\n          pointerEvents: 'none',\n          zIndex: 0,\n        }}\n      >\n        <polygon\n          points={`0,${leftTop} 0,${leftBottom} ${width},${rightBottom} ${width},${rightTop}`}\n          fill=\"var(--rn-surface, #1f2937)\"\n          stroke={borderColor}\n          strokeWidth=\"2\"\n        />\n      </svg>\n      \n      {/* Left side handles */}\n      {renderInputHandles()}\n      \n      {/* Center label */}\n      <div\n        style={{\n          position: 'absolute',\n          top: '50%',\n          left: '50%',\n          transform: 'translate(-50%, -50%)',\n          textAlign: 'center',\n          pointerEvents: 'none',\n          zIndex: 1,\n        }}\n      >\n        <div style={{ fontWeight: 'bold', fontSize: '9px' }}>{displayLabel}</div>\n      </div>\n      \n      {/* Right side handles */}\n      {renderOutputHandles()}\n    </div>\n  );\n};\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const FibreSplitNode = React.memo(FibreSplitNodeInner);\n","import React from \"react\";\nimport type { NodeProps, NodeTypes } from \"@xyflow/react\";\nimport type { NetworkFlowNode } from \"../types\";\nimport { RackNode } from \"./RackNode\";\nimport { FibreNode } from \"./FibreNode\";\nimport { DeviceNode } from \"./DeviceNode\";\nimport { VerticalPDU } from \"./VerticalPDU\";\nimport { DistributionBoardNode } from \"./DistributionBoardNode\";\nimport { SpliceNode } from \"./SpliceNode\";\nimport { SpliceTrayNode } from \"./SpliceTrayNode\";\nimport { TubeNode } from \"./TubeNode\";\nimport { CableNode } from \"./CableNode\";\nimport { MultiTubeCableNode } from \"./MultiTubeCableNode\";\nimport { CouplerNode } from \"./CouplerNode\";\nimport { PatchPanelNode } from \"./PatchPanelNode\";\nimport { ClosureNode } from \"./ClosureNode\";\nimport { FibreSplitNode } from \"./FibreSplitNode\";\nimport { useIsRackDropTarget } from \"../context/DiagramRackDropTargetContext\";\n\n// Wrapper component that adds data-type attribute to React Flow node wrapper\n// React Flow doesn't natively set data-type, so we add it for testing/querying purposes\n// Uses ref callback + useEffect to ensure attribute is set reliably\nconst NodeWithDataType: React.FC<{\n  type: string;\n  children: React.ReactNode;\n}> = ({ type, children }) => {\n  const wrapperRef = React.useRef<HTMLDivElement>(null);\n\n  // Set data-type on React Flow's node wrapper using ref callback (synchronous)\n  const setDataTypeRef = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      wrapperRef.current = element;\n      if (element) {\n        // Find the React Flow wrapper (parent with class react-flow__node)\n        let parent = element.parentElement;\n        let attempts = 0;\n        while (\n          parent &&\n          !parent.classList.contains(\"react-flow__node\") &&\n          attempts < 10\n        ) {\n          parent = parent.parentElement;\n          attempts++;\n        }\n        if (parent && parent.classList.contains(\"react-flow__node\")) {\n          parent.setAttribute(\"data-type\", type);\n        }\n      }\n    },\n    [type],\n  );\n\n  // Fallback: also set in useEffect in case ref callback runs before parent is mounted\n  React.useEffect(() => {\n    if (wrapperRef.current) {\n      let parent = wrapperRef.current.parentElement;\n      let attempts = 0;\n      while (\n        parent &&\n        !parent.classList.contains(\"react-flow__node\") &&\n        attempts < 10\n      ) {\n        parent = parent.parentElement;\n        attempts++;\n      }\n      if (parent && parent.classList.contains(\"react-flow__node\")) {\n        parent.setAttribute(\"data-type\", type);\n      }\n    }\n  }, [type]);\n\n  return (\n    <div ref={setDataTypeRef} style={{ width: \"100%\", height: \"100%\" }}>\n      {children}\n    </div>\n  );\n};\n\n// Create nodeTypes with face change callbacks - memoized outside component\n/**\n * Rack node type. Subscribes to the diagram's drop-target store so the\n * highlight repaints when a dragged device enters or leaves this rack — React\n * Flow does not re-render a memoised node for diagram-level state changes.\n */\nconst RackNodeType: React.FC<\n  NodeProps<NetworkFlowNode> & {\n    onFaceChange?: (nodeId: string, face: \"front\" | \"rear\") => void;\n    onRackFaceChange?: (rackId: string, face: \"front\" | \"rear\") => void;\n  }\n> = ({ onFaceChange, onRackFaceChange, ...props }) => {\n  const isDragOver = useIsRackDropTarget(props.id);\n  return (\n    <NodeWithDataType type=\"rack\">\n      <RackNode\n        {...props}\n        onFaceChange={onFaceChange}\n        onRackFaceChange={onRackFaceChange}\n        isDragOver={isDragOver}\n      />\n    </NodeWithDataType>\n  );\n};\n\nexport const createNodeTypes = (\n  onFaceChange?: (nodeId: string, face: \"front\" | \"rear\") => void,\n  onRackFaceChange?: (rackId: string, face: \"front\" | \"rear\") => void,\n): NodeTypes => ({\n  rack: (props: NodeProps<NetworkFlowNode>) => (\n    <RackNodeType\n      {...props}\n      onFaceChange={onFaceChange}\n      onRackFaceChange={onRackFaceChange}\n    />\n  ),\n  fiber: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"fiber\">\n      <FibreNode {...props} />\n    </NodeWithDataType>\n  ),\n  device: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"device\">\n      <DeviceNode {...props} />\n    </NodeWithDataType>\n  ),\n  server: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"server\">\n      <DeviceNode {...props} />\n    </NodeWithDataType>\n  ),\n  switch: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"switch\">\n      <DeviceNode {...props} />\n    </NodeWithDataType>\n  ),\n  router: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"router\">\n      <DeviceNode {...props} />\n    </NodeWithDataType>\n  ),\n  \"vertical-pdu\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"vertical-pdu\">\n      <VerticalPDU {...props} />\n    </NodeWithDataType>\n  ),\n  \"distribution-board\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"distribution-board\">\n      <DistributionBoardNode {...props} />\n    </NodeWithDataType>\n  ),\n  splice: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"splice\">\n      <SpliceNode {...props} />\n    </NodeWithDataType>\n  ),\n  \"splice-tray\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"splice-tray\">\n      <SpliceTrayNode {...props} />\n    </NodeWithDataType>\n  ),\n  tube: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"tube\">\n      <TubeNode {...props} />\n    </NodeWithDataType>\n  ),\n  cable: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"cable\">\n      <CableNode {...props} />\n    </NodeWithDataType>\n  ),\n  \"multi-tube-cable\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"multi-tube-cable\">\n      <MultiTubeCableNode {...props} />\n    </NodeWithDataType>\n  ),\n  coupler: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"coupler\">\n      <CouplerNode {...props} />\n    </NodeWithDataType>\n  ),\n  \"patch-panel\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"patch-panel\">\n      <PatchPanelNode {...props} />\n    </NodeWithDataType>\n  ),\n  closure: (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"closure\">\n      <ClosureNode {...props} />\n    </NodeWithDataType>\n  ),\n  \"fibre-split\": (props: NodeProps<NetworkFlowNode>) => (\n    <NodeWithDataType type=\"fibre-split\">\n      <FibreSplitNode {...props} />\n    </NodeWithDataType>\n  ),\n});\n","import React, { useEffect } from \"react\";\nimport { useStore, type Viewport } from \"@xyflow/react\";\n\n/** Records viewport changes without re-rendering the full diagram. */\nexport const DiagramViewportTracker: React.FC<{\n  onViewportChange: (viewport: Viewport) => void;\n}> = ({ onViewportChange }) => {\n  // Some test/host adapters expose a deliberately partial store shape.\n  const [x, y, zoom] =\n    useStore((state) => state.transform) ?? ([0, 0, 1] as const);\n  useEffect(() => {\n    onViewportChange({ x, y, zoom });\n  }, [onViewportChange, x, y, zoom]);\n  return null;\n};\n","import React, { useLayoutEffect, useState } from \"react\";\nimport { useStore, useReactFlow, type Node } from \"@xyflow/react\";\nimport { getNodeRenderedSize } from \"../utils/node-dimensions\";\nimport { resolveNodeFace } from \"../utils/diagram-selection\";\nimport type { NetworkNode } from \"../types\";\n\nexport interface DiagramActionPanelButtons {\n  showRearToggle: boolean;\n  showDeRack: boolean;\n  showZoomToEdit: boolean;\n  count: number;\n}\n\ninterface DiagramActionPanelProps {\n  node: Node;\n  buttons: DiagramActionPanelButtons;\n  wrapperRef: React.RefObject<HTMLDivElement | null>;\n  getAbsoluteNodePosition: (node: Node) => { x: number; y: number };\n  onToggleFace: () => void;\n  onDeRack: () => void;\n  onZoomToEdit: () => void;\n}\n\n/**\n * The floating action panel anchored to the selected node (face toggle,\n * de-rack and rack editing zoom). It lives in its own small component so it\n * can subscribe to the viewport transform — the position must recompute on\n * every camera move, and\n * subscribing the parent NetworkDiagram would re-render the whole 2k-line\n * orchestrator per frame (same pattern as DiagramViewportTracker).\n *\n * Position inputs are re-read on every render rather than memoized: a memo\n * against the node object alone goes stale when the camera or the wrapper\n * moves without a nodes-array change, and a panel anchored to stale\n * coordinates can teleport under an in-flight pointer gesture — landing\n * pointerdown on the button but pointerup on the pane, which swallows the\n * click entirely.\n */\nexport const DiagramActionPanel: React.FC<DiagramActionPanelProps> = ({\n  node,\n  buttons,\n  wrapperRef,\n  getAbsoluteNodePosition,\n  onToggleFace,\n  onDeRack,\n  onZoomToEdit,\n}) => {\n  // Subscribe this tiny component to the transform so anchors recompute on\n  // pan/zoom/refit (some test/host adapters expose a partial store shape).\n  useStore((state) => state.transform) ?? [0, 0, 1];\n  const { flowToScreenPosition } = useReactFlow();\n\n  // Track the wrapper rect continuously — it can move or resize without a\n  // nodes change (toolbar reflow above the diagram, window resize, scroll).\n  const [wrapperRect, setWrapperRect] = useState<{\n    top: number;\n    left: number;\n    width: number;\n    height: number;\n  } | null>(null);\n\n  useLayoutEffect(() => {\n    const el = wrapperRef.current;\n    if (!el) return;\n    const update = () => {\n      const r = el.getBoundingClientRect();\n      setWrapperRect((prev) =>\n        prev &&\n        prev.top === r.top &&\n        prev.left === r.left &&\n        prev.width === r.width &&\n        prev.height === r.height\n          ? prev\n          : { top: r.top, left: r.left, width: r.width, height: r.height },\n      );\n    };\n    update();\n    const observer = new ResizeObserver(update);\n    observer.observe(el);\n    window.addEventListener(\"scroll\", update, true);\n    return () => {\n      observer.disconnect();\n      window.removeEventListener(\"scroll\", update, true);\n    };\n  }, [wrapperRef]);\n\n  if (buttons.count === 0) return null;\n\n  let top = 0;\n  let left = 0;\n  if (!wrapperRect || wrapperRect.width <= 0 || wrapperRect.height <= 0) {\n    return null;\n  }\n\n  const absolute = getAbsoluteNodePosition(node);\n  const { width: nodeWidth, height: nodeHeight } = getNodeRenderedSize(node);\n  const panelWidth = buttons.count > 1 ? 220 : 110;\n  const panelHeight = 34;\n\n  try {\n    const topCenterAnchor = flowToScreenPosition({\n      x: absolute.x + nodeWidth / 2,\n      y: absolute.y,\n    });\n    const bottomCenterAnchor = flowToScreenPosition({\n      x: absolute.x + nodeWidth / 2,\n      y: absolute.y + nodeHeight,\n    });\n\n    left = topCenterAnchor.x - wrapperRect.left - panelWidth / 2;\n    top = topCenterAnchor.y - wrapperRect.top - panelHeight - 10;\n\n    // If there isn't enough space above, place below the node.\n    if (top < 8) {\n      top = bottomCenterAnchor.y - wrapperRect.top + 10;\n    }\n\n    left = Math.max(8, Math.min(left, wrapperRect.width - panelWidth - 8));\n    top = Math.max(8, Math.min(top, wrapperRect.height - panelHeight - 8));\n  } catch {\n    return null;\n  }\n\n  const nodeData = node.data as NetworkNode[\"data\"];\n  const face: \"front\" | \"rear\" =\n    node.type === \"rack\"\n      ? ((nodeData?.face as \"front\" | \"rear\") || \"front\")\n      : resolveNodeFace(nodeData);\n\n  return (\n    <div\n      className=\"diagram-action-panel\"\n      style={{\n        position: \"absolute\",\n        top,\n        left,\n        display: \"flex\",\n        gap: 8,\n        zIndex: 2000,\n      }}\n    >\n      {buttons.showRearToggle && (\n        <button\n          onClick={(e) => {\n            e.preventDefault();\n            onToggleFace();\n          }}\n          style={{\n            background: \"#374151\",\n            color: \"#e5e7eb\",\n            border: \"1px solid #4b5563\",\n            borderRadius: \"var(--rn-ui-radius, 1px)\",\n            padding: \"6px 10px\",\n            fontSize: 11,\n            fontFamily: \"monospace\",\n            cursor: \"pointer\",\n          }}\n          title={face === \"front\" ? \"Switch to rear\" : \"Switch to front\"}\n        >\n          {face === \"front\" ? \"⇄ Rear\" : \"⇄ Front\"}\n        </button>\n      )}\n      {buttons.showDeRack && (\n        <button\n          onClick={(e) => {\n            e.preventDefault();\n            onDeRack();\n          }}\n          style={{\n            background: \"#7f1d1d\",\n            color: \"#fee2e2\",\n            border: \"1px solid #b91c1c\",\n            borderRadius: \"var(--rn-ui-radius, 1px)\",\n            padding: \"6px 10px\",\n            fontSize: 11,\n            fontFamily: \"monospace\",\n            cursor: \"pointer\",\n          }}\n          title=\"Remove device from rack so it can be moved/reassigned\"\n        >\n          De-rack\n        </button>\n      )}\n      {buttons.showZoomToEdit && (\n        <button\n          type=\"button\"\n          onClick={(event) => {\n            event.preventDefault();\n            onZoomToEdit();\n          }}\n          style={{\n            background: \"#374151\",\n            color: \"#e5e7eb\",\n            border: \"1px solid #4b5563\",\n            borderRadius: \"var(--rn-ui-radius, 1px)\",\n            padding: \"6px 10px\",\n            fontSize: 11,\n            fontFamily: \"monospace\",\n            cursor: \"pointer\",\n          }}\n          title=\"Zoom in to edit rack devices; pan to reach other rows\"\n        >\n          Zoom to edit\n        </button>\n      )}\n    </div>\n  );\n};\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { NetworkDiagramProps, NetworkNode, NetworkEdge } from \"../types\";\nimport { diagramDocumentToNetworkGraph, networkGraphToDiagramDocument } from \"../utils/document-adapter\";\nimport { graphContentEqual, mergeRuntimeStateIntoEdges, mergeRuntimeStateIntoNodes } from \"../utils/document-runtime-merge\";\nimport { warnDev } from \"../utils/diagram-logger\";\n\nexport const NetworkDiagramDocumentBridge: React.FC<\n  NetworkDiagramProps & { Inner: React.ComponentType<NetworkDiagramProps> }\n> = (props) => {\n  const {\n    Inner,\n    document,\n    initialDocument,\n    onDocumentChange,\n    onNodesChange,\n    onEdgesChange,\n    ...rest\n  } = props;\n\n  const usesDocumentModel =\n    document !== undefined || initialDocument !== undefined;\n  const isControlledDocument = document !== undefined;\n  const [uncontrolledDocument, setUncontrolledDocument] = useState(\n    () => initialDocument,\n  );\n  const activeDocument = document ?? uncontrolledDocument;\n\n  // Dev-only footgun #2: the document model and the nodes/edges model are\n  // mutually exclusive. When both are supplied the document wins and the\n  // nodes/edges props are dropped on the floor without a word. Warn once.\n  const documentOverrideWarnedRef = useRef(false);\n  const suppliedNodes = rest.nodes;\n  const suppliedEdges = rest.edges;\n  useEffect(() => {\n    if (documentOverrideWarnedRef.current) return;\n    if (!usesDocumentModel) return;\n    if (suppliedNodes === undefined && suppliedEdges === undefined) return;\n    documentOverrideWarnedRef.current = true;\n    const ignored = [\n      suppliedNodes !== undefined ? \"nodes\" : null,\n      suppliedEdges !== undefined ? \"edges\" : null,\n    ]\n      .filter(Boolean)\n      .join(\" and \");\n    warnDev(\n      `NetworkDiagram received both \\`${\n        document !== undefined ? \"document\" : \"initialDocument\"\n      }\\` and \\`${ignored}\\`. The document model wins: \\`${ignored}\\` ` +\n        `${ignored.includes(\" and \") ? \"are\" : \"is\"} ignored entirely. Drive ` +\n        `the diagram from the document (and onDocumentChange), or drop the ` +\n        `document prop and use nodes/edges.`,\n    );\n  }, [usesDocumentModel, suppliedNodes, suppliedEdges, document]);\n\n  const documentGraph = useMemo(\n    () =>\n      activeDocument ? diagramDocumentToNetworkGraph(activeDocument) : null,\n    [activeDocument],\n  );\n\n  const mergeGraphWithRuntimeState = useCallback(\n    (graph: { nodes: NetworkNode[]; edges: NetworkEdge[] } | null) => {\n      if (!graph) {\n        return null;\n      }\n\n      return {\n        nodes: mergeRuntimeStateIntoNodes(\n          graph.nodes,\n          latestGraphRef.current?.nodes ?? [],\n        ),\n        edges: mergeRuntimeStateIntoEdges(\n          graph.edges,\n          latestGraphRef.current?.edges ?? [],\n        ),\n      };\n    },\n    [],\n  );\n\n  const latestGraphRef = useRef(documentGraph);\n  const latestDocumentRef = useRef(activeDocument);\n  // F21: baseline for the document-emit dirty-check. Seeded to the current\n  // graph, and re-seeded whenever the document changes from the outside (a\n  // controlled `document` prop update) so a later selection is compared against\n  // current content, not a stale snapshot.\n  const lastEmittedGraphRef = useRef(documentGraph);\n  const graphForRender = useMemo(\n    () => mergeGraphWithRuntimeState(documentGraph),\n    [documentGraph, mergeGraphWithRuntimeState],\n  );\n\n  useEffect(() => {\n    latestGraphRef.current = graphForRender;\n  }, [graphForRender]);\n\n  useEffect(() => {\n    latestDocumentRef.current = activeDocument;\n  }, [activeDocument]);\n\n  useEffect(() => {\n    lastEmittedGraphRef.current = documentGraph;\n  }, [documentGraph]);\n\n  const emitDocumentChange = useCallback(\n    (nextNodes: NetworkNode[], nextEdges: NetworkEdge[]) => {\n      const nextGraph = { nodes: nextNodes, edges: nextEdges };\n\n      // F21: React Flow forwards selection (and, in non-controlled contexts,\n      // dimension) batches here even though they carry no serialization-relevant\n      // change. Skip the document emission — and the uncontrolled re-render —\n      // when the content matches what we last emitted, so consumers aren't\n      // notified of pure view-state churn. `latestGraphRef` still tracks the\n      // runtime nodes so selection/drag state keeps rendering.\n      const isDirty = !graphContentEqual(lastEmittedGraphRef.current, nextGraph);\n\n      latestGraphRef.current = nextGraph;\n      lastEmittedGraphRef.current = nextGraph;\n\n      if (!isDirty) {\n        return;\n      }\n\n      const nextDocument = networkGraphToDiagramDocument(\n        nextGraph,\n        latestDocumentRef.current,\n      );\n\n      latestDocumentRef.current = nextDocument;\n\n      if (!isControlledDocument) {\n        setUncontrolledDocument(nextDocument);\n      }\n\n      onDocumentChange?.(nextDocument);\n    },\n    [isControlledDocument, onDocumentChange],\n  );\n\n  const handleDocumentNodesChange = useCallback(\n    (nextNodes: NetworkNode[]) => {\n      const nextEdges = latestGraphRef.current?.edges ?? [];\n      emitDocumentChange(nextNodes, nextEdges);\n      onNodesChange?.(nextNodes);\n    },\n    [emitDocumentChange, onNodesChange],\n  );\n\n  const handleDocumentEdgesChange = useCallback(\n    (nextEdges: NetworkEdge[]) => {\n      const nextNodes = latestGraphRef.current?.nodes ?? [];\n      emitDocumentChange(nextNodes, nextEdges);\n      onEdgesChange?.(nextEdges);\n    },\n    [emitDocumentChange, onEdgesChange],\n  );\n\n  if (usesDocumentModel) {\n    const graph = graphForRender ?? { nodes: [], edges: [] };\n    return (\n      <Inner\n        {...rest}\n        nodes={graph.nodes}\n        edges={graph.edges}\n        onNodesChange={handleDocumentNodesChange}\n        onEdgesChange={handleDocumentEdgesChange}\n      />\n    );\n  }\n\n  return <Inner {...props} />;\n};\n","import React, { useEffect } from \"react\";\nimport { useStore } from \"@xyflow/react\";\nimport {\n  DIAGRAM_ZOOM_LOD_CLASS,\n  isBelowLabelLodZoom,\n  readTransformZoom,\n} from \"../utils/zoom-lod\";\n\nexport interface DiagramZoomLodProps {\n  /** The diagram wrapper that owns the theme class and contains every node. */\n  wrapperRef: React.RefObject<HTMLDivElement | null>;\n  /**\n   * The className React currently renders onto that wrapper.\n   *\n   * The level-of-detail class is written imperatively so a zoom crossing does\n   * not re-render the diagram. React owns `className` on the same element, so\n   * whenever React writes a new value (theme flip, consumer `className` change)\n   * it clobbers the imperative class — re-running this effect on that value\n   * puts it straight back.\n   */\n  wrapperClassName: string;\n  /** Whether an image export is currently reading the wrapper DOM. */\n  isCapturingImage?: boolean;\n  /** Threshold override, mainly for tests. */\n  threshold?: number;\n}\n\n/**\n * Renders nothing. Subscribes to the React Flow viewport zoom and toggles\n * {@link DIAGRAM_ZOOM_LOD_CLASS} on the diagram wrapper.\n *\n * The store selector returns a boolean, not the zoom, so React bails out of\n * re-rendering except on an actual threshold crossing — the whole component\n * costs one boolean comparison per viewport frame.\n */\nexport const DiagramZoomLod: React.FC<DiagramZoomLodProps> = ({\n  wrapperRef,\n  wrapperClassName,\n  isCapturingImage = false,\n  threshold,\n}) => {\n  const belowDetail = useStore((state: { transform?: unknown }) =>\n    isBelowLabelLodZoom(readTransformZoom(state?.transform), threshold),\n  );\n\n  useEffect(() => {\n    const element = wrapperRef.current;\n    if (!element) {\n      return;\n    }\n    element.classList.toggle(\n      DIAGRAM_ZOOM_LOD_CLASS,\n      belowDetail && !isCapturingImage,\n    );\n    return () => {\n      element.classList.remove(DIAGRAM_ZOOM_LOD_CLASS);\n    };\n    // wrapperClassName is a re-sync trigger, not a value this effect reads;\n    // wrapperRef is a stable ref object and is deliberately not a dependency.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [belowDetail, isCapturingImage, wrapperClassName]);\n\n  return null;\n};\n","/**\n * Shared React Flow theming utilities\n * Ensures consistent styling across all React Flow components in the library\n */\n\nexport type ColorMode = 'light' | 'dark' | 'system';\n\n// Re-export for convenience\nexport type { ColorMode as ReactFlowColorMode };\n\n/**\n * Resolve system color mode to light or dark based on system preference\n */\nfunction resolveSystemColorMode(colorMode: ColorMode): 'light' | 'dark' {\n  if (colorMode === 'system') {\n    if (typeof window !== 'undefined' && window.matchMedia) {\n      return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';\n    }\n    // Default to dark if we can't detect system preference\n    return 'dark';\n  }\n  return colorMode;\n}\n\n/**\n * Get the React Flow theme class name based on color mode\n */\nexport function getReactFlowThemeClassName(colorMode: ColorMode, className?: string): string {\n  const resolvedMode = resolveSystemColorMode(colorMode);\n  const themeClass = resolvedMode === 'light' ? 'react-flow-light' : 'react-flow-dark';\n  return className ? `${themeClass} ${className}` : themeClass;\n}\n\n/**\n * Get the background color for React Flow Background component\n */\nexport function getReactFlowBackgroundColor(colorMode: ColorMode): string {\n  const resolvedMode = resolveSystemColorMode(colorMode);\n  return resolvedMode === 'light' ? '#e5e5e5' : '#2a2a2a';\n}\n\n/**\n * Get the wrapper div style for React Flow components\n * Provides consistent container styling\n */\nexport function getReactFlowWrapperStyle(\n  colorMode: ColorMode,\n  customStyle?: React.CSSProperties\n): React.CSSProperties {\n  return {\n    width: '100%',\n    height: '100%',\n    ...customStyle,\n  };\n}\n\n/**\n * Get the background color for React Flow Background component with custom opacity\n */\nexport function getReactFlowBackgroundColorWithOpacity(colorMode: ColorMode, opacity: number = 0.08): string {\n  const resolvedMode = resolveSystemColorMode(colorMode);\n  const baseColor = resolvedMode === 'light' ? '15, 23, 42' : '148, 163, 184';\n  return `rgba(${baseColor}, ${opacity})`;\n}\n","import { useSyncExternalStore } from 'react';\nimport type { ColorMode } from '../utils/react-flow-theming';\n\nconst LIGHT_QUERY = '(prefers-color-scheme: light)';\n\nfunction subscribeToSystemScheme(onChange: () => void): () => void {\n  if (typeof window === 'undefined' || !window.matchMedia) {\n    return () => {};\n  }\n  const query = window.matchMedia(LIGHT_QUERY);\n  query.addEventListener('change', onChange);\n  return () => query.removeEventListener('change', onChange);\n}\n\nfunction readSystemScheme(): 'light' | 'dark' {\n  if (typeof window !== 'undefined' && window.matchMedia) {\n    return window.matchMedia(LIGHT_QUERY).matches ? 'light' : 'dark';\n  }\n  return 'dark';\n}\n\nconst noopSubscribe = () => () => {};\n\n/**\n * Resolve a ColorMode to the concrete 'light' | 'dark' it renders as, staying\n * subscribed to the OS preference while mode is 'system'. This is what makes\n * the library's own theme class reactive without a remount — React Flow's\n * native v12 `colorMode` handles its internals the same way.\n */\nexport function useResolvedColorMode(colorMode: ColorMode): 'light' | 'dark' {\n  const system = useSyncExternalStore(\n    colorMode === 'system' ? subscribeToSystemScheme : noopSubscribe,\n    readSystemScheme,\n    () => 'dark' as const,\n  );\n  return colorMode === 'system' ? system : colorMode;\n}\n","import React from 'react';\nimport { EdgeProps, getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';\nimport { NetworkEdge, NetworkFlowEdge } from '../types';\nimport { fibreTracerColorFor, getRibbonColors } from '../utils/fibre-colors';\n\n/**\n * Render a ribbon fiber edge - colors displayed side-by-side (parallel stripes)\n * Uses layered strokes with decreasing widths to create parallel color bands\n */\nfunction renderRibbonEdge(\n  id: string,\n  path: string,\n  ribbonFiberIds: number[],\n  selected: boolean,\n  style: React.CSSProperties,\n  labelX: number,\n  labelY: number,\n  data: NetworkEdge['data'],\n  interactionWidth?: number\n): React.ReactElement {\n  const ribbonColors = getRibbonColors(ribbonFiberIds);\n  const numColors = ribbonColors.length;\n  // Each stripe gets equal width - reduced for narrower edges\n  const stripeWidth = selected ? 1.2 : 1.0;\n  const highlightEnabled = Boolean(data?.highlight);\n  const baseStrokeWidth = selected ? 2 : 1.5;\n  const highlightWidth = data?.highlightWidth ?? baseStrokeWidth * 3;\n  const highlightColor = data?.highlightColor ?? ribbonColors[0]?.base ?? '#22d3ee';\n  \n  // Draw paths from outside to inside (first color = outermost, last = innermost)\n  // Each path has decreasing stroke width so inner colors show on top\n  // Each visible stripe will have equal width = stripeWidth\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor,\n            strokeWidth: highlightWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightWidth}px`,\n          }}\n        />\n      )}\n      {ribbonColors.map((colorInfo, index) => {\n        // Calculate stroke width so each color gets equal visible width\n        // Outer path: numColors * stripeWidth, inner paths decrease by stripeWidth each\n        const remainingStripes = numColors - index;\n        const currentStrokeWidth = remainingStripes * stripeWidth;\n        \n        return (\n          <path\n            key={`${id}-ribbon-${index}`}\n            d={path}\n            stroke={colorInfo.base}\n            strokeWidth={currentStrokeWidth}\n            fill=\"none\"\n            strokeLinecap=\"butt\"\n            opacity={colorInfo.striped ? 0.9 : 1}\n            style={style}\n          />\n        );\n      })}\n      \n      {/* Overlay striped pattern for striped fibers (13-24) */}\n      {ribbonColors.some(c => c.striped) && (\n        <BaseEdge\n          id={`${id}-ribbon-stripe`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: '#000',\n            strokeWidth: 1,\n            strokeDasharray: '3 3',\n            opacity: 0.4,\n          }}\n        />\n      )}\n      \n      <EdgeLabelRenderer>\n        {selected && data && (data.bandwidth || data.length) && (\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n              background: 'var(--rn-surface, #1f2937)',\n              padding: '4px 8px',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: '10px',\n              color: 'var(--rn-text, #e5e7eb)',\n              fontFamily: 'monospace',\n              border: `1px solid ${ribbonColors[0]?.base || '#00FFFF'}`,\n              pointerEvents: 'none',\n              zIndex: 9999,\n            }}\n          >\n            {data.bandwidth && <div>{data.bandwidth}</div>}\n            {data.length && <div>{data.length}m</div>}\n            <div style={{ fontSize: '8px', opacity: 0.8 }}>{ribbonFiberIds.length}F</div>\n          </div>\n        )}\n      </EdgeLabelRenderer>\n    </>\n  );\n}\n\n/**\n * Fiber edge component for connecting fiber ports between nodes.\n * \n * Supports both single fiber connections with color coding and ribbon fiber\n * connections (6-12 fibers) with parallel stripe visualization.\n * \n * @example\n * ```tsx\n * {\n *   id: 'edge-1',\n *   source: 'node-1',\n *   target: 'node-2',\n *   sourceHandle: 'fiber-1',\n *   targetHandle: 'fiber-2',\n *   type: 'fiber',\n *   data: {\n *     kind: 'fiber',\n *     ribbonFiberIds: [1, 2, 3, 4, 5, 6] // For ribbon mode\n *   }\n * }\n * ```\n * \n * @public\n */\nconst FibreEdgeComponent: React.FC<EdgeProps<NetworkFlowEdge> & { className?: string }> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  data,\n  selected,\n  style = {},\n  className: _className,\n  interactionWidth,\n}) => {\n  const [edgePath, labelX, labelY] = getBezierPath({\n    sourceX,\n    sourceY,\n    sourcePosition,\n    targetX,\n    targetY,\n    targetPosition,\n  });\n\n  const { bandwidth, length, color = '#00FFFF', striped = false, ribbonFiberIds, isRibbon, highlight, highlightColor, highlightWidth } = data || {};\n  const highlightEnabled = Boolean(highlight);\n  \n  // Check if this is a ribbon edge\n  const ribbonMode = isRibbon || (ribbonFiberIds && ribbonFiberIds.length >= 6 && ribbonFiberIds.length <= 12);\n\n  // Render ribbon edge if in ribbon mode\n  if (ribbonMode && ribbonFiberIds) {\n    return renderRibbonEdge(id, edgePath, ribbonFiberIds, selected ?? false, style ?? {}, labelX, labelY, data, interactionWidth);\n  }\n\n  // Traditional single-fiber edge rendering\n  // Use BaseEdge so React Flow layering/zIndex works consistently like power edges\n  // Spread style first, then override with our specific properties to ensure data color takes precedence\n  const baseStrokeWidth = selected ? 3 : 2;\n  const highlightMaxWidth = highlightWidth ?? baseStrokeWidth * 3;\n  // No strokeDasharray here: a caller's style.strokeDasharray survives the\n  // spread, and leaving it unset when absent lets consumer CSS dash the path.\n  const baseEdgeStyle = {\n    ...style, // includes zIndex from React Flow\n    stroke: color, // Override any stroke from style prop with our data color\n    strokeWidth: baseStrokeWidth,\n  } as React.CSSProperties;\n\n\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor ?? color,\n            strokeWidth: highlightMaxWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightMaxWidth}px`,\n          }}\n        />\n      )}\n      {/* Base colored stroke */}\n      <BaseEdge \n        id={id} \n        path={edgePath}\n        interactionWidth={interactionWidth}\n        style={baseEdgeStyle}\n      />\n      {/* Overlay black dashed stroke to simulate tracer for 13–24 */}\n      {striped && (\n        <BaseEdge\n          id={`${id}-stripe`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: fibreTracerColorFor(color),\n            strokeWidth: 1.5,\n            strokeDasharray: '6 6',\n            opacity: 0.9,\n          }}\n        />\n      )}\n      <EdgeLabelRenderer>\n        {selected && data && (bandwidth || length) && (\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n              background: 'var(--rn-surface, #1f2937)',\n              padding: '4px 8px',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: '10px',\n              color: 'var(--rn-text, #e5e7eb)',\n              fontFamily: 'monospace',\n              border: `1px solid ${color}`,\n              pointerEvents: 'none',\n              zIndex: 9999,\n            }}\n          >\n            {bandwidth && <div>{bandwidth}</div>}\n            {length && <div>{length}m</div>}\n          </div>\n        )}\n      </EdgeLabelRenderer>\n    </>\n  );\n};\n\nexport const FibreEdge = React.memo(FibreEdgeComponent);\n","import React from 'react';\nimport { EdgeProps, getBezierPath, BaseEdge } from '@xyflow/react';\nimport type { NetworkFlowEdge } from '../types';\n\nconst HIGHLIGHT_LAYER_OPACITY = 0.7;\nconst HIGHLIGHT_BASE_OPACITY = 0.28;\n\nconst readNumeric = (value: unknown): number | undefined =>\n  typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\nconst PowerEdgeComponent: React.FC<EdgeProps<NetworkFlowEdge>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  style = {},\n  data,\n  markerEnd,\n  interactionWidth,\n}) => {\n  const [edgePath] = getBezierPath({\n    sourceX,\n    sourceY,\n    sourcePosition,\n    targetX,\n    targetY,\n    targetPosition,\n  });\n\n  const connectorType = data?.connectorType || 'C13';\n  const metadata = (data?.metadata ?? {}) as Record<string, unknown>;\n  const customStrokeWidth = readNumeric(data?.powerStrokeWidth) ?? readNumeric(metadata.strokeWidth);\n  const customStrokeDasharray =\n    typeof data?.powerStrokeDasharray === 'string'\n      ? data.powerStrokeDasharray\n      : typeof metadata.strokeDasharray === 'string'\n        ? metadata.strokeDasharray\n        : undefined;\n\n  const resolvedOpacity =\n    readNumeric(style.opacity) ??\n    readNumeric(data?.opacity) ??\n    readNumeric(data?.powerOpacity) ??\n    readNumeric(metadata.opacity);\n\n  const getEdgeStyle = (opacity?: number): React.CSSProperties => {\n    const connectorStrokeWidth = connectorType === 'C19' ? 5 : 3;\n    const strokeWidth = customStrokeWidth ?? connectorStrokeWidth;\n    const strokeDasharray = customStrokeDasharray ?? (connectorType === 'C19' ? 'none' : '6,3');\n    return {\n      stroke: data?.color || '#f59e0b',\n      strokeWidth,\n      strokeLinecap: 'round',\n      strokeLinejoin: 'round',\n      strokeDasharray,\n      ...(opacity !== undefined ? { opacity } : {}),\n      ...style,\n    };\n  };\n\n  // Energisation overlay (see utils/power-energization). Absent = classic\n  // styling; present = live (marching dashes) or de-energised (dimmed grey).\n  const energized = typeof data?.energized === 'boolean' ? data.energized : undefined;\n  const flowDirection = data?.flowDirection;\n  const energizedClassName =\n    energized === true\n      ? flowDirection === 'target-to-source'\n        ? 'rn-power-edge--energized-reverse'\n        : 'rn-power-edge--energized'\n      : undefined;\n  // SVG ids are document-global: two diagrams on one page can hold edges with\n  // the same id, and a shared marker id would paint one diagram's arrow in the\n  // other's colour. Namespace per edge instance (sanitised for url(#…)).\n  const instanceId = React.useId().replace(/[^a-zA-Z0-9_-]/g, '');\n  const arrowMarkerId =\n    energized === true && flowDirection\n      ? `${id}-power-flow-arrow-${instanceId}`\n      : undefined;\n\n  const applyEnergizationStyle = (base: React.CSSProperties): React.CSSProperties => {\n    if (energized === undefined) return base;\n    if (!energized) {\n      return {\n        ...base,\n        stroke: '#9ca3af',\n        opacity: readNumeric(base.opacity) !== undefined ? base.opacity : 0.35,\n      };\n    }\n    return {\n      ...base,\n      strokeDasharray: customStrokeDasharray ?? '10 6',\n    };\n  };\n\n  const highlight = Boolean(data?.highlight);\n  const highlightColor = data?.highlightColor ?? data?.color ?? '#f59e0b';\n  const baseStyle = applyEnergizationStyle(\n    getEdgeStyle(\n      highlight\n        ? resolvedOpacity !== undefined\n          ? Math.min(resolvedOpacity, HIGHLIGHT_BASE_OPACITY)\n          : HIGHLIGHT_BASE_OPACITY\n        : resolvedOpacity,\n    ),\n  );\n  const baseStrokeWidth =\n    typeof baseStyle.strokeWidth === 'number' ? baseStyle.strokeWidth : 3;\n  const highlightMaxWidth = data?.highlightWidth ?? baseStrokeWidth * 2.5;\n\n  return (\n    <>\n      {highlight ? (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          markerEnd={markerEnd}\n          style={{\n            ...baseStyle,\n            stroke: highlightColor,\n            strokeWidth: highlightMaxWidth,\n            opacity: HIGHLIGHT_LAYER_OPACITY,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightMaxWidth}px`,\n          }}\n        />\n      ) : null}\n      {arrowMarkerId ? (\n        <defs>\n          <marker\n            id={arrowMarkerId}\n            markerWidth={4}\n            markerHeight={4}\n            refX={3.2}\n            refY={2}\n            orient={flowDirection === 'target-to-source' ? 'auto-start-reverse' : 'auto'}\n            markerUnits=\"strokeWidth\"\n          >\n            <path d=\"M0,0 L4,2 L0,4 z\" fill={String(baseStyle.stroke ?? '#f59e0b')} />\n          </marker>\n        </defs>\n      ) : null}\n      <BaseEdge\n        id={id}\n        path={edgePath}\n        interactionWidth={interactionWidth}\n        className={energizedClassName}\n        markerEnd={\n          arrowMarkerId && flowDirection === 'source-to-target'\n            ? `url(#${arrowMarkerId})`\n            : markerEnd\n        }\n        markerStart={\n          arrowMarkerId && flowDirection === 'target-to-source'\n            ? `url(#${arrowMarkerId})`\n            : undefined\n        }\n        style={baseStyle}\n      />\n    </>\n  );\n};\n\nexport const PowerEdge = React.memo(PowerEdgeComponent);\n","import React from 'react';\nimport { EdgeProps, SmoothStepEdge as ReactFlowSmoothStepEdge, getSmoothStepPath, BaseEdge, EdgeLabelRenderer, Position } from '@xyflow/react';\nimport { NetworkEdge, NetworkFlowEdge } from '../types';\nimport { fibreTracerColorFor, getRibbonColors } from '../utils/fibre-colors';\n\n/**\n * Render a ribbon fiber edge - colors displayed side-by-side (parallel stripes)\n * Uses layered strokes with decreasing widths to create parallel color bands\n */\nfunction renderRibbonSmoothStepEdge(\n  id: string,\n  sourceX: number,\n  sourceY: number,\n  targetX: number,\n  targetY: number,\n  sourcePosition: Position,\n  targetPosition: Position,\n  ribbonFiberIds: number[],\n  selected: boolean,\n  style: React.CSSProperties,\n  data: NetworkEdge['data'],\n  interactionWidth?: number\n): React.ReactElement {\n  const ribbonColors = getRibbonColors(ribbonFiberIds);\n  const numColors = ribbonColors.length;\n  // Each stripe gets equal width - slightly slimmer than before\n  const stripeWidth = selected ? 1.8 : 1.5;\n  const highlightEnabled = Boolean(data?.highlight);\n  const baseStrokeWidth = selected ? 3 : 2.5;\n  const highlightWidth = data?.highlightWidth ?? baseStrokeWidth * 3;\n  const highlightColor = data?.highlightColor ?? ribbonColors[0]?.base ?? '#22d3ee';\n  \n  const [path, labelX, labelY] = getSmoothStepPath({\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    sourcePosition,\n    targetPosition,\n  });\n  \n  // Draw paths from outside to inside (first color = outermost, last = innermost)\n  // Each path has decreasing stroke width so inner colors show on top\n  // Each visible stripe will have equal width = stripeWidth\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor,\n            strokeWidth: highlightWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightWidth}px`,\n          }}\n        />\n      )}\n      {ribbonColors.map((colorInfo, index) => {\n        // Calculate stroke width so each color gets equal visible width\n        // Outer path: numColors * stripeWidth, inner paths decrease by stripeWidth each\n        const remainingStripes = numColors - index;\n        const currentStrokeWidth = remainingStripes * stripeWidth;\n        \n        return (\n          <path\n            key={`${id}-ribbon-${index}`}\n            d={path}\n            stroke={colorInfo.base}\n            strokeWidth={currentStrokeWidth}\n            fill=\"none\"\n            strokeLinecap=\"butt\"\n            opacity={colorInfo.striped ? 0.9 : 1}\n            style={style}\n          />\n        );\n      })}\n      \n      {ribbonColors.some(c => c.striped) && (\n        <BaseEdge\n          id={`${id}-ribbon-stripe`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: '#000',\n            strokeWidth: 1,\n            strokeDasharray: '3 3',\n            opacity: 0.4,\n          }}\n        />\n      )}\n      \n      <EdgeLabelRenderer>\n        {selected && data && (data.bandwidth || data.length) && (\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n              background: 'var(--rn-surface, #1f2937)',\n              padding: '4px 8px',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: '10px',\n              color: 'var(--rn-text, #e5e7eb)',\n              fontFamily: 'monospace',\n              border: `1px solid ${ribbonColors[0]?.base || '#00FFFF'}`,\n              pointerEvents: 'none',\n              zIndex: 9999,\n            }}\n          >\n            {data.bandwidth && <div>{data.bandwidth}</div>}\n            {data.length && <div>{data.length}m</div>}\n            <div style={{ fontSize: '8px', opacity: 0.8 }}>{ribbonFiberIds.length}F</div>\n          </div>\n        )}\n      </EdgeLabelRenderer>\n    </>\n  );\n}\n\n/**\n * SmoothStepFibreEdge\n * - Wrapper around React Flow's SmoothStepEdge that applies color from edge data\n * - Used for cable-to-tube connections\n * - Supports ribbon fiber rendering\n */\nconst SmoothStepFibreEdgeComponent: React.FC<EdgeProps<NetworkFlowEdge>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  data,\n  selected,\n  style = {},\n  ...props\n}) => {\n  const { color = '#00FFFF', striped = false, ribbonFiberIds, isRibbon, highlight, highlightColor, highlightWidth } = data || {};\n  const highlightEnabled = Boolean(highlight);\n\n  // Check if this is a ribbon edge\n  const ribbonMode = isRibbon || (ribbonFiberIds && ribbonFiberIds.length >= 6 && ribbonFiberIds.length <= 12);\n\n  // Render ribbon edge if in ribbon mode\n  if (ribbonMode && ribbonFiberIds) {\n    return renderRibbonSmoothStepEdge(\n      id,\n      sourceX,\n      sourceY,\n      targetX,\n      targetY,\n      sourcePosition,\n      targetPosition,\n      ribbonFiberIds,\n      selected ?? false,\n      style ?? {},\n      data,\n      props.interactionWidth\n    );\n  }\n\n  // Traditional single-fiber edge rendering\n  // Apply color from data to the edge style\n  const baseStrokeWidth = selected ? 4 : 3;\n  const highlightMaxWidth = highlightWidth ?? baseStrokeWidth * 3;\n  // No strokeDasharray here, matching the bezier and step fibre edges: a\n  // caller's style.strokeDasharray survives the spread, and leaving it unset\n  // when absent lets consumer CSS dash the path.\n  const edgeStyle = {\n    ...style,\n    stroke: color,\n    strokeWidth: baseStrokeWidth,\n  } as React.CSSProperties;\n\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          interactionWidth={props.interactionWidth}\n          path={getSmoothStepPath({\n            sourceX,\n            sourceY,\n            targetX,\n            targetY,\n            sourcePosition,\n            targetPosition,\n          })[0]}\n          style={{\n            ...style,\n            stroke: highlightColor ?? color,\n            strokeWidth: highlightMaxWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightMaxWidth}px`,\n          }}\n        />\n      )}\n      <ReactFlowSmoothStepEdge\n        {...({\n          ...props,\n          id,\n          sourceX,\n          sourceY,\n          targetX,\n          targetY,\n          sourcePosition,\n          targetPosition,\n          data,\n          selected,\n          style: edgeStyle,\n        } as unknown as React.ComponentProps<typeof ReactFlowSmoothStepEdge>)}\n      />\n      {/* Tracer overlay for fibres 13-24, matching the bezier and step edges. */}\n      {striped && (\n        <BaseEdge\n          id={`${id}-stripe`}\n          interactionWidth={props.interactionWidth}\n          path={getSmoothStepPath({\n            sourceX,\n            sourceY,\n            targetX,\n            targetY,\n            sourcePosition,\n            targetPosition,\n          })[0]}\n          style={{\n            ...style,\n            stroke: fibreTracerColorFor(color),\n            strokeWidth: 1.5,\n            strokeDasharray: '6 6',\n            opacity: 0.9,\n          }}\n        />\n      )}\n    </>\n  );\n};\n\nexport const SmoothStepFibreEdge = React.memo(SmoothStepFibreEdgeComponent);\n","import React from 'react';\nimport { EdgeProps, BaseEdge, Position, EdgeLabelRenderer } from '@xyflow/react';\nimport { NetworkEdge, NetworkFlowEdge } from '../types';\nimport { fibreTracerColorFor, getRibbonColors } from '../utils/fibre-colors';\n\n/**\n * Calculate a step path (right-angle path) between two points\n * The path goes horizontally first, then vertically (or vice versa)\n */\nfunction getStepPath({\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition: _targetPosition,\n}: {\n  sourceX: number;\n  sourceY: number;\n  targetX: number;\n  targetY: number;\n  sourcePosition: Position;\n  targetPosition: Position;\n}): [path: string, labelX: number, labelY: number] {\n  // Calculate the midpoint for the step\n  // For step edges, we go horizontally first, then vertically\n  const isHorizontalFirst = sourcePosition === Position.Left || sourcePosition === Position.Right;\n  \n  let path: string;\n  \n  if (isHorizontalFirst) {\n    // Go horizontal first, then vertical\n    const midX = (sourceX + targetX) / 2;\n    path = `M ${sourceX},${sourceY} L ${midX},${sourceY} L ${midX},${targetY} L ${targetX},${targetY}`;\n  } else {\n    // Go vertical first, then horizontal\n    const midY = (sourceY + targetY) / 2;\n    path = `M ${sourceX},${sourceY} L ${sourceX},${midY} L ${targetX},${midY} L ${targetX},${targetY}`;\n  }\n  \n  // Both step shapes cross the midpoint of source and target on their middle\n  // segment, so that is where labels sit.\n  return [path, (sourceX + targetX) / 2, (sourceY + targetY) / 2];\n}\n\n/**\n * Render a ribbon fiber edge - colors displayed side-by-side (parallel stripes)\n * Uses layered strokes with decreasing widths to create parallel color bands\n */\nfunction renderRibbonStepEdge(\n  id: string,\n  path: string,\n  labelX: number,\n  labelY: number,\n  ribbonFiberIds: number[],\n  selected: boolean,\n  style: React.CSSProperties,\n  data: NetworkEdge['data'],\n  interactionWidth?: number\n): React.ReactElement {\n  const ribbonColors = getRibbonColors(ribbonFiberIds);\n  const numColors = ribbonColors.length;\n  // Each stripe gets equal width - reduced for narrower edges\n  const stripeWidth = selected ? 1.2 : 1.0;\n  const highlightEnabled = Boolean(data?.highlight);\n  const baseStrokeWidth = selected ? 2 : 1.5;\n  const highlightWidth = data?.highlightWidth ?? baseStrokeWidth * 3;\n  const highlightColor = data?.highlightColor ?? ribbonColors[0]?.base ?? '#22d3ee';\n  \n  // Draw paths from outside to inside (first color = outermost, last = innermost)\n  // Each path has decreasing stroke width so inner colors show on top\n  // Each visible stripe will have equal width = stripeWidth\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor,\n            strokeWidth: highlightWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightWidth}px`,\n          }}\n        />\n      )}\n      {ribbonColors.map((colorInfo, index) => {\n        // Calculate stroke width so each color gets equal visible width\n        // Outer path: numColors * stripeWidth, inner paths decrease by stripeWidth each\n        const remainingStripes = numColors - index;\n        const currentStrokeWidth = remainingStripes * stripeWidth;\n        \n        return (\n          <path\n            key={`${id}-ribbon-${index}`}\n            d={path}\n            stroke={colorInfo.base}\n            strokeWidth={currentStrokeWidth}\n            fill=\"none\"\n            strokeLinecap=\"butt\"\n            opacity={colorInfo.striped ? 0.9 : 1}\n            style={style}\n          />\n        );\n      })}\n      \n      {ribbonColors.some(c => c.striped) && (\n        <BaseEdge\n          id={`${id}-ribbon-stripe`}\n          path={path}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: '#000',\n            strokeWidth: 1,\n            strokeDasharray: '3 3',\n            opacity: 0.4,\n          }}\n        />\n      )}\n      \n      <EdgeLabelRenderer>\n        {selected && data && (data.bandwidth || data.length) && (\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n              background: 'var(--rn-surface, #1f2937)',\n              padding: '4px 8px',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: '10px',\n              color: 'var(--rn-text, #e5e7eb)',\n              fontFamily: 'monospace',\n              border: `1px solid ${ribbonColors[0]?.base || '#00FFFF'}`,\n              pointerEvents: 'none',\n              zIndex: 9999,\n            }}\n          >\n            {data.bandwidth && <div>{data.bandwidth}</div>}\n            {data.length && <div>{data.length}m</div>}\n            <div style={{ fontSize: '8px', opacity: 0.8 }}>{ribbonFiberIds.length}F</div>\n          </div>\n        )}\n      </EdgeLabelRenderer>\n    </>\n  );\n}\n\n/**\n * StepFibreEdge\n * - Custom step edge that applies color from edge data\n * - Used for tube-to-cable connections\n * - Supports striped edges for fibers 13-24\n */\nconst StepFibreEdgeInner: React.FC<EdgeProps<NetworkFlowEdge>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  data,\n  selected,\n  style = {},\n  interactionWidth,\n  ..._props\n}) => {\n  const { color = '#00FFFF', striped = false, ribbonFiberIds, isRibbon, highlight, highlightColor, highlightWidth } = data || {};\n  const highlightEnabled = Boolean(highlight);\n\n  // Calculate step path\n  const [edgePath, labelX, labelY] = getStepPath({\n    sourceX,\n    sourceY,\n    sourcePosition,\n    targetX,\n    targetY,\n    targetPosition,\n  });\n\n  // Check if this is a ribbon edge\n  const ribbonMode = isRibbon || (ribbonFiberIds && ribbonFiberIds.length >= 6 && ribbonFiberIds.length <= 12);\n\n  // Render ribbon edge if in ribbon mode\n  if (ribbonMode && ribbonFiberIds) {\n    return renderRibbonStepEdge(id, edgePath, labelX, labelY, ribbonFiberIds, selected ?? false, style ?? {}, data, interactionWidth);\n  }\n\n  // Traditional single-fiber edge rendering\n  // Apply color from data to the edge style\n  const baseStrokeWidth = selected ? 3 : 2;\n  const highlightMaxWidth = highlightWidth ?? baseStrokeWidth * 3;\n  // No strokeDasharray here: a caller's style.strokeDasharray survives the\n  // spread, and leaving it unset when absent lets consumer CSS dash the path.\n  const edgeStyle = {\n    ...style,\n    stroke: color,\n    strokeWidth: baseStrokeWidth,\n  } as React.CSSProperties;\n\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor ?? color,\n            strokeWidth: highlightMaxWidth,\n            opacity: 0.7,\n            strokeDasharray: '14 10',\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightMaxWidth}px`,\n          }}\n        />\n      )}\n      {/* Base colored stroke */}\n      <BaseEdge\n        id={id}\n        path={edgePath}\n        interactionWidth={interactionWidth}\n        style={edgeStyle}\n      />\n      {/* Overlay black dashed stroke to simulate tracer for 13–24 */}\n      {striped && (\n        <BaseEdge\n          id={`${id}-stripe`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: fibreTracerColorFor(color),\n            strokeWidth: 1.5,\n            strokeDasharray: '6 6',\n            opacity: 0.9,\n          }}\n        />\n      )}\n    </>\n  );\n};\n\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const StepFibreEdge = React.memo(StepFibreEdgeInner);\n","import React from 'react';\nimport { EdgeProps, getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';\nimport type { NetworkFlowEdge } from '../types';\n\n/**\n * ThickCableEdge\n * - Thick edge for cable-to-cable connections\n * - Used for connecting cables via button handles\n * - Supports label display\n * - Color comes from edge data (set by NetworkDiagram onConnect)\n */\nconst ThickCableEdgeInner: React.FC<EdgeProps<NetworkFlowEdge>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  style = {},\n  data,\n  markerEnd,\n  selected: _selected,\n  interactionWidth,\n}) => {\n  const { color = '#6366f1', label, highlight, highlightColor, highlightWidth } = data || {};\n  const highlightEnabled = Boolean(highlight);\n  const baseStrokeWidth = 8;\n  const highlightMaxWidth = highlightWidth ?? baseStrokeWidth * 1.8;\n\n  const [edgePath, labelX, labelY] = getBezierPath({\n    sourceX,\n    sourceY,\n    sourcePosition,\n    targetX,\n    targetY,\n    targetPosition,\n  });\n\n  // Use same pattern as other edges - spread style first, then override with our properties\n  const edgeStyle = {\n    ...style, // This includes zIndex from React Flow\n    stroke: color, // Override any stroke from style prop with our data color\n    strokeWidth: baseStrokeWidth, // Thick line for cable connections\n    strokeLinecap: 'round' as const,\n    strokeLinejoin: 'round' as const,\n    strokeDasharray: 'none', // Solid line\n  } as React.CSSProperties;\n\n  return (\n    <>\n      {highlightEnabled && (\n        <BaseEdge\n          id={`${id}-highlight`}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            ...style,\n            stroke: highlightColor ?? color,\n            strokeWidth: highlightMaxWidth,\n            opacity: 0.85,\n            strokeDasharray: '18 12',\n            filter: `drop-shadow(0 0 8px ${highlightColor ?? color}) drop-shadow(0 0 16px ${highlightColor ?? color})`,\n            ['--rn-edge-highlight-from' as string]: `${baseStrokeWidth}px`,\n            ['--rn-edge-highlight-to' as string]: `${highlightMaxWidth}px`,\n          }}\n        />\n      )}\n      <BaseEdge\n        id={id}\n        path={edgePath}\n        interactionWidth={interactionWidth}\n        markerEnd={markerEnd}\n        style={edgeStyle}\n      />\n      {label && (\n        <EdgeLabelRenderer>\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n              background: 'var(--rn-surface, #1f2937)',\n              padding: '4px 8px',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: '10px',\n              color: 'var(--rn-text, #e5e7eb)',\n              fontFamily: 'monospace',\n              border: `1px solid ${color}`,\n              pointerEvents: 'none',\n              zIndex: 9999,\n            }}\n          >\n            {label}\n          </div>\n        </EdgeLabelRenderer>\n      )}\n    </>\n  );\n};\n\n\n/** Memoized: React Flow re-renders nodes often; bail out unless our props changed. */\nexport const ThickCableEdge = React.memo(ThickCableEdgeInner);\n","import React, { Component, type ErrorInfo, type ReactNode } from 'react';\n\nexport interface DiagramErrorBoundaryProps {\n  /** Content to render when an error is caught */\n  children: ReactNode;\n  /** Optional fallback UI when an error occurs (default: simple message) */\n  fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n  /** Optional callback when an error is caught (e.g. for logging) */\n  onError?: (error: Error, errorInfo: ErrorInfo) => void;\n  /**\n   * Optional callback fired when the boundary is reset (the `reset` handed to a\n   * render-prop fallback, or the default fallback's \"Try again\"). Lets a parent\n   * clear any state it derived from the caught error.\n   */\n  onReset?: () => void;\n}\n\nexport interface DiagramErrorBoundaryState {\n  error: Error | null;\n}\n\n/**\n * Error boundary that catches errors in the diagram tree (e.g. React Flow,\n * custom nodes) and renders a fallback instead of crashing the whole app.\n * Wrap &lt;NetworkDiagram&gt; (or the diagram subtree) with this for production.\n *\n * @example\n * ```tsx\n * <DiagramErrorBoundary\n *   fallback={(error, reset) => (\n *     <div>\n *       <p>Diagram failed: {error.message}</p>\n *       <button onClick={reset}>Try again</button>\n *     </div>\n *   )}\n *   onError={(err, info) => logToService(err, info)}\n * >\n *   <NetworkDiagram nodes={nodes} edges={edges} />\n * </DiagramErrorBoundary>\n * ```\n */\nexport class DiagramErrorBoundary extends Component<DiagramErrorBoundaryProps, DiagramErrorBoundaryState> {\n  state: DiagramErrorBoundaryState = { error: null };\n\n  static getDerivedStateFromError(error: Error): DiagramErrorBoundaryState {\n    return { error };\n  }\n\n  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n    this.props.onError?.(error, errorInfo);\n  }\n\n  reset = (): void => {\n    this.setState({ error: null });\n    this.props.onReset?.();\n  };\n\n  render(): ReactNode {\n    const { error } = this.state;\n    const { children, fallback } = this.props;\n\n    if (error) {\n      if (typeof fallback === 'function') {\n        return (\n          <div className=\"react-networks-diagram-error-fallback\">\n            {fallback(error, this.reset)}\n          </div>\n        );\n      }\n      if (fallback != null) {\n        return (\n          <div className=\"react-networks-diagram-error-fallback\">{fallback}</div>\n        );\n      }\n      // Themed via `--rn-error-*` (styles/index.css). The rules carry literal\n      // fallbacks, so this still reads correctly when the boundary is used\n      // outside a `.react-flow-dark` / `.react-flow-light` wrapper.\n      return (\n        <div role=\"alert\" className=\"react-networks-diagram-error\">\n          <div>\n            <strong>Diagram error</strong>\n          </div>\n          <div className=\"react-networks-diagram-error-message\">{error.message}</div>\n          <button\n            type=\"button\"\n            onClick={this.reset}\n            className=\"react-networks-diagram-error-retry\"\n          >\n            Try again\n          </button>\n        </div>\n      );\n    }\n\n    return children;\n  }\n}\n","import { Connection, Edge } from '@xyflow/react';\nimport { NetworkEdge } from '../types';\nimport { normalizeHandleId } from './handle-ids';\n\n// Monotonic suffix so rapidly re-created edges (e.g. delete + reconnect within the\n// same millisecond, or batched operations) never collide on a generated id.\nlet edgeIdSequence = 0;\n\nexport function createEdgeWithMetadata(\n  connection: Connection,\n  edgeType: NonNullable<Edge['type']>,\n  selectEdgeZIndex: (edge: NetworkEdge) => number,\n  data?: NetworkEdge['data'],\n): Edge {\n  const id = `edge-${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}-${Date.now()}-${(edgeIdSequence += 1)}`;\n  const zIndex = selectEdgeZIndex({ ...connection, id, type: edgeType } as NetworkEdge);\n\n  return {\n    id,\n    source: connection.source!,\n    target: connection.target!,\n    sourceHandle: connection.sourceHandle!,\n    targetHandle: connection.targetHandle!,\n    type: edgeType,\n    zIndex,\n    style: { zIndex },\n    data: data ?? {},\n  };\n}\n\n/**\n * Options for replacing an edge in the edges array\n */\nexport interface ReplaceEdgeOptions {\n  /** Existing edges array */\n  edges: Edge[];\n  /** Connection parameters for the new edge */\n  connection: Connection;\n  /** Type of edge to create */\n  edgeType: 'power' | 'fiber' | 'step' | 'smoothstep' | 'thick-cable';\n  /** Function to calculate zIndex for the edge */\n  selectEdgeZIndex: (edge: NetworkEdge) => number;\n  /** Which end of the connection to use (source or target) for finding the edge */\n  findBy: 'source' | 'target';\n  /** Which end of the existing edge to match against. Defaults to findBy. Use 'source' when the connection's target handle is on the edge's source (e.g. replacing switch connection when dragging from server to switch) */\n  matchOn?: 'source' | 'target';\n  /** Optional edge data to set on the new edge */\n  data?: NetworkEdge['data'];\n}\n\n/**\n * Replace an existing edge with a new connection\n * This is used for PDU and switch connections that allow replacing existing edges\n * @param options - Options for edge replacement\n * @returns New edges array with the edge replaced, or null if no existing edge found\n * @public\n */\nexport function replaceEdge(\n  options: ReplaceEdgeOptions\n): Edge[] | null {\n  const { edges, connection, edgeType, selectEdgeZIndex, findBy, matchOn = findBy, data } = options;\n\n  // Determine which handle to search by from the connection\n  const handleId = findBy === 'source' ? connection.sourceHandle : connection.targetHandle;\n  const nodeId = findBy === 'source' ? connection.source : connection.target;\n\n  if (!handleId || !nodeId) {\n    return null;\n  }\n\n  const normalizedHandle = normalizeHandleId(handleId);\n\n  // Find existing edge - use normalized handle comparison for bidirectional handles (e.g. Network-1-target)\n  const existingEdge = edges.find(e => {\n    if (matchOn === 'source') {\n      return e.source === nodeId && normalizeHandleId(e.sourceHandle) === normalizedHandle;\n    } else {\n      return e.target === nodeId && normalizeHandleId(e.targetHandle) === normalizedHandle;\n    }\n  });\n\n  if (!existingEdge) {\n    return null;\n  }\n\n  const filtered = edges.filter(e => e.id !== existingEdge.id);\n  const newEdge = createEdgeWithMetadata(connection, edgeType, selectEdgeZIndex, data);\n\n  return [...filtered, newEdge];\n}\n","import type { NetworkNode } from '../types';\nimport { baseColorFor } from './fibre-colors';\n\nexport type DerivedEdgeType =\n  | 'power'\n  | 'fiber'\n  | 'step'\n  | 'smoothstep'\n  | 'thick-cable';\n\n/**\n * Render data attached to a connection/reconnection edge: a solid `color`\n * (optionally `striped`), or a multi-colour ribbon (`ribbonFiberIds` +\n * `isRibbon`). Mutually exclusive in practice — ribbon edges carry no solid\n * colour.\n */\nexport interface DerivedEdgeData {\n  /**\n   * Index signature so this shape satisfies the edge's `NetworkEdgeData`\n   * (`@xyflow/react` v12 requires `Record<string, unknown>` edge data). The\n   * derivation only ever sets the fields below.\n   */\n  [key: string]: unknown;\n  color?: string;\n  striped?: boolean;\n  ribbonFiberIds?: number[];\n  isRibbon?: boolean;\n}\n\n/** A splice node in ribbon (multi-fibre) mode paints its edges as a ribbon. */\nfunction isSpliceRibbonMode(spliceNode: NetworkNode | undefined): boolean {\n  if (!spliceNode || spliceNode.type !== 'splice') return false;\n  const spliceData = spliceNode.data as NetworkNode['data'];\n  return spliceData?.mode === 'ribbon';\n}\n\nexport interface DeriveEdgeDataInput {\n  edgeType: DerivedEdgeType;\n  sourceNode: NetworkNode | undefined;\n  targetNode: NetworkNode | undefined;\n  /** Both endpoints use the reserved `cable-button` handle (trunk cable). */\n  isButtonHandleConnection: boolean;\n  /**\n   * Resolve the plain-fibre colour for a `fiber`/`smoothstep` edge. Injected\n   * because the real resolver closes over the current edge set and per-node\n   * port colours; return `undefined` when no colour applies.\n   */\n  resolveFibreColor: () => { color?: string; striped?: boolean } | undefined;\n}\n\n/**\n * Single source of truth for an edge's render data, shared by the initial\n * connect and the reconnect paths so a reconnected edge keeps the same\n * ribbon-splice / thick-cable / fibre colouring that a fresh connection would\n * receive. Previously this logic lived only in `handleConnect`; `handleEdgeUpdate`\n * re-derived a fibre colour but silently dropped the ribbon-splice and\n * thick-cable branches, so reconnecting a ribbon or trunk-cable edge stripped\n * its colour. Pure except for the injected {@link DeriveEdgeDataInput.resolveFibreColor}.\n */\nexport function deriveEdgeData(input: DeriveEdgeDataInput): DerivedEdgeData | undefined {\n  const { edgeType, sourceNode, targetNode, isButtonHandleConnection, resolveFibreColor } = input;\n  const isPduSource = sourceNode?.type === 'vertical-pdu';\n\n  let edgeData: DerivedEdgeData | undefined;\n\n  // Ribbon splice: render the edge as a multi-colour ribbon (no solid colour).\n  const isRibbonSpliceTarget = isSpliceRibbonMode(targetNode);\n  const isRibbonSpliceSource = isSpliceRibbonMode(sourceNode);\n  if (isRibbonSpliceTarget || isRibbonSpliceSource) {\n    const ribbonFiberIds = isRibbonSpliceTarget\n      ? (targetNode?.data as NetworkNode['data'])?.ribbonFiberIds\n      : (sourceNode?.data as NetworkNode['data'])?.ribbonFiberIds;\n    if (\n      ribbonFiberIds &&\n      Array.isArray(ribbonFiberIds) &&\n      ribbonFiberIds.length >= 6 &&\n      ribbonFiberIds.length <= 12\n    ) {\n      edgeData = { ribbonFiberIds, isRibbon: true };\n    }\n  }\n\n  // Plain fibre colour (skip PDU sources — power edges are coloured elsewhere).\n  if (!edgeData && (edgeType === 'fiber' || edgeType === 'smoothstep') && !isPduSource) {\n    const colorInfo = resolveFibreColor();\n    if (colorInfo) {\n      edgeData = { color: colorInfo.color, striped: colorInfo.striped, isRibbon: false };\n    }\n  } else if (edgeType === 'thick-cable' && isButtonHandleConnection) {\n    // Trunk cable: colour from the source cable node.\n    const sourceNodeData = sourceNode?.data as NetworkNode['data'];\n    const cableColor =\n      sourceNodeData?.color ||\n      (sourceNodeData?.tubeStartIndex ? baseColorFor(sourceNodeData.tubeStartIndex) : '#6366f1');\n    edgeData = { color: cableColor };\n  }\n\n  return edgeData;\n}\n","import type { Connection } from \"@xyflow/react\";\nimport type { NetworkEdge, NetworkNode } from \"../types\";\nimport {\n  getDevicePortMounts,\n  isMountedPortHandle,\n  normalizePatchPanelPortConnection,\n  normalizeSwitchPortConnection,\n  resolvableMountedHandle,\n} from \"../utils/diagram-connection\";\nimport {\n  defaultValidateConnection,\n  type ConnectionRejectionCode,\n  type NetworkDiagramState,\n  type ValidateConnectionParams,\n  type ValidationResult,\n} from \"./connection-validation\";\nimport {\n  classifyConnectionPair,\n  findEndpointOccupants,\n  type ConnectionPairClassification,\n} from \"./endpoint-occupancy\";\n\nexport type { ConnectionRejectionCode } from \"./connection-validation\";\n\n/** Context handed to the injected edge-presentation resolvers. */\nexport interface ConnectionPlanContext {\n  /** The connection after handle-class normalization. */\n  connection: Connection;\n  sourceNode: NetworkNode | undefined;\n  targetNode: NetworkNode | undefined;\n  classification: ConnectionPairClassification;\n}\n\nexport interface ConnectionPlanAccept {\n  kind: \"accept\";\n  /** The connection after handle-class normalization. */\n  connection: Connection;\n  sourceNode: NetworkNode | undefined;\n  targetNode: NetworkNode | undefined;\n  classification: ConnectionPairClassification;\n  /** Existing edge ids the plan evicts to satisfy endpoint capacity (documented replacement). */\n  evictEdgeIds: string[];\n  /** Edge type the plan intends, from the injected {@link PlanConnectionOptions.resolveEdgeType}. */\n  edgeType?: string;\n  /** Edge data the plan intends, from the injected {@link PlanConnectionOptions.resolveEdgeData}. */\n  edgeData?: Record<string, unknown>;\n}\n\nexport interface ConnectionPlanReject {\n  kind: \"reject\";\n  code: ConnectionRejectionCode;\n  /** Display reason for the rejection. */\n  reason: string;\n}\n\nexport type ConnectionPlan = ConnectionPlanAccept | ConnectionPlanReject;\n\nexport interface PlanConnectionOptions {\n  nodes: NetworkNode[];\n  edges: NetworkEdge[];\n  connection: Connection;\n  /** Edge id to exclude from occupancy checks — a reconnect supplies the edge being moved. */\n  ignoreEdgeId?: string;\n  /**\n   * The validator to run (the component's configured one, or a consumer's\n   * `NetworkDiagramOptions.validateConnection`); defaults to\n   * `defaultValidateConnection`. A rejection whose `code` is absent is\n   * treated as `invalid` and is authoritative — custom validator rejections\n   * are never bypassed to reach replacement.\n   */\n  validate?: (\n    params: ValidateConnectionParams,\n    ctx: NetworkDiagramState,\n  ) => ValidationResult;\n  /** Resolve the intended edge type from the accepted context. */\n  resolveEdgeType?: (ctx: ConnectionPlanContext) => string;\n  /** Resolve the intended edge data from the accepted context. */\n  resolveEdgeData?: (\n    ctx: ConnectionPlanContext,\n  ) => Record<string, unknown> | undefined;\n}\n\n/**\n * Pure connection plan — the single authority for what a connect or\n * reconnect attempt does. Both interactive paths and headless validation\n * share it, so occupancy, capacity, and replacement can never diverge.\n *\n * Order of decisions:\n * 1. Wrong-class source-slot repair: when the emitted `sourceHandle` is a\n *    mounted port-strip id that can never serve as a source (target-only\n *    ports — disconnected generic ports, power inlets), the pair is swapped\n *    if the other end can source and this end can target; otherwise the\n *    attempt rejects `invalid` rather than minting an unrenderable edge.\n * 2. Handle-class normalization for mounted device ports, then\n *    the configured validator. A rejection is\n *    final — `invalid` — unless it carries the `occupied` code (only the\n *    built-in validator emits that) and the pair's endpoints support\n *    documented replacement.\n * 3. Occupancy over the canonical endpoint identity (normalized handle ids,\n *    both edge sides). Unbounded endpoints never conflict; the edge named by\n *    `ignoreEdgeId` never self-conflicts.\n * 4. Occupied bounded endpoints evict their occupants when the pair is\n *    replaceable (fibre / power / switch-network), or reject `occupied`\n *    otherwise — replacing is never inferred from reason text.\n */\nexport function planConnection(options: PlanConnectionOptions): ConnectionPlan {\n  const { nodes, edges, ignoreEdgeId } = options;\n  let sourceNode = nodes.find((n) => n.id === options.connection.source);\n  let targetNode = nodes.find((n) => n.id === options.connection.target);\n\n  if (!sourceNode || !targetNode) {\n    return {\n      kind: \"reject\",\n      code: \"invalid\",\n      reason: \"Connection references a node that is not in the graph\",\n    };\n  }\n\n  // A loose-mode drag can emit a mounted handle into the slot its class\n  // cannot serve: a target-only port (disconnected generic ports, power\n  // inlets) in the source slot, or a source-only port (connected generic\n  // ports) in the target slot. If reversing the pair satisfies both slots,\n  // that is the only edge the user can have meant — swap it; otherwise\n  // reject rather than persist an edge React Flow can never render.\n  const sourceMounts = getDevicePortMounts(sourceNode);\n  const targetMounts = getDevicePortMounts(targetNode);\n  let emitted = options.connection;\n  const sourceWrongClass =\n    isMountedPortHandle(sourceMounts, emitted.sourceHandle) &&\n    resolvableMountedHandle(sourceMounts, emitted.sourceHandle, \"source\") ===\n      null;\n  const targetWrongClass =\n    isMountedPortHandle(targetMounts, emitted.targetHandle) &&\n    resolvableMountedHandle(targetMounts, emitted.targetHandle, \"target\") ===\n      null;\n  if (sourceWrongClass || targetWrongClass) {\n    const swappedSource = resolvableMountedHandle(\n      targetMounts,\n      emitted.targetHandle,\n      \"source\",\n    );\n    const swappedTarget = resolvableMountedHandle(\n      sourceMounts,\n      emitted.sourceHandle,\n      \"target\",\n    );\n    if (swappedSource && swappedTarget) {\n      emitted = {\n        ...emitted,\n        source: emitted.target,\n        target: emitted.source,\n        sourceHandle: swappedSource,\n        targetHandle: swappedTarget,\n      };\n      const swap = sourceNode;\n      sourceNode = targetNode;\n      targetNode = swap;\n    } else {\n      return {\n        kind: \"reject\",\n        code: \"invalid\",\n        reason: sourceWrongClass\n          ? `Handle ${emitted.sourceHandle} on node ${emitted.source} can only be a connection target`\n          : `Handle ${emitted.targetHandle} on node ${emitted.target} can only be a connection source`,\n      };\n    }\n  }\n\n  // A loose-mode drop onto a node that mounts no handles emits null endpoint\n  // handles. Connection rules evaluate per-handle and React Flow anchors such\n  // edges to the node centre, so accepting one would mint an edge that bypasses\n  // every rule — reject instead.\n  if (!emitted.sourceHandle || !emitted.targetHandle) {\n    return {\n      kind: \"reject\",\n      code: \"invalid\",\n      reason: \"Connection requires a source handle and a target handle\",\n    };\n  }\n\n  const normalized = normalizePatchPanelPortConnection(\n    normalizeSwitchPortConnection(emitted, sourceNode, targetNode),\n    sourceNode,\n    targetNode,\n  );\n  // Resolve all known device handles to their mounted directional sibling,\n  // including router ports. Opaque handles retain their existing grammar.\n  // Resolve the emitted id first to preserve explicit authored ids verbatim.\n  const connection: Connection = {\n    ...normalized,\n    sourceHandle:\n      resolvableMountedHandle(\n        getDevicePortMounts(sourceNode), emitted.sourceHandle, \"source\",\n      ) ?? normalized.sourceHandle,\n    targetHandle:\n      resolvableMountedHandle(\n        getDevicePortMounts(targetNode), emitted.targetHandle, \"target\",\n      ) ?? normalized.targetHandle,\n  };\n  const classification = classifyConnectionPair(\n    sourceNode,\n    connection.sourceHandle,\n    targetNode,\n    connection.targetHandle,\n  );\n  const ctx: ConnectionPlanContext = {\n    connection,\n    sourceNode,\n    targetNode,\n    classification,\n  };\n\n  const sourceOccupants = findEndpointOccupants(\n    edges,\n    connection.source,\n    sourceNode,\n    connection.sourceHandle,\n    ignoreEdgeId,\n  );\n  const targetOccupants = findEndpointOccupants(\n    edges,\n    connection.target,\n    targetNode,\n    connection.targetHandle,\n    ignoreEdgeId,\n  );\n  const occupants = [...sourceOccupants, ...targetOccupants];\n\n  const validate = options.validate ?? defaultValidateConnection;\n  const result = validate(\n    {\n      source: connection.source ?? null,\n      target: connection.target ?? null,\n      sourceHandle: connection.sourceHandle,\n      targetHandle: connection.targetHandle,\n      ignoreEdgeId,\n    },\n    { nodes, edges },\n  );\n\n  if (!result.valid) {\n    const replaceableOccupancy =\n      result.code === \"occupied\" &&\n      classification.replaceable &&\n      occupants.length > 0;\n    if (!replaceableOccupancy) {\n      return {\n        kind: \"reject\",\n        code: result.code ?? \"invalid\",\n        reason: result.reason ?? \"Connection not allowed\",\n      };\n    }\n  } else if (occupants.length > 0 && !classification.replaceable) {\n    const first = sourceOccupants.length > 0 ? \"Source\" : \"Target\";\n    const handle =\n      sourceOccupants.length > 0\n        ? connection.sourceHandle\n        : connection.targetHandle;\n    return {\n      kind: \"reject\",\n      code: \"occupied\",\n      reason: `${first} handle ${handle} is already connected`,\n    };\n  }\n\n  return {\n    kind: \"accept\",\n    connection,\n    sourceNode,\n    targetNode,\n    classification,\n    evictEdgeIds: occupants.map((e) => e.id),\n    edgeType: options.resolveEdgeType?.(ctx),\n    edgeData: options.resolveEdgeData?.(ctx),\n  };\n}\n","import type { CSSProperties } from 'react';\nimport type { NetworkEdge, NetworkNode } from '../types';\n\nexport interface DiagramFocusOptions {\n  highlightedNodeIds?: readonly string[];\n  highlightedEdgeIds?: readonly string[];\n  dimUnhighlighted?: boolean;\n  dimOpacity?: number;\n  /** When false, only explicit highlightedEdgeIds stay bright (not every edge on a highlighted node). */\n  connectEdgesToHighlightedNodes?: boolean;\n}\n\nexport const DEFAULT_DIAGRAM_DIM_OPACITY = 0.25;\n\nexport function hasDiagramFocus(options: DiagramFocusOptions): boolean {\n  return (\n    (options.highlightedNodeIds?.length ?? 0) > 0 ||\n    (options.highlightedEdgeIds?.length ?? 0) > 0\n  );\n}\n\nexport function isNodeInDiagramFocus(nodeId: string, options: DiagramFocusOptions): boolean {\n  if (!hasDiagramFocus(options)) {\n    return true;\n  }\n\n  return options.highlightedNodeIds?.includes(nodeId) ?? false;\n}\n\nexport function isEdgeInDiagramFocus(\n  edgeId: string,\n  edge: Pick<NetworkEdge, 'source' | 'target'>,\n  options: DiagramFocusOptions,\n): boolean {\n  if (!hasDiagramFocus(options)) {\n    return true;\n  }\n\n  if (options.highlightedEdgeIds?.includes(edgeId)) {\n    return true;\n  }\n\n  const expandEdgesToHighlightedNodes =\n    options.connectEdgesToHighlightedNodes ??\n    (options.highlightedEdgeIds?.length ?? 0) === 0;\n\n  if (!expandEdgesToHighlightedNodes) {\n    return false;\n  }\n\n  if (options.highlightedNodeIds?.includes(edge.source)) {\n    return true;\n  }\n\n  if (options.highlightedNodeIds?.includes(edge.target)) {\n    return true;\n  }\n\n  return false;\n}\n\nfunction clampDimOpacity(value: number | undefined): number {\n  if (typeof value !== 'number' || Number.isNaN(value)) {\n    return DEFAULT_DIAGRAM_DIM_OPACITY;\n  }\n\n  return Math.min(1, Math.max(0, value));\n}\n\n/** A {@link NetworkNode} carrying the runtime `style` React Flow applies (e.g. dim opacity). @public */\nexport type RuntimeNode = NetworkNode & { style?: CSSProperties };\n/** A {@link NetworkEdge} carrying the runtime `style` React Flow applies (e.g. dim opacity). @public */\nexport type RuntimeEdge = NetworkEdge & { style?: CSSProperties };\n\nexport function applyDiagramFocusToNodes<TNode extends RuntimeNode>(\n  nodes: TNode[],\n  options: DiagramFocusOptions,\n): TNode[] {\n  if (!options.dimUnhighlighted || !hasDiagramFocus(options)) {\n    return nodes;\n  }\n\n  const dimOpacity = clampDimOpacity(options.dimOpacity);\n  const nodeIds = new Set(options.highlightedNodeIds);\n\n  return nodes.map((node) => {\n    if (nodeIds.has(node.id)) {\n      return node;\n    }\n\n    return {\n      ...node,\n      style: {\n        ...(node.style ?? {}),\n        opacity: dimOpacity,\n      },\n    };\n  });\n}\n\nexport function applyDiagramFocusToEdges<TEdge extends RuntimeEdge>(\n  edges: TEdge[],\n  options: DiagramFocusOptions,\n): TEdge[] {\n  if (!options.dimUnhighlighted || !hasDiagramFocus(options)) {\n    return edges;\n  }\n\n  const dimOpacity = clampDimOpacity(options.dimOpacity);\n  const nodeIds = new Set(options.highlightedNodeIds);\n  const edgeIds = new Set(options.highlightedEdgeIds);\n  const expandEdges = options.connectEdgesToHighlightedNodes ?? edgeIds.size === 0;\n\n  return edges.map((edge) => {\n    if (\n      edgeIds.has(edge.id) ||\n      (expandEdges && (nodeIds.has(edge.source) || nodeIds.has(edge.target)))\n    ) {\n      return edge;\n    }\n\n    return {\n      ...edge,\n      style: {\n        ...(edge.style ?? {}),\n        opacity: dimOpacity,\n      },\n      data: {\n        ...(edge.data ?? {}),\n        opacity: dimOpacity,\n      },\n    };\n  });\n}\n","import type { NetworkDiagramHighlightGroup } from \"../types\";\nimport type { RuntimeEdge } from \"./diagram-focus\";\n\n/**\n * Per-edge paint resolved from {@link NetworkDiagramHighlightGroup | groups}.\n *\n * @public\n * @remarks\n * Keyed by edge id. Only edges named in a group's `edgeIds` appear. When two\n * groups list the same edge, the later group in array order wins (see\n * {@link deriveHighlightGroupEdgePaint}). `color`/`width` are `undefined` when\n * the winning group omitted them, in which case the edge is still marked\n * highlighted but keeps its own base color/width.\n */\nexport interface HighlightGroupEdgePaint {\n  color?: string;\n  width?: number;\n}\n\n/**\n * Resolve the per-edge highlight paint for a set of highlight groups.\n *\n * @public\n * @remarks\n * Use this when you need the raw color/width decision per edge id — e.g. to\n * drive a custom edge renderer or a legend — without mutating an edge array.\n * {@link NetworkDiagram}'s `highlightGroups` prop applies this internally via\n * {@link applyHighlightGroupsToEdges}; call this directly only when you are\n * reimplementing that plumbing.\n *\n * Overlap policy: **later groups win**. Groups are treated as an ordered list\n * of severity tiers (least to most specific), so a group placed later in the\n * array overrides the color/width of any earlier group that named the same\n * edge. This mirrors dc_designer's severity-ranked coloring where the\n * highest-priority issue is appended last.\n *\n * @param groups - Ordered highlight groups, or `undefined`.\n * @returns A map from edge id to its resolved paint. Empty when `groups` is\n *   empty/undefined or names no edges.\n *\n * @example\n * ```ts\n * const paint = deriveHighlightGroupEdgePaint([\n *   { edgeIds: [\"e1\", \"e2\"], color: \"#d97706\", width: 4 }, // warning tier\n *   { edgeIds: [\"e2\"], color: \"#dc2626\", width: 6 },        // error tier wins on e2\n * ]);\n * paint.get(\"e1\"); // { color: \"#d97706\", width: 4 }\n * paint.get(\"e2\"); // { color: \"#dc2626\", width: 6 }\n * ```\n */\nexport function deriveHighlightGroupEdgePaint(\n  groups: ReadonlyArray<NetworkDiagramHighlightGroup> | undefined,\n): Map<string, HighlightGroupEdgePaint> {\n  const paint = new Map<string, HighlightGroupEdgePaint>();\n  if (!groups) {\n    return paint;\n  }\n\n  for (const group of groups) {\n    if (!group.edgeIds) {\n      continue;\n    }\n    for (const edgeId of group.edgeIds) {\n      // Later group wins: an unconditional set overwrites an earlier entry.\n      paint.set(edgeId, { color: group.color, width: group.width });\n    }\n  }\n\n  return paint;\n}\n\nfunction collectUnion(\n  groups: ReadonlyArray<NetworkDiagramHighlightGroup> | undefined,\n  select: (group: NetworkDiagramHighlightGroup) => readonly string[] | undefined,\n): string[] {\n  if (!groups) {\n    return [];\n  }\n\n  const seen = new Set<string>();\n  for (const group of groups) {\n    const ids = select(group);\n    if (!ids) {\n      continue;\n    }\n    for (const id of ids) {\n      seen.add(id);\n    }\n  }\n\n  return Array.from(seen);\n}\n\n/**\n * Collect the de-duplicated union of every group's `nodeIds`.\n *\n * @public\n * @remarks\n * The union is the \"kept bright\" node set when `dimUnhighlighted` is combined\n * with `highlightGroups`: it is merged with the legacy `highlightedNodeIds`\n * prop to form the dim complement. Order follows first appearance across groups.\n *\n * @example\n * ```ts\n * collectHighlightGroupNodeIds([\n *   { nodeIds: [\"a\", \"b\"] },\n *   { nodeIds: [\"b\", \"c\"] },\n * ]); // [\"a\", \"b\", \"c\"]\n * ```\n */\nexport function collectHighlightGroupNodeIds(\n  groups: ReadonlyArray<NetworkDiagramHighlightGroup> | undefined,\n): string[] {\n  return collectUnion(groups, (group) => group.nodeIds);\n}\n\n/**\n * Collect the de-duplicated union of every group's `edgeIds`.\n *\n * @public\n * @remarks\n * Merged with the legacy `highlightedEdgeIds` prop to form the dim complement\n * when `highlightGroups` and `dimUnhighlighted` are used together. Order follows\n * first appearance across groups.\n *\n * @example\n * ```ts\n * collectHighlightGroupEdgeIds([\n *   { edgeIds: [\"e1\"] },\n *   { edgeIds: [\"e1\", \"e2\"] },\n * ]); // [\"e1\", \"e2\"]\n * ```\n */\nexport function collectHighlightGroupEdgeIds(\n  groups: ReadonlyArray<NetworkDiagramHighlightGroup> | undefined,\n): string[] {\n  return collectUnion(groups, (group) => group.edgeIds);\n}\n\n/**\n * Apply {@link NetworkDiagramHighlightGroup | highlight groups} to an edge array,\n * writing each member edge's color/width through the same `data.highlight` /\n * `data.highlightColor` / `data.highlightWidth` channel the built-in edge\n * renderers already read.\n *\n * @public\n * @remarks\n * This is the reusable core behind {@link NetworkDiagram}'s `highlightGroups`\n * prop. It reuses the existing per-edge highlight channel rather than inventing\n * a parallel one, so highlighted edges render identically to edges highlighted\n * by hand-writing `data.highlight`.\n *\n * Overlap policy is **later group wins** (see\n * {@link deriveHighlightGroupEdgePaint}). An edge named by any group is marked\n * `data.highlight = true`; if the winning group omits `color`/`width`, the edge\n * keeps its own base color/width but still renders as highlighted.\n *\n * Referential stability: edges not named by any group are returned by identity,\n * and when `groups` is empty/undefined the original array is returned unchanged.\n * This lets React Flow skip re-rendering untouched edges, and lets a `useMemo`\n * keyed on `(edges, groups)` hold a stable result.\n *\n * @param edges - The edges to paint.\n * @param groups - Ordered highlight groups, or `undefined`.\n * @returns A new array with member edges repainted, or the original array when\n *   there is nothing to paint.\n *\n * @example\n * ```tsx\n * const painted = applyHighlightGroupsToEdges(edges, [\n *   { edgeIds: [\"e-warn\"], color: \"#d97706\", width: 4, label: \"warning\" },\n *   { edgeIds: [\"e-err\"], color: \"#dc2626\", width: 6, label: \"error\" },\n * ]);\n * // painted edge \"e-err\": data.highlight === true, data.highlightColor === \"#dc2626\"\n * ```\n */\nexport function applyHighlightGroupsToEdges<TEdge extends RuntimeEdge>(\n  edges: TEdge[],\n  groups: ReadonlyArray<NetworkDiagramHighlightGroup> | undefined,\n): TEdge[] {\n  const paint = deriveHighlightGroupEdgePaint(groups);\n  if (paint.size === 0) {\n    return edges;\n  }\n\n  let changed = false;\n  const next = edges.map((edge) => {\n    const edgePaint = paint.get(edge.id);\n    if (!edgePaint) {\n      return edge;\n    }\n    changed = true;\n    return {\n      ...edge,\n      data: {\n        ...(edge.data ?? {}),\n        highlight: true,\n        ...(edgePaint.color !== undefined\n          ? { highlightColor: edgePaint.color }\n          : {}),\n        ...(edgePaint.width !== undefined\n          ? { highlightWidth: edgePaint.width }\n          : {}),\n      },\n    };\n  });\n\n  return changed ? next : edges;\n}\n","import type { NetworkNode } from '../types';\nimport { isRackDeviceNodeType } from './rack-device-types';\n\nconst DEVICE_NODE_TYPES = new Set(['device', 'server', 'switch', 'router', 'patch-panel']);\n\nexport function isDeviceDiagramNode(node: NetworkNode): boolean {\n  if (!node.type) {\n    return false;\n  }\n  return DEVICE_NODE_TYPES.has(node.type) || isRackDeviceNodeType(node.type);\n}\n\n/**\n * Cache of merged `data` objects keyed on the raw `node.data` that produced them,\n * so injecting the global `showDeviceImages` yields a *stable* data identity for\n * an unchanged node. Without this, every recompute (i.e. every drag frame, since\n * the memo depends on the whole node array) allocated a fresh `{ ...data,\n * showDeviceImages }` and defeated DeviceNode's data-reference memo, re-rendering\n * every device node. Keyed on consumer data objects, so entries die with them.\n */\nconst mergedDeviceDataCache = new WeakMap<object, { value: boolean; data: NetworkNode['data'] }>();\n\nfunction shallowFieldsEqual(previous: object, next: object): boolean {\n  const keys = Object.keys(previous);\n  return keys.length === Object.keys(next).length && keys.every(key =>\n    Object.prototype.hasOwnProperty.call(next, key) &&\n    Object.is(\n      (previous as Record<string, unknown>)[key],\n      (next as Record<string, unknown>)[key],\n    ),\n  );\n}\n\n// The caller compares data separately because it may be the cached object\n// carrying showDeviceImages. Every other property must survive this render\n// cache, including parentId, hidden, style, dimensions and consumer additions.\nfunction hasSameNodePropertiesExceptData(previous: NetworkNode, next: NetworkNode): boolean {\n  const keys = Object.keys(previous).filter(key => key !== 'data');\n  const nextKeys = Object.keys(next).filter(key => key !== 'data');\n  const previousFields = previous as Record<string, unknown>;\n  const nextFields = next as Record<string, unknown>;\n  return keys.length === nextKeys.length && keys.every(key => {\n    if (!Object.prototype.hasOwnProperty.call(next, key)) return false;\n    const before = previousFields[key];\n    const after = nextFields[key];\n    if (Object.is(before, after)) return true;\n    return (key === 'position' || key === 'positionAbsolute' || key === 'measured') &&\n      typeof before === 'object' && before !== null &&\n      typeof after === 'object' && after !== null &&\n      shallowFieldsEqual(before, after);\n  });\n}\n\nexport function applyDeviceDisplayToNodes(\n  nodes: NetworkNode[],\n  options?: { showDeviceImages?: boolean },\n  previousNodes?: NetworkNode[],\n): NetworkNode[] {\n  const { showDeviceImages } = options ?? {};\n  if (showDeviceImages === undefined) {\n    if (!previousNodes || previousNodes.length !== nodes.length) {\n      return nodes;\n    }\n\n    let changed = false;\n    const stabilized = nodes.map((node, index) => {\n      const previous = previousNodes[index];\n      if (\n        previous?.id === node.id &&\n        previous.data === node.data &&\n        hasSameNodePropertiesExceptData(previous, node)\n      ) {\n        return previous;\n      }\n      changed = true;\n      return node;\n    });\n\n    return changed ? stabilized : previousNodes;\n  }\n\n  let changed = false;\n  const nextNodes = nodes.map((node, index) => {\n    if (!isDeviceDiagramNode(node)) {\n      const previous = previousNodes?.[index];\n      if (previous?.id === node.id && previous.data === node.data && hasSameNodePropertiesExceptData(previous, node)) {\n        return previous;\n      }\n      if (previous !== node) {\n        changed = true;\n      }\n      return node;\n    }\n\n    const nodeData = node.data ?? {};\n    if (nodeData.showDeviceImages !== undefined) {\n      const previous = previousNodes?.[index];\n      if (previous?.id === node.id && previous.data === node.data && hasSameNodePropertiesExceptData(previous, node)) {\n        return previous;\n      }\n      if (previous !== node) {\n        changed = true;\n      }\n      return node;\n    }\n\n    // Reuse a merged data object for this raw data + showDeviceImages value so an\n    // unchanged node keeps a stable data identity across recomputes.\n    let cached = mergedDeviceDataCache.get(nodeData);\n    if (!cached || cached.value !== showDeviceImages) {\n      cached = { value: showDeviceImages, data: { ...nodeData, showDeviceImages } };\n      mergedDeviceDataCache.set(nodeData, cached);\n    }\n\n    const previous = previousNodes?.[index];\n    if (\n      previous?.id === node.id &&\n      previous.data === cached.data &&\n      hasSameNodePropertiesExceptData(previous, node)\n    ) {\n      return previous;\n    }\n\n    changed = true;\n    return {\n      ...node,\n      data: cached.data,\n    } as NetworkNode;\n  });\n\n  if (!changed && previousNodes && previousNodes.length === nextNodes.length) {\n    return previousNodes;\n  }\n\n  return nextNodes;\n}\n","import { handlesMatch } from \"../utils/handle-ids\";\nimport { useDiagramViewport } from \"./use-diagram-viewport\";\nimport { useDiagramRackDrag } from \"./use-diagram-rack-drag\";\nimport { useDiagramState } from \"./use-diagram-state\";\nimport { useDiagramFlowMethods } from \"./use-diagram-flow-methods\";\nimport React, {\n  useCallback,\n  useMemo,\n  useState,\n  useEffect,\n  useRef,\n} from \"react\";\nimport {\n  ReactFlow,\n  Background,\n  Controls,\n  MiniMap,\n  applyEdgeChanges,\n  applyNodeChanges,\n  Connection,\n  Edge,\n  OnInit,\n  IsValidConnection,\n  Node,\n  EdgeTypes,\n  useReactFlow,\n  NodeChange,\n  OnNodesChange,\n  OnEdgesChange,\n  OnNodeDrag,\n  EdgeChange,\n  ReactFlowProvider,\n  Panel,\n  ConnectionMode,\n  type FinalConnectionState,\n} from \"@xyflow/react\";\nimport {\n  NetworkDiagramProps,\n  NetworkNode,\n  NetworkEdge,\n  NetworkFlowNode,\n  NetworkFlowEdge,\n  DiagramPortClickPayload,\n  DiagramBayClickPayload,\n} from \"../types\";\nimport { createNodeTypes } from \"./diagram-node-types\";\nimport { resolveNodeFace, selectNodesById, setNodeFaceData } from \"../utils/diagram-selection\";\nimport { DiagramViewportTracker } from \"./DiagramViewportTracker\";\nimport { DiagramActionPanel } from \"./DiagramActionPanel\";\nimport { NetworkDiagramDocumentBridge } from \"./NetworkDiagramDocumentBridge\";\nimport { DiagramZoomLod } from \"./DiagramZoomLod\";\nimport { RACK_HEADER_HEIGHT } from \"../utils/constants\";\nimport { warnDev } from \"../utils/diagram-logger\";\nimport {\n  getReactFlowThemeClassName,\n  getReactFlowBackgroundColor,\n  getReactFlowWrapperStyle,\n  type ColorMode,\n} from \"../utils/react-flow-theming\";\nimport { useResolvedColorMode } from \"./use-resolved-color-mode\";\nimport { FibreEdge } from \"./FibreEdge\";\nimport { PowerEdge } from \"./PowerEdge\";\nimport { SmoothStepFibreEdge } from \"./SmoothStepFibreEdge\";\nimport { StepFibreEdge } from \"./StepFibreEdge\";\nimport { ThickCableEdge } from \"./ThickCableEdge\";\nimport { DiagramErrorBoundary } from \"./DiagramErrorBoundary\";\nimport {\n  snapToUPosition,\n  calculateDevicePositionFromU,\n} from \"../utils/u-positioning\";\nimport {\n  computeEdgeZIndex,\n  validateNetworkConnection,\n  type ValidateConnectionParams,\n} from \"../core/connection-validation\";\nimport { createEdgeWithMetadata } from \"../utils/edge-connection\";\nimport {\n  deriveEdgeData,\n  type DerivedEdgeData,\n  type DerivedEdgeType,\n} from \"../utils/edge-data\";\nimport {\n  planConnection,\n  type ConnectionPlanContext,\n} from \"../core/connection-plan\";\nimport {\n  findEndpointOccupants,\n  type ConnectionPairClassification,\n} from \"../core/endpoint-occupancy\";\nimport {\n  colorFromFibreEndpoint,\n  resolveFibreEdgePaint,\n} from \"../utils/fibre-edge-color\";\nimport {\n  snapToSplicePosition,\n  calculateSplicePositionFromNumberWithData,\n} from \"../utils/splice-positioning\";\nimport {\n  evaluateFibreConnectionRule,\n  isFibreConnection,\n} from \"../utils/connection-rules\";\nimport {\n  isRackAssignableNode,\n  isRackDeviceNode,\n} from \"../utils/rack-device-types\";\nimport {\n  applyRackPixelDimensionsToNodes,\n  getRackNodePixelDimensions,\n  getRackUnitHeightPx,\n  isSplitFaceRackLayout,\n} from \"../utils/rack-layout\";\nimport { getNodeRenderedSize } from \"../utils/node-dimensions\";\nimport {\n  applyDiagramFocusToEdges,\n  applyDiagramFocusToNodes,\n  type DiagramFocusOptions,\n} from \"../utils/diagram-focus\";\nimport {\n  applyHighlightGroupsToEdges,\n  collectHighlightGroupEdgeIds,\n  collectHighlightGroupNodeIds,\n} from \"../utils/highlight-groups\";\nimport { applyDeviceDisplayToNodes } from \"../utils/device-display\";\nimport { DiagramBayHighlightSync, DiagramPortHighlightStoreProvider, DiagramPortHighlightSync, DiagramPortProvider } from \"../context/DiagramPortContext\";\nimport { DiagramMutationProvider } from \"../context/DiagramMutationContext\";\nimport {\n  RackDropTargetProvider,\n  useRackDropTargetStore,\n} from \"../context/DiagramRackDropTargetContext\";\n\n/** Whether any node is mid-drag (layout passes skip while a drag is live). */\nfunction isAnyNodeDragging(nodes: readonly Node[]): boolean {\n  return nodes.some(\n    (node) => (node as Node & { dragging?: boolean }).dragging === true,\n  );\n}\n\nconst defaultEdgeTypes: EdgeTypes = {\n  fiber: FibreEdge,\n  ethernet: FibreEdge,\n  power: PowerEdge,\n  smoothstep: SmoothStepFibreEdge,\n  step: StepFibreEdge,\n  \"thick-cable\": ThickCableEdge,\n};\n\n/**\n * Default empty-state content: a small centred, themed message. Colours come\n * from `--rn-empty-text` (styles/index.css), so it follows the diagram theme.\n */\nconst DEFAULT_EMPTY_STATE = <span>No nodes to display</span>;\n\n/**\n * How long the node count has to stay at zero before the empty state appears.\n * Long enough that a controlled consumer rendering `nodes={[]}` for a tick\n * while data loads never flashes the message, short enough to feel immediate\n * on a genuinely empty diagram.\n */\nconst EMPTY_STATE_DELAY_MS = 150;\n\n/**\n * Wraps the diagram render in {@link DiagramErrorBoundary} unless the consumer\n * opted out with `errorBoundary={false}` (in which case errors propagate to\n * their own boundary, as they did before this was built in).\n */\nconst DiagramRenderBoundary: React.FC<{\n  enabled: boolean;\n  fallback?: NetworkDiagramProps[\"errorFallback\"];\n  onError?: NetworkDiagramProps[\"onError\"];\n  onReset?: () => void;\n  children: React.ReactNode;\n}> = ({ enabled, fallback, onError, onReset, children }) => {\n  if (!enabled) {\n    return <>{children}</>;\n  }\n  return (\n    <DiagramErrorBoundary\n      fallback={fallback}\n      onError={onError}\n      onReset={onReset}\n    >\n      {children}\n    </DiagramErrorBoundary>\n  );\n};\n\nconst NetworkDiagramInner: React.FC<NetworkDiagramProps> = ({\n  nodes,\n  edges,\n  initialNodes,\n  initialEdges,\n  onNodesChange: externalOnNodesChange,\n  onEdgesChange: externalOnEdgesChange,\n  debug,\n  onNodeClick,\n  onEdgeClick,\n  onPaneClick,\n  nodeTypes: externalNodeTypes,\n  edgeTypes: externalEdgeTypes,\n  onConnect: externalOnConnect,\n  onEdgeUpdate: externalOnEdgeUpdate,\n  isValidConnection: externalIsValidConnection,\n  connectionMode: externalConnectionMode,\n  onFaceChange,\n  onRackFaceChange,\n  className,\n  style,\n  alignRacksToBottom,\n  reAssignable = true,\n  colorMode = \"dark\",\n  onInit,\n  onFlowMethods,\n  showDownloadButton = false,\n  downloadButtonPosition = \"top-right\",\n  downloadButtonText = \"Download Image\",\n  downloadButtonStyle,\n  useSmoothstepEdgesForTubes = false,\n  readOnly = false,\n  interaction,\n  actionPanel,\n  fitViewOptions,\n  fitViewOnInit = true,\n  minZoom = 0.1,\n  maxZoom = 4,\n  onlyRenderVisibleElements = false,\n  highlightedNodeIds,\n  highlightedEdgeIds,\n  dimUnhighlighted,\n  dimOpacity,\n  connectEdgesToHighlightedNodes,\n  highlightGroups,\n  showDeviceImages,\n  highlightedPortHandles,\n  onPortClick,\n  highlightedBays,\n  onBayClick,\n  connectionRadius,\n  edgeInteractionWidth = 8,\n  onConnectRejected,\n  initialViewport,\n  onViewportReady,\n  hideUntilViewportReady,\n  emptyState,\n  errorBoundary = true,\n  errorFallback,\n  onError,\n}) => {\n  const {\n    isControlled, reactFlowNodes, reactFlowEdges, setNodes, setEdges, onEdgesChange,\n    latestNodesRef, latestEdgesRef, nodesMapRef, edgesByTargetRef, commitNodes, commitEdges,\n  } = useDiagramState({ nodes, edges, initialNodes, initialEdges,\n    onNodesChange: externalOnNodesChange, onEdgesChange: externalOnEdgesChange, debug,\n  });\n  const reactFlowWrapper = useRef<HTMLDivElement>(null);\n\n  // Detect a touch-first device to relax pointer/keyboard interaction\n  // defaults. This keys off the *input device* (a coarse primary pointer, or a\n  // mobile user agent), never the window width: a narrow desktop window still\n  // has a mouse wheel and still needs device selection (De-rack, face toggle).\n  const [isMobile, setIsMobile] = useState<boolean>(false);\n  useEffect(() => {\n    if (typeof window === \"undefined\") return;\n    const coarseQuery =\n      typeof window.matchMedia === \"function\"\n        ? window.matchMedia(\"(pointer: coarse)\")\n        : null;\n    const checkMobile = () => {\n      setIsMobile(\n        Boolean(coarseQuery?.matches) ||\n          (typeof navigator !== \"undefined\" &&\n            /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n              navigator.userAgent,\n            )),\n      );\n    };\n    checkMobile();\n    if (!coarseQuery) return;\n    if (typeof coarseQuery.addEventListener === \"function\") {\n      coarseQuery.addEventListener(\"change\", checkMobile);\n      return () => coarseQuery.removeEventListener(\"change\", checkMobile);\n    }\n    // Safari < 14 only implements the deprecated listener API.\n    coarseQuery.addListener?.(checkMobile);\n    return () => coarseQuery.removeListener?.(checkMobile);\n  }, []);\n\n  const isReadOnly = readOnly === true;\n  const interactionConfig = useMemo(() => {\n    const defaults = {\n      // Keep node dragging available on touch devices.\n      nodesDraggable: true,\n      nodesConnectable: true,\n      // Tap-to-select, pinch-to-zoom and double-tap-to-zoom are the only ways\n      // to inspect a dense diagram on a phone, so they stay on for touch. Only\n      // the *wheel/trackpad* gestures are disabled there: panOnScroll and\n      // zoomOnScroll hijack the page's own scrolling on a touch device.\n      // Every one of these is overridable through the `interaction` prop.\n      elementsSelectable: true,\n      edgesFocusable: !isMobile,\n      edgesReconnectable: true,\n      connectOnClick: true,\n      panOnDrag: true,\n      panOnScroll: !isMobile,\n      zoomOnScroll: !isMobile,\n      zoomOnPinch: true,\n      zoomOnDoubleClick: true,\n      allowKeyboardDelete: true,\n      showControls: true,\n      // MiniMap is opt-in: it overlaps diagram content, so callers enable it explicitly.\n      showMiniMap: false,\n      showInteractiveControl: true,\n    };\n\n    const merged = { ...defaults, ...(interaction ?? {}) };\n    if (!isReadOnly) {\n      return merged;\n    }\n\n    return {\n      ...merged,\n      nodesDraggable: false,\n      nodesConnectable: false,\n      elementsSelectable: merged.elementsSelectable !== false,\n      edgesFocusable: false,\n      edgesReconnectable: false,\n      connectOnClick: false,\n      allowKeyboardDelete: false,\n      showInteractiveControl: false,\n    };\n  }, [isMobile, interaction, isReadOnly]);\n\n  const actionPanelConfig = useMemo(\n    () => ({\n      enabled: actionPanel?.enabled ?? true,\n      showRearToggle: actionPanel?.showRearToggle ?? true,\n      showDeRack: actionPanel?.showDeRack ?? true,\n      showZoomToEdit: actionPanel?.showZoomToEdit ?? true,\n    }),\n    [actionPanel],\n  );\n\n  const { getNodes } = useReactFlow();\n  const { reactFlowApiRef, isCapturingImage, isDownloading, onDownload } = useDiagramFlowMethods({\n    reactFlowWrapper, commitNodes, commitEdges,\n    onFlowMethods, fitViewOptions, debug, colorMode,\n  });\n\n  const rackDropTargetStore = useRackDropTargetStore();\n\n  const { getNodeById, getNodeParentId, isSplitFaceRackNode, isSplitFaceRackDeviceNode, getAbsoluteNodePosition, clampRackChildPosition, handleNodeDragStart, handleNodeDrag, handleNodeDragStop } = useDiagramRackDrag({\n    reAssignable, latestNodesRef, nodesMapRef, commitNodes, rackDropTargetStore,\n  });\n\n  // Store callbacks in refs to keep nodeTypes stable even if callbacks change\n  const onFaceChangeRef = useRef(onFaceChange);\n  const onRackFaceChangeRef = useRef(onRackFaceChange);\n\n  // Update refs when callbacks change\n  useEffect(() => {\n    onFaceChangeRef.current = onFaceChange;\n  }, [onFaceChange]);\n\n  useEffect(() => {\n    onRackFaceChangeRef.current = onRackFaceChange;\n  }, [onRackFaceChange]);\n\n  // Create stable wrapper functions using useCallback with empty deps\n  // These functions access refs via .current, so they can be stable even when callbacks change\n  const onFaceChangeStable = useCallback(\n    (nodeId: string, face: \"front\" | \"rear\") => {\n      onFaceChangeRef.current?.(nodeId, face);\n    },\n    [],\n  );\n\n  const onRackFaceChangeStable = useCallback(\n    (rackId: string, face: \"front\" | \"rear\") => {\n      onRackFaceChangeRef.current?.(rackId, face);\n    },\n    [],\n  );\n\n  // Surface connection rejections to host apps. Kept stable via a ref so the\n  // connection handlers do not need to list the callback in their deps.\n  const onConnectRejectedRef = useRef(onConnectRejected);\n  useEffect(() => {\n    onConnectRejectedRef.current = onConnectRejected;\n  }, [onConnectRejected]);\n  const notifyConnectRejected = useCallback(\n    (\n      connection: Connection,\n      reason: string,\n      phase: \"connect\" | \"reconnect\",\n    ) => {\n      onConnectRejectedRef.current?.({ connection, reason, phase });\n    },\n    [],\n  );\n  // isValidConnection is polled continuously while dragging, so we cannot notify\n  // from there without spamming the callback. Instead we record the last fiber-rule\n  // rejection seen during the drag and emit it exactly once when the gesture ends\n  // (onConnectEnd / onEdgeUpdateEnd), which is the only reliable point at which\n  // React Flow has decided not to create/update an edge. `reconnecting` selects the\n  // phase, since isValidConnection has no oldEdge context to tell connect from reconnect.\n  const pendingConnectRejectionRef = useRef<{\n    connection: Connection;\n    reason: string;\n  } | null>(null);\n  const reconnectingRef = useRef(false);\n  const flushPendingConnectRejection = useCallback(\n    (phase: \"connect\" | \"reconnect\") => {\n      const pending = pendingConnectRejectionRef.current;\n      pendingConnectRejectionRef.current = null;\n      if (pending) {\n        notifyConnectRejected(pending.connection, pending.reason, phase);\n      }\n    },\n    [notifyConnectRejected],\n  );\n\n  const builtInNodeTypes = useMemo(\n    () =>\n      createNodeTypes(onFaceChangeStable, onRackFaceChangeStable),\n    [onFaceChangeStable, onRackFaceChangeStable],\n  );\n\n  const mergedNodeTypes = useMemo(\n    () => ({\n      ...builtInNodeTypes,\n      ...(externalNodeTypes ?? {}),\n    }),\n    [builtInNodeTypes, externalNodeTypes],\n  );\n\n  const mergedEdgeTypes = useMemo(\n    () => ({\n      ...defaultEdgeTypes,\n      ...(externalEdgeTypes ?? {}),\n    }),\n    [externalEdgeTypes],\n  );\n\n  // Dev-only footgun #3: a fresh type-map object on every render makes React\n  // Flow repeatedly reconcile its registry. Component identity decides whether\n  // a particular node or edge remounts: stable values remain mounted, while a\n  // freshly-created component function remounts the items that use it.\n  //\n  // A *single* identity change is legitimate (e.g. swapping in a different map\n  // once, in response to a mode change), so only warn once a map's identity has\n  // changed on at least two separate renders — that is the signature of an\n  // inline literal rather than a deliberate swap.\n  const UNSTABLE_TYPES_CHANGE_THRESHOLD = 2;\n  const lastNodeTypesRef = useRef(externalNodeTypes);\n  const lastEdgeTypesRef = useRef(externalEdgeTypes);\n  const nodeTypesChangeCountRef = useRef(0);\n  const edgeTypesChangeCountRef = useRef(0);\n  const unstableTypesWarnedRef = useRef(false);\n  useEffect(() => {\n    const nodeTypesChanged =\n      externalNodeTypes !== undefined &&\n      lastNodeTypesRef.current !== undefined &&\n      lastNodeTypesRef.current !== externalNodeTypes;\n    const edgeTypesChanged =\n      externalEdgeTypes !== undefined &&\n      lastEdgeTypesRef.current !== undefined &&\n      lastEdgeTypesRef.current !== externalEdgeTypes;\n    lastNodeTypesRef.current = externalNodeTypes;\n    lastEdgeTypesRef.current = externalEdgeTypes;\n    if (nodeTypesChanged) nodeTypesChangeCountRef.current += 1;\n    if (edgeTypesChanged) edgeTypesChangeCountRef.current += 1;\n    if (unstableTypesWarnedRef.current) return;\n    const nodeTypesUnstable = nodeTypesChangeCountRef.current >= UNSTABLE_TYPES_CHANGE_THRESHOLD;\n    const edgeTypesUnstable = edgeTypesChangeCountRef.current >= UNSTABLE_TYPES_CHANGE_THRESHOLD;\n    if (!nodeTypesUnstable && !edgeTypesUnstable) return;\n    unstableTypesWarnedRef.current = true;\n    const changed = [nodeTypesUnstable ? \"nodeTypes\" : null, edgeTypesUnstable ? \"edgeTypes\" : null]\n      .filter(Boolean)\n      .join(\" and \");\n    warnDev(\n      `NetworkDiagram received a new \\`${changed}\\` object identity between ` +\n        `renders. React Flow must reconcile the type registry repeatedly, and ` +\n        `new component functions remount the nodes or edges that use them. ` +\n        `Hoist the map and its component values to module scope or wrap them ` +\n        `in useMemo with stable dependencies.`,\n    );\n  }, [externalNodeTypes, externalEdgeTypes]);\n  const defaultEdgeOptions = useMemo(\n    () => ({\n      zIndex: 1000,\n      reconnectable: interactionConfig.edgesReconnectable,\n    }),\n    [interactionConfig.edgesReconnectable],\n  );\n  const proOptions = useMemo(() => ({ hideAttribution: true }), []);\n\n  const validateConnection = useCallback(\n    (params: ValidateConnectionParams) =>\n      validateNetworkConnection(params, {\n        nodes: latestNodesRef.current as NetworkNode[],\n        edges: latestEdgesRef.current as NetworkEdge[],\n      }),\n    [latestEdgesRef, latestNodesRef],\n  );\n\n  const selectEdgeZIndex = useCallback(\n    (edge: NetworkEdge) =>\n      computeEdgeZIndex(edge, nodesMapRef.current as Map<string, NetworkNode>),\n    [nodesMapRef],\n  );\n\n  // Persistent layout corrections (rack alignment, parent extent,\n  // holder/U-derived positions, PDU clamping) go through commitNodes: one\n  // onNodesChange — and one onDocumentChange in document mode — per pass,\n  // with latestNodesRef updated synchronously. Each distinct correction is\n  // notified once; after that a controlled value that still disagrees is\n  // authoritative, so the correction is neither re-applied nor re-notified.\n  // Uncontrolled diagrams have no consumer to defer to and apply every pass.\n  // Transient state (measured dimensions, selection) never enters this path.\n  const notifiedCorrectionsRef = useRef(new Map<string, string>());\n\n  const correctionSignature = useCallback(\n    (node: Node) =>\n      `${node.position?.x ?? 0},${node.position?.y ?? 0},${node.extent ?? \"\"}`,\n    [],\n  );\n\n  const commitPersistentNodeCorrections = useCallback(\n    (proposed: NetworkFlowNode[], signatures: Map<string, string>): boolean => {\n      const proposedById = new Map(proposed.map((n) => [n.id, n]));\n      const presentIds = new Set(latestNodesRef.current.map((n) => n.id));\n      for (const id of notifiedCorrectionsRef.current.keys()) {\n        if (!presentIds.has(id)) {\n          notifiedCorrectionsRef.current.delete(id);\n        }\n      }\n      let applied = false;\n      const next = latestNodesRef.current.map((node) => {\n        const signature = signatures.get(node.id);\n        const proposedNode = signature ? proposedById.get(node.id) : undefined;\n        if (!signature || !proposedNode) {\n          // Keep the mark while the store still holds the notified value —\n          // it is our own correction, not proof the consumer accepted it.\n          // Clear it once the node carries a different value (consumer\n          // override) or stops needing the correction entirely.\n          if (\n            notifiedCorrectionsRef.current.get(node.id) !==\n            correctionSignature(node)\n          ) {\n            notifiedCorrectionsRef.current.delete(node.id);\n          }\n          return node;\n        }\n        if (\n          isControlled &&\n          notifiedCorrectionsRef.current.get(node.id) === signature\n        ) {\n          return node;\n        }\n        notifiedCorrectionsRef.current.set(node.id, signature);\n        applied = true;\n        return proposedNode;\n      });\n      if (!applied) {\n        return false;\n      }\n      commitNodes(next as NetworkFlowNode[]);\n      return true;\n    },\n    [isControlled, commitNodes, correctionSignature, latestNodesRef],\n  );\n\n  // Optionally align rack bottoms to a common baseline\n  useEffect(() => {\n    if (!alignRacksToBottom) return;\n    if (!reactFlowNodes || reactFlowNodes.length === 0) return;\n    if (\n      reactFlowNodes.some(\n        (node) => (node as Node & { dragging?: boolean }).dragging,\n      )\n    ) {\n      return;\n    }\n\n    const racks = reactFlowNodes.filter((n) => n.type === \"rack\");\n    if (racks.length === 0) return;\n\n    // Compute each rack's bottom using the rendered rack dimensions.\n    const rackBottoms = racks.map((r) => {\n      const heightPx = getRackNodePixelDimensions(\n        r.data as NetworkNode[\"data\"],\n      ).height;\n      return (r.position?.y || 0) + heightPx;\n    });\n\n    // Find the range of bottom positions\n    const minBottom = Math.min(...rackBottoms);\n    const maxBottom = Math.max(...rackBottoms);\n    const range = maxBottom - minBottom;\n\n    // Only align if there's a significant difference (more than 20px)\n    // This prevents constant micro-adjustments during dragging\n    if (range < 20) return;\n\n    // Use the highest bottom as the baseline (floor level)\n    const baselineBottom = maxBottom;\n\n    // Align all racks to the baseline - move them up or down as needed\n    const signatures = new Map<string, string>();\n    const nextNodes = reactFlowNodes.map((n) => {\n      if (n.type !== \"rack\") return n;\n      const heightPx = getRackNodePixelDimensions(\n        n.data as NetworkNode[\"data\"],\n      ).height;\n      const currentBottom = (n.position?.y || 0) + heightPx;\n      const delta = baselineBottom - currentBottom;\n      if (Math.abs(delta) < 5) return n; // Don't make tiny adjustments\n      const corrected = {\n        ...n,\n        position: {\n          x: n.position?.x || 0,\n          y: (n.position?.y || 0) + delta,\n        },\n      } as Node;\n      signatures.set(n.id, correctionSignature(corrected));\n      return corrected;\n    });\n\n    if (signatures.size === 0) return;\n\n    commitPersistentNodeCorrections(\n      nextNodes as NetworkFlowNode[],\n      signatures,\n    );\n  }, [alignRacksToBottom, reactFlowNodes, correctionSignature, commitPersistentNodeCorrections]);\n\n  const diagramMutationValue = useMemo(\n    () => ({\n      updateNodeData: (nodeId: string, dataPatch: Record<string, unknown>) => {\n        commitNodes(\n          latestNodesRef.current.map((node) =>\n            node.id === nodeId\n              ? {\n                  ...node,\n                  data: { ...node.data, ...dataPatch },\n                }\n              : node,\n          ),\n        );\n      },\n      updateNodes: (updater: (nodes: NetworkNode[]) => NetworkNode[]) => {\n        commitNodes(updater(latestNodesRef.current as NetworkNode[]));\n      },\n    }),\n    [commitNodes, latestNodesRef],\n  );\n\n  // Helper: derive fiber color from a node endpoint handle (tube/cable/WDM/etc.)\n  const deriveFibreColorFromEndpoint = useCallback(\n    (\n      nodeId: string,\n      handle: string | null | undefined,\n    ): { color: string; striped?: boolean } | undefined => {\n      if (!nodeId || !handle) return undefined;\n      const node = getNodeById(nodeId);\n      if (!node) return undefined;\n      const normalizedHandle = handle.replace(/-(source|target)$/i, \"\");\n\n      if (\n        node.type === \"fibre-split\" &&\n        normalizedHandle.startsWith(\"output-\")\n      ) {\n        for (const edge of edgesByTargetRef.current.get(nodeId) ?? []) {\n          if (\n            !(edge.targetHandle || \"\")\n              .replace(/-(source|target)$/i, \"\")\n              .startsWith(\"input-\")\n          ) {\n            continue;\n          }\n          const inputColor = edge.data?.color;\n          if (inputColor) {\n            return { color: inputColor };\n          }\n        }\n      }\n\n      const fromHandle = colorFromFibreEndpoint(node.type, handle);\n      if (fromHandle) {\n        return fromHandle;\n      }\n\n      const nodeData = node.data as NetworkNode[\"data\"];\n      if (nodeData.color) {\n        return { color: nodeData.color };\n      }\n\n      return undefined;\n    },\n    [edgesByTargetRef, getNodeById],\n  );\n\n  const resolveFibreEdgeColor = useCallback(\n    (\n      connection: Pick<Connection, \"source\" | \"target\" | \"sourceHandle\" | \"targetHandle\">,\n      sourceNode: NetworkNode | undefined,\n      targetNode: NetworkNode | undefined,\n      excludeEdgeId?: string,\n    ) =>\n      resolveFibreEdgePaint({\n        sourceId: connection.source!,\n        sourceType: sourceNode?.type,\n        sourceHandle: connection.sourceHandle,\n        sourcePassThroughColor:\n          sourceNode?.type === \"coupler\"\n            ? Boolean(sourceNode.data?.passThroughColor)\n            : undefined,\n        targetId: connection.target!,\n        targetType: targetNode?.type,\n        targetHandle: connection.targetHandle,\n        targetPassThroughColor:\n          targetNode?.type === \"coupler\"\n            ? Boolean(targetNode.data?.passThroughColor)\n            : undefined,\n        edges: latestEdgesRef.current,\n        excludeEdgeId,\n      }) ??\n      deriveFibreColorFromEndpoint(connection.source!, connection.sourceHandle) ??\n      deriveFibreColorFromEndpoint(connection.target!, connection.targetHandle),\n    [deriveFibreColorFromEndpoint, latestEdgesRef],\n  );\n\n  // Update edges zIndex when nodes change - only update if zIndex actually changed.\n  // Skip during active drags: face visibility does not change mid-drag, and the\n  // O(E) setEdges here was blocking the main thread every pointer-move frame.\n  const edgesZIndexRef = useRef<Map<string, number>>(new Map());\n  useEffect(() => {\n    if (isAnyNodeDragging(reactFlowNodes)) {\n      return;\n    }\n\n    const nextZIndex = new Map(edgesZIndexRef.current);\n    let hasChanges = false;\n    const updated = latestEdgesRef.current.map((edge) => {\n      const newZIndex = selectEdgeZIndex(edge as NetworkEdge);\n      const oldZIndex = nextZIndex.get(edge.id);\n      if (oldZIndex === newZIndex) {\n        return edge;\n      }\n      hasChanges = true;\n      nextZIndex.set(edge.id, newZIndex);\n      return {\n        ...edge,\n        zIndex: newZIndex,\n        style: { ...edge.style, zIndex: newZIndex },\n      };\n    });\n\n    if (!hasChanges) {\n      return;\n    }\n\n    edgesZIndexRef.current = nextZIndex;\n    setEdges(updated as NetworkFlowEdge[]);\n  }, [reactFlowNodes, setEdges, selectEdgeZIndex, latestEdgesRef]);\n\n  // Last slot assignment (`parentId|uPosition|uHeight`) applied per racked\n  // device; see the device-positioning branch below.\n  const uSlotKeysRef = useRef(new Map<string, string>());\n\n  // One-time layout fixes (extent, uPosition-derived positions). Skip during\n  // drags — position changes are handled by handleNodesChange, not this pass.\n  useEffect(() => {\n    if (isAnyNodeDragging(reactFlowNodes)) {\n      return;\n    }\n    if (uSlotKeysRef.current.size > 0) {\n      const presentIds = new Set(reactFlowNodes.map((node) => node.id));\n      for (const id of uSlotKeysRef.current.keys()) {\n        if (!presentIds.has(id)) {\n          uSlotKeysRef.current.delete(id);\n        }\n      }\n    }\n\n    const updatedNodes = reactFlowNodes.map((node) => {\n      // Ensure extent is set for couplers in panels (no repositioning - they're not movable)\n      if (node.type === \"coupler\" && node.parentId) {\n        const parentPanel = getNodeById(node.parentId);\n        if (\n          parentPanel &&\n          (parentPanel.type === \"patch-panel\" || parentPanel.type === \"closure\")\n        ) {\n          // Ensure extent is set for proper parent-child relationship\n          if (!node.extent || node.extent !== \"parent\") {\n            return {\n              ...node,\n              extent: \"parent\" as const,\n            };\n          }\n        }\n      }\n      // Handle splice positioning\n      if (\n        node.type === \"splice\" &&\n        node.parentId &&\n        node.data.holderPosition &&\n        (!node.position || (node.position.x === 0 && node.position.y === 0))\n      ) {\n        const parentTray = getNodeById(node.parentId);\n        if (parentTray && parentTray.type === \"splice-tray\") {\n          const isRibbonMode = node.data.mode === \"ribbon\";\n          const calculatedPosition = calculateSplicePositionFromNumberWithData(\n            parentTray as NetworkNode,\n            node.data.holderPosition,\n            isRibbonMode,\n          );\n          return {\n            ...node,\n            position: calculatedPosition,\n          };\n        }\n      }\n      // Handle device positioning. `uPosition` is authoritative for a device\n      // racked in a rack: whenever a slot assignment is first seen — on load,\n      // when a node is added, or when its parent/uPosition/uHeight changes —\n      // a stored y that disagrees with the slot is snapped onto it. Later\n      // passes leave the device alone, so drags (which keep y and uPosition in\n      // step), rack renumbering and other rack-level edits never move it.\n      if (\n        isRackDeviceNode(node as NetworkNode) &&\n        node.parentId &&\n        node.data.uPosition\n      ) {\n        const parentRack = getNodeById(node.parentId);\n        if (parentRack) {\n          const deviceHeight = node.data.uHeight || 1;\n          const missingPosition =\n            !node.position || (node.position.x === 0 && node.position.y === 0);\n          const slotKey = `${node.parentId}|${node.data.uPosition}|${deviceHeight}`;\n          const slotChanged = uSlotKeysRef.current.get(node.id) !== slotKey;\n          uSlotKeysRef.current.set(node.id, slotKey);\n          if (missingPosition || (slotChanged && parentRack.type === \"rack\")) {\n            const calculatedPosition = calculateDevicePositionFromU(\n              parentRack as NetworkNode,\n              node.data.uPosition,\n              deviceHeight,\n              node.data,\n            );\n            if (missingPosition) {\n              return {\n                ...node,\n                position: calculatedPosition,\n              };\n            }\n            if (Math.abs(node.position.y - calculatedPosition.y) > 0.5) {\n              return {\n                ...node,\n                position: { x: node.position.x, y: calculatedPosition.y },\n              };\n            }\n          }\n        }\n      }\n      if (node.type === \"vertical-pdu\" && node.parentId) {\n        const parentRack = getNodeById(node.parentId);\n        if (parentRack && parentRack.type === \"rack\") {\n          const position = clampRackChildPosition(\n            parentRack as NetworkNode,\n            node,\n            node.position || { x: 0, y: RACK_HEADER_HEIGHT },\n          );\n\n          if (\n            node.extent !== \"parent\" ||\n            !node.position ||\n            node.position.x !== position.x ||\n            node.position.y !== position.y\n          ) {\n            return {\n              ...node,\n              extent: \"parent\" as const,\n              position,\n            };\n          }\n        }\n      }\n      return node;\n    });\n\n    const signatures = new Map<string, string>();\n    updatedNodes.forEach((node, index) => {\n      const originalNode = reactFlowNodes[index];\n      if (\n        node.extent !== originalNode.extent ||\n        node.position?.x !== originalNode.position?.x ||\n        node.position?.y !== originalNode.position?.y\n      ) {\n        signatures.set(node.id, correctionSignature(node));\n      }\n    });\n    commitPersistentNodeCorrections(\n      updatedNodes as NetworkFlowNode[],\n      signatures,\n    );\n  }, [reactFlowNodes, getNodeById, clampRackChildPosition, correctionSignature, commitPersistentNodeCorrections]);\n\n  const handleNodesChange = useCallback(\n    (changes: NodeChange[]): Node[] | null => {\n      const isDimensionsOnly =\n        changes.length > 0 &&\n        changes.every((change) => change.type === \"dimensions\");\n      if (isReadOnly && !isDimensionsOnly) {\n        return null;\n      }\n\n      const holderPositionUpdates = new Map<string, number>();\n      const uPositionUpdates = new Map<string, number>();\n      const parentExtentIds = new Set<string>();\n\n      const updatedChanges = changes.map((change) => {\n        if (change.type === \"position\" && change.position) {\n          const node = getNodeById(change.id);\n\n          // Prevent couplers from being moved (they're fixed in position)\n          if (node && node.type === \"coupler\" && node.parentId) {\n            const parentPanel = getNodeById(node.parentId);\n            if (\n              parentPanel &&\n              (parentPanel.type === \"patch-panel\" ||\n                parentPanel.type === \"closure\")\n            ) {\n              // Ensure extent is set for proper parent-child relationship\n              if (!node.extent || node.extent !== \"parent\") {\n                parentExtentIds.add(node.id);\n              }\n\n              // Prevent movement by returning original position\n              return { ...change, position: node.position || { x: 0, y: 0 } };\n            }\n          }\n\n          // Prevent tubes and trays from being moved inside closures (they're locked in position)\n          if (\n            node &&\n            (node.type === \"tube\" || node.type === \"splice-tray\") &&\n            node.parentId\n          ) {\n            const parentClosure = getNodeById(node.parentId);\n            if (parentClosure && parentClosure.type === \"closure\") {\n              // Ensure extent is set for proper parent-child relationship\n              if (!node.extent || node.extent !== \"parent\") {\n                parentExtentIds.add(node.id);\n              }\n\n              // Prevent movement by returning original position\n              return { ...change, position: node.position || { x: 0, y: 0 } };\n            }\n          }\n\n          // Handle splice positioning within tray\n          if (node && node.type === \"splice\" && node.parentId) {\n            const parentTray = getNodeById(node.parentId);\n            if (parentTray && parentTray.type === \"splice-tray\") {\n              const snappedResult = snapToSplicePosition(\n                node as NetworkNode,\n                parentTray as NetworkNode,\n                change.position,\n                latestNodesRef.current as NetworkNode[],\n              );\n\n              if (snappedResult.holderPosition !== undefined) {\n                holderPositionUpdates.set(\n                  node.id,\n                  snappedResult.holderPosition,\n                );\n              }\n\n              return {\n                ...change,\n                position: { x: snappedResult.x, y: snappedResult.y },\n              };\n            }\n          }\n\n          // Handle rack-mounted device positioning within rack\n          if (node && isRackDeviceNode(node as NetworkNode) && node.parentId) {\n            const parentRack = getNodeById(node.parentId);\n            if (parentRack && parentRack.type === \"rack\") {\n              const snappedResult = snapToUPosition(\n                node as NetworkNode,\n                parentRack as NetworkNode,\n                change.position,\n                latestNodesRef.current as NetworkNode[],\n              );\n\n              if (snappedResult.uPosition !== undefined) {\n                uPositionUpdates.set(node.id, snappedResult.uPosition);\n              }\n\n              return {\n                ...change,\n                position: { x: snappedResult.x, y: snappedResult.y },\n              };\n            }\n          }\n\n          if (node && node.type === \"vertical-pdu\" && node.parentId) {\n            const parentRack = getNodeById(node.parentId);\n            if (parentRack && parentRack.type === \"rack\") {\n              if (!node.extent || node.extent !== \"parent\") {\n                parentExtentIds.add(node.id);\n              }\n\n              return {\n                ...change,\n                position: clampRackChildPosition(\n                  parentRack as NetworkNode,\n                  node,\n                  change.position,\n                ),\n              };\n            }\n          }\n        }\n        return change;\n      });\n\n      let nextNodes = applyNodeChanges(\n        updatedChanges,\n        latestNodesRef.current,\n      ).map((node) => {\n        let updatedNode = node;\n\n        if (parentExtentIds.has(node.id)) {\n          updatedNode = {\n            ...updatedNode,\n            extent: \"parent\" as const,\n          };\n        }\n\n        const holderPosition = holderPositionUpdates.get(node.id);\n        if (holderPosition !== undefined) {\n          updatedNode = {\n            ...updatedNode,\n            data: {\n              ...updatedNode.data,\n              holderPosition,\n            },\n          };\n        }\n\n        const uPosition = uPositionUpdates.get(node.id);\n        if (uPosition !== undefined) {\n          updatedNode = {\n            ...updatedNode,\n            data: {\n              ...updatedNode.data,\n              uPosition,\n            },\n          };\n        }\n\n        return updatedNode;\n      });\n\n      nextNodes = applyRackPixelDimensionsToNodes(nextNodes as NetworkNode[]);\n\n      latestNodesRef.current = nextNodes;\n      const map = new Map<string, Node>();\n      nextNodes.forEach((node) => {\n        map.set(node.id, node);\n      });\n      nodesMapRef.current = map;\n      setNodes(nextNodes as NetworkFlowNode[]);\n\n      return nextNodes;\n    },\n    [isReadOnly, latestNodesRef, nodesMapRef, setNodes, getNodeById, clampRackChildPosition],\n  );\n\n  // Connection validation for React Flow - allows connections before onConnect validation\n  const handleIsValidConnection = useCallback(\n    (connection: Connection): boolean => {\n      if (\n        !connection.source ||\n        !connection.target ||\n        !connection.sourceHandle ||\n        !connection.targetHandle\n      ) {\n        return true;\n      }\n\n      const sourceNode = getNodeById(connection.source);\n      const targetNode = getNodeById(connection.target);\n\n      // UNIVERSAL RULE: Prevent multiple connections to ANY handle\n      const sourceHandleAlreadyConnected =\n        findEndpointOccupants(\n          latestEdgesRef.current,\n          connection.source,\n          sourceNode as NetworkNode | undefined,\n          connection.sourceHandle,\n        ).length > 0;\n      const targetHandleAlreadyConnected =\n        findEndpointOccupants(\n          latestEdgesRef.current,\n          connection.target,\n          targetNode as NetworkNode | undefined,\n          connection.targetHandle,\n        ).length > 0;\n\n      if (sourceHandleAlreadyConnected && debug) {\n        console.log(\n          \"Connection will replace: Source handle already connected\",\n          {\n            source: connection.source,\n            sourceHandle: connection.sourceHandle,\n            existingEdges: latestEdgesRef.current.filter(\n              (e) =>\n                (e.source === connection.source &&\n                  e.sourceHandle === connection.sourceHandle) ||\n                (e.target === connection.source &&\n                  e.targetHandle === connection.sourceHandle),\n            ),\n          },\n        );\n      }\n\n      if (targetHandleAlreadyConnected && debug) {\n        console.log(\n          \"Connection will replace: Target handle already connected\",\n          {\n            target: connection.target,\n            targetHandle: connection.targetHandle,\n            existingEdges: latestEdgesRef.current.filter(\n              (e) =>\n                (e.target === connection.target &&\n                  e.targetHandle === connection.targetHandle) ||\n                (e.source === connection.target &&\n                  e.sourceHandle === connection.targetHandle),\n            ),\n          },\n        );\n      }\n\n      const fiberConnection = isFibreConnection(\n        sourceNode as NetworkNode | undefined,\n        targetNode as NetworkNode | undefined,\n      );\n\n      if (!fiberConnection) {\n        // For non-fiber connections, final occupancy checks run in onConnect/onEdgeUpdate.\n        // This keeps reconnect UX responsive, because isValidConnection does not receive oldEdge context.\n        pendingConnectRejectionRef.current = null;\n        return true;\n      }\n\n      // Fiber connection structure is governed by the shared rule authority so the\n      // interactive validator and the headless store validator never diverge.\n      const fiberRuleResult = evaluateFibreConnectionRule(\n        sourceNode as NetworkNode | undefined,\n        connection.sourceHandle,\n        targetNode as NetworkNode | undefined,\n        connection.targetHandle,\n      );\n      if (!fiberRuleResult.valid) {\n        if (debug) {\n          console.log(\n            `Connection rejected: ${fiberRuleResult.reason}`,\n            connection,\n          );\n        }\n        // Remember the rejection; it is surfaced once the gesture ends. React Flow\n        // will not call onConnect/onEdgeUpdate for a connection that fails here.\n        pendingConnectRejectionRef.current = {\n          connection,\n          reason:\n            fiberRuleResult.reason ?? \"Connection not allowed by fiber rules\",\n        };\n        return false;\n      }\n      pendingConnectRejectionRef.current = null;\n      return true;\n    },\n    [getNodeById, latestEdgesRef, debug],\n  );\n\n  // Edge presentation policy for a planned connection — injected into\n  // planConnection so connect and reconnect derive identical type/data.\n  const planEdgeType = useCallback(\n    (classification: ConnectionPairClassification): DerivedEdgeType => {\n      if (classification.power) return \"power\";\n      if (classification.cableButton) return \"thick-cable\";\n      if (classification.tube) {\n        return useSmoothstepEdgesForTubes ? \"smoothstep\" : \"fiber\";\n      }\n      return \"fiber\";\n    },\n    [useSmoothstepEdgesForTubes],\n  );\n  const planEdgeData = useCallback(\n    (ctx: ConnectionPlanContext, excludeEdgeId?: string) =>\n      deriveEdgeData({\n        edgeType: planEdgeType(ctx.classification),\n        sourceNode: ctx.sourceNode,\n        targetNode: ctx.targetNode,\n        isButtonHandleConnection: ctx.classification.cableButton,\n        resolveFibreColor: () =>\n          resolveFibreEdgeColor(\n            ctx.connection,\n            ctx.sourceNode,\n            ctx.targetNode,\n            excludeEdgeId,\n          ),\n      }),\n    [planEdgeType, resolveFibreEdgeColor],\n  );\n\n  const handleConnect = useCallback(\n    (params: Connection) => {\n      if (isReadOnly) {\n        return;\n      }\n\n      if (debug) {\n        console.log(\"onConnect called:\", params);\n      }\n\n      const plan = planConnection({\n        nodes: latestNodesRef.current as NetworkNode[],\n        edges: latestEdgesRef.current as NetworkEdge[],\n        connection: params,\n        validate: validateConnection,\n        resolveEdgeType: (ctx) => planEdgeType(ctx.classification),\n        resolveEdgeData: (ctx) => planEdgeData(ctx),\n      });\n\n      if (plan.kind === \"reject\") {\n        if (debug) {\n          console.warn(`Connection rejected (${plan.code}): ${plan.reason}`);\n        }\n        notifyConnectRejected(params, plan.reason, \"connect\");\n        return;\n      }\n\n      if (debug && plan.evictEdgeIds.length > 0) {\n        console.log(\n          \"Handle replacement: Replaced\",\n          plan.evictEdgeIds.length,\n          \"edge(s)\",\n        );\n      }\n\n      const evicted = new Set(plan.evictEdgeIds);\n      const newEdge = createEdgeWithMetadata(\n        plan.connection,\n        (plan.edgeType ?? \"fiber\") as NonNullable<Edge[\"type\"]>,\n        selectEdgeZIndex,\n        plan.edgeData,\n      );\n      const newEdges = [\n        ...latestEdgesRef.current.filter((e) => !evicted.has(e.id)),\n        newEdge,\n      ];\n      setAnimatingEdges((prev) => new Set(prev).add(newEdge.id));\n      // Remove animation class after animation completes. Timers are tracked\n      // so an unmount mid-animation cancels them instead of setting state on\n      // a dead component.\n      const timer = setTimeout(() => {\n        edgeAnimationTimersRef.current.delete(timer);\n        setAnimatingEdges((prev) => {\n          const next = new Set(prev);\n          next.delete(newEdge.id);\n          return next;\n        });\n      }, 1000); // Animation duration matches CSS\n      edgeAnimationTimersRef.current.add(timer);\n      commitEdges(newEdges);\n    },\n    [isReadOnly, debug, validateConnection, latestEdgesRef, latestNodesRef, planEdgeType, planEdgeData, selectEdgeZIndex, commitEdges, notifyConnectRejected],\n  );\n\n  // Handle edge updates (when user drags an edge endpoint to reconnect)\n  const handleEdgeUpdate = useCallback(\n    (oldEdge: Edge, newConnection: Connection) => {\n      if (isReadOnly) {\n        return;\n      }\n\n      if (\n        !newConnection.source ||\n        !newConnection.target ||\n        !newConnection.sourceHandle ||\n        !newConnection.targetHandle\n      ) {\n        return;\n      }\n\n      const plan = planConnection({\n        nodes: latestNodesRef.current as NetworkNode[],\n        edges: latestEdgesRef.current as NetworkEdge[],\n        connection: newConnection,\n        ignoreEdgeId: oldEdge.id,\n        validate: validateConnection,\n        resolveEdgeType: (ctx) => planEdgeType(ctx.classification),\n        resolveEdgeData: (ctx) => planEdgeData(ctx, oldEdge.id),\n      });\n\n      if (plan.kind === \"reject\") {\n        if (debug) {\n          console.warn(`Edge update rejected (${plan.code}): ${plan.reason}`);\n        }\n        notifyConnectRejected(newConnection, plan.reason, \"reconnect\");\n        return;\n      }\n\n      const edgeType = (plan.edgeType ?? \"fiber\") as NonNullable<Edge[\"type\"]>;\n      const edgeData = plan.edgeData as DerivedEdgeData | undefined;\n      const evicted = new Set(plan.evictEdgeIds);\n      // The planner canonicalises both ends to their mounted directional\n      // handle (e.g. a generic port's `<id>-target`). A reconnect only moves\n      // one end, so an end that still points at the same port keeps the id the\n      // edge was saved with rather than being silently rewritten.\n      const keepHandle = (\n        planned: string | null | undefined,\n        nodeId: string | null | undefined,\n        previousNodeId: string,\n        previous: string | null | undefined,\n      ) =>\n        planned && previous && nodeId === previousNodeId && handlesMatch(planned, previous)\n          ? previous\n          : planned;\n      const nextSourceHandle = keepHandle(\n        plan.connection.sourceHandle,\n        plan.connection.source,\n        oldEdge.source,\n        oldEdge.sourceHandle,\n      );\n      const nextTargetHandle = keepHandle(\n        plan.connection.targetHandle,\n        plan.connection.target,\n        oldEdge.target,\n        oldEdge.targetHandle,\n      );\n\n      const updated = latestEdgesRef.current\n        .filter((e) => !evicted.has(e.id))\n        .map((e) => {\n          if (e.id === oldEdge.id) {\n            const zIndex = selectEdgeZIndex({\n              ...plan.connection,\n              id: e.id,\n              type: edgeType,\n            } as NetworkEdge);\n            const nextData = { ...(e.data ?? {}) };\n            if (edgeData?.isRibbon && edgeData.ribbonFiberIds) {\n              nextData.ribbonFiberIds = edgeData.ribbonFiberIds;\n              nextData.isRibbon = true;\n              delete nextData.color;\n              delete nextData.striped;\n            } else if (edgeData?.color) {\n              nextData.color = edgeData.color;\n              nextData.striped = edgeData.striped;\n              nextData.isRibbon = false;\n              delete nextData.ribbonFiberIds;\n            } else {\n              delete nextData.color;\n              delete nextData.striped;\n              delete nextData.isRibbon;\n              delete nextData.ribbonFiberIds;\n            }\n            return {\n              ...e,\n              source: plan.connection.source!,\n              target: plan.connection.target!,\n              sourceHandle: nextSourceHandle,\n              targetHandle: nextTargetHandle,\n              type: edgeType,\n              data: nextData,\n              zIndex,\n              style: { ...e.style, zIndex },\n            };\n          }\n          return e;\n        });\n      commitEdges(updated);\n    },\n    [isReadOnly, latestEdgesRef, latestNodesRef, validateConnection, commitEdges, debug, notifyConnectRejected, planEdgeType, planEdgeData, selectEdgeZIndex],\n  );\n\n  const updateNodeSelection = useCallback(\n    (selectedNodeId: string | null) => {\n      setNodes((nodes) => selectNodesById(nodes, selectedNodeId));\n    },\n    [setNodes],\n  );\n\n  // One face-toggle policy shared by the action panel's Rear button and\n  // Cmd/Ctrl+click:\n  //  - read-only diagrams never edit their own nodes — they only notify;\n  //  - a controlled host that handles the matching face callback\n  //    (onRackFaceChange for racks, onFaceChange for devices) owns the change;\n  //  - otherwise the diagram applies the toggle itself (through commitNodes, so\n  //    controlled/document hosts get onNodesChange) and still notifies the\n  //    callbacks, which are change notifications rather than overrides.\n  const toggleNodeFace = useCallback(\n    (nodeId: string) => {\n      const node = getNodeById(nodeId);\n      if (!node) return;\n      const isRack = node.type === \"rack\";\n      const nodeData = ((node.data as NetworkNode[\"data\"]) || {}) as NetworkNode[\"data\"];\n      if (isRack) {\n        if (isSplitFaceRackLayout(nodeData)) return;\n      } else if (\n        !isRackDeviceNode(node as NetworkNode) ||\n        isSplitFaceRackDeviceNode(node)\n      ) {\n        return;\n      }\n\n      const current: \"front\" | \"rear\" = isRack\n        ? ((nodeData.face as \"front\" | \"rear\") || \"front\")\n        : resolveNodeFace(nodeData);\n      const next: \"front\" | \"rear\" = current === \"front\" ? \"rear\" : \"front\";\n      const childIds = isRack\n        ? latestNodesRef.current\n            .filter((candidate) => candidate.parentId === node.id)\n            .map((candidate) => candidate.id)\n        : [];\n\n      if (debug) {\n        console.log(`Toggling ${node.id} face from ${current} to ${next}`);\n      }\n\n      const notify = () => {\n        if (isRack) {\n          onRackFaceChange?.(node.id, next);\n          if (onFaceChange) {\n            childIds.forEach((childId) => onFaceChange(childId, next));\n          }\n        } else {\n          onFaceChange?.(node.id, next);\n        }\n      };\n\n      const hostOwnsFace = isRack\n        ? Boolean(onRackFaceChange)\n        : Boolean(onFaceChange);\n      if (isReadOnly || (isControlled && hostOwnsFace)) {\n        notify();\n        return;\n      }\n\n      const childIdSet = new Set(childIds);\n      commitNodes(\n        latestNodesRef.current.map((candidate) => {\n          if (candidate.id === node.id) {\n            return isRack\n              ? ({\n                  ...candidate,\n                  data: { ...candidate.data, face: next },\n                } as NetworkNode)\n              : ({\n                  ...candidate,\n                  data: setNodeFaceData(\n                    candidate.data as NetworkNode[\"data\"],\n                    next,\n                  ),\n                } as NetworkNode);\n          }\n          if (childIdSet.has(candidate.id)) {\n            return {\n              ...candidate,\n              data: setNodeFaceData(candidate.data as NetworkNode[\"data\"], next),\n            } as NetworkNode;\n          }\n          return candidate;\n        }),\n      );\n      notify();\n    },\n    [\n      getNodeById,\n      isSplitFaceRackDeviceNode,\n      latestNodesRef,\n      debug,\n      onRackFaceChange,\n      onFaceChange,\n      isReadOnly,\n      isControlled,\n      commitNodes,\n    ],\n  );\n\n  const handleNodeClick = useCallback(\n    (event: React.MouseEvent, node: Node) => {\n      const isCommandClick = event.metaKey || event.ctrlKey;\n      const allNodes = getNodes();\n      let selectedTarget = node;\n\n      // If a tube is clicked and it has a parent cable, select the parent instead\n      const parentId = getNodeParentId(node);\n      if (node.type === \"tube\" && parentId && !isCommandClick) {\n        const parentNode = allNodes.find(\n          (n) =>\n            n.id === parentId &&\n            (n.type === \"cable\" || n.type === \"multi-tube-cable\"),\n        );\n        if (parentNode) {\n          selectedTarget = parentNode;\n        }\n      }\n\n      // Mirror selected node locally so action buttons do not depend on RF selection subscriptions.\n      setSelectedNode(selectedTarget);\n      updateNodeSelection(selectedTarget.id);\n\n      if (\n        isCommandClick &&\n        isRackDeviceNode(selectedTarget as NetworkNode) &&\n        !isSplitFaceRackDeviceNode(selectedTarget)\n      ) {\n        toggleNodeFace(selectedTarget.id);\n      }\n\n      onNodeClick && onNodeClick(selectedTarget as NetworkNode);\n    },\n    [\n      onNodeClick,\n      getNodes,\n      getNodeParentId,\n      isSplitFaceRackDeviceNode,\n      updateNodeSelection,\n      toggleNodeFace,\n    ],\n  );\n\n  // Double-click no longer removes edges to avoid accidental disconnects during interactions\n  const handleNodeDoubleClick = useCallback(() => {}, []);\n\n  const handleEdgeClick = useCallback(\n    (event: React.MouseEvent, edge: Edge) => {\n      setSelectedNode(null);\n      updateNodeSelection(null);\n      onNodeClick?.(null);\n      if (onEdgeClick) {\n        onEdgeClick(edge as NetworkEdge);\n      }\n    },\n    [onEdgeClick, onNodeClick, updateNodeSelection],\n  );\n\n  const handlePaneClick = useCallback(() => {\n    setSelectedNode(null);\n    updateNodeSelection(null);\n    onNodeClick?.(null);\n    onPaneClick?.();\n  }, [onNodeClick, onPaneClick, updateNodeSelection]);\n\n  // Track selection to show a toggle UI\n  const [selectedNode, setSelectedNode] = useState<Node | null>(null);\n  const [animatingEdges, setAnimatingEdges] = useState<Set<string>>(new Set());\n  const edgeAnimationTimersRef = useRef(\n    new Set<ReturnType<typeof setTimeout>>(),\n  );\n  useEffect(() => {\n    const timers = edgeAnimationTimersRef.current;\n    return () => {\n      timers.forEach((timer) => clearTimeout(timer));\n      timers.clear();\n    };\n  }, []);\n\n  // Keyboard deletion is handled natively by React Flow (deleteKeyCode), which\n  // only fires when the flow itself has focus — the old window-level keydown\n  // listener deleted edges while focus was in unrelated UI and made every\n  // mounted diagram instance respond to one keypress. This gate keeps the\n  // library's deletion policy: edges only (nodes are never keyboard-deleted),\n  // and edges attached to tube handles are permanent. The remove changes then\n  // flow through handleReactFlowEdgesChange, which commits via the same\n  // controlled/uncontrolled paths as every other edit.\n  const handleBeforeDelete = useCallback(\n    async ({\n      nodes,\n      edges,\n    }: {\n      nodes: NetworkFlowNode[];\n      edges: NetworkFlowEdge[];\n    }): Promise<false | { nodes: NetworkFlowNode[]; edges: NetworkFlowEdge[] }> => {\n      // Nodes are never keyboard-deleted. When a node is in the deletion set,\n      // React Flow bundles that node's incident edges into `edges` — those are\n      // the node's cabling, pulled in by the node selection, NOT edges the user\n      // chose to remove. Since we refuse to delete the node, deleting its edges\n      // too would silently strip the cabling of a node we're keeping. Refuse the\n      // whole batch: an edge is deletable only when the user selects the edge\n      // itself (a node-free deletion set).\n      if (nodes.length > 0) {\n        return false;\n      }\n      const deletableEdges = edges.filter((edge) => {\n        if (edge.deletable === false) {\n          return false;\n        }\n        const sourceHandle = edge.sourceHandle || \"\";\n        const targetHandle = edge.targetHandle || \"\";\n        const isTubeHandle =\n          sourceHandle.startsWith(\"tube-\") || targetHandle.startsWith(\"tube-\");\n        if (isTubeHandle && debug) {\n          console.log(\"Cannot delete edge connected to tube handle\", edge);\n        }\n        return !isTubeHandle;\n      });\n      if (deletableEdges.length === 0) {\n        return false;\n      }\n      return { nodes: [], edges: deletableEdges };\n    },\n    [debug],\n  );\n\n  // Handle connection start - track when user starts connecting\n  const handleConnectStart = useCallback(\n    (\n      _event: MouseEvent | TouchEvent,\n      {\n        nodeId,\n        handleType,\n      }: { nodeId: string | null; handleType: string | null },\n    ) => {\n      // Do NOT reset reconnectingRef here: during a reconnect React Flow fires\n      // onConnectStart AFTER onEdgeUpdateStart, so resetting would misreport the\n      // gesture's rejection phase as \"connect\". The ref is owned by the\n      // onEdgeUpdateStart/onEdgeUpdateEnd pair, which always brackets a reconnect.\n      pendingConnectRejectionRef.current = null;\n      if (debug) {\n        console.log(\"Connection started from\", { nodeId, handleType });\n      }\n    },\n    [debug],\n  );\n\n  // Handle connection end - animate the newly created edge\n  const handleConnectEnd = useCallback(\n    (event: MouseEvent | TouchEvent, connectionState?: FinalConnectionState) => {\n      // The edge is added via onConnect; if instead the drag was rejected by the\n      // fiber rules in isValidConnection, surface that once here — but only\n      // when the gesture actually ended on a handle. Releasing over empty\n      // canvas is a cancel, even after hovering a handle the rules refused.\n      if (!reconnectingRef.current) {\n        if (connectionState && !connectionState.toHandle) {\n          pendingConnectRejectionRef.current = null;\n        } else {\n          flushPendingConnectRejection(\"connect\");\n        }\n      }\n      if (debug) {\n        console.log(\"Connection ended\", event);\n      }\n    },\n    [debug, flushPendingConnectRejection],\n  );\n\n  const handleEdgeUpdateStart = useCallback(() => {\n    reconnectingRef.current = true;\n    pendingConnectRejectionRef.current = null;\n  }, []);\n\n  const handleEdgeUpdateEnd = useCallback(\n    (\n      _event?: MouseEvent | TouchEvent,\n      _edge?: Edge,\n      _handleType?: unknown,\n      connectionState?: FinalConnectionState,\n    ) => {\n      // A reconnect blocked by the fiber rules never reaches onEdgeUpdate;\n      // surface it here unless the drop landed on no handle at all (a cancel).\n      if (connectionState && !connectionState.toHandle) {\n        pendingConnectRejectionRef.current = null;\n      } else {\n        flushPendingConnectRejection(\"reconnect\");\n      }\n      reconnectingRef.current = false;\n    },\n    [flushPendingConnectRejection],\n  );\n\n  const toggleSelectedFace = useCallback(() => {\n    if (!selectedNode) return;\n    toggleNodeFace(selectedNode.id);\n  }, [selectedNode, toggleNodeFace]);\n\n  const deRackSelectedDevice = useCallback(() => {\n    if (isReadOnly || !reAssignable || !selectedNode) return;\n    if (!isRackAssignableNode(selectedNode)) return;\n\n    const node = getNodeById(selectedNode.id);\n    const parentId = getNodeParentId(node);\n    if (!node || !isRackAssignableNode(node as NetworkNode) || !parentId)\n      return;\n\n    const absolutePosition = getAbsoluteNodePosition(node);\n    const currentNodes = latestNodesRef.current;\n    const updatedNodes = currentNodes.map((currentNode) => {\n      if (currentNode.id !== node.id) {\n        return currentNode;\n      }\n\n      return {\n        ...currentNode,\n        parentId: undefined,\n        extent: undefined,\n        position: {\n          x: absolutePosition.x + (node.origin?.[0] ?? 0) * getNodeRenderedSize(node).width,\n          y: absolutePosition.y + (node.origin?.[1] ?? 0) * getNodeRenderedSize(node).height,\n        },\n        data: {\n          ...currentNode.data,\n          uPosition: undefined,\n        },\n      } as Node;\n    });\n\n    commitNodes(updatedNodes);\n  }, [isReadOnly, reAssignable, selectedNode, getNodeById, getNodeParentId, getAbsoluteNodePosition, latestNodesRef, commitNodes]);\n\n  // Add animation class and hide edges whose endpoints belong to the inactive rack face.\n  const edgesWithAnimation = useMemo(() => {\n    return reactFlowEdges.map((edge) => {\n      const faceHidden = selectEdgeZIndex(edge as NetworkEdge) < 1000;\n      const nextClassName = animatingEdges.has(edge.id)\n        ? \"edge-connecting\"\n        : edge.className;\n      const hidden = faceHidden || edge.hidden === true;\n\n      if (\n        nextClassName === edge.className &&\n        hidden === (edge.hidden === true)\n      ) {\n        return edge;\n      }\n\n      return {\n        ...edge,\n        ...(nextClassName !== edge.className\n          ? { className: nextClassName }\n          : {}),\n        hidden: hidden ? true : undefined,\n      };\n    });\n  }, [reactFlowEdges, animatingEdges, selectEdgeZIndex]);\n\n  // highlightGroups membership feeds two channels: the per-edge color/width\n  // paint (applied below via the same data.highlight* channel the legacy\n  // highlighted* props use), and the dim complement — when dimUnhighlighted is\n  // set, the \"kept bright\" set is the union of every group plus the legacy\n  // highlighted* props. Both derivations are memoized on the prop so a stable\n  // highlightGroups yields referentially-stable results.\n  const highlightGroupNodeIds = useMemo(\n    () => collectHighlightGroupNodeIds(highlightGroups),\n    [highlightGroups],\n  );\n  const highlightGroupEdgeIds = useMemo(\n    () => collectHighlightGroupEdgeIds(highlightGroups),\n    [highlightGroups],\n  );\n\n  const effectiveHighlightedNodeIds = useMemo(() => {\n    if (highlightGroupNodeIds.length === 0) {\n      return highlightedNodeIds;\n    }\n    return [...(highlightedNodeIds ?? []), ...highlightGroupNodeIds];\n  }, [highlightedNodeIds, highlightGroupNodeIds]);\n\n  const effectiveHighlightedEdgeIds = useMemo(() => {\n    if (highlightGroupEdgeIds.length === 0) {\n      return highlightedEdgeIds;\n    }\n    return [...(highlightedEdgeIds ?? []), ...highlightGroupEdgeIds];\n  }, [highlightedEdgeIds, highlightGroupEdgeIds]);\n\n  const diagramFocusOptions = useMemo<DiagramFocusOptions>(\n    () => ({\n      highlightedNodeIds: effectiveHighlightedNodeIds,\n      highlightedEdgeIds: effectiveHighlightedEdgeIds,\n      dimUnhighlighted,\n      dimOpacity,\n      connectEdgesToHighlightedNodes,\n    }),\n    [\n      connectEdgesToHighlightedNodes,\n      dimOpacity,\n      dimUnhighlighted,\n      effectiveHighlightedEdgeIds,\n      effectiveHighlightedNodeIds,\n    ],\n  );\n\n  const nodesWithDeviceDisplayRef = useRef<NetworkNode[]>([]);\n  const nodesWithDeviceDisplay = useMemo(\n    () =>\n      applyDeviceDisplayToNodes(\n        reactFlowNodes as NetworkNode[],\n        { showDeviceImages },\n        nodesWithDeviceDisplayRef.current,\n      ),\n    [reactFlowNodes, showDeviceImages],\n  );\n  // Record the last committed result outside render so the memo above can reuse\n  // node identities without writing a ref during render — a StrictMode/concurrent\n  // render can otherwise advance the ref to a value that was never committed.\n  useEffect(() => {\n    nodesWithDeviceDisplayRef.current = nodesWithDeviceDisplay;\n  }, [nodesWithDeviceDisplay]);\n\n  const nodesWithDiagramFocus = useMemo(\n    () => applyDiagramFocusToNodes(nodesWithDeviceDisplay, diagramFocusOptions),\n    [diagramFocusOptions, nodesWithDeviceDisplay],\n  );\n\n  // Stabilise onPortClick so a consumer passing an inline handler does not churn\n  // the context value (which every DeviceNode reads) on each parent render.\n  const onPortClickRef = useRef(onPortClick);\n  useEffect(() => {\n    onPortClickRef.current = onPortClick;\n  }, [onPortClick]);\n  const stableOnPortClick = useCallback(\n    (payload: DiagramPortClickPayload) => onPortClickRef.current?.(payload),\n    [],\n  );\n  const portClickEnabled = Boolean(onPortClick);\n  // Bay clicks ride the same stabilised-ref pattern as port clicks, so an inline\n  // onBayClick handler does not churn the context value every parent render.\n  const onBayClickRef = useRef(onBayClick);\n  useEffect(() => {\n    onBayClickRef.current = onBayClick;\n  }, [onBayClick]);\n  const stableOnBayClick = useCallback(\n    (payload: DiagramBayClickPayload) => onBayClickRef.current?.(payload),\n    [],\n  );\n  const bayClickEnabled = Boolean(onBayClick);\n  // Highlighting is delivered via the instance-scoped highlight store, not this\n  // value — so the value now changes only when clickability toggles, never on a\n  // highlight change.\n  const diagramPortContextValue = useMemo(\n    () => ({\n      onPortClick: portClickEnabled ? stableOnPortClick : undefined,\n      portClickEnabled,\n      onBayClick: bayClickEnabled ? stableOnBayClick : undefined,\n      bayClickEnabled,\n    }),\n    [portClickEnabled, stableOnPortClick, bayClickEnabled, stableOnBayClick],\n  );\n\n  // Paint highlightGroup member edges (color/width) before dimming, so the dim\n  // pass sees an already-highlighted edge. Referentially stable when\n  // highlightGroups is empty (returns edgesWithAnimation unchanged).\n  const edgesWithHighlightGroups = useMemo(\n    () =>\n      applyHighlightGroupsToEdges(\n        edgesWithAnimation as NetworkEdge[],\n        highlightGroups,\n      ),\n    [edgesWithAnimation, highlightGroups],\n  );\n\n  const edgesWithDiagramFocus = useMemo(\n    () =>\n      applyDiagramFocusToEdges(\n        edgesWithHighlightGroups as NetworkEdge[],\n        diagramFocusOptions,\n      ),\n    [diagramFocusOptions, edgesWithHighlightGroups],\n  );\n\n  // React Flow's 20px interaction stroke sits above neighboring handles and\n  // swallows connection drops on tightly packed ports; the diagram default\n  // keeps edges clickable without blanketing adjacent handles.\n  const edgesForRender = useMemo(\n    () =>\n      edgesWithDiagramFocus.map((edge) =>\n        edge.interactionWidth == null\n          ? { ...edge, interactionWidth: edgeInteractionWidth }\n          : edge,\n      ),\n    [edgesWithDiagramFocus, edgeInteractionWidth],\n  );\n\n  // Read from the reactive node array, not getNodeById (which is backed by a\n  // ref and is identity-stable): the action panel must recompute when the\n  // selected node's parentId changes — e.g. right after de-racking, so the\n  // De-rack button disappears instead of lingering on a now-unracked device.\n  // reactFlowNodes is this component's existing reactive read source.\n  const selectedNodeForActions = useMemo<Node | null>(() => {\n    if (!selectedNode) {\n      return null;\n    }\n    // A node the host (or flowMethods) removed has no panel: falling back to\n    // the stale mirror left buttons acting on a node that no longer exists.\n    return reactFlowNodes.find((node) => node.id === selectedNode.id) ?? null;\n  }, [selectedNode, reactFlowNodes]);\n\n  // Drop the local selection mirror once its node is gone, so a later node\n  // re-using the id is not silently re-selected.\n  useEffect(() => {\n    if (\n      selectedNode &&\n      !reactFlowNodes.some((node) => node.id === selectedNode.id)\n    ) {\n      setSelectedNode(null);\n    }\n  }, [selectedNode, reactFlowNodes]);\n\n  const selectedActionButtons = useMemo(() => {\n    if (!selectedNodeForActions || !actionPanelConfig.enabled) {\n      return {\n        showRearToggle: false,\n        showDeRack: false,\n        showZoomToEdit: false,\n        count: 0,\n      };\n    }\n\n    const supportsRearToggle = (() => {\n      if (selectedNodeForActions.type === \"rack\") {\n        return !isSplitFaceRackNode(selectedNodeForActions);\n      }\n      if (isRackDeviceNode(selectedNodeForActions)) {\n        return !isSplitFaceRackDeviceNode(selectedNodeForActions);\n      }\n      return false;\n    })();\n    // A read-only diagram never edits its own faces, so the toggle is only\n    // offered when there is a host callback to receive it.\n    const faceToggleReachable =\n      !isReadOnly ||\n      (selectedNodeForActions.type === \"rack\"\n        ? Boolean(onRackFaceChange || onFaceChange)\n        : Boolean(onFaceChange));\n    const showRearToggle =\n      actionPanelConfig.showRearToggle &&\n      supportsRearToggle &&\n      faceToggleReachable;\n    const showDeRack =\n      actionPanelConfig.showDeRack &&\n      reAssignable &&\n      !isReadOnly &&\n      isRackAssignableNode(selectedNodeForActions) &&\n      !!getNodeParentId(selectedNodeForActions);\n    const showZoomToEdit =\n      actionPanelConfig.showZoomToEdit && selectedNodeForActions.type === \"rack\";\n\n    return {\n      showRearToggle,\n      showDeRack,\n      showZoomToEdit,\n      count: (showRearToggle ? 1 : 0) + (showDeRack ? 1 : 0) + (showZoomToEdit ? 1 : 0),\n    };\n  }, [\n    selectedNodeForActions,\n    actionPanelConfig,\n    reAssignable,\n    isReadOnly,\n    getNodeParentId,\n    isSplitFaceRackDeviceNode,\n    isSplitFaceRackNode,\n    onRackFaceChange,\n    onFaceChange,\n  ]);\n\n  const zoomToEditSelectedRack = useCallback(() => {\n    if (!selectedNodeForActions || selectedNodeForActions.type !== \"rack\") return;\n    // Overview Fit keeps the entire scene visible. This explicit editing action\n    // instead makes each U row at least 24 screen pixels, within host zoom limits.\n    const editingZoom = 24 / getRackUnitHeightPx(selectedNodeForActions.data);\n    void reactFlowApiRef.current.fitView({\n      nodes: [selectedNodeForActions],\n      padding: 0.14,\n      minZoom: Math.min(maxZoom, Math.max(minZoom, editingZoom)),\n      maxZoom,\n      duration: 250,\n    });\n  }, [selectedNodeForActions, reactFlowApiRef, minZoom, maxZoom]);\n\n  // Resolved reactively: in 'system' mode an OS theme change re-renders with\n  // the other class — no remount, matching React Flow's native colorMode.\n  const resolvedColorMode = useResolvedColorMode(colorMode as ColorMode);\n  const flowClassName = getReactFlowThemeClassName(resolvedColorMode, className);\n\n  const {\n    viewportReady, resolvedFitViewOptions, trackViewport, handleViewportMoveStart,\n    handleReactFlowInit, hasCaughtError, handleBoundaryError, handleBoundaryReset,\n  } = useDiagramViewport({\n    reactFlowNodes, latestNodesRef, reactFlowWrapper, reactFlowApiRef,\n    initialViewport, fitViewOnInit, fitViewOptions, minZoom, maxZoom,\n    onViewportReady, onInit, onError, onlyRenderVisibleElements,\n  });\n\n  const shouldHideUntilReady =\n    hideUntilViewportReady ?? initialViewport?.mode === \"fit\";\n\n  const wrapperStyle = getReactFlowWrapperStyle(colorMode as ColorMode, {\n    touchAction: \"none\", // Prevent default touch behaviors like scrolling\n    opacity: shouldHideUntilReady && !viewportReady ? 0 : 1,\n    transition: shouldHideUntilReady ? \"opacity 120ms ease-out\" : undefined,\n    ...style,\n  });\n  const connectHandler = externalOnConnect ?? handleConnect;\n  const edgeUpdateHandler = externalOnEdgeUpdate ?? handleEdgeUpdate;\n  const connectionValidator =\n    externalIsValidConnection ?? handleIsValidConnection;\n  const handleReactFlowNodesChange = useCallback(\n    (changes: NodeChange[]) => {\n      const isDimensionsOnly =\n        changes.length > 0 &&\n        changes.every((change) => change.type === \"dimensions\");\n      if (isReadOnly && !isDimensionsOnly) {\n        return;\n      }\n\n      const nextNodes = handleNodesChange(changes);\n      if (!nextNodes) {\n        return;\n      }\n\n      if (!isControlled) {\n        return;\n      }\n\n      // Measurement-only batches are transient view state: React Flow re-measures\n      // node DOM after every render and emits type:\"dimensions\" changes. In\n      // controlled mode, forwarding these to the parent turns each re-measure into a\n      // parent state update, which yields a new document/graph identity, re-renders,\n      // and re-measures -> infinite loop (/odf, and any controlled consumer such as\n      // /fibre-flow). handleNodesChange() above has ALREADY applied the measured\n      // sizes to React Flow's internal node state (setNodes + latestNodesRef), so\n      // rendering stays correct; we just don't propagate a non-edit upward. Any batch\n      // that also contains a position/add/remove/select change is a real edit and is\n      // still forwarded.\n      if (isDimensionsOnly) {\n        return;\n      }\n\n      externalOnNodesChange?.(nextNodes as NetworkNode[]);\n    },\n    [\n      isReadOnly,\n      handleNodesChange,\n      isControlled,\n      externalOnNodesChange,\n    ],\n  );\n\n  const handleReactFlowEdgesChange = useCallback(\n    (changes: EdgeChange[]) => {\n      if (isReadOnly) {\n        return;\n      }\n\n      // Filter out deletions of edges connected to tube handles (tube-* handles)\n      // But allow deletions for all other edges\n      const filteredChanges = changes.filter((change) => {\n        if (change.type === \"remove\") {\n          // Find the edge being removed\n          const edgeToRemove = latestEdgesRef.current.find(\n            (edge) => edge.id === change.id,\n          );\n          if (edgeToRemove) {\n            // Check if either handle is a tube handle (tube-*)\n            const sourceHandle = edgeToRemove.sourceHandle || \"\";\n            const targetHandle = edgeToRemove.targetHandle || \"\";\n            const isTubeHandle =\n              sourceHandle.startsWith(\"tube-\") ||\n              targetHandle.startsWith(\"tube-\");\n\n            if (isTubeHandle) {\n              if (debug) {\n                console.log(\n                  \"Edge deletion blocked: Edge connected to tube handle is not detachable\",\n                  edgeToRemove,\n                );\n              }\n              return false;\n            }\n          }\n        }\n        return true;\n      });\n\n      if (filteredChanges.length === 0) {\n        return;\n      }\n\n      const nextEdges = applyEdgeChanges(\n        filteredChanges,\n        latestEdgesRef.current,\n      );\n      onEdgesChange(filteredChanges as EdgeChange<NetworkFlowEdge>[]);\n      if (!isControlled) {\n        return;\n      }\n\n      externalOnEdgesChange?.(nextEdges as NetworkEdge[]);\n    },\n    [isReadOnly, latestEdgesRef, onEdgesChange, isControlled, externalOnEdgesChange, debug],\n  );\n\n  // Dev-only footgun #4: the wrapper is `width/height: 100%`, so a parent with\n  // no resolved height collapses the canvas to nothing and the diagram renders\n  // silently into a 0px box. Measure once after mount (layout is already\n  // flushed by the time effects run) and warn once per instance.\n  const zeroSizeWarnedRef = useRef(false);\n  useEffect(() => {\n    if (zeroSizeWarnedRef.current) return;\n    const wrapper = reactFlowWrapper.current;\n    if (!wrapper) return;\n    const { width, height } = wrapper.getBoundingClientRect();\n    if (width > 0 && height > 0) return;\n    zeroSizeWarnedRef.current = true;\n    warnDev(\n      `NetworkDiagram measured a zero-size container (${Math.round(width)}×${Math.round(\n        height,\n      )}px), so nothing will be visible. The diagram fills its parent ` +\n        `(width/height: 100%) — give the parent an explicit height, e.g. ` +\n        `<div style={{ width: \"100%\", height: \"600px\" }}><NetworkDiagram … /></div> ` +\n        `(see the README quick start). A flex/grid parent may also need ` +\n        `min-height: 0. Ignore this if the diagram is mounted inside a ` +\n        `deliberately hidden or collapsed container.`,\n    );\n  }, []);\n\n  const emptyStateContent =\n    emptyState === undefined ? DEFAULT_EMPTY_STATE : emptyState;\n  const isEmpty = emptyState !== false && nodesWithDiagramFocus.length === 0;\n\n  // Controlled consumers commonly render `nodes={[]}` for a beat while their\n  // data loads, and a message that flashes in and straight back out is worse\n  // than no message. Defer the overlay by a short delay and cancel it the\n  // moment nodes arrive. Consumers who know they are loading can pass\n  // `emptyState={false}` instead.\n  const [emptyStateDeferralElapsed, setEmptyStateDeferralElapsed] =\n    useState(false);\n  useEffect(() => {\n    if (!isEmpty) {\n      setEmptyStateDeferralElapsed(false);\n      return;\n    }\n    const timer = window.setTimeout(\n      () => setEmptyStateDeferralElapsed(true),\n      EMPTY_STATE_DELAY_MS,\n    );\n    return () => window.clearTimeout(timer);\n  }, [isEmpty]);\n\n  // Hidden during a capture: `toImage`/`toDataURL` should produce the diagram,\n  // not the \"no nodes\" chrome drawn on top of it.\n  const showEmptyState =\n    isEmpty && emptyStateDeferralElapsed && !isCapturingImage;\n\n  const resolvedWrapperStyle = hasCaughtError\n    ? { ...wrapperStyle, opacity: 1, transition: undefined }\n    : wrapperStyle;\n\n  return (\n    <div\n      ref={reactFlowWrapper}\n      className={\n        isCapturingImage\n          ? `${flowClassName} react-networks-capturing`\n          : flowClassName\n      }\n      style={resolvedWrapperStyle}\n    >\n      <DiagramRenderBoundary\n        enabled={errorBoundary}\n        fallback={errorFallback}\n        onError={handleBoundaryError}\n        onReset={handleBoundaryReset}\n      >\n      <DiagramViewportTracker onViewportChange={trackViewport} />\n      <DiagramMutationProvider value={diagramMutationValue}>\n        <RackDropTargetProvider value={rackDropTargetStore}>\n        <DiagramPortHighlightStoreProvider>\n        <DiagramPortProvider value={diagramPortContextValue}>\n          <DiagramPortHighlightSync highlightedPortHandles={highlightedPortHandles} />\n          <DiagramBayHighlightSync highlightedBays={highlightedBays} />\n          <DiagramZoomLod\n            wrapperRef={reactFlowWrapper}\n            wrapperClassName={flowClassName}\n            isCapturingImage={isCapturingImage}\n          />\n          <ReactFlow\n          nodes={nodesWithDiagramFocus as NetworkFlowNode[]}\n          edges={edgesForRender as NetworkFlowEdge[]}\n          onNodesChange={handleReactFlowNodesChange as OnNodesChange<NetworkFlowNode>}\n          onEdgesChange={handleReactFlowEdgesChange as OnEdgesChange<NetworkFlowEdge>}\n          colorMode={colorMode}\n          deleteKeyCode={\n            interactionConfig.allowKeyboardDelete ? [\"Backspace\", \"Delete\"] : null\n          }\n          onBeforeDelete={handleBeforeDelete}\n          onConnect={isReadOnly ? undefined : connectHandler}\n          onConnectStart={handleConnectStart}\n          onConnectEnd={handleConnectEnd}\n          onReconnect={isReadOnly ? undefined : edgeUpdateHandler}\n          onReconnectStart={handleEdgeUpdateStart}\n          onReconnectEnd={handleEdgeUpdateEnd}\n          isValidConnection={connectionValidator as IsValidConnection<NetworkFlowEdge>}\n          connectionMode={externalConnectionMode ?? ConnectionMode.Loose}\n          connectionRadius={connectionRadius}\n          onNodeClick={handleNodeClick}\n          onMoveStart={handleViewportMoveStart}\n          onNodeDoubleClick={handleNodeDoubleClick}\n          onPaneClick={handlePaneClick}\n          onNodeDragStart={\n            handleNodeDragStart as unknown as OnNodeDrag<NetworkFlowNode>\n          }\n          onNodeDrag={handleNodeDrag as unknown as OnNodeDrag<NetworkFlowNode>}\n          onNodeDragStop={\n            handleNodeDragStop as unknown as OnNodeDrag<NetworkFlowNode>\n          }\n          onEdgeClick={handleEdgeClick}\n          onInit={handleReactFlowInit as unknown as OnInit<NetworkFlowNode, NetworkFlowEdge>}\n          nodeTypes={mergedNodeTypes}\n          edgeTypes={mergedEdgeTypes}\n          defaultEdgeOptions={defaultEdgeOptions}\n          nodesDraggable={interactionConfig.nodesDraggable}\n          nodesConnectable={interactionConfig.nodesConnectable}\n          elementsSelectable={interactionConfig.elementsSelectable}\n          edgesFocusable={interactionConfig.edgesFocusable}\n          elevateNodesOnSelect={false}\n          selectNodesOnDrag={false}\n          selectionKeyCode=\"Shift\"\n          multiSelectionKeyCode={[\"Meta\", \"Control\"]}\n          connectOnClick={interactionConfig.connectOnClick}\n          panOnDrag={interactionConfig.panOnDrag}\n          panOnScroll={interactionConfig.panOnScroll}\n          minZoom={minZoom}\n          maxZoom={maxZoom}\n          zoomOnScroll={interactionConfig.zoomOnScroll}\n          zoomOnPinch={interactionConfig.zoomOnPinch}\n          zoomOnDoubleClick={interactionConfig.zoomOnDoubleClick}\n          // Only claim the wheel (blocking page scroll) when the diagram\n          // actually consumes it; otherwise let the page scroll past.\n          preventScrolling={\n            interactionConfig.panOnScroll || interactionConfig.zoomOnScroll\n          }\n          onlyRenderVisibleElements={\n            onlyRenderVisibleElements && !isCapturingImage\n          }\n          // The init fit is library-managed (see useDiagramViewport): React\n          // Flow's native one-shot fit can resolve before every node is\n          // measured and frame a single device.\n          fitView={false}\n          fitViewOptions={resolvedFitViewOptions}\n          proOptions={proOptions}\n        >\n          <Background\n            color={getReactFlowBackgroundColor(resolvedColorMode)}\n          />\n          {showEmptyState && (\n            // Rendered as a React Flow child so it is positioned against the\n            // flow container, and kept `pointer-events: none` (see\n            // styles/index.css) so an empty canvas is still a drop target and\n            // still pans.\n            <div\n              className=\"react-networks-empty-state\"\n              data-testid=\"diagram-empty-state\"\n            >\n              {emptyStateContent}\n            </div>\n          )}\n          {interactionConfig.showControls && (\n            <Controls\n              showInteractive={interactionConfig.showInteractiveControl}\n              fitViewOptions={resolvedFitViewOptions}\n            />\n          )}\n          {interactionConfig.showMiniMap && <MiniMap />}\n          <Panel position=\"bottom-right\" className=\"react-networks-attribution\">\n            <a\n              href=\"https://github.com/MP70/react-networks\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              className=\"react-networks-attribution-link\"\n            >\n              react-networks\n            </a>\n          </Panel>\n        </ReactFlow>\n        {showDownloadButton && (\n          <button\n            className=\"download-button\"\n            onClick={onDownload}\n            disabled={isDownloading}\n            aria-busy={isDownloading}\n            style={{\n              position: \"absolute\",\n              top: downloadButtonPosition.includes(\"top\") ? \"10px\" : \"auto\",\n              bottom: downloadButtonPosition.includes(\"bottom\")\n                ? \"10px\"\n                : \"auto\",\n              right: downloadButtonPosition.includes(\"right\") ? \"10px\" : \"auto\",\n              left: downloadButtonPosition.includes(\"left\") ? \"10px\" : \"auto\",\n              zIndex: 1000,\n              background: \"#374151\",\n              color: \"#e5e7eb\",\n              border: \"1px solid #4b5563\",\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              padding: \"0.5rem 0.75rem\",\n              fontSize: \"0.875rem\",\n              cursor: isDownloading ? \"not-allowed\" : \"pointer\",\n              opacity: isDownloading ? 0.7 : 1,\n              transition: \"all 0.2s ease\",\n              ...downloadButtonStyle,\n            }}\n          >\n            {isDownloading ? `${downloadButtonText}...` : downloadButtonText}\n          </button>\n        )}\n        {selectedNodeForActions && selectedActionButtons.count > 0 && (\n          <DiagramActionPanel\n            node={selectedNodeForActions}\n            buttons={selectedActionButtons}\n            wrapperRef={reactFlowWrapper}\n            getAbsoluteNodePosition={getAbsoluteNodePosition}\n            onToggleFace={toggleSelectedFace}\n            onDeRack={deRackSelectedDevice}\n            onZoomToEdit={zoomToEditSelectedRack}\n          />\n        )}\n      </DiagramPortProvider>\n        </DiagramPortHighlightStoreProvider>\n        </RackDropTargetProvider>\n      </DiagramMutationProvider>\n      </DiagramRenderBoundary>\n    </div>\n  );\n};\n\n/**\n * Main component for rendering interactive network diagrams.\n *\n * The NetworkDiagram component provides a complete solution for visualizing\n * network infrastructure including racks, devices, fiber connections, and power distribution.\n *\n * @example\n * ```tsx\n * import { NetworkDiagram, NetworkNode, NetworkEdge } from 'react-networks';\n *\n * const nodes: NetworkNode[] = [\n *   {\n *     id: 'rack-1',\n *     type: 'rack',\n *     position: { x: 100, y: 100 },\n *     data: { label: 'Main Rack', uHeight: 42 }\n *   }\n * ];\n *\n * <NetworkDiagram nodes={nodes} edges={[]} />\n * ```\n *\n * ## Empty and error states\n *\n * - `emptyState` — what to show inside the canvas when there are no nodes.\n *   Defaults to a small centred, themed \"No nodes to display\" message; pass\n *   your own node, or `false` for a bare grid. The built-in overlay is\n *   `pointer-events: none`, so dropping onto an empty canvas still works. It\n *   only appears once the node count has been zero for ~150ms, so a controlled\n *   diagram passing `nodes={[]}` while it loads never flashes the message —\n *   and `emptyState={false}` is the escape hatch to suppress it outright while\n *   your own loading UI is on screen.\n * - `errorBoundary` / `errorFallback` / `onError` — the React Flow subtree is\n *   wrapped in `DiagramErrorBoundary` by default, so a throwing custom node\n *   type renders a themed fallback inside the canvas instead of unmounting the\n *   host page. `errorFallback` replaces that fallback (a function receives\n *   `(error, reset)`), `onError` reports the error, and `errorBoundary={false}`\n *   opts out entirely so errors reach your own boundary. Note the default is\n *   `true`, so errors no longer reach a boundary you wrap around the diagram —\n *   pass `onError` to keep your existing error reporting.\n *\n * In development the diagram also warns once (through `warnDev`) if its\n * container measures 0px in either axis — the usual cause of a diagram that\n * renders \"nothing\" is a parent without an explicit height.\n *\n * @example\n * ```tsx\n * <NetworkDiagram\n *   nodes={nodes}\n *   edges={edges}\n *   emptyState={<p>Nothing here yet — drag a device in.</p>}\n *   onError={(error) => reportToSentry(error)}\n * />\n * ```\n *\n * @public\n */\nexport const NetworkDiagram: React.FC<NetworkDiagramProps> = (props) => {\n  return (\n    <ReactFlowProvider>\n      <NetworkDiagramDocumentBridge {...props} Inner={NetworkDiagramInner} />\n    </ReactFlowProvider>\n  );\n};\n","import { baseColorFor, fibreSolidOrStriped, getFibreColor, isStriped } from './fibre-colors';\nimport type { FibreFlowCable, FibreFlowClosure, FibreFlowEndpoint } from '../types/fibre-flow';\n\n/** Hard ceiling on fibres drawn inside a single (real or virtual) tube. */\nexport const MAX_FIBERS_PER_TUBE = 24;\n/** Hard ceiling on tubes drawn for a single cable. */\nexport const MAX_TUBES = 24;\n/** Default fibres per virtual tube when a flat `fiberCount` is auto-grouped. */\nexport const DEFAULT_FIBERS_PER_TUBE = 12;\n\nexport interface FibreFlowRow {\n  key: string;\n  tubeIndex?: number;\n  /**\n   * Index of the group (real tube, or virtual tube for auto-grouped flat\n   * cables) this row belongs to. Always set by\n   * `buildFibreFlowCableLayout` — unlike `tubeIndex`, which stays\n   * `undefined` for `simple` cables so handle ids remain unchanged. Optional so\n   * hand-built layouts predating this field still type-check; consumers should\n   * read it as `groupIndex ?? tubeIndex ?? 1`.\n   */\n  groupIndex?: number;\n  fiberIndex: number;\n  y: number;\n  color: string;\n  striped: boolean;\n  backgroundImage?: string;\n}\n\nexport interface FibreFlowTubeLabel {\n  tubeIndex: number;\n  y: number;\n  label: string;\n  color: string;\n}\n\n/**\n * A collapsed placeholder standing in for strands hidden by\n * `LayoutOptions.usedKeys`. One per group that lost at least one row.\n */\nexport interface FibreFlowTubeIndicator {\n  groupIndex: number;\n  y: number;\n  hiddenCount: number;\n  totalCount: number;\n  color: string;\n  label: string;\n}\n\nexport interface FibreFlowCableLayout {\n  rows: FibreFlowRow[];\n  tubeLabels: FibreFlowTubeLabel[];\n  /**\n   * Collapsed \"N of M unused\" placeholders. Always emitted by\n   * `buildFibreFlowCableLayout` (empty when nothing is hidden); optional\n   * so hand-built layouts from before `hideUnusedFibres` still type-check.\n   */\n  indicators?: FibreFlowTubeIndicator[];\n  height: number;\n}\n\ninterface LayoutOptions {\n  rowHeight: number;\n  tubeGap: number;\n  /**\n   * When provided, only strands whose `\"<tube>-<fiber>\"` key is in this set are\n   * laid out; the rest collapse into per-group indicator rows. Build it with\n   * {@link computeUsedFibreStrands}.\n   */\n  usedKeys?: ReadonlySet<string>;\n}\n\n/** Canonical strand key. Mirrors `buildFibreHandleId(fiber, tube ?? 1)`. */\nexport const fibreStrandKey = (tube: number | undefined, fiber: number): string =>\n  `${tube ?? 1}-${fiber}`;\n\n/**\n * Collect, per cable id, the strands referenced by any splice in any closure.\n *\n * Cables are shared between adjacent closures through `leftCable`/`rightCable`,\n * so the set must be global: a pass-through strand spliced only in the\n * neighbouring closure is still \"used\" on this one.\n */\nexport function computeUsedFibreStrands(\n  closures: readonly FibreFlowClosure[]\n): Map<string, Set<string>> {\n  const used = new Map<string, Set<string>>();\n  const add = (cableId: string, endpoint: FibreFlowEndpoint) => {\n    let set = used.get(cableId);\n    if (!set) {\n      set = new Set<string>();\n      used.set(cableId, set);\n    }\n    set.add(fibreStrandKey(endpoint.tube, endpoint.fiber));\n  };\n\n  closures.forEach((closure) => {\n    if (!used.has(closure.leftCable.id)) used.set(closure.leftCable.id, new Set());\n    if (!used.has(closure.rightCable.id)) used.set(closure.rightCable.id, new Set());\n    closure.splices.forEach((splice) => {\n      add(closure.leftCable.id, splice.from);\n      add(closure.rightCable.id, splice.to);\n    });\n  });\n\n  return used;\n}\n\nconst emitClampWarning = (cable: FibreFlowCable, what: string, requested: number, applied: number) => {\n  // eslint-disable-next-line no-console\n  console.warn(\n    `[react-networks] FibreFlowMap: cable \"${cable.id}\" ${what} ${requested} exceeds the ` +\n      `supported maximum ${applied}; rendering ${applied}.`\n  );\n};\n\ninterface Grouping {\n  tubeCount: number;\n  fibersPerTube: number;\n  /** Real tubes get a `tubeIndex` on each row; virtual tubes do not. */\n  realTubes: boolean;\n  /** Fibre numbering restarts per tube (real) or runs continuously (virtual). */\n  continuousFiberNumbers: boolean;\n}\n\nfunction resolveGrouping(cable: FibreFlowCable, warn = true): Grouping {\n  const warnClamp = warn ? emitClampWarning : () => undefined;\n  const kind = cable.kind ?? (cable.tubeCount ? 'tube' : 'simple');\n\n  if (kind === 'tube') {\n    const requestedTubes = Math.max(1, cable.tubeCount ?? 1);\n    const tubeCount = Math.min(MAX_TUBES, requestedTubes);\n    if (requestedTubes > tubeCount) warnClamp(cable, 'tubeCount', requestedTubes, tubeCount);\n\n    const requestedPerTube = Math.max(1, cable.fibersPerTube ?? DEFAULT_FIBERS_PER_TUBE);\n    const fibersPerTube = Math.min(MAX_FIBERS_PER_TUBE, requestedPerTube);\n    if (requestedPerTube > fibersPerTube) {\n      warnClamp(cable, 'fibersPerTube', requestedPerTube, fibersPerTube);\n    }\n\n    return { tubeCount, fibersPerTube, realTubes: true, continuousFiberNumbers: false };\n  }\n\n  const fiberCount = Math.max(1, cable.fiberCount ?? DEFAULT_FIBERS_PER_TUBE);\n  if (fiberCount <= MAX_FIBERS_PER_TUBE) {\n    return {\n      tubeCount: 1,\n      fibersPerTube: fiberCount,\n      realTubes: false,\n      continuousFiberNumbers: true,\n    };\n  }\n\n  // Flat counts above one tube's worth are auto-grouped into virtual tubes\n  // rather than silently truncated.\n  const requestedPerTube = Math.max(1, cable.fibersPerTube ?? DEFAULT_FIBERS_PER_TUBE);\n  const fibersPerTube = Math.min(MAX_FIBERS_PER_TUBE, requestedPerTube);\n  if (requestedPerTube > fibersPerTube) {\n    warnClamp(cable, 'fibersPerTube', requestedPerTube, fibersPerTube);\n  }\n\n  const requestedTubes = Math.ceil(fiberCount / fibersPerTube);\n  const tubeCount = Math.min(MAX_TUBES, requestedTubes);\n  if (requestedTubes > tubeCount) {\n    warnClamp(cable, `fiberCount ${fiberCount} needs ${requestedTubes} tubes which`, requestedTubes, tubeCount);\n  }\n\n  return { tubeCount, fibersPerTube, realTubes: false, continuousFiberNumbers: true };\n}\n\nfunction colorIndexFor(\n  grouping: Pick<Grouping, 'realTubes' | 'tubeCount' | 'fibersPerTube'>,\n  fiberStartIndex: number,\n  fiberIndex: number,\n): number {\n  if (grouping.realTubes || grouping.tubeCount === 1) {\n    return fiberIndex;\n  }\n  const offset = fiberIndex - fiberStartIndex;\n  return offset < 0 ? fiberIndex : (offset % grouping.fibersPerTube) + 1;\n}\n\n/**\n * The TIA-598 colour position (1-24) a strand is painted with in\n * {@link buildFibreFlowCableLayout}. Real tubes number fibres per tube, so the\n * fibre number is the colour position; auto-grouped flat cables number fibres\n * continuously but restart the colour sequence in every virtual tube. Edge\n * painting must use this rather than the raw fibre number, or a splice edge\n * and the fibre handle it lands on disagree (fibre 13 of a flat 48F cable is a\n * solid blue handle, not a striped one).\n */\nexport function getFibreFlowStrandColorIndex(\n  cable: FibreFlowCable,\n  fiber: number,\n): number {\n  const fiberStartIndex = Math.max(1, cable.fiberStartIndex ?? 1);\n  return colorIndexFor(resolveGrouping(cable, false), fiberStartIndex, fiber);\n}\n\nexport function buildFibreFlowCableLayout(\n  cable: FibreFlowCable,\n  { rowHeight, tubeGap, usedKeys }: LayoutOptions\n): FibreFlowCableLayout & { indicators: FibreFlowTubeIndicator[] } {\n  const rows: FibreFlowRow[] = [];\n  const tubeLabels: FibreFlowTubeLabel[] = [];\n  const indicators: FibreFlowTubeIndicator[] = [];\n\n  const { tubeCount, fibersPerTube, realTubes, continuousFiberNumbers } = resolveGrouping(cable);\n  const tubeStartIndex = Math.max(1, cable.tubeStartIndex ?? 1);\n  const fiberStartIndex = Math.max(1, cable.fiberStartIndex ?? 1);\n  const totalFibers =\n    continuousFiberNumbers && cable.fiberCount\n      ? Math.max(1, cable.fiberCount)\n      : tubeCount * fibersPerTube;\n\n  let currentY = 0;\n\n  for (let tubeOffset = 0; tubeOffset < tubeCount; tubeOffset += 1) {\n    const tubeIndex = tubeStartIndex + tubeOffset;\n    const tubeColor = cable.tubeColorMap?.[tubeIndex] ?? baseColorFor(tubeIndex);\n    const tubeTop = currentY;\n\n    let visibleInTube = 0;\n    let totalInTube = 0;\n\n    for (let fiberOffset = 0; fiberOffset < fibersPerTube; fiberOffset += 1) {\n      const fiberIndex = continuousFiberNumbers\n        ? fiberStartIndex + tubeOffset * fibersPerTube + fiberOffset\n        : fiberStartIndex + fiberOffset;\n\n      // A short last virtual tube (e.g. 30 fibres = 12 + 12 + 6).\n      if (continuousFiberNumbers && fiberIndex > fiberStartIndex + totalFibers - 1) break;\n\n      totalInTube += 1;\n\n      const rowTubeIndex = realTubes ? tubeIndex : undefined;\n      if (usedKeys && !usedKeys.has(fibreStrandKey(rowTubeIndex, fiberIndex))) {\n        continue;\n      }\n\n      // Colour/striping follow the position *within the group*: a real tube\n      // restarts numbering already, and an auto-grouped flat cable must reset\n      // per virtual tube so fibre 1 of every tube is blue. The continuous\n      // `fiberIndex` still drives labels and handle ids.\n      const colorIndex = colorIndexFor(\n        { realTubes, tubeCount, fibersPerTube },\n        fiberStartIndex,\n        fiberIndex,\n      );\n\n      const style = fibreSolidOrStriped(colorIndex);\n      rows.push({\n        key: `${tubeIndex}-${fiberIndex}`,\n        tubeIndex: rowTubeIndex,\n        groupIndex: tubeIndex,\n        fiberIndex,\n        y: currentY,\n        color: getFibreColor(colorIndex),\n        striped: isStriped(colorIndex),\n        backgroundImage: typeof style.backgroundImage === 'string' ? style.backgroundImage : undefined,\n      });\n      currentY += rowHeight;\n      visibleInTube += 1;\n    }\n\n    if (totalInTube === 0) {\n      continue;\n    }\n\n    const hiddenCount = totalInTube - visibleInTube;\n    if (hiddenCount > 0) {\n      indicators.push({\n        groupIndex: tubeIndex,\n        y: currentY,\n        hiddenCount,\n        totalCount: totalInTube,\n        color: tubeColor,\n        label:\n          tubeCount > 1\n            ? `T${tubeIndex} · ${hiddenCount} of ${totalInTube} unused`\n            : `· ${hiddenCount} of ${totalInTube} unused`,\n      });\n      currentY += rowHeight;\n    }\n\n    tubeLabels.push({\n      tubeIndex,\n      y: tubeTop,\n      label: `T${tubeIndex}`,\n      color: tubeColor,\n    });\n\n    if (tubeOffset < tubeCount - 1) {\n      currentY += tubeGap;\n    }\n  }\n\n  return {\n    rows,\n    tubeLabels,\n    indicators,\n    height: currentY,\n  };\n}\n","import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n  addEdge,\n  BaseEdge,\n  Connection,\n  ConnectionMode,\n  Edge,\n  EdgeLabelRenderer,\n  EdgeProps,\n  Handle,\n  Node,\n  NodeProps,\n  Position,\n  getBezierPath,\n} from '@xyflow/react';\nimport { NetworkDiagram } from './NetworkDiagram';\nimport type {\n  FibreFlowCableNodeData as BaseFibreFlowCableNodeData,\n  FibreFlowClosureNodeData,\n  NetworkDiagramFitViewOptions,\n  NetworkDiagramFlowMethods,\n  NetworkDiagramInteractionOptions,\n  NetworkDiagramToImageOptions,\n  NetworkEdge,\n  NetworkNode,\n} from '../types';\nimport type {\n  FibreFlowCable,\n  FibreFlowCircuit,\n  FibreFlowClosure,\n  FibreFlowEndpoint,\n} from '../types/fibre-flow';\nimport {\n  buildFibreFlowCableLayout,\n  computeUsedFibreStrands,\n  FibreFlowCableLayout,\n  getFibreFlowStrandColorIndex,\n} from '../utils/fibre-flow-layout';\nimport { fibreTracerColorFor, getFibreColor, isStriped } from '../utils/fibre-colors';\nimport { buildFibreHandleId, parseHandleId } from '../utils/handle-ids';\nimport { buildPortAriaLabel } from '../utils/a11y';\n\nexport type FibreFlowLayoutDirection = 'horizontal' | 'vertical';\nexport type FibreFlowColorMode = 'dark' | 'light';\n\ntype FibreFlowCableNodeData = Omit<BaseFibreFlowCableNodeData, 'layout'> & {\n  layout: FibreFlowCableLayout;\n};\n\ninterface FibreFlowEdgeData {\n  [key: string]: unknown;\n  label?: string;\n  lossDb?: number;\n  color?: string;\n  sourceColor?: string; // Color from source fiber handle\n  targetColor?: string; // Color from target fiber handle\n  sourceStriped?: boolean;\n  targetStriped?: boolean;\n  highlighted?: boolean;\n  dimmed?: boolean;\n  circuitId?: string;\n  circuitLabel?: string;\n  showCircuitId?: boolean;\n}\n\ninterface FibreFlowLinkEdgeData {\n  [key: string]: unknown;\n  label?: string;\n  color?: string;\n}\n\ntype FibreFlowMapNode =\n  | NetworkNode<'fibre-flow-cable', FibreFlowCableNodeData>\n  | NetworkNode<'fibre-flow-closure', FibreFlowClosureNodeData>;\ntype FibreFlowMapEdgeType = 'fibre-flow' | 'fibre-flow-link';\ntype FibreFlowMapEdge = Omit<NetworkEdge<FibreFlowEdgeData | FibreFlowLinkEdgeData>, 'type'> & {\n  type: FibreFlowMapEdgeType;\n};\n\n/**\n * Frozen module-level empties used as the default `closures`/`circuits` props.\n * Sharing one stable identity means consumers can omit these props (or pass\n * `x ?? []`) without every render minting a fresh array that trips the reset\n * effect. Frozen so an accidental in-place mutation fails loudly in dev.\n */\nconst EMPTY_CLOSURES: FibreFlowClosure[] = [];\nObject.freeze(EMPTY_CLOSURES);\nconst EMPTY_CIRCUITS: FibreFlowCircuit[] = [];\nObject.freeze(EMPTY_CIRCUITS);\n\n\n/**\n * Per-instance uid context. Each mounted FibreFlowMap provides a unique id\n * (React.useId) so the document-global SVG element ids it renders (gradients,\n * edge paths, stripe overlays) cannot collide when two maps show the same\n * closure on one page. `userSpaceOnUse` gradients are especially collision\n * sensitive: a duplicate id makes an edge paint with the wrong instance's\n * coordinates. Defaults to '' (no namespace) for standalone edge rendering.\n */\nexport const FibreFlowInstanceContext = React.createContext<string>('');\n\n/**\n * Build a per-instance-namespaced SVG element id for a fibre-flow edge path.\n * Deterministic within an instance (same uid + edgeId always yields the same\n * id) and unique across instances (distinct uid). An empty uid leaves the id\n * un-namespaced so the raw edge id is preserved for standalone rendering.\n */\nexport const buildFibreFlowElementId = (\n  uid: string,\n  edgeId: string,\n  suffix?: string\n): string => {\n  const base = uid ? `${uid}-${edgeId}` : edgeId;\n  return suffix ? `${base}-${suffix}` : base;\n};\n\n/** Build a per-instance-namespaced linear-gradient id for a fibre-flow edge. */\nexport const buildFibreFlowGradientId = (uid: string, edgeId: string): string =>\n  uid ? `fibre-flow-gradient-${uid}-${edgeId}` : `fibre-flow-gradient-${edgeId}`;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n  return typeof value === 'object' && value !== null;\n};\n\nconst isFibreFlowNode = (node: NetworkNode): node is FibreFlowMapNode => {\n  if (!isRecord(node.data)) {\n    return false;\n  }\n\n  if (node.type === 'fibre-flow-cable') {\n    const data = node.data as Partial<FibreFlowCableNodeData>;\n    return (\n      typeof data.label === 'string' &&\n      isRecord(data.cable) &&\n      (data.side === 'left' || data.side === 'right') &&\n      typeof data.rowHeight === 'number' &&\n      typeof data.columnWidth === 'number' &&\n      typeof data.showTubeLabels === 'boolean' &&\n      typeof data.showFiberNumbers === 'boolean' &&\n      isRecord(data.layout)\n    );\n  }\n\n  if (node.type === 'fibre-flow-closure') {\n    const data = node.data as Partial<FibreFlowClosureNodeData>;\n    return (\n      typeof data.label === 'string' &&\n      typeof data.width === 'number' &&\n      typeof data.height === 'number' &&\n      typeof data.headerHeight === 'number'\n    );\n  }\n\n  return false;\n};\n\nconst isFibreFlowEdgeType = (\n  edgeType: NetworkEdge['type'] | undefined\n): edgeType is FibreFlowMapEdgeType => {\n  return edgeType === 'fibre-flow' || edgeType === 'fibre-flow-link';\n};\n\nconst hasEdgeEndpoints = (\n  edge: NetworkEdge\n): edge is NetworkEdge & { id: string; source: string; target: string } => {\n  return (\n    typeof edge.id === 'string' &&\n    edge.id.length > 0 &&\n    typeof edge.source === 'string' &&\n    edge.source.length > 0 &&\n    typeof edge.target === 'string' &&\n    edge.target.length > 0\n  );\n};\n\nconst inferFibreFlowEdgeType = (edge: NetworkEdge): FibreFlowMapEdgeType => {\n  if (edge.sourceHandle === 'cable-run' || edge.targetHandle === 'cable-run') {\n    return 'fibre-flow-link';\n  }\n  return 'fibre-flow';\n};\n\nconst isFibreFlowFibreHandle = (handle?: string | null) =>\n  typeof handle === 'string' && handle.startsWith('fiber-');\n\nconst edgeUsesHandle = (edge: Edge, nodeId?: string | null, handleId?: string | null) =>\n  (edge.source === nodeId && edge.sourceHandle === handleId) ||\n  (edge.target === nodeId && edge.targetHandle === handleId);\n\nexport const normalizeFibreFlowEdges = (\n  nextEdges: NetworkEdge[],\n  previousEdges: FibreFlowMapEdge[] = []\n): FibreFlowMapEdge[] => {\n  const previousById = new Map(previousEdges.map((edge) => [edge.id, edge] as const));\n  const normalized: FibreFlowMapEdge[] = [];\n\n  nextEdges.forEach((edge) => {\n    if (!hasEdgeEndpoints(edge)) {\n      return;\n    }\n\n    const previous = previousById.get(edge.id);\n    const edgeType = isFibreFlowEdgeType(edge.type)\n      ? edge.type\n      : previous?.type ?? inferFibreFlowEdgeType(edge);\n\n    normalized.push({\n      ...(previous ?? {}),\n      ...edge,\n      type: edgeType,\n    });\n  });\n\n  return normalized;\n};\n\n/**\n * Props for {@link FibreFlowMap}.\n *\n * `FibreFlowMap` is a **viewer by default**: `readOnly` defaults to `true`, so\n * connect / reconnect / delete gestures are inert unless you explicitly pass\n * `readOnly={false}`. Even with editing enabled the edit state is **ephemeral**\n * — edits mutate internal component state only and the component exposes no edit\n * callback, so they are lost on the next layout rebuild unless a consumer\n * observes them. Treat `readOnly={false}` as a preview affordance, not a\n * persistence path.\n *\n * Referential stability of `closures`, `circuits`, and `highlightedCircuitId`\n * is a **perf nicety, not a correctness requirement**. Internal node/edge\n * (layout and edit) state is reset only when the *content* actually changes —\n * the reset effect compares ordered ids (plus the layout-affecting scalar\n * props), so passing a freshly-built but value-equal array (`x ?? []`,\n * `closures.map(...)` that yields the same ids) is safe and preserves view/edit\n * state. Memoizing still avoids wasted layout work (a dev-mode warning flags the\n * value-equal-but-new-identity case). For full control over when resets happen,\n * pass {@link FibreFlowMapProps.resetKey}.\n */\nexport interface FibreFlowMapProps {\n  /**\n   * Closures to render. May be omitted (defaults to an empty diagram).\n   * Referential stability is optional — a new but value-equal array (same\n   * ordered closure ids) does not reset internal state.\n   */\n  closures?: FibreFlowClosure[];\n  /**\n   * Circuits used for edge colouring/labels. Referential stability is optional\n   * — a new but value-equal array (same ordered circuit ids) does not reset\n   * internal state.\n   */\n  circuits?: FibreFlowCircuit[];\n  /**\n   * Circuit to highlight (others are dimmed). Changing it re-derives the edge\n   * colouring and resets internal state; a value-equal value does not.\n   */\n  highlightedCircuitId?: string;\n  layout?: FibreFlowLayoutDirection;\n  rowHeight?: number;\n  tubeGap?: number;\n  closureGap?: number;\n  cableColumnWidth?: number;\n  spliceAreaWidth?: number;\n  showTubeLabels?: boolean;\n  showFiberNumbers?: boolean;\n  colorMode?: FibreFlowColorMode;\n  showCircuitId?: boolean;\n  /**\n   * When `true`, strands that carry nothing are removed from the drawing.\n   *\n   * A strand `(tube, fiber)` on a cable counts as **used** when any splice in\n   * any closure of this diagram references it on that cable. Cables are shared\n   * between adjacent closures via `leftCable`/`rightCable`, so the set is\n   * computed globally — a pass-through strand spliced only in the neighbouring\n   * closure stays visible here.\n   *\n   * Hidden strands are never dropped silently: each tube that lost rows renders\n   * a slim collapsed indicator (`· 10 of 12 unused`), and a fully unused tube\n   * collapses to that indicator alone. Handles remain keyed by `(tube, fiber)`,\n   * so splice edges resolve unchanged and the prop can be toggled at runtime.\n   *\n   * Hidden strands mount **no handles**, so a new splice cannot be drawn to or\n   * from them while they are collapsed. Prefer this prop for read-only viewing;\n   * leave it `false` (or toggle it off) when `readOnly={false}` editing is in\n   * use.\n   *\n   * @defaultValue false\n   */\n  hideUnusedFibres?: boolean;\n  className?: string;\n  style?: React.CSSProperties;\n  /**\n   * When `true` (the **default**) the map is a read-only viewer: connect,\n   * reconnect and delete gestures are inert. Pass `readOnly={false}` to enable\n   * editing, but note edit state is ephemeral (no edit callback is exposed).\n   */\n  readOnly?: boolean;\n  interaction?: NetworkDiagramInteractionOptions;\n  /**\n   * Opt-in explicit reset control. When provided, internal node/edge state is\n   * reset **only** when `resetKey` changes — never on `closures`/`circuits`\n   * identity or content changes. Use this when you drive edits and want to keep\n   * transient state across data updates, bumping the key yourself to force a\n   * rebuild (e.g. on an explicit \"reload\" action). When omitted, resets are\n   * content-driven (ordered ids + layout props).\n   */\n  resetKey?: string | number;\n  /**\n   * Callback invoked with imperative flow methods (export / fit / read) once\n   * they are available, mirroring `NetworkDiagram`'s `onFlowMethods`. Lets\n   * closure diagrams join the same PNG/jsPDF export pipeline as `NetworkDiagram`\n   * (`toImage`) and re-fit imperatively. See {@link FibreFlowMapFlowMethods}.\n   */\n  onFlowMethods?: (methods: FibreFlowMapFlowMethods) => void;\n}\n\n/**\n * Imperative flow methods surfaced by {@link FibreFlowMap} through the\n * `onFlowMethods` callback.\n *\n * @remarks\n * This is the {@link FibreFlowMap} counterpart to `NetworkDiagram`'s\n * `NetworkDiagramFlowMethods`, exposing the read/export-oriented subset that\n * closure diagrams need to join the same export pipeline as `NetworkDiagram`\n * (for example a shared \"Download PNG\" / jsPDF report path). The method\n * semantics and option types are identical to the corresponding\n * `NetworkDiagramFlowMethods` members — `toImage` clamps its options to safe\n * bounds and lazy-loads `html-to-image`, and `fitView` accepts node-id targets.\n *\n * Reach for it when you need to trigger an export or re-fit imperatively from a\n * parent (a toolbar button, a PDF export routine) rather than declaratively via\n * props.\n *\n * @example\n * ```tsx\n * const methodsRef = useRef<FibreFlowMapFlowMethods>();\n * <FibreFlowMap\n *   closures={closures}\n *   onFlowMethods={(methods) => { methodsRef.current = methods; }}\n * />;\n * // later, e.g. in a \"Download\" button handler:\n * const pngDataUrl = await methodsRef.current?.toImage({ pixelRatio: 2 });\n * ```\n *\n * @public\n */\nexport interface FibreFlowMapFlowMethods {\n  /** Export the current diagram to a PNG data URL. Options are clamped to safe bounds. */\n  toImage: (options?: NetworkDiagramToImageOptions) => Promise<string>;\n  /** Fit the viewport to the diagram, optionally targeting specific node ids. */\n  fitView: (options?: NetworkDiagramFitViewOptions) => void;\n  /** Get the current React Flow nodes. */\n  getNodes: () => NetworkNode[];\n  /** Get the current React Flow edges. */\n  getEdges: () => NetworkEdge[];\n}\n\nconst handleIdForEndpoint = (endpoint: FibreFlowEndpoint): string =>\n  buildFibreHandleId(endpoint.fiber, endpoint.tube ?? 1);\n\n/**\n * Paint for a splice endpoint, taken from the same colour position the cable\n * layout paints that strand's handle with (see\n * `getFibreFlowStrandColorIndex`) so the edge and the handle it lands on\n * always agree — including auto-grouped flat cables, whose colour sequence\n * restarts in every virtual tube.\n */\nconst getEndpointPaint = (\n  cable: FibreFlowCable,\n  endpoint: FibreFlowEndpoint\n): { color: string; striped: boolean } => {\n  const colorIndex = getFibreFlowStrandColorIndex(cable, endpoint.fiber);\n  return { color: getFibreColor(colorIndex), striped: isStriped(colorIndex) };\n};\n\nconst formatDistanceLabel = (distanceMeters?: number, distanceLabel?: string): string | undefined => {\n  if (distanceLabel) return distanceLabel;\n  if (typeof distanceMeters !== 'number' || Number.isNaN(distanceMeters)) return undefined;\n  if (distanceMeters >= 1000) {\n    return `${(distanceMeters / 1000).toFixed(1)} km`;\n  }\n  return `${Math.round(distanceMeters)} m`;\n};\n\nexport const buildFibreFlowStripeDasharray = (\n  needsStripeFirstHalf: boolean,\n  needsStripeSecondHalf: boolean,\n  firstHalfDashPattern: string,\n  secondHalfDashPattern: string,\n  halfLength: number,\n  approximatePathLength: number\n): string => {\n  if (needsStripeFirstHalf) {\n    return `${firstHalfDashPattern} ${approximatePathLength}`;\n  }\n\n  if (needsStripeSecondHalf) {\n    // Leading 0 creates an initial gap so the solid first half is not overpainted by stripe stroke.\n    return `0 ${halfLength} ${secondHalfDashPattern} ${approximatePathLength}`;\n  }\n\n  return '6 6';\n};\n\nexport const FibreFlowEdge: React.FC<EdgeProps<Edge<FibreFlowEdgeData>>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  data,\n  interactionWidth,\n}) => {\n  const uid = React.useContext(FibreFlowInstanceContext);\n  const [edgePath, labelX, labelY] = getBezierPath({\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    sourcePosition,\n    targetPosition,\n    curvature: 0.35,\n  });\n\n  const lossText = typeof data?.lossDb === 'number' ? `${data.lossDb.toFixed(2)} dB` : '';\n  const labelText = data?.label ?? lossText;\n  const sourceColor = data?.sourceColor;\n  const targetColor = data?.targetColor;\n  const edgeColor = data?.color ?? sourceColor ?? targetColor ?? '#94a3b8';\n  const sourceStriped = data?.sourceStriped ?? false;\n  const targetStriped = data?.targetStriped ?? false;\n  const isHighlighted = data?.highlighted ?? true;\n  const isDimmed = data?.dimmed ?? false;\n  const circuitId = data?.circuitId;\n  const circuitLabel = data?.circuitLabel ?? circuitId; // Use friendly label if available, fallback to ID\n  const showCircuitId = data?.showCircuitId ?? false;\n\n  // Check if we need a gradient (source and target colors are different)\n  const needsGradient = sourceColor && targetColor && sourceColor !== targetColor;\n  \n  // Calculate gradient stop position based on label position\n  // Use straight-line distance approximation for bezier path\n  const dx = targetX - sourceX;\n  const dy = targetY - sourceY;\n  const totalDistance = Math.sqrt(dx * dx + dy * dy);\n  const labelDx = labelX - sourceX;\n  const labelDy = labelY - sourceY;\n  const labelDistance = Math.sqrt(labelDx * labelDx + labelDy * labelDy);\n  const gradientStopPercent = totalDistance > 0 ? (labelDistance / totalDistance) * 100 : 50;\n  const gradientId = buildFibreFlowGradientId(uid, id);\n  const pathId = buildFibreFlowElementId(uid, id);\n  const stripePathId = buildFibreFlowElementId(uid, id, 'stripe');\n\n  // Determine stripe rendering: only on the half corresponding to the striped fibre\n  const needsStripeFirstHalf = sourceStriped && !targetStriped;\n  const needsStripeSecondHalf = targetStriped && !sourceStriped;\n  const needsStripeFull = sourceStriped && targetStriped;\n  \n  // Calculate approximate path length for dash array positioning\n  // Bezier curves are typically 10-30% longer than straight-line distance\n  // Use a conservative estimate of 1.25x for better coverage\n  const approximatePathLength = totalDistance * 1.25;\n  const halfLength = approximatePathLength / 2;\n  \n  // Calculate dash pattern for half-edge rendering\n  // Each dash-gap pair is 12px (6px dash + 6px gap)\n  const dashGapPair = 12;\n  // Add extra pairs to ensure full coverage of the half (20% buffer)\n  const numPairsWithBuffer = Math.ceil((halfLength * 1.2) / dashGapPair);\n  const firstHalfDashPattern = Array(numPairsWithBuffer).fill('6 6').join(' ');\n  // For second half, ensure we have enough to fully cover it (20% buffer)\n  const secondHalfNumPairs = Math.ceil((halfLength * 1.2) / dashGapPair);\n  const secondHalfDashPattern = Array(secondHalfNumPairs).fill('6 6').join(' ');\n\n  // Calculate label dimensions based on content\n  const hasCircuitId = showCircuitId && circuitLabel;\n  const labelWidth = Math.max(\n    labelText ? labelText.length * 6.2 + 16 : 0,\n    hasCircuitId ? (circuitLabel?.length || 0) * 5.5 + 16 : 0\n  );\n  const labelHeight = hasCircuitId ? 32 : 16;\n\n  return (\n    <>\n      <defs>\n        {needsGradient && (\n          <linearGradient \n            id={gradientId} \n            x1={sourceX} \n            y1={sourceY} \n            x2={targetX} \n            y2={targetY}\n            gradientUnits=\"userSpaceOnUse\"\n          >\n            <stop offset=\"0%\" stopColor={sourceColor} />\n            <stop offset={`${gradientStopPercent}%`} stopColor={sourceColor} />\n            <stop offset={`${gradientStopPercent}%`} stopColor={targetColor} />\n            <stop offset=\"100%\" stopColor={targetColor} />\n          </linearGradient>\n        )}\n      </defs>\n      {needsGradient ? (\n        <path\n          id={pathId}\n          d={edgePath}\n          fill=\"none\"\n          stroke={`url(#${gradientId})`}\n          strokeWidth={isHighlighted ? 4.0 : 3.0}\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          opacity={isDimmed ? 0.25 : 1.0}\n          strokeOpacity={isDimmed ? 0.25 : 1.0}\n          style={{\n            mixBlendMode: 'normal',\n          }}\n        />\n      ) : (\n        <BaseEdge\n          id={pathId}\n          path={edgePath}\n          interactionWidth={interactionWidth}\n          style={{\n            stroke: edgeColor,\n            strokeWidth: isHighlighted ? 4.0 : 3.0, // Increased for better visibility and solid appearance\n            strokeLinecap: 'round',\n            strokeLinejoin: 'round',\n            opacity: isDimmed ? 0.25 : 1.0, // Full opacity for highlighted, increased for dimmed\n            strokeOpacity: isDimmed ? 0.25 : 1.0, // SVG-specific opacity for better rendering\n            mixBlendMode: 'normal', // Ensure normal blending mode\n          }}\n        />\n      )}\n      {/* Stripe overlay - conditionally rendered on appropriate half */}\n      {(needsStripeFull || needsStripeFirstHalf || needsStripeSecondHalf) && (\n        <path\n          id={stripePathId}\n          d={edgePath}\n          fill=\"none\"\n          stroke={fibreTracerColorFor(\n            needsStripeSecondHalf ? targetColor ?? edgeColor : sourceColor ?? edgeColor\n          )}\n          strokeWidth={isHighlighted ? 1.6 : 1.3}\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n          strokeDasharray={\n            buildFibreFlowStripeDasharray(\n              needsStripeFirstHalf,\n              needsStripeSecondHalf,\n              firstHalfDashPattern,\n              secondHalfDashPattern,\n              halfLength,\n              approximatePathLength\n            )\n          }\n          strokeDashoffset={0}\n          opacity={isDimmed ? 0.2 : 0.9}\n          style={{\n            mixBlendMode: 'normal',\n          }}\n        />\n      )}\n      {(labelText || hasCircuitId) && isHighlighted && (\n        <EdgeLabelRenderer>\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n              width: labelWidth,\n              height: labelHeight,\n              background: 'rgba(248, 250, 252, 0.92)',\n              border: '1px solid rgba(15, 23, 42, 0.4)',\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: 10,\n              fontFamily: 'monospace',\n              color: 'rgba(15, 23, 42, 0.9)',\n              display: 'flex',\n              flexDirection: 'column',\n              alignItems: 'center',\n              justifyContent: 'center',\n              pointerEvents: 'none',\n              padding: '2px 4px',\n              zIndex: 9999, // Ensure labels are on top of fibre edges\n            }}\n          >\n            {labelText && (\n              <div style={{ lineHeight: 1.2 }}>\n                {labelText}\n              </div>\n            )}\n            {hasCircuitId && (\n              <div style={{ \n                fontSize: 8, \n                opacity: 0.8, \n                lineHeight: 1.2,\n                marginTop: labelText ? 2 : 0,\n              }}>\n                {circuitLabel}\n              </div>\n            )}\n          </div>\n        </EdgeLabelRenderer>\n      )}\n    </>\n  );\n};\n\nconst FibreFlowCableLinkEdge: React.FC<EdgeProps<Edge<FibreFlowLinkEdgeData>>> = ({\n  id,\n  sourceX,\n  sourceY,\n  targetX,\n  targetY,\n  sourcePosition,\n  targetPosition,\n  data,\n  interactionWidth,\n}) => {\n  const uid = React.useContext(FibreFlowInstanceContext);\n  const [edgePath, labelX, labelY] = getBezierPath({\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    sourcePosition,\n    targetPosition,\n    curvature: 0.2,\n  });\n\n  const labelText = data?.label;\n  const edgeColor = data?.color ?? '#64748b';\n\n  return (\n    <>\n      <BaseEdge\n        id={buildFibreFlowElementId(uid, id)}\n        path={edgePath}\n        interactionWidth={interactionWidth}\n        style={{\n          stroke: edgeColor,\n          strokeWidth: 3.0, // Increased for better visibility\n          opacity: 0.85, // Increased opacity\n          strokeDasharray: '8 10',\n        }}\n      />\n      {labelText && (\n        <EdgeLabelRenderer>\n          <div\n            style={{\n              position: 'absolute',\n              transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n              padding: '2px 8px',\n              background: 'rgba(30, 41, 59, 0.9)',\n              border: `1px solid ${edgeColor}`,\n              borderRadius: 'var(--rn-ui-radius, 1px)',\n              fontSize: 10,\n              fontFamily: 'monospace',\n              color: '#e2e8f0',\n              letterSpacing: 0.2,\n              whiteSpace: 'nowrap',\n              pointerEvents: 'none',\n              zIndex: 9999, // Ensure labels are on top of cable link edges\n            }}\n          >\n            {labelText}\n          </div>\n        </EdgeLabelRenderer>\n      )}\n    </>\n  );\n};\n\nconst FibreFlowCableNode: React.FC<NodeProps<Node<FibreFlowCableNodeData & Record<string, unknown>>>> = ({ data }) => {\n  const {\n    cable,\n    side,\n    rowHeight,\n    columnWidth,\n    showTubeLabels,\n    showFiberNumbers,\n    layout,\n    colorMode = 'dark',\n  } = data;\n  \n  const isLightMode = colorMode === 'light';\n\n  const jacketWidth = 8;\n  const handleSize = 12;\n  const labelOffset = -1; // Reduced to move numbers closer to fibres\n  const panelInset = 0;\n  const jacketColor = cable.jacketColor ?? '#facc15';\n  const hasTubes = (cable.kind ?? (cable.tubeCount ? 'tube' : 'simple')) === 'tube';\n  const effectiveShowTubeLabels = showTubeLabels && hasTubes;\n  // Auto-grouped flat cables have no real tubes but do have >1 layout group;\n  // they still get tube segments and borders so the groups read apart.\n  const hasGroups = hasTubes || layout.tubeLabels.length > 1;\n\n  // Vertical extent of each tube group, including its collapsed \"unused\"\n  // indicator row so tube borders still wrap everything drawn for that tube\n  // when `hideUnusedFibres` removed strands.\n  const tubeExtentFor = (tubeIndex: number): { top: number; height: number } | null => {\n    const tubeRows = layout.rows.filter((row) => (row.groupIndex ?? row.tubeIndex ?? 1) === tubeIndex);\n    const indicator = (layout.indicators ?? []).find((item) => item.groupIndex === tubeIndex);\n    if (tubeRows.length === 0 && !indicator) return null;\n    const tops: number[] = tubeRows.map((row) => row.y);\n    if (indicator) tops.push(indicator.y);\n    const top = Math.min(...tops);\n    const bottom = Math.max(...tops) + rowHeight;\n    return { top, height: bottom - top };\n  };\n\n  const panelLeft = side === 'left' ? jacketWidth + panelInset : panelInset;\n  const panelRight = side === 'right' ? jacketWidth + panelInset : panelInset;\n  // Simple: LEFT cable = Position.Right (handles on right edge), RIGHT cable = Position.Left (handles on left edge)\n  const handlePosition = side === 'left' ? Position.Right : Position.Left;\n\n  const cableRunHandleStyle: React.CSSProperties = {\n    width: 4,\n    height: 4,\n    borderRadius: '50%',\n    background: jacketColor,\n    border: `1px solid ${jacketColor}`,\n    boxShadow: '0 0 1px rgba(0,0,0,0.4)',\n    top: '50%',\n    position: 'absolute',\n    ...(side === 'left'\n      ? { left: 0, transform: 'translate(-50%, -50%)' }\n      : { right: 0, transform: 'translate(50%, -50%)' }),\n  };\n\n  return (\n    <div\n      style={{\n        position: 'relative',\n        width: columnWidth,\n        height: layout.height,\n        fontFamily: 'monospace',\n        color: 'inherit',\n        overflow: 'visible',\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          top: -18,\n          left: side === 'left' ? 0 : undefined,\n          right: side === 'right' ? 0 : undefined,\n          fontSize: 10,\n          fontWeight: 700,\n          letterSpacing: 0.4,\n          textTransform: 'uppercase',\n          background: isLightMode ? 'rgba(248, 250, 252, 0.95)' : 'rgba(15, 23, 42, 0.9)',\n          color: isLightMode ? '#1a1a1a' : '#f8fafc',\n          padding: '2px 6px',\n          borderRadius: 'var(--rn-ui-radius, 1px)',\n          whiteSpace: 'nowrap',\n          maxWidth: columnWidth,\n          overflow: 'hidden',\n          textOverflow: 'ellipsis',\n          zIndex: 10,\n          ...(side === 'right' ? { \n            textAlign: 'right',\n          } : {}),\n        }}\n      >\n        {cable.label}\n      </div>\n\n      {/* Cable jacket container */}\n      <div\n        style={{\n          position: 'absolute',\n          top: 0,\n          bottom: 0,\n          width: jacketWidth,\n          left: side === 'left' ? 0 : undefined,\n          right: side === 'right' ? 0 : undefined,\n          borderRadius: 4,\n          background: hasGroups ? 'transparent' : jacketColor,\n          border: `1px solid ${jacketColor}`,\n          boxShadow: hasGroups ? 'none' : 'inset 0 0 3px rgba(0,0,0,0.3)',\n          overflow: 'hidden',\n        }}\n      >\n        {/* Individual tube segments within the cable jacket */}\n        {hasGroups && layout.tubeLabels.map((tube, tubeIdx) => {\n          // Extent covers visible fibres plus any collapsed indicator row.\n          const extent = tubeExtentFor(tube.tubeIndex);\n          if (!extent) return null;\n\n          const tubeTop = extent.top;\n          const tubeHeight = extent.height;\n          const tubeBottom = tubeTop + tubeHeight;\n\n          // Check if there's a gap after this tube (for tubeGap)\n          const nextTube = layout.tubeLabels[tubeIdx + 1];\n          const hasGapAfter = nextTube && (nextTube.y - tubeBottom) > 0;\n          \n          return (\n            <div\n              key={`tube-segment-${tube.tubeIndex}`}\n              style={{\n                position: 'absolute',\n                top: tubeTop,\n                left: 0,\n                right: 0,\n                height: tubeHeight,\n                background: tube.color,\n                borderLeft: `2px solid ${tube.color}`,\n                borderRight: `2px solid ${tube.color}`,\n                borderTop: tubeIdx === 0 ? `2px solid ${tube.color}` : '1px solid rgba(0,0,0,0.15)',\n                borderBottom: hasGapAfter ? '1px solid rgba(0,0,0,0.15)' : `2px solid ${tube.color}`,\n                boxShadow: 'inset 0 0 3px rgba(0,0,0,0.1)',\n                borderRadius: tubeIdx === 0 && !hasGapAfter ? '4px 4px 0 0' :\n                             tubeIdx === 0 ? '4px 4px 0 0' :\n                             !hasGapAfter ? '0 0 4px 4px' : '0',\n                zIndex: 2,\n              }}\n            />\n          );\n        })}\n      </div>\n\n      {/* Panel border - removed background to prevent edge overlay effect */}\n      {/* Only show border for visual separation without background overlay */}\n      {!effectiveShowTubeLabels && (\n        <div\n          style={{\n            position: 'absolute',\n            top: 0,\n            bottom: 0,\n            left: panelLeft,\n            right: panelRight,\n            // Only border on the fiber side (opposite to jacket)\n            // For left cable: border on right side, for right cable: border on left side\n            ...(side === 'left' \n              ? { borderRight: `1px solid ${jacketColor}` }\n              : { borderLeft: `1px solid ${jacketColor}` }\n            ),\n            pointerEvents: 'none',\n            zIndex: 0,\n          }}\n        />\n      )}\n      \n      {/* Tube color borders wrapping around fibers with lower opacity */}\n      {hasGroups && layout.tubeLabels.map((tube, _tubeIdx) => {\n        const extent = tubeExtentFor(tube.tubeIndex);\n        if (!extent) return null;\n\n        const tubeTop = extent.top;\n        const tubeHeight = extent.height;\n\n        return (\n          <div\n            key={`tube-border-${tube.tubeIndex}`}\n            style={{\n              position: 'absolute',\n              top: tubeTop,\n              left: panelLeft,\n              right: panelRight,\n              height: tubeHeight,\n              // Border on the outside edge where fibers connect\n              // For left cable: border on right side (outside), for right cable: border on left side (outside)\n              ...(side === 'left' \n                ? { borderRight: `2px solid ${tube.color}` }\n                : { borderLeft: `2px solid ${tube.color}` }\n              ),\n              opacity: 0.5,\n              pointerEvents: 'none',\n            }}\n          />\n        );\n      })}\n\n      {/* Tube labels removed - not needed, saves space */}\n\n      {/* Collapsed placeholders for strands removed by `hideUnusedFibres`. */}\n      {(layout.indicators ?? []).map((indicator) => (\n        <div\n          key={`fibre-indicator-${indicator.groupIndex}`}\n          className=\"fibre-flow-unused-indicator\"\n          data-group-index={indicator.groupIndex}\n          data-hidden-count={indicator.hiddenCount}\n          style={{\n            position: 'absolute',\n            top: indicator.y,\n            height: rowHeight,\n            left: panelLeft,\n            right: panelRight,\n            display: 'flex',\n            alignItems: 'center',\n            justifyContent: side === 'left' ? 'flex-end' : 'flex-start',\n            fontSize: 7,\n            lineHeight: 1,\n            whiteSpace: 'nowrap',\n            color: isLightMode ? 'rgba(71, 85, 105, 0.75)' : 'rgba(148, 163, 184, 0.75)',\n            pointerEvents: 'none',\n            zIndex: 3,\n          }}\n          title={`${indicator.hiddenCount} of ${indicator.totalCount} fibres unused`}\n        >\n          {indicator.label}\n        </div>\n      ))}\n\n      {layout.rows.map((row) => {\n        const handleId = buildFibreHandleId(row.fiberIndex, row.tubeIndex ?? 1);\n        // Real tubes name the tube; simple and auto-grouped flat cables have\n        // no tube of their own, so the strand is named by its cable.\n        const fibreGroupName =\n          row.tubeIndex !== undefined ? `tube ${row.tubeIndex}` : cable.label;\n        const top = row.y + rowHeight / 2;\n        \n        // Handle style - position on edge like CouplerNode pattern\n        const handleStyle: React.CSSProperties & { '--rn-fibre-bg'?: string; '--rn-fibre-bg-image'?: string } = {\n          position: 'absolute',\n          width: handleSize,\n          height: handleSize,\n          borderRadius: '50%',\n          top: top,\n          background: row.backgroundImage ? row.backgroundImage : row.color,\n          backgroundColor: row.color,\n          border: `2px solid ${row.color}`,\n          boxShadow: '0 0 6px rgba(0,0,0,0.3)',\n          // Position handle 50% over edge (centered on border) - exactly like CouplerNode\n          ...(side === 'left' \n            ? { right: -handleSize / 2 }  // Right edge - handle center at edge, half outside\n            : { left: -handleSize / 2 }   // Left edge - handle center at edge, half outside\n          ),\n          transform: 'translateY(-50%)',\n          zIndex: 1000,\n          cursor: 'crosshair',\n          '--rn-fibre-bg': row.color,\n          minWidth: handleSize,\n          minHeight: handleSize,\n        };\n\n        if (row.backgroundImage) {\n          handleStyle['--rn-fibre-bg-image'] = row.backgroundImage;\n        }\n\n        // Position numbers inside the panel and anchor them to the fiber edge.\n        // For left cables: handles are on the right edge, numbers should be to the left of handles\n        // For right cables: handles are on the left edge, numbers should be to the right of handles\n        // Handle is centered on edge (half outside, half inside), so position number at handleSize + offset\n        const numberPosition: React.CSSProperties =\n          side === 'left'\n            ? { right: handleSize + labelOffset, textAlign: 'right' }\n            : { left: handleSize + labelOffset, textAlign: 'left' };\n\n        return (\n          <React.Fragment key={row.key}>\n            <Handle\n              id={handleId}\n              type=\"source\"\n              position={handlePosition}\n              aria-label={buildPortAriaLabel({\n                kind: 'Fibre',\n                group: fibreGroupName,\n                label: row.fiberIndex,\n                status: ['output'],\n              })}\n              role=\"img\"\n              className=\"fibre-flow-handle\"\n              style={handleStyle}\n              isConnectable={true}\n              data-fiber-idx={row.fiberIndex}\n              data-tube-idx={row.tubeIndex}\n              data-fiber-color={row.color}\n            />\n            <Handle\n              id={handleId}\n              type=\"target\"\n              position={handlePosition}\n              aria-label={buildPortAriaLabel({\n                kind: 'Fibre',\n                group: fibreGroupName,\n                label: row.fiberIndex,\n                status: ['input'],\n              })}\n              role=\"img\"\n              className=\"fibre-flow-handle\"\n              style={handleStyle}\n              isConnectable={true}\n              data-fiber-idx={row.fiberIndex}\n              data-tube-idx={row.tubeIndex}\n              data-fiber-color={row.color}\n            />\n            {showFiberNumbers && (\n              <div\n                style={{\n                  position: 'absolute',\n                  top: row.y + rowHeight / 2 - 6,\n                  ...numberPosition,\n                  fontSize: 8,\n                  fontWeight: 600,\n                  color: isLightMode ? 'rgba(15, 23, 42, 0.7)' : 'rgba(148, 163, 184, 0.95)',\n                  whiteSpace: 'nowrap',\n                  lineHeight: 1,\n                }}\n              >\n                {row.fiberIndex}\n              </div>\n            )}\n          </React.Fragment>\n        );\n      })}\n\n      <Handle\n        id=\"cable-run\"\n        type=\"source\"\n        position={side === 'left' ? Position.Left : Position.Right}\n        aria-label={buildPortAriaLabel({ kind: 'Cable run', status: [side, 'output'] })}\n        role=\"img\"\n        className=\"cable-run-handle\"\n        style={cableRunHandleStyle}\n        isConnectable={true}\n        data-handle-type=\"cable-run\"\n      />\n      <Handle\n        id=\"cable-run\"\n        type=\"target\"\n        position={side === 'left' ? Position.Left : Position.Right}\n        aria-label={buildPortAriaLabel({ kind: 'Cable run', status: [side, 'input'] })}\n        role=\"img\"\n        className=\"cable-run-handle\"\n        style={cableRunHandleStyle}\n        isConnectable={true}\n        data-handle-type=\"cable-run\"\n      />\n    </div>\n  );\n};\n\nconst FibreFlowClosureNode: React.FC<NodeProps<Node<FibreFlowClosureNodeData & Record<string, unknown>>>> = ({ data }) => {\n  const isLightMode = data.colorMode === 'light';\n  return (\n    <div\n      style={{\n        width: data.width,\n        height: data.height,\n        // Keep the body transparent so fibre edges stay fully saturated.\n        background: 'transparent',\n        border: isLightMode \n          ? '1px solid rgba(148, 163, 184, 0.3)'\n          : '1px solid rgba(148, 163, 184, 0.5)',\n        borderRadius: 'var(--rn-ui-radius, 1px)',\n        boxShadow: isLightMode\n          ? '0 12px 24px rgba(0, 0, 0, 0.1)'\n          : '0 12px 24px rgba(15, 23, 42, 0.2)',\n        position: 'relative',\n        pointerEvents: 'none',\n      }}\n    >\n      <div\n        style={{\n          height: data.headerHeight,\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          fontFamily: 'monospace',\n          fontWeight: 700,\n          fontSize: 12,\n          letterSpacing: 0.6,\n          textTransform: 'uppercase',\n          color: isLightMode ? 'rgba(15, 23, 42, 0.9)' : 'rgba(226, 232, 240, 0.9)',\n          background: isLightMode ? 'rgba(241, 245, 249, 0.7)' : 'rgba(15, 23, 42, 0.6)',\n          borderBottom: isLightMode \n            ? '1px solid rgba(148, 163, 184, 0.2)'\n            : '1px solid rgba(148, 163, 184, 0.35)',\n          borderTopLeftRadius: 'var(--rn-ui-radius, 1px)',\n          borderTopRightRadius: 'var(--rn-ui-radius, 1px)',\n        }}\n      >\n        {data.label}\n      </div>\n    </div>\n  );\n};\n\nconst buildFlowNodes = (\n  closures: FibreFlowClosure[],\n  layout: FibreFlowLayoutDirection,\n  rowHeight: number,\n  tubeGap: number,\n  closureGap: number,\n  cableColumnWidth: number,\n  spliceAreaWidth: number,\n  showTubeLabels: boolean,\n  showFiberNumbers: boolean,\n  colorMode: FibreFlowColorMode = 'dark',\n  hideUnusedFibres: boolean = false\n): FibreFlowMapNode[] => {\n  const nodes: FibreFlowMapNode[] = [];\n\n  // Used strands are computed across *all* closures: cables are shared between\n  // neighbours, so a pass-through strand spliced next door must stay visible.\n  const usedByCable = hideUnusedFibres ? computeUsedFibreStrands(closures) : undefined;\n\n  let cursor = 0;\n\n  closures.forEach((closure) => {\n    const leftLayout = buildFibreFlowCableLayout(closure.leftCable, {\n      rowHeight,\n      tubeGap,\n      usedKeys: usedByCable?.get(closure.leftCable.id) ?? (usedByCable ? new Set<string>() : undefined),\n    });\n    const rightLayout = buildFibreFlowCableLayout(closure.rightCable, {\n      rowHeight,\n      tubeGap,\n      usedKeys:\n        usedByCable?.get(closure.rightCable.id) ?? (usedByCable ? new Set<string>() : undefined),\n    });\n\n    const bodyHeight = Math.max(leftLayout.height, rightLayout.height);\n    const bodyWidth = cableColumnWidth * 2 + spliceAreaWidth;\n    const headerHeight = 32;\n    const padding = 16;\n    const closureWidth = bodyWidth + padding * 2;\n    const closureHeight = bodyHeight + headerHeight + padding * 2;\n\n    const closureId = `flow-closure-${closure.id}`;\n    const leftCableId = `flow-cable-${closure.id}-left`;\n    const rightCableId = `flow-cable-${closure.id}-right`;\n\n    const position =\n      layout === 'horizontal'\n        ? { x: cursor, y: 0 }\n        : { x: 0, y: cursor };\n\n    cursor += (layout === 'horizontal' ? closureWidth : closureHeight) + closureGap;\n\n    const headerLabel = closure.chamberId ? `${closure.id} - ${closure.chamberId}` : closure.id;\n\n    nodes.push({\n      id: closureId,\n      type: 'fibre-flow-closure',\n      position,\n      data: {\n        label: headerLabel,\n        width: closureWidth,\n        height: closureHeight,\n        headerHeight,\n        colorMode,\n      },\n      selectable: false,\n      draggable: false,\n      style: {\n        width: closureWidth,\n        height: closureHeight,\n      },\n    });\n\n    const leftOffsetY = headerHeight + padding + (bodyHeight - leftLayout.height) / 2;\n    const rightOffsetY = headerHeight + padding + (bodyHeight - rightLayout.height) / 2;\n\n    nodes.push({\n      id: leftCableId,\n      type: 'fibre-flow-cable',\n      position: { x: padding, y: leftOffsetY },\n      parentId: closureId,\n      extent: 'parent',\n      data: {\n        label: closure.leftCable.label,\n        cable: closure.leftCable,\n        side: 'left',\n        rowHeight,\n        columnWidth: cableColumnWidth,\n        showTubeLabels,\n        showFiberNumbers,\n        layout: leftLayout,\n        colorMode,\n      },\n      style: {\n        width: cableColumnWidth,\n        height: leftLayout.height,\n      },\n    });\n\n    nodes.push({\n      id: rightCableId,\n      type: 'fibre-flow-cable',\n      position: { x: padding + bodyWidth - cableColumnWidth, y: rightOffsetY },\n      parentId: closureId,\n      extent: 'parent',\n      data: {\n        label: closure.rightCable.label,\n        cable: closure.rightCable,\n        side: 'right',\n        rowHeight,\n        columnWidth: cableColumnWidth,\n        showTubeLabels,\n        showFiberNumbers,\n        layout: rightLayout,\n        colorMode,\n      },\n      style: {\n        width: cableColumnWidth,\n        height: rightLayout.height,\n      },\n    });\n\n  });\n\n  return nodes;\n};\n\nexport const buildFlowEdges = (\n  closures: FibreFlowClosure[],\n  circuits: FibreFlowCircuit[] | undefined,\n  highlightedCircuitId: string | undefined,\n  showCircuitId: boolean = false\n): FibreFlowMapEdge[] => {\n  const edges: FibreFlowMapEdge[] = [];\n  const circuitColorMap = new Map<string, string>();\n  const circuitLabelMap = new Map<string, string>();\n  const lastCableById = new Map<\n    string,\n    {\n      nodeId: string;\n      closureId: string;\n      jacketColor?: string;\n      distanceLabel?: string;\n      distanceMeters?: number;\n    }\n  >();\n\n  (circuits ?? []).forEach((circuit) => {\n    if (circuit.color) {\n      circuitColorMap.set(circuit.id, circuit.color);\n    }\n    if (circuit.label) {\n      circuitLabelMap.set(circuit.id, circuit.label);\n    }\n  });\n\n  closures.forEach((closure) => {\n    const leftCableId = `flow-cable-${closure.id}-left`;\n    const rightCableId = `flow-cable-${closure.id}-right`;\n    const cables = [\n      { nodeId: leftCableId, cable: closure.leftCable },\n      { nodeId: rightCableId, cable: closure.rightCable },\n    ];\n\n    cables.forEach(({ nodeId, cable }) => {\n      const previous = lastCableById.get(cable.id);\n      if (previous && previous.closureId !== closure.id) {\n        const label = formatDistanceLabel(\n          cable.distanceMeters ?? previous.distanceMeters,\n          cable.distanceLabel ?? previous.distanceLabel\n        );\n        const color = cable.jacketColor ?? previous.jacketColor ?? '#64748b';\n\n        edges.push({\n          id: `flow-link-${previous.nodeId}-${nodeId}`,\n          type: 'fibre-flow-link',\n          source: previous.nodeId,\n          target: nodeId,\n          sourceHandle: 'cable-run',\n          targetHandle: 'cable-run',\n          data: {\n            label,\n            color,\n          },\n        });\n      }\n\n      lastCableById.set(cable.id, {\n        nodeId,\n        closureId: closure.id,\n        jacketColor: cable.jacketColor,\n        distanceLabel: cable.distanceLabel,\n        distanceMeters: cable.distanceMeters,\n      });\n    });\n\n    closure.splices.forEach((splice) => {\n      const isHighlighted = highlightedCircuitId\n        ? splice.circuitId === highlightedCircuitId\n        : true;\n      const isDimmed = Boolean(highlightedCircuitId) && !isHighlighted;\n      const circuitColor = splice.circuitId ? circuitColorMap.get(splice.circuitId) : undefined;\n      const circuitLabel = splice.circuitId ? circuitLabelMap.get(splice.circuitId) : undefined;\n      const edgeColor = splice.color ?? circuitColor;\n      \n      // Get source and target fiber colors\n      const { color: sourceColor, striped: sourceStriped } = getEndpointPaint(\n        closure.leftCable,\n        splice.from\n      );\n      const { color: targetColor, striped: targetStriped } = getEndpointPaint(\n        closure.rightCable,\n        splice.to\n      );\n\n      edges.push({\n        id: `flow-edge-${closure.id}-${splice.id}`,\n        type: 'fibre-flow',\n        source: leftCableId,\n        target: rightCableId,\n        sourceHandle: handleIdForEndpoint(splice.from),\n        targetHandle: handleIdForEndpoint(splice.to),\n        data: {\n          lossDb: splice.lossDb,\n          label: splice.label,\n          color: edgeColor,\n          sourceColor,\n          targetColor,\n          sourceStriped,\n          targetStriped,\n          highlighted: isHighlighted,\n          dimmed: isDimmed,\n          circuitId: splice.circuitId,\n          circuitLabel,\n          showCircuitId,\n        },\n      });\n    });\n  });\n\n  return edges;\n};\n\nconst FibreFlowMapInner: React.FC<FibreFlowMapProps> = ({\n  closures = EMPTY_CLOSURES,\n  circuits = EMPTY_CIRCUITS,\n  highlightedCircuitId,\n  layout = 'horizontal',\n  rowHeight = 20,\n  tubeGap = 10,\n  closureGap = 140,\n  cableColumnWidth = 32, // Reduced slightly: 8px jacket + 24px panel (handle 12px + number space ~12px)\n  spliceAreaWidth = 260,\n  showTubeLabels = true,\n  showFiberNumbers = true,\n  colorMode = 'dark',\n  showCircuitId = false,\n  hideUnusedFibres = false,\n  className,\n  style,\n  readOnly = true,\n  interaction,\n  resetKey,\n  onFlowMethods,\n}) => {\n  // Per-instance id namespace so this map's SVG element ids (gradients, edge\n  // paths, stripe overlays) cannot collide with a sibling map rendering the\n  // same closure. Sanitised so it is safe inside CSS/url(#...) references.\n  const uid = React.useId().replace(/[^a-zA-Z0-9_-]/g, '');\n\n  const layoutNodes = useMemo(\n    () =>\n      buildFlowNodes(\n        closures,\n        layout,\n        rowHeight,\n        tubeGap,\n        closureGap,\n        cableColumnWidth,\n        spliceAreaWidth,\n        showTubeLabels,\n        showFiberNumbers,\n        colorMode,\n        hideUnusedFibres\n      ),\n    [\n      closures,\n      layout,\n      rowHeight,\n      tubeGap,\n      closureGap,\n      cableColumnWidth,\n      spliceAreaWidth,\n      showTubeLabels,\n      showFiberNumbers,\n      colorMode,\n      hideUnusedFibres,\n    ]\n  );\n\n  const layoutEdges = useMemo(\n    () => buildFlowEdges(closures, circuits, highlightedCircuitId, showCircuitId),\n    [closures, circuits, highlightedCircuitId, showCircuitId]\n  );\n\n  const [nodes, setNodes] = useState(layoutNodes);\n  const [edges, setEdges] = useState(layoutEdges);\n\n  // Content signature for the reset decision. Built from the *derived* layout:\n  // ordered node ids + positions + data, and ordered edge ids + handles + data\n  // (together these encode closures, cables, splices, splice endpoints, circuit\n  // assignment, colours and dimming), plus the circuit ids and every\n  // layout-affecting scalar prop. Any render-relevant content change — an\n  // added/removed splice, a re-pointed splice's endpoints, a re-assigned\n  // circuitId, a recoloured highlight — changes the signature and resets\n  // internal state, while a fresh-but-value-equal array leaves it untouched.\n  // `resetKey`, when provided, overrides the signature entirely.\n  const resetSignature = useMemo(\n    () => {\n      // The explicit key is the sole reset trigger. Avoid serializing every\n      // node and edge on prop changes when that work cannot affect the result.\n      if (resetKey !== undefined) return '';\n      return [\n        layoutNodes\n          .map(\n            (node) =>\n              `${node.id}@${node.position.x},${node.position.y}#${JSON.stringify(node.data ?? null)}`\n          )\n          .join(','),\n        layoutEdges\n          .map(\n            (edge) =>\n              `${edge.id}~${edge.sourceHandle ?? ''}>${edge.targetHandle ?? ''}#${JSON.stringify(edge.data ?? null)}`\n          )\n          .join(','),\n        circuits.map((circuit) => circuit.id).join(','),\n        highlightedCircuitId ?? '',\n        layout,\n        rowHeight,\n        tubeGap,\n        closureGap,\n        cableColumnWidth,\n        spliceAreaWidth,\n        showTubeLabels,\n        showFiberNumbers,\n        colorMode,\n        showCircuitId,\n      ].join('|');\n    },\n    [\n      resetKey,\n      layoutNodes,\n      layoutEdges,\n      circuits,\n      highlightedCircuitId,\n      layout,\n      rowHeight,\n      tubeGap,\n      closureGap,\n      cableColumnWidth,\n      spliceAreaWidth,\n      showTubeLabels,\n      showFiberNumbers,\n      colorMode,\n      showCircuitId,\n    ]\n  );\n\n  // Refs seeded from the first render so the mount pass never triggers a\n  // redundant reset (useState already holds the initial layout).\n  const prevResetSignatureRef = useRef(resetSignature);\n  const prevResetKeyRef = useRef(resetKey);\n  const prevClosuresRef = useRef(closures);\n  const prevCircuitsRef = useRef(circuits);\n  const wastedAllocWarnedRef = useRef(false);\n\n  useEffect(() => {\n    // Dev-only wasted-allocation hint: a *new* array identity carrying the same\n    // ordered ids means the parent rebuilt the prop without a content change.\n    // Referential stability is no longer required for correctness, so this is a\n    // pure perf nudge — warn once, and stay silent in production.\n    if (\n      process.env.NODE_ENV !== 'production' &&\n      !wastedAllocWarnedRef.current &&\n      (closures !== prevClosuresRef.current ||\n        circuits !== prevCircuitsRef.current) &&\n      prevResetSignatureRef.current === resetSignature\n    ) {\n      wastedAllocWarnedRef.current = true;\n      console.warn(\n        '[react-networks] FibreFlowMap received a new `closures`/`circuits` array ' +\n          'with identical content. Referential stability is not required for ' +\n          'correctness, but memoizing these props (e.g. useMemo) avoids wasted ' +\n          'layout work. Pass `resetKey` to control resets explicitly.'\n      );\n    }\n\n    // When `resetKey` is provided it is the sole reset trigger (content changes\n    // are ignored); otherwise reset when the content signature changes.\n    const shouldReset =\n      resetKey !== undefined\n        ? prevResetKeyRef.current !== resetKey\n        : prevResetSignatureRef.current !== resetSignature;\n\n    if (shouldReset) {\n      setNodes(layoutNodes);\n      setEdges(layoutEdges);\n    }\n\n    prevResetSignatureRef.current = resetSignature;\n    prevResetKeyRef.current = resetKey;\n    prevClosuresRef.current = closures;\n    prevCircuitsRef.current = circuits;\n  }, [closures, circuits, resetKey, resetSignature, layoutNodes, layoutEdges]);\n\n  // Get fiber info (color and striped status) from a handle\n  const getHandleFibreInfo = useCallback(\n    (nodeId?: string | null, handleId?: string | null): { color: string; striped: boolean } | undefined => {\n      if (!nodeId || !handleId || !handleId.startsWith('fiber-')) {\n        return undefined;\n      }\n      const node = nodes.find((n) => n.id === nodeId);\n      if (!node || node.type !== 'fibre-flow-cable') {\n        return undefined;\n      }\n      const { shape } = parseHandleId(handleId);\n      // Only the two-segment FibreFlowMap row form (fiber-<tube>-<n>) applies here.\n      if (shape.kind !== 'fiber' || shape.tube === undefined) {\n        return undefined;\n      }\n      const tubeIndex = shape.tube;\n      const fiberIndex = shape.fiber;\n      const layout = (node.data as FibreFlowCableNodeData).layout;\n      const row = layout.rows.find(\n        (r) => (r.tubeIndex ?? 1) === tubeIndex && r.fiberIndex === fiberIndex\n      );\n      if (!row) {\n        return undefined;\n      }\n      return { color: row.color, striped: row.striped ?? false };\n    },\n    [nodes]\n  );\n\n  // Validate connections - allow any fiber-to-fiber connections (any to any)\n  const isValidConnection = useCallback(\n    (connection: Connection): boolean => {\n      if (readOnly) {\n        return false;\n      }\n\n      if (!connection.source || !connection.target || !connection.sourceHandle || !connection.targetHandle) {\n        return false;\n      }\n\n      // Find source and target nodes\n      const sourceNode = nodes.find((n) => n.id === connection.source);\n      const targetNode = nodes.find((n) => n.id === connection.target);\n\n      if (!sourceNode || !targetNode) {\n        return false;\n      }\n\n      // Check if both handles are fiber handles (format: \"fiber-<tube>-<fiber>\")\n      const isSourceFibreHandle = typeof connection.sourceHandle === 'string' && connection.sourceHandle.startsWith('fiber-');\n      const isTargetFibreHandle = typeof connection.targetHandle === 'string' && connection.targetHandle.startsWith('fiber-');\n      const isCableRunHandle = connection.sourceHandle === 'cable-run' || connection.targetHandle === 'cable-run';\n\n      // Allow cable-run connections\n      if (isCableRunHandle) {\n        return true;\n      }\n\n      // Allow any fiber-to-fiber connections (any cable to any cable)\n      if (isSourceFibreHandle && isTargetFibreHandle) {\n        const bothAreCables = sourceNode.type === 'fibre-flow-cable' && targetNode.type === 'fibre-flow-cable';\n        return bothAreCables;\n      }\n\n      return false;\n    },\n    [nodes, readOnly]\n  );\n\n  const onConnect = useCallback(\n    (params: Connection) => {\n      if (readOnly) {\n        return;\n      }\n\n      const isCableRun =\n        params.sourceHandle === 'cable-run' || params.targetHandle === 'cable-run';\n      const sourceIsFibre = isFibreFlowFibreHandle(params.sourceHandle);\n      const targetIsFibre = isFibreFlowFibreHandle(params.targetHandle);\n\n      // Get fiber info for manual connections\n      const sourceInfo = !isCableRun ? getHandleFibreInfo(params.source, params.sourceHandle) : undefined;\n      const targetInfo = !isCableRun ? getHandleFibreInfo(params.target, params.targetHandle) : undefined;\n      \n      const edgeData: FibreFlowEdgeData | undefined = !isCableRun\n        ? {\n            sourceColor: sourceInfo?.color,\n            targetColor: targetInfo?.color,\n            color: sourceInfo?.color ?? targetInfo?.color,\n            sourceStriped: sourceInfo?.striped,\n            targetStriped: targetInfo?.striped,\n          }\n        : undefined;\n\n      setEdges((eds) => {\n        const pruned = eds.filter((e) => {\n          const sourceTaken = sourceIsFibre && edgeUsesHandle(e, params.source, params.sourceHandle);\n          const targetTaken = targetIsFibre && edgeUsesHandle(e, params.target, params.targetHandle);\n          return !(sourceTaken || targetTaken);\n        });\n\n        return addEdge(\n          {\n            ...params,\n            type: isCableRun ? 'fibre-flow-link' : 'fibre-flow',\n            data: edgeData,\n          },\n          pruned\n        ) as FibreFlowMapEdge[];\n      });\n    },\n    [getHandleFibreInfo, readOnly, setEdges]\n  );\n\n  // Handle edge reconnection (when dragging an edge endpoint to a new handle)\n  const onEdgeUpdate = useCallback(\n    (oldEdge: Edge, newConnection: Connection) => {\n      if (readOnly) {\n        return;\n      }\n\n      const isCableRun =\n        newConnection.sourceHandle === 'cable-run' || newConnection.targetHandle === 'cable-run';\n      \n      // Get fiber info for the new connection\n      const sourceInfo = !isCableRun ? getHandleFibreInfo(newConnection.source, newConnection.sourceHandle) : undefined;\n      const targetInfo = !isCableRun ? getHandleFibreInfo(newConnection.target, newConnection.targetHandle) : undefined;\n      \n      const edgeData: FibreFlowEdgeData | undefined = !isCableRun\n        ? {\n            sourceColor: sourceInfo?.color,\n            targetColor: targetInfo?.color,\n            color: sourceInfo?.color ?? targetInfo?.color,\n            sourceStriped: sourceInfo?.striped,\n            targetStriped: targetInfo?.striped,\n          }\n        : undefined;\n\n      setEdges((eds) => {\n        // Remove the old edge\n        const withoutOld = eds.filter((e) => e.id !== oldEdge.id);\n        \n        // Prune edges that use the new source handle\n        const sourceIsFibre = isFibreFlowFibreHandle(newConnection.sourceHandle);\n        const targetIsFibre = isFibreFlowFibreHandle(newConnection.targetHandle);\n        const pruned = withoutOld.filter((e) => {\n          const sourceTaken = sourceIsFibre && edgeUsesHandle(e, newConnection.source, newConnection.sourceHandle);\n          const targetTaken = targetIsFibre && edgeUsesHandle(e, newConnection.target, newConnection.targetHandle);\n          return !(sourceTaken || targetTaken);\n        });\n\n        // Add the updated edge\n        return addEdge(\n          {\n            ...newConnection,\n            type: isCableRun ? 'fibre-flow-link' : 'fibre-flow',\n            data: edgeData,\n          },\n          pruned\n        ) as FibreFlowMapEdge[];\n      });\n    },\n    [getHandleFibreInfo, readOnly, setEdges]\n  );\n\n  const handleNodesChange = useCallback(\n    (nextNodes: NetworkNode[]) => {\n      if (readOnly) {\n        return;\n      }\n      setNodes(nextNodes.filter(isFibreFlowNode));\n    },\n    [readOnly, setNodes]\n  );\n\n  const handleEdgesChange = useCallback(\n    (nextEdges: NetworkEdge[]) => {\n      if (readOnly) {\n        return;\n      }\n      setEdges((previousEdges) => normalizeFibreFlowEdges(nextEdges, previousEdges));\n    },\n    [readOnly, setEdges]\n  );\n\n  const nodeTypes = useMemo(\n    () => ({\n      'fibre-flow-cable': FibreFlowCableNode,\n      'fibre-flow-closure': FibreFlowClosureNode,\n    }),\n    []\n  );\n\n  const edgeTypes = useMemo(\n    () => ({\n      'fibre-flow': FibreFlowEdge,\n      'fibre-flow-link': FibreFlowCableLinkEdge,\n    }),\n    []\n  );\n\n  // Forward NetworkDiagram's flow methods, reshaped to the FibreFlowMap subset.\n  // The methods are reused by reference (no re-implementation): closure diagrams\n  // get the identical toImage/fitView/getNodes/getEdges semantics NetworkDiagram\n  // exposes, so they can join the same PNG/jsPDF export pipeline.\n  const handleFlowMethods = useCallback(\n    (methods: NetworkDiagramFlowMethods) => {\n      onFlowMethods?.({\n        toImage: methods.toImage,\n        fitView: methods.fitView,\n        getNodes: methods.getNodes,\n        getEdges: methods.getEdges,\n      });\n    },\n    [onFlowMethods]\n  );\n\n  const wrapperStyle: React.CSSProperties = {\n    borderRadius: 'var(--rn-ui-radius, 1px)',\n    overflow: 'hidden',\n    ...style,\n  };\n\n  return (\n    <FibreFlowInstanceContext.Provider value={uid}>\n      <NetworkDiagram\n        nodes={nodes}\n        edges={edges}\n        onNodesChange={handleNodesChange}\n        onEdgesChange={handleEdgesChange}\n        onConnect={onConnect}\n        onEdgeUpdate={onEdgeUpdate}\n        isValidConnection={isValidConnection}\n        connectionMode={ConnectionMode.Loose}\n        nodeTypes={nodeTypes}\n        edgeTypes={edgeTypes}\n        className={className}\n        style={wrapperStyle}\n        colorMode={colorMode}\n        readOnly={readOnly}\n        interaction={interaction}\n        onFlowMethods={onFlowMethods ? handleFlowMethods : undefined}\n        fitViewOptions={{ padding: 0.2 }}\n      />\n    </FibreFlowInstanceContext.Provider>\n  );\n};\n\nexport const FibreFlowMap: React.FC<FibreFlowMapProps> = (props) => {\n  return <FibreFlowMapInner {...props} />;\n};\n","import type { ColorMode } from \"../utils/react-flow-theming\";\n\n/**\n * The single source of truth for the OTDR strip's colours.\n *\n * Every entry produces BOTH sides of the contract so they cannot drift:\n *  - the hex values returned by {@link otdrTraceStripPalette}, which hosts use\n *    to paint linked surfaces (route maps, event tables) in the strip's own\n *    colours; and\n *  - the `--rn-otdr-*` custom properties declared in `src/styles/index.css`,\n *    which the rendered SVG consumes as `var(--rn-otdr-…, <hex fallback>)` so a\n *    consumer who forgot to import the stylesheet still gets these exact\n *    colours.\n *\n * A test asserts the stylesheet's declarations match this table verbatim.\n */\nexport const OTDR_COLOR_TOKENS = {\n  text: { cssVar: \"--rn-otdr-text\", light: \"#0f172a\", dark: \"#e2e8f0\" },\n  subtext: { cssVar: \"--rn-otdr-subtext\", light: \"#64748b\", dark: \"#94a3b8\" },\n  axis: { cssVar: \"--rn-otdr-axis\", light: \"#cbd5e1\", dark: \"#475569\" },\n  fibre: { cssVar: \"--rn-otdr-fibre\", light: \"#0284c7\", dark: \"#38bdf8\" },\n  cumulative: { cssVar: \"--rn-otdr-cumulative\", light: \"#d97706\", dark: \"#f59e0b\" },\n  eventFill: { cssVar: \"--rn-otdr-event-fill\", light: \"#0369a1\", dark: \"#0ea5e9\" },\n  end: { cssVar: \"--rn-otdr-end\", light: \"#16a34a\", dark: \"#22c55e\" },\n  fault: { cssVar: \"--rn-otdr-fault\", light: \"#be123c\", dark: \"#f43f5e\" },\n} as const;\n\nexport type OtdrColorToken = keyof typeof OTDR_COLOR_TOKENS;\n\n/** Every colour key the strip paints with, including the non-tokenised ones. */\nexport interface OtdrTraceStripColors {\n  text: string;\n  subtext: string;\n  axis: string;\n  fibre: string;\n  cumulative: string;\n  eventFill: string;\n  end: string;\n  fault: string;\n  /** Always `\"transparent\"` — the strip inherits its host's surface. */\n  background: string;\n}\n\nconst TOKEN_KEYS = Object.keys(OTDR_COLOR_TOKENS) as OtdrColorToken[];\n\n/**\n * Resolve a ColorMode without React. `'system'` reads the OS preference once;\n * the component itself stays reactive via `useResolvedColorMode`.\n */\nfunction resolveMode(mode: ColorMode): \"light\" | \"dark\" {\n  if (mode !== \"system\") return mode;\n  if (typeof window !== \"undefined\" && window.matchMedia) {\n    return window.matchMedia(\"(prefers-color-scheme: light)\").matches ? \"light\" : \"dark\";\n  }\n  return \"dark\";\n}\n\n/**\n * The strip's colour palette for a given mode, as literal hex values. Exported\n * so hosts can paint linked surfaces — a route map, an event list — in the same\n * colours the strip uses for events, faults, the fibre axis and the cumulative\n * line. `'system'` resolves against the OS preference (and falls back to dark\n * when there is no `window`).\n */\nexport const otdrTraceStripPalette = (mode: ColorMode): OtdrTraceStripColors => {\n  const resolved = resolveMode(mode);\n  const out = { background: \"transparent\" } as OtdrTraceStripColors;\n  for (const key of TOKEN_KEYS) {\n    out[key] = OTDR_COLOR_TOKENS[key][resolved];\n  }\n  return out;\n};\n\n/**\n * The same palette expressed as `var(--rn-otdr-…, <hex>)` references — what the\n * SVG actually paints with, so a host can retheme the strip through CSS while\n * a host that never imported the stylesheet still renders the hex fallbacks.\n */\nexport const otdrTraceStripCssPalette = (mode: \"light\" | \"dark\"): OtdrTraceStripColors => {\n  const out = { background: \"transparent\" } as OtdrTraceStripColors;\n  for (const key of TOKEN_KEYS) {\n    const token = OTDR_COLOR_TOKENS[key];\n    out[key] = `var(${token.cssVar}, ${token[mode]})`;\n  }\n  return out;\n};\n","import React, { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { ColorMode } from \"../utils/react-flow-theming\";\nimport { useResolvedColorMode } from \"./use-resolved-color-mode\";\nimport { otdrTraceStripCssPalette, type OtdrTraceStripColors } from \"./otdr-theme\";\nimport { activateOnEnterOrSpace } from \"../utils/a11y\";\n\n/**\n * OTDR loss-budget strip: a standalone SVG visualisation of an optical\n * time-domain reflectometer trace's key events — ordered along the fibre by\n * distance, each annotated with its splice/insertion loss (and reflectance\n * when present), with the cumulative loss running along a secondary axis.\n *\n * Deliberately renders MEASURED data only: the component never estimates,\n * interpolates missing events, or scales distances to fit — callers pass the\n * parsed events (e.g. from a Telcordia SR-4731 .sor file) and, optionally, a\n * fault distance to highlight. No React Flow dependency; safe to render\n * anywhere.\n */\n\n/**\n * Event vocabulary. The semantic kinds (`connector`, `splice`) say what the\n * plant IS — pass them when the caller knows. The raw kinds (`reflective`,\n * `non-reflective`, `saturated`) say only what the OTDR saw: a reflective\n * event is a connector OR a mechanical splice, a non-reflective one a fusion\n * splice or a bend. The legend words raw kinds honestly instead of guessing —\n * a trace full of mechanical splices must not be captioned \"connectors\".\n */\nexport type OtdrTraceStripEventKind =\n  | \"connector\"\n  | \"splice\"\n  | \"reflective\"\n  | \"non-reflective\"\n  | \"saturated\"\n  | \"end\";\n\nexport interface OtdrTraceStripEvent {\n  /** Metres from the launch end. */\n  distanceM: number;\n  /** Splice/insertion loss at this event in dB. */\n  lossDb: number;\n  /** Reflectance in dB (negative), when the event is reflective. */\n  reflectanceDb?: number | null;\n  /**\n   * Measured attenuation slope of the fibre section LEADING INTO this event,\n   * in dB/km (SR-4731 semantics, as .sor parsers report it). When any event\n   * carries one, the cumulative line slopes across each section — the way a\n   * real trace's backscatter falls between events — and lands on the measured\n   * total instead of just the sum of event losses. Absent slopes render the\n   * legacy flat step-line; the strip never estimates one.\n   */\n  slopeDbPerKm?: number | null;\n  /** An OtdrTraceStripEvent kind; unrecognised strings render as a plain event. */\n  type?: string;\n  comment?: string;\n}\n\n/**\n * A measured backscatter waveform (SR-4731 DataPts): uniformly sampled power\n * levels in dB. When provided, the strip draws the real trace — noise, dead\n * zones, reflection spikes, post-end noise floor — instead of the derived\n * cumulative-loss line. Levels are displayed power (higher = more light),\n * exactly as decoded from the file; nothing is smoothed or synthesised.\n *\n * A capture usually runs past the fibre: samples beyond the axis end are\n * clipped rather than drawn, so the waveform and the axis always agree. The\n * waveform itself never extends the axis — only events or `faultDistanceM`\n * beyond `lengthM` do that, and the waveform is then drawn out to wherever\n * they put the axis end (see `lengthM`).\n */\nexport interface OtdrTraceStripTrace {\n  /** Metres between consecutive samples. */\n  stepM: number;\n  /** Power level per sample, in dB. */\n  levelsDb: number[];\n  /** Distance of the first sample from the launch end; defaults to 0 m. */\n  startM?: number;\n}\n\nexport interface OtdrTraceStripProps {\n  events: OtdrTraceStripEvent[];\n  /**\n   * Measured DataPts waveform; replaces the cumulative-loss line when set.\n   * The waveform is clipped to the axis end and never extends it — the tail of\n   * a capture is post-end noise, not plant. Note the axis end is not always\n   * `lengthM`: an event or `faultDistanceM` beyond the declared length pushes\n   * it out, and the waveform is then drawn that far.\n   */\n  trace?: OtdrTraceStripTrace | null;\n  /**\n   * Fibre-metre span to spotlight — typically the stretch currently visible\n   * on a linked route map. The strip draws a soft band over it and dims\n   * events outside, so the strip reads as the overview while the map is the\n   * working surface.\n   *\n   * The strip renders exactly the span it is given (clamped to the axis) and\n   * applies no \"that is basically everything\" heuristic of its own: the\n   * producer of the range knows how much of the route is on screen, so it\n   * decides when there is nothing to spotlight and passes null.\n   */\n  focusRangeM?: [number, number] | null;\n  /**\n   * Fibre length for the axis; defaults to the furthest event distance (or,\n   * with no events, the end of `trace`). When supplied it is the authority on\n   * how long the fibre is against the capture: a longer `trace` does not\n   * stretch the axis — only an event or `faultDistanceM` beyond it can, and\n   * the waveform is clipped to whatever axis end those produce (the strip\n   * captions the clip when samples were dropped).\n   */\n  lengthM?: number | null;\n  /** Measured end-to-end loss; defaults to the sum of event losses. */\n  totalLossDb?: number | null;\n  wavelengthNm?: number | null;\n  /** e.g. \"A → Z\" — purely a caption, the strip draws launch-end left. */\n  directionLabel?: string;\n  /** Distance to highlight as an actual fault/break, in metres. Reserve for\n   * real anomalies — ordinary events (splices/connectors/end) are not faults. */\n  faultDistanceM?: number | null;\n  /** Index (into the sorted events) to render as selected/emphasised — e.g.\n   * the event a consumer is locating on a route map. */\n  selectedEventIndex?: number | null;\n  /**\n   * `'light'` (the default, kept for backwards compatibility), `'dark'`, or\n   * `'system'` to follow the OS `prefers-color-scheme` reactively — the same\n   * `ColorMode` the rest of the library takes.\n   */\n  colorMode?: ColorMode;\n  /**\n   * Label density. `'auto'` (the default) drops the legend and the per-event\n   * dB labels — the selected event always keeps its label — and scales the\n   * remaining text up once the strip renders narrower than 640 CSS pixels.\n   * `'compact'` forces that phone treatment at any width; `'full'` keeps the\n   * legend and labels however narrow the strip is, for hosts that would rather\n   * have a dense strip than a summarised one.\n   */\n  density?: \"auto\" | \"full\" | \"compact\";\n  /** Pixel height of the strip (width is fluid). Default 190. */\n  height?: number;\n  onEventClick?: (event: OtdrTraceStripEvent, index: number) => void;\n}\n\nconst VIEW_WIDTH = 1000;\nconst MARGIN = { left: 46, right: 46, top: 34, bottom: 30 };\n/** Rendered CSS width below which `density: 'auto'` switches to compact. */\nconst COMPACT_WIDTH_PX = 640;\n/** Readability gap between two rendered dB labels, in CSS pixels. */\nconst LABEL_CLEARANCE_PX = 40;\n\nconst useIsomorphicLayoutEffect =\n  typeof window !== \"undefined\" ? React.useLayoutEffect : React.useEffect;\n\n/**\n * Format a fibre distance the way the strip's own axis and captions do:\n * metres below 1 km, then 2 decimals of km, dropping to 1 decimal past 10 km.\n * Exported so a host's surrounding chrome (map popups, event tables) can label\n * the same distances identically instead of rounding them its own way.\n */\nexport const formatOtdrDistance = (metres: number): string =>\n  metres >= 10_000\n    ? `${(metres / 1000).toFixed(1)} km`\n    : metres >= 1000\n      ? `${(metres / 1000).toFixed(2)} km`\n      : `${metres.toFixed(0)} m`;\n\nexport {\n  otdrTraceStripPalette,\n  OTDR_COLOR_TOKENS,\n  type OtdrColorToken,\n  type OtdrTraceStripColors,\n} from \"./otdr-theme\";\n\n/** Collapse an event's type string onto the glyph/legend vocabulary. */\nconst kindOf = (type: string | undefined): OtdrTraceStripEventKind | \"event\" => {\n  switch (type) {\n    case \"connector\":\n    case \"splice\":\n    case \"reflective\":\n    case \"non-reflective\":\n    case \"saturated\":\n    case \"end\":\n      return type;\n    default:\n      return \"event\";\n  }\n};\n\n/** Diamond = reflective plant (connectors, mech. splices); circle = loss-only. */\nconst markerForKind = (\n  kind: OtdrTraceStripEventKind | \"event\",\n  x: number,\n  y: number,\n  colors: OtdrTraceStripColors,\n  scale = 1,\n  atFault = false,\n): React.ReactElement => {\n  switch (kind) {\n    case \"connector\":\n    case \"reflective\": {\n      const r = 6 * scale;\n      return (\n        <path\n          d={`M ${x} ${y - r} L ${x + r} ${y} L ${x} ${y + r} L ${x - r} ${y} Z`}\n          fill={colors.eventFill}\n        />\n      );\n    }\n    case \"splice\": {\n      // Splices are drawn hollow so a connector (solid diamond) and a splice\n      // (open circle) stay distinguishable at a glance.\n      return (\n        <circle\n          cx={x}\n          cy={y}\n          r={4.6 * scale}\n          fill=\"none\"\n          stroke={colors.eventFill}\n          strokeWidth={2 * scale}\n        />\n      );\n    }\n    case \"end\": {\n      // An end event AT the fault distance IS the break — colour it as such\n      // instead of the healthy fibre-end green.\n      const half = 5 * scale;\n      return (\n        <rect\n          x={x - half}\n          y={y - half}\n          width={half * 2}\n          height={half * 2}\n          fill={atFault ? colors.fault : colors.end}\n        />\n      );\n    }\n    case \"saturated\": {\n      const arm = 5 * scale;\n      return (\n        <g stroke={colors.eventFill} strokeWidth={2.4 * scale} strokeLinecap=\"round\">\n          <line x1={x - arm} y1={y - arm} x2={x + arm} y2={y + arm} />\n          <line x1={x - arm} y1={y + arm} x2={x + arm} y2={y - arm} />\n        </g>\n      );\n    }\n    default:\n      return <circle cx={x} cy={y} r={5.2 * scale} fill={colors.eventFill} />;\n  }\n};\n\n/**\n * Legend entries derive from the kinds actually present — and never claim more\n * than the data says: semantic kinds get their name, raw kinds get the honest\n * either/or reading.\n */\nconst LEGEND_ORDER: Array<OtdrTraceStripEventKind | \"event\"> = [\n  \"connector\",\n  \"splice\",\n  \"reflective\",\n  \"non-reflective\",\n  \"saturated\",\n  \"end\",\n  \"event\",\n];\n\nconst LEGEND_LABEL: Record<OtdrTraceStripEventKind | \"event\", string> = {\n  connector: \"connector\",\n  splice: \"splice\",\n  reflective: \"reflective (connector or mech. splice)\",\n  \"non-reflective\": \"loss only (fusion splice or bend)\",\n  saturated: \"saturated reflection\",\n  end: \"end of fibre\",\n  event: \"event\",\n};\n\n/**\n * The strip's legend wording for an event `type`, e.g. `'reflective'` →\n * `\"reflective (connector or mech. splice)\"`. Exported so a host's popups and\n * tables word an event exactly as the legend does — never claiming more than\n * the file says — instead of maintaining a second vocabulary. Unrecognised\n * types read as a plain `\"event\"`.\n */\nexport const otdrEventKindLabel = (type: string | undefined): string =>\n  LEGEND_LABEL[kindOf(type)];\n\n/** Accessible name for a clickable event marker; mirrors its tooltip. */\nconst eventAccessibleName = (event: OtdrTraceStripEvent): string =>\n  `${otdrEventKindLabel(event.type)} at ${formatOtdrDistance(event.distanceM)}, loss ${event.lossDb.toFixed(2)} dB` +\n  (event.reflectanceDb !== null && event.reflectanceDb !== undefined\n    ? `, reflectance ${event.reflectanceDb.toFixed(2)} dB`\n    : \"\") +\n  (event.comment ? `, ${event.comment}` : \"\");\n\nexport const OtdrTraceStrip: React.FC<OtdrTraceStripProps> = ({\n  events,\n  trace,\n  focusRangeM,\n  lengthM,\n  totalLossDb,\n  wavelengthNm,\n  directionLabel,\n  faultDistanceM,\n  selectedEventIndex,\n  colorMode = \"light\",\n  density = \"auto\",\n  height = 190,\n  onEventClick,\n}) => {\n  const resolvedColorMode = useResolvedColorMode(colorMode);\n  // The SVG paints with `var(--rn-otdr-..., <hex>)` so hosts can retheme through\n  // the stylesheet; the hex fallbacks keep the strip correct without it.\n  const colors = otdrTraceStripCssPalette(resolvedColorMode);\n  // One nullable-fault guard for the whole component.\n  const faultM = faultDistanceM ?? null;\n\n  // Every density decision below is pixel-scale — how wide a dB label is, how\n  // many waveform vertices a screen can resolve — so they all run off the\n  // measured CSS width, with `pxPerUnit` converting to viewBox units. The\n  // measurement is taken in a layout effect (before paint), so a phone never\n  // flashes the full-width layout before compacting. Only derived values reach\n  // state: whole pixels and the compact flag, so sub-pixel resize ticks don't\n  // re-render the SVG.\n  const svgRef = useRef<SVGSVGElement | null>(null);\n  const [measured, setMeasured] = useState<{\n    widthPx: number;\n    compact: boolean;\n  } | null>(null);\n  const measure = useCallback((width: number) => {\n    const widthPx = Math.round(width);\n    if (widthPx <= 0) return;\n    setMeasured((previous) =>\n      previous && previous.widthPx === widthPx\n        ? previous\n        : { widthPx, compact: widthPx < COMPACT_WIDTH_PX },\n    );\n  }, []);\n  useIsomorphicLayoutEffect(() => {\n    const el = svgRef.current;\n    if (!el) return undefined;\n    measure(el.getBoundingClientRect().width);\n    if (typeof ResizeObserver === \"undefined\") return undefined;\n    const observer = new ResizeObserver((entries) => {\n      const width = entries[0]?.contentRect.width;\n      if (width) measure(width);\n    });\n    observer.observe(el);\n    return () => observer.disconnect();\n  }, [measure]);\n  const compact =\n    density === \"auto\" ? (measured?.compact ?? false) : density === \"compact\";\n  const fontScale = compact ? 1.55 : 1;\n  /** Rendered CSS pixels per viewBox unit; 1 until the strip is measured. */\n  const pxPerUnit = measured !== null ? measured.widthPx / VIEW_WIDTH : 1;\n\n  const ordered = useMemo(\n    () => [...events].sort((a, b) => a.distanceM - b.distanceM),\n    [events],\n  );\n\n  // Axis end. A supplied lengthM is the authority on how long the fibre is: an\n  // OTDR captures well past the far end, and folding that tail in would leave\n  // the axis reading 8 km under a \"Length 3.79 km\" caption. Events and a fault\n  // still extend the axis — they are plant the caller located — but the\n  // waveform is clipped instead. With no lengthM the trace's own end is the\n  // best length available, so it may define the axis.\n  const axisEndM = useMemo(() => {\n    const maxEvent = ordered.length > 0 ? ordered[ordered.length - 1].distanceM : 0;\n    const traceEnd =\n      trace && (lengthM === null || lengthM === undefined)\n        ? (trace.startM ?? 0) + trace.stepM * (trace.levelsDb.length - 1)\n        : 0;\n    const candidate = Math.max(lengthM ?? 0, maxEvent, faultM ?? 0, traceEnd);\n    return candidate > 0 ? candidate : 1;\n  }, [ordered, lengthM, faultM, trace]);\n\n  // Cumulative loss at each event: `before` is the value on arrival (after\n  // the preceding fibre section's slope, when measured slopes are present),\n  // `after` adds the event's own loss. With slopes the line ends at the\n  // measured link loss; without them it degrades to the flat event-only sum.\n  const cumulativeSegments = useMemo(() => {\n    const hasSlopes = ordered.some(\n      (event) => typeof event.slopeDbPerKm === \"number\" && event.slopeDbPerKm !== 0,\n    );\n    let running = 0;\n    let previousM = 0;\n    return ordered.map((event) => {\n      const sectionRise = hasSlopes\n        ? Math.max(0, (event.slopeDbPerKm ?? 0) * ((event.distanceM - previousM) / 1000))\n        : 0;\n      const before = running + sectionRise;\n      const after = before + event.lossDb;\n      running = after;\n      previousM = event.distanceM;\n      return { before, after };\n    });\n  }, [ordered]);\n  // The last event this trace actually measured: a fault ends the measurable\n  // fibre, so anything beyond it is plant the caller knows about, not something\n  // the instrument saw. Null when a fault precedes every event.\n  const lastMeasuredEventIndex = useMemo(() => {\n    if (faultM === null) return ordered.length - 1;\n    for (let index = ordered.length - 1; index >= 0; index -= 1) {\n      if (ordered[index].distanceM <= faultM) return index;\n    }\n    return -1;\n  }, [ordered, faultM]);\n  const cumulativeTraceTotal =\n    lastMeasuredEventIndex >= 0\n      ? cumulativeSegments[lastMeasuredEventIndex].after\n      : null;\n\n  const cumulativeTotal = totalLossDb ?? cumulativeTraceTotal;\n  // The loss axis is scaled by every event, not just the measured ones: a fault\n  // before the first event measures nothing, and a 0.1 dB axis would throw the\n  // known downstream plant clean off the top of the strip.\n  const cumulativeAllTotal =\n    cumulativeSegments.length > 0\n      ? cumulativeSegments[cumulativeSegments.length - 1].after\n      : 0;\n  const lossScaleMax = Math.max(\n    cumulativeTotal ?? 0,\n    cumulativeTraceTotal ?? 0,\n    cumulativeAllTotal,\n    0.1,\n  );\n\n  const innerWidth = VIEW_WIDTH - MARGIN.left - MARGIN.right;\n  const fibreY = height - MARGIN.bottom - 34;\n  const lossTop = MARGIN.top + 12;\n  const lossBottom = fibreY - 18;\n\n  const xFor = (metres: number) => MARGIN.left + (metres / axisEndM) * innerWidth;\n  const yForLoss = (loss: number) =>\n    lossBottom - (Math.max(0, loss) / lossScaleMax) * (lossBottom - lossTop);\n\n  const cumulativePath = useMemo(() => {\n    if (ordered.length === 0) return \"\";\n    const parts: string[] = [`M ${MARGIN.left} ${yForLoss(0)}`];\n    let previous = 0;\n    ordered.forEach((event, index) => {\n      // A fault ends the measurable fibre. Callers may still supply known\n      // downstream plant events, but their attenuation is not present in this\n      // trace and must not be drawn beyond the break.\n      if (faultM !== null && event.distanceM > faultM) {\n        return;\n      }\n      const x = xFor(event.distanceM);\n      // Slope across the section (a straight rise when measured slopes are\n      // present — `before` differs from `previous`), then the event's step.\n      parts.push(`L ${x} ${yForLoss(cumulativeSegments[index].before)}`);\n      parts.push(`L ${x} ${yForLoss(cumulativeSegments[index].after)}`);\n      previous = cumulativeSegments[index].after;\n    });\n    const measuredEndM = faultM !== null ? Math.min(faultM, axisEndM) : axisEndM;\n    parts.push(`L ${xFor(measuredEndM)} ${yForLoss(previous)}`);\n    return parts.join(\" \");\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [ordered, cumulativeSegments, axisEndM, lossScaleMax, height, faultM]);\n\n  // Real waveform, drawn the way instrument software draws it: a connected\n  // jagged polyline. Each pixel-ish bucket contributes its max (keeping\n  // reflection spikes and the backscatter line), and buckets with a large\n  // internal range — the noise floor — also contribute their min at the\n  // bucket's far edge, giving the characteristic hash without painting a\n  // solid block. Values are the file's own samples; nothing is smoothed.\n  // Samples past the axis end are the capture's post-end tail: clipped, not\n  // squeezed in, so the drawn waveform and the axis agree about the fibre.\n  // `clippedSamples` is how many the caller's declared length discarded — the\n  // strip says so rather than silently dropping measured data.\n  const traceClip = useMemo(() => {\n    if (!trace || trace.levelsDb.length < 2)\n      return { sampleCount: 0, clippedSamples: 0 };\n    const { stepM, levelsDb } = trace;\n    const startM = trace.startM ?? 0;\n    const sampleCount =\n      stepM > 0\n        ? Math.max(\n            0,\n            Math.min(levelsDb.length, Math.floor((axisEndM - startM) / stepM) + 1),\n          )\n        : levelsDb.length;\n    return { sampleCount, clippedSamples: levelsDb.length - sampleCount };\n  }, [trace, axisEndM]);\n\n  const tracePath = useMemo(() => {\n    if (!trace || trace.levelsDb.length < 2) return \"\";\n    const { stepM, levelsDb } = trace;\n    const startM = trace.startM ?? 0;\n    const { sampleCount } = traceClip;\n    if (sampleCount < 2) return \"\";\n    let minDb = Infinity;\n    let maxDb = -Infinity;\n    for (let i = 0; i < sampleCount; i += 1) {\n      const v = levelsDb[i];\n      if (v < minDb) minDb = v;\n      if (v > maxDb) maxDb = v;\n    }\n    const span = maxDb - minDb || 1;\n    const yFor = (db: number) =>\n      lossBottom - ((db - minDb) / span) * (lossBottom - lossTop);\n    // One bucket per rendered pixel of the plot area: a phone gets a few\n    // hundred vertices instead of a fixed 908, a 2000px screen is not\n    // undersampled. Falls back to the viewBox width until measured.\n    const buckets = Math.min(sampleCount, Math.max(1, Math.round(innerWidth * pxPerUnit)));\n    const perBucket = sampleCount / buckets;\n    const parts: string[] = [];\n    for (let b = 0; b < buckets; b += 1) {\n      const from = Math.floor(b * perBucket);\n      const to = Math.min(sampleCount, Math.max(from + 1, Math.floor((b + 1) * perBucket)));\n      let lo = Infinity;\n      let hi = -Infinity;\n      for (let i = from; i < to; i += 1) {\n        const v = levelsDb[i];\n        if (v < lo) lo = v;\n        if (v > hi) hi = v;\n      }\n      const xLeft = xFor(startM + from * stepM).toFixed(1);\n      parts.push(`${parts.length === 0 ? \"M\" : \"L\"} ${xLeft} ${yFor(hi).toFixed(1)}`);\n      // Only genuinely noisy buckets get their floor drawn — the smooth\n      // backscatter stays a line instead of a filled band.\n      if (hi - lo > 1.5) {\n        const xRight = xFor(startM + (to - 1) * stepM).toFixed(1);\n        parts.push(`L ${xRight} ${yFor(lo).toFixed(1)}`);\n      }\n    }\n    return parts.join(\" \");\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [trace, traceClip, axisEndM, height, pxPerUnit]);\n\n  // Clipping discards measured samples, so it is stated even when fewer than\n  // two samples survive and no measured path can be drawn. In that edge case\n  // the visible/accessible explanation also names the event-derived fallback;\n  // otherwise the derived line can look like a measured trace.\n  const clipNote =\n    traceClip.clippedSamples > 0\n      ? `capture clipped at ${\n          lengthM != null && axisEndM === lengthM\n            ? `${formatOtdrDistance(lengthM)} declared length`\n            : `${formatOtdrDistance(axisEndM)} axis end`\n        }${\n          traceClip.sampleCount < 2\n            ? `; ${\n                traceClip.sampleCount === 0\n                  ? \"no measured samples remain\"\n                  : \"only one measured sample remains\"\n              } — ${\n                ordered.length > 0\n                  ? \"showing derived cumulative loss from events\"\n                  : \"no events are available, so no trace can be drawn\"\n              }`\n            : \"\"\n        }`\n      : null;\n  // Keep the plotted caption short enough for a compact strip. The complete\n  // location/source explanation remains in the SVG name and this text's title.\n  const visibleClipNote =\n    clipNote && traceClip.sampleCount < 2\n      ? `capture clipped; ${\n          traceClip.sampleCount === 0 ? \"no measured samples\" : \"1 measured sample\"\n        } — ${ordered.length > 0 ? \"derived loss shown\" : \"no trace available\"}`\n      : clipNote;\n  const traceSourceNote = tracePath\n    ? \"measured backscatter shown\"\n    : ordered.length > 0\n      ? trace\n        ? \"measured trace unavailable; derived cumulative loss from events shown\"\n        : \"derived cumulative loss from events shown\"\n      : null;\n\n  // Greedy label thinning: a dB label renders only when its row (labels\n  // alternate above/below the fibre) has ~40 CSS pixels of clearance since the\n  // previous rendered label — a readability distance, so it is measured in\n  // rendered pixels and converted to viewBox units, not baked in as units.\n  // Dense circuits collapse to clean markers; the selected event always keeps\n  // its label.\n  const labelVisible = useMemo(() => {\n    const visible: boolean[] = new Array(ordered.length).fill(false);\n    const lastShown: Record<number, number> = { 0: -Infinity, 1: -Infinity };\n    const clearance = LABEL_CLEARANCE_PX / pxPerUnit;\n    ordered.forEach((event, index) => {\n      const row = index % 2;\n      const x = xFor(event.distanceM);\n      if (x - lastShown[row] >= clearance) {\n        visible[index] = true;\n        lastShown[row] = x;\n      }\n    });\n    return visible;\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [ordered, axisEndM, pxPerUnit]);\n\n  // Focus span from a linked map viewport, clamped to the axis and otherwise\n  // rendered exactly as given: whether \"everything is visible, so spotlight\n  // nothing\" is the producer's call — it knows what fraction of the route is on\n  // screen — and it says so by passing null.\n  const focus = useMemo(() => {\n    if (!focusRangeM) return null;\n    const [a, b] = focusRangeM;\n    const lo = Math.max(0, Math.min(a, b));\n    const hi = Math.min(axisEndM, Math.max(a, b));\n    return hi - lo > 0 ? ([lo, hi] as const) : null;\n  }, [focusRangeM, axisEndM]);\n  const inFocus = (metres: number) =>\n    focus === null || (metres >= focus[0] && metres <= focus[1]);\n\n  const caption = [\n    cumulativeTotal !== null && cumulativeTotal !== undefined\n      ? `Total loss ${cumulativeTotal.toFixed(2)} dB`\n      : null,\n    lengthM ? `Length ${formatOtdrDistance(lengthM)}` : null,\n    wavelengthNm ? `${wavelengthNm} nm` : null,\n    directionLabel ?? null,\n  ]\n    .filter(Boolean)\n    .join(\"  ·  \");\n\n  const handleCompactStripClick = (click: React.MouseEvent<SVGSVGElement>) => {\n    if (!compact || !onEventClick || ordered.length === 0) return;\n    const svg = svgRef.current;\n    if (!svg) return;\n    const bounds = svg.getBoundingClientRect();\n    if (bounds.width <= 0 || bounds.height <= 0) return;\n\n    const pointerX = ((click.clientX - bounds.left) / bounds.width) * VIEW_WIDTH;\n    const pointerY = ((click.clientY - bounds.top) / bounds.height) * height;\n    // Keep a roughly 48px touch target regardless of how far the viewBox has\n    // been scaled down. Resolving the nearest event avoids overlapping large\n    // hit circles choosing whichever marker happens to be painted last.\n    const targetRadiusX = (24 / bounds.width) * VIEW_WIDTH;\n    const targetRadiusY = (24 / bounds.height) * height;\n    if (Math.abs(pointerY - fibreY) > targetRadiusY) return;\n\n    let nearestIndex = 0;\n    let nearestDistance = Infinity;\n    ordered.forEach((event, index) => {\n      const distance = Math.abs(pointerX - xFor(event.distanceM));\n      if (distance < nearestDistance) {\n        nearestDistance = distance;\n        nearestIndex = index;\n      }\n    });\n    if (nearestDistance <= targetRadiusX) {\n      onEventClick(ordered[nearestIndex], nearestIndex);\n    }\n  };\n\n  return (\n    <svg\n      ref={svgRef}\n      onClick={compact && onEventClick ? handleCompactStripClick : undefined}\n      viewBox={`0 0 ${VIEW_WIDTH} ${height}`}\n      // A static strip is one graphic. With `onEventClick` its events are\n      // controls, and `img` would make every child presentational — so the\n      // strip becomes a named group whose event markers are buttons.\n      role={onEventClick ? \"group\" : \"img\"}\n      aria-label={`OTDR trace, ${ordered.length} events${caption ? `, ${caption}` : \"\"}${\n        clipNote ? `, ${clipNote}` : \"\"\n      }${traceSourceNote ? `, ${traceSourceNote}` : \"\"}`}\n      className={`rn-otdr-strip rn-otdr-${resolvedColorMode}`}\n      style={{\n        width: \"100%\",\n        height: \"auto\",\n        display: \"block\",\n        background: colors.background,\n        touchAction: compact && onEventClick ? \"manipulation\" : undefined,\n      }}\n      data-testid=\"otdr-trace-strip\"\n    >\n      <text x={MARGIN.left} y={18} fontSize={13 * fontScale} fontWeight={600} fill={colors.text}>\n        {caption || \"OTDR trace\"}\n      </text>\n\n      {/* measured backscatter waveform, when the caller has DataPts */}\n      {tracePath ? (\n        <path\n          d={tracePath}\n          fill=\"none\"\n          stroke={colors.cumulative}\n          strokeWidth={0.9}\n          strokeLinejoin=\"round\"\n          data-testid=\"otdr-backscatter\"\n        />\n      ) : null}\n      {tracePath ? (\n        <text\n          x={VIEW_WIDTH - MARGIN.right}\n          y={lossTop - 4}\n          fontSize={11}\n          textAnchor=\"end\"\n          fill={colors.cumulative}\n        >\n          measured backscatter (dB)\n        </text>\n      ) : null}\n      {clipNote ? (\n        <text\n          x={MARGIN.left}\n          y={lossTop - 4}\n          fontSize={9.5}\n          fill={colors.subtext}\n          data-testid=\"otdr-trace-clipped\"\n        >\n          <title>{clipNote}</title>\n          {visibleClipNote}\n        </text>\n      ) : null}\n\n      {/* cumulative loss step-line (derived) — superseded by the waveform */}\n      {ordered.length > 0 && !tracePath ? (\n        <>\n          <path\n            d={cumulativePath}\n            fill=\"none\"\n            stroke={colors.cumulative}\n            strokeWidth={1.8}\n            strokeDasharray=\"1 0\"\n            data-testid=\"otdr-cumulative\"\n          />\n          <text\n            x={VIEW_WIDTH - MARGIN.right}\n            y={yForLoss(cumulativeTraceTotal ?? 0) - 6}\n            fontSize={11}\n            textAnchor=\"end\"\n            fill={colors.cumulative}\n          >\n            {/* With a fault ahead of every event the trace measured no loss at\n                all — say which, rather than reading as a pristine 0.00 dB. */}\n            {cumulativeTraceTotal === null\n              ? \"0.00 dB cumulative to fault\"\n              : `${cumulativeTraceTotal.toFixed(2)} dB cumulative`}\n          </text>\n        </>\n      ) : null}\n\n      {/* fibre axis */}\n      <line\n        x1={MARGIN.left}\n        y1={fibreY}\n        x2={MARGIN.left + innerWidth}\n        y2={fibreY}\n        stroke={colors.fibre}\n        strokeWidth={3}\n        strokeLinecap=\"round\"\n      />\n      {/* Axis captions sit below the event-label band (fibreY + 20) so an\n          event near either end can't collide with them. */}\n      <text x={MARGIN.left} y={fibreY + 34} fontSize={11 * fontScale} fill={colors.subtext}>\n        0 m\n      </text>\n      <text\n        x={MARGIN.left + innerWidth}\n        y={fibreY + 34}\n        fontSize={11 * fontScale}\n        textAnchor=\"end\"\n        fill={colors.subtext}\n      >\n        {formatOtdrDistance(axisEndM)}\n      </text>\n\n      {/* focus band: the stretch currently visible on the linked map */}\n      {focus !== null ? (\n        <rect\n          x={xFor(focus[0])}\n          y={lossTop - 6}\n          width={Math.max(2, xFor(focus[1]) - xFor(focus[0]))}\n          height={fibreY + 10 - (lossTop - 6)}\n          fill={colors.eventFill}\n          opacity={0.08}\n          data-testid=\"otdr-focus-band\"\n        />\n      ) : null}\n\n      {/* events */}\n      {ordered.map((event, index) => {\n        const x = xFor(event.distanceM);\n        const isSelected = selectedEventIndex === index;\n        const labelAbove = index % 2 === 0;\n        const labelY = labelAbove ? fibreY - 14 : fibreY + 20;\n        // Past the fault the trace measured nothing — the derived line already\n        // stops there, so the markers beyond it are dimmed the same way an\n        // out-of-focus event is: still drawn, plainly not measured.\n        const beyondFault = faultM !== null && event.distanceM > faultM;\n        return (\n          <g\n            key={`${event.distanceM}-${index}`}\n            onClick={onEventClick && !compact ? () => onEventClick(event, index) : undefined}\n            // Keyboard path for the same action. Compact strips resolve\n            // pointer clicks at the SVG level (nearest event), but each\n            // event stays individually focusable and activatable.\n            onKeyDown={\n              onEventClick\n                ? activateOnEnterOrSpace<SVGGElement>(() => onEventClick(event, index))\n                : undefined\n            }\n            role={onEventClick ? \"button\" : undefined}\n            tabIndex={onEventClick ? 0 : undefined}\n            aria-label={onEventClick ? eventAccessibleName(event) : undefined}\n            aria-current={onEventClick && isSelected ? \"true\" : undefined}\n            className={onEventClick ? \"rn-otdr-event-clickable\" : undefined}\n            opacity={\n              (inFocus(event.distanceM) && !beyondFault) || isSelected ? 1 : 0.3\n            }\n            data-testid=\"otdr-event\"\n          >\n            <title>\n              {`${event.type ?? \"event\"} @ ${formatOtdrDistance(event.distanceM)} — loss ${event.lossDb.toFixed(3)} dB` +\n                (event.reflectanceDb !== null && event.reflectanceDb !== undefined\n                  ? `, reflectance ${event.reflectanceDb.toFixed(2)} dB`\n                  : \"\") +\n                (event.comment ? ` — ${event.comment}` : \"\")}\n            </title>\n            <line\n              x1={x}\n              y1={fibreY - 8}\n              x2={x}\n              y2={fibreY + 8}\n              stroke={colors.axis}\n              strokeWidth={1}\n            />\n            {onEventClick ? (\n              <>\n                {/* invisible enlarged hit target + visible affordance ring */}\n                <circle cx={x} cy={fibreY} r={14} fill=\"transparent\" />\n                <circle\n                  className=\"rn-otdr-event-ring\"\n                  cx={x}\n                  cy={fibreY}\n                  r={9.5}\n                  fill=\"none\"\n                  stroke={colors.eventFill}\n                  strokeWidth={isSelected ? 2.4 : 1.4}\n                  style={isSelected ? { opacity: 1 } : undefined}\n                  data-testid={isSelected ? \"otdr-event-ring-selected\" : \"otdr-event-ring\"}\n                />\n              </>\n            ) : null}\n            {markerForKind(\n              kindOf(event.type),\n              x,\n              fibreY,\n              colors,\n              1,\n              faultM !== null && Math.abs(event.distanceM - faultM) < 1,\n            )}\n            {(compact ? isSelected : labelVisible[index] || isSelected) ? (\n              <text\n                x={x}\n                y={labelY}\n                fontSize={10 * fontScale}\n                textAnchor=\"middle\"\n                fill={colors.text}\n              >\n                {event.lossDb.toFixed(2)} dB\n              </text>\n            ) : null}\n          </g>\n        );\n      })}\n\n      {/* legend: only the kinds present, worded no stronger than the data —\n          a raw reflective event is a connector OR a mechanical splice. */}\n      {ordered.length > 0 && !compact\n        ? (() => {\n            const present = new Set(ordered.map((event) => kindOf(event.type)));\n            const entries = LEGEND_ORDER.filter((kind) => present.has(kind));\n            let cursor = MARGIN.left;\n            const cy = height - 12;\n            return (\n              <g fontSize={9.5} fill={colors.subtext} data-testid=\"otdr-legend\">\n                {entries.map((kind) => {\n                  const label = LEGEND_LABEL[kind];\n                  const glyphX = cursor + 6;\n                  const textX = cursor + 15;\n                  cursor = textX + label.length * 5.4 + 16;\n                  return (\n                    <g key={kind}>\n                      {markerForKind(kind, glyphX, cy, colors, 0.8)}\n                      <text x={textX} y={cy + 3}>\n                        {label}\n                      </text>\n                    </g>\n                  );\n                })}\n              </g>\n            );\n          })()\n        : null}\n\n      {/* fault highlight */}\n      {faultM !== null ? (\n        <g data-testid=\"otdr-fault\">\n          <line\n            x1={xFor(Math.min(faultM, axisEndM))}\n            y1={lossTop}\n            x2={xFor(Math.min(faultM, axisEndM))}\n            y2={fibreY + 10}\n            stroke={colors.fault}\n            strokeWidth={1.6}\n            strokeDasharray=\"5 4\"\n          />\n          <text\n            x={xFor(Math.min(faultM, axisEndM))}\n            y={lossTop - 4}\n            fontSize={11}\n            fontWeight={600}\n            textAnchor=\"middle\"\n            fill={colors.fault}\n          >\n            fault @ {formatOtdrDistance(faultM)}\n          </text>\n        </g>\n      ) : null}\n    </svg>\n  );\n};\n\nexport default OtdrTraceStrip;\n","/**\n * Generic validation result structure\n */\nexport interface PlacementValidationResult<TConflict, TOutOfBounds> {\n  isValid: boolean;\n  conflicts: TConflict[];\n  outOfBounds: TOutOfBounds[];\n}\n\n/**\n * Options for generic placement validation\n */\nexport interface PlacementValidationOptions<TItem, TConflict, TOutOfBounds> {\n  items: TItem[];\n  checkOutOfBounds: (item: TItem) => { isValid: boolean; error?: TOutOfBounds } | null;\n  checkConflicts: (itemA: TItem, itemB: TItem) => { hasConflict: boolean; conflict?: TConflict } | null;\n}\n\n/**\n * Generic placement validation function\n * Validates items for out-of-bounds conditions and conflicts\n * \n * @param options - Validation options including items and checker functions\n * @returns Validation result with conflicts and out-of-bounds errors\n * \n * @example\n * ```ts\n * const result = validatePlacements({\n *   items: devices,\n *   checkOutOfBounds: (device) => {\n *     if (device.unit + device.height > rack.units) {\n *       return { isValid: false, error: { deviceId: device.id, ... } };\n *     }\n *     return null;\n *   },\n *   checkConflicts: (deviceA, deviceB) => {\n *     if (overlaps(deviceA, deviceB)) {\n *       return { hasConflict: true, conflict: { deviceId: deviceA.id, ... } };\n *     }\n *     return null;\n *   }\n * });\n * ```\n */\nexport function validatePlacements<TItem, TConflict, TOutOfBounds>(\n  options: PlacementValidationOptions<TItem, TConflict, TOutOfBounds>\n): PlacementValidationResult<TConflict, TOutOfBounds> {\n  const { items, checkOutOfBounds, checkConflicts } = options;\n  const conflicts: TConflict[] = [];\n  const outOfBounds: TOutOfBounds[] = [];\n  \n  // Check for out-of-bounds placements\n  for (const item of items) {\n    const boundsCheck = checkOutOfBounds(item);\n    if (boundsCheck && !boundsCheck.isValid && boundsCheck.error) {\n      outOfBounds.push(boundsCheck.error);\n    }\n  }\n  \n  // Check for item-to-item conflicts\n  for (let i = 0; i < items.length; i++) {\n    const itemA = items[i];\n    \n    for (let j = i + 1; j < items.length; j++) {\n      const itemB = items[j];\n      const conflictCheck = checkConflicts(itemA, itemB);\n      \n      if (conflictCheck && conflictCheck.hasConflict && conflictCheck.conflict) {\n        conflicts.push(conflictCheck.conflict);\n      }\n    }\n  }\n  \n  return {\n    isValid: conflicts.length === 0 && outOfBounds.length === 0,\n    conflicts,\n    outOfBounds,\n  };\n}\n","import type { DeviceFace, DevicePort, NetworkNodeDataBase, Port, PortBlock, PortCableType, PortType } from '../types';\nimport { buildPortHandleId, isPowerPortType, normalizeHandleId } from './handle-ids';\nimport { normalizePortPositionInput } from './port-position';\nimport { buildPortBlocksFromPorts, getResolvedPortBlocks } from './render-ports';\n\nconst DEFAULT_FRONT_GROUP_NAME = 'Network Ports';\nconst DEFAULT_REAR_GROUP_NAME = 'Rear Network Ports';\nconst DEFAULT_POWER_GROUP_NAME = 'Power Ports';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst toText = (value: unknown): string | undefined => {\n  if (typeof value !== 'string') {\n    return undefined;\n  }\n  const trimmed = value.trim();\n  return trimmed.length > 0 ? trimmed : undefined;\n};\n\nconst normalizeFaceInput = (value: unknown): DeviceFace | undefined => {\n  const normalized = toText(value)?.toLowerCase();\n  if (normalized === 'front') {\n    return 'front';\n  }\n  if (normalized === 'rear' || normalized === 'back') {\n    return 'rear';\n  }\n  return undefined;\n};\n\n/**\n * Every IEC 60320 designation the mate table in `validation/connectors` knows.\n * Narrower whitelists silently dropped C15/C16/C21/C22, so those pairings could\n * never be checked.\n */\nconst CONNECTOR_TYPES: readonly NonNullable<DevicePort['connectorType']>[] = [\n  'C13',\n  'C14',\n  'C15',\n  'C16',\n  'C19',\n  'C20',\n  'C21',\n  'C22',\n];\n\nconst normalizeConnectorType = (\n  value: unknown,\n): NonNullable<DevicePort['connectorType']> | undefined => {\n  const normalized = toText(value)?.toUpperCase();\n  if (\n    normalized !== undefined &&\n    (CONNECTOR_TYPES as readonly string[]).includes(normalized)\n  ) {\n    return normalized as NonNullable<DevicePort['connectorType']>;\n  }\n  return undefined;\n};\n\nconst resolveDefaultFace = (portType: string | undefined): DeviceFace =>\n  isPowerPortType(portType) ? 'rear' : 'front';\n\nconst resolveDefaultGroup = ({\n  face,\n  type,\n}: {\n  face: DeviceFace;\n  type: string | undefined;\n}): string => {\n  if (isPowerPortType(type)) {\n    return DEFAULT_POWER_GROUP_NAME;\n  }\n  return face === 'rear' ? DEFAULT_REAR_GROUP_NAME : DEFAULT_FRONT_GROUP_NAME;\n};\n\n/**\n * Resolve the effective device face for a flat port input.\n * @public\n */\nexport function resolveDevicePortFace(\n  port: Pick<DevicePort, 'face' | 'type'>,\n): DeviceFace {\n  return normalizeFaceInput(port.face) ?? resolveDefaultFace(port.type);\n}\n\n/**\n * Resolve the effective port group name for a flat port input.\n * @public\n */\nexport function resolveDevicePortGroup(\n  port: Pick<DevicePort, 'face' | 'group' | 'type'>,\n): string {\n  return (\n    toText(port.group) ??\n    resolveDefaultGroup({\n      face: resolveDevicePortFace(port),\n      type: port.type,\n    })\n  );\n}\n\n/**\n * Normalize unknown **flat** port array input into canonical {@link DevicePort} entries.\n *\n * This handles a flat `DevicePort[]` only; grouped `PortBlock[]` input returns\n * `undefined` (blocks have no `label`). To resolve flat ports from any supported\n * form — including grouped `PortBlock[]` — use {@link resolveNodeDevicePorts}.\n * @public\n */\nexport function normalizeDevicePorts(value: unknown): DevicePort[] | undefined {\n  if (!Array.isArray(value)) {\n    return undefined;\n  }\n\n  const ports = value\n    .map((entry): DevicePort | null => {\n      if (!isRecord(entry)) {\n        return null;\n      }\n\n      const label = toText(entry.label);\n      if (!label) {\n        return null;\n      }\n\n      const type = toText(entry.type) as PortType | undefined;\n      const face = resolveDevicePortFace({\n        face: normalizeFaceInput(entry.face),\n        type,\n      });\n      const group = resolveDevicePortGroup({\n        face,\n        group: toText(entry.group),\n        type,\n      });\n      const position = normalizePortPositionInput(entry.position ?? entry);\n      const order =\n        typeof entry.order === 'number' && Number.isFinite(entry.order) ? entry.order : undefined;\n\n      const port: DevicePort = {\n        ...(toText(entry.id) ? { id: toText(entry.id) } : {}),\n        label,\n        face,\n        group,\n        connected: typeof entry.connected === 'boolean' ? entry.connected : undefined,\n        ...(type ? { type } : {}),\n        ...(toText(entry.cableType) ? { cableType: toText(entry.cableType) as PortCableType } : {}),\n        ...(normalizeConnectorType(entry.connectorType)\n          ? { connectorType: normalizeConnectorType(entry.connectorType) }\n          : {}),\n        ...(typeof order === 'number' ? { order } : {}),\n        ...(position ? { position } : {}),\n      };\n\n      return port;\n    })\n    .filter((entry): entry is DevicePort => entry !== null);\n\n  return ports.length > 0 ? ports : undefined;\n}\n\nfunction normalizePortBlocks(value: unknown, face: DeviceFace): DevicePort[] {\n  if (!Array.isArray(value)) {\n    return [];\n  }\n\n  return value.flatMap((entry) => {\n    if (!isRecord(entry) || !Array.isArray(entry.ports)) {\n      return [];\n    }\n\n    return entry.ports\n      .map((rawPort, index): DevicePort | null => {\n        if (!isRecord(rawPort)) {\n          return null;\n        }\n\n        const label = toText(rawPort.label);\n        if (!label) {\n          return null;\n        }\n\n        const type = toText(rawPort.type) as PortType | undefined;\n        const group = resolveDevicePortGroup({\n          face,\n          group: toText(entry.name),\n          type,\n        });\n        const position = normalizePortPositionInput(rawPort.position ?? rawPort);\n        const order =\n          typeof rawPort.order === 'number' && Number.isFinite(rawPort.order)\n            ? rawPort.order\n            : index;\n\n        return {\n          ...(toText(rawPort.id) ? { id: toText(rawPort.id) } : {}),\n          label,\n          face,\n          group,\n          connected: typeof rawPort.connected === 'boolean' ? rawPort.connected : undefined,\n          ...(type ? { type } : {}),\n          ...(toText(rawPort.cableType)\n            ? { cableType: toText(rawPort.cableType) as PortCableType }\n            : {}),\n          ...(normalizeConnectorType(rawPort.connectorType)\n            ? { connectorType: normalizeConnectorType(rawPort.connectorType) }\n            : {}),\n          order,\n          ...(position ? { position } : {}),\n        };\n      })\n      .filter((port): port is DevicePort => port !== null);\n  });\n}\n\n/**\n * Resolve flat device ports from any supported port input.\n *\n * Accepts every form the renderer accepts (`getResolvedPortBlocks`): a node-data\n * record (`ports` / `portGroups` / `rearPortGroups` / `rearPorts`), a flat\n * `DevicePort[]`, or a grouped `PortBlock[]`. Grouped `PortBlock[]` carried on\n * `ports` are treated as front groups (taking precedence over `portGroups`, matching\n * the renderer); rear groups come from `rearPortGroups`, falling back to `rearPorts`.\n * @public\n */\nexport function resolveNodeDevicePorts(value: unknown): DevicePort[] | undefined {\n  if (isRecord(value)) {\n    const flatPorts = normalizeDevicePorts(value.ports);\n    if (flatPorts) {\n      return flatPorts;\n    }\n\n    // `ports` may instead carry grouped PortBlock[] (front). It wins over\n    // `portGroups`, mirroring getResolvedPortBlocks' precedence.\n    const frontFromPorts = normalizePortBlocks(value.ports, 'front');\n    const frontPorts =\n      frontFromPorts.length > 0\n        ? frontFromPorts\n        : normalizePortBlocks(value.portGroups, 'front');\n    const rearFromGroups = normalizePortBlocks(value.rearPortGroups, 'rear');\n    const rearPorts =\n      rearFromGroups.length > 0\n        ? rearFromGroups\n        : normalizePortBlocks(value.rearPorts, 'rear');\n    const ports = [...frontPorts, ...rearPorts];\n    return ports.length > 0 ? ports : undefined;\n  }\n\n  // Bare array input: a flat DevicePort[] or a grouped PortBlock[] (front groups).\n  const flatPorts = normalizeDevicePorts(value);\n  if (flatPorts) {\n    return flatPorts;\n  }\n  const blockPorts = normalizePortBlocks(value, 'front');\n  return blockPorts.length > 0 ? blockPorts : undefined;\n}\n\n/**\n * Flatten grouped port blocks into canonical flat {@link DevicePort} entries.\n * @public\n */\nexport function flattenDevicePortsFromGroups({\n  portGroups,\n  rearPortGroups,\n}: {\n  portGroups?: PortBlock[];\n  rearPortGroups?: PortBlock[];\n}): DevicePort[] {\n  const ports: DevicePort[] = [];\n\n  const appendFace = (blocks: PortBlock[] | undefined, face: DeviceFace) => {\n    blocks?.forEach((block) => {\n      block.ports.forEach((port, index) => {\n        ports.push({\n          ...(typeof port.id === 'string' && port.id.trim().length > 0 ? { id: port.id } : {}),\n          label: port.label,\n          face,\n          group: block.name,\n          connected: port.connected,\n          ...(port.type ? { type: port.type } : {}),\n          ...(port.cableType ? { cableType: port.cableType } : {}),\n          ...(port.connectorType ? { connectorType: port.connectorType } : {}),\n          order: index,\n          ...(port.position ? { position: port.position } : {}),\n        });\n      });\n    });\n  };\n\n  appendFace(portGroups, 'front');\n  appendFace(rearPortGroups, 'rear');\n  return ports;\n}\n\n/**\n * Build grouped port blocks from flat device port input.\n * @public\n */\nexport function buildDevicePortGroupsFromPorts(\n  ports: DevicePort[] | undefined,\n): {\n  portGroups: PortBlock[];\n  rearPortGroups: PortBlock[];\n} {\n  if (!Array.isArray(ports) || ports.length === 0) {\n    return { portGroups: [], rearPortGroups: [] };\n  }\n\n  const { ports: portGroups, rearPorts: rearPortGroups } = buildPortBlocksFromPorts(ports);\n  return {\n    portGroups,\n    rearPortGroups,\n  };\n}\n\nexport interface ListPossiblePortHandleIdsOptions {\n  deviceType?: string;\n  face?: DeviceFace;\n}\n\n/**\n * List the **canonical** handle ids that may exist on a rendered node for a port.\n *\n * The returned ids are in canonical form. When a device renders both faces and a\n * front/rear handle id collides, the renderer disambiguates the hidden face with a\n * `--rear` / `--front` suffix (see `normalizeHandleId`). This function does not emit\n * those split-face aliases, so callers reconciling stored/rendered handles should\n * compare through {@link normalizeHandleId} (which strips the suffix) rather than by\n * raw string equality. {@link validatePortHandleOnNodeData} does this for you.\n * @public\n */\nexport function listPossiblePortHandleIds(\n  portBlockName: string,\n  port: Pick<Port, 'id' | 'label' | 'type'>,\n  options: ListPossiblePortHandleIdsOptions = {},\n): string[] {\n  const baseHandle = buildPortHandleId(portBlockName, port);\n  // Power-ness must be decided with the same rule the renderer and handle-id\n  // builder use (`isPowerPortType`, a substring match), otherwise this list\n  // diverges from the mounted handles for types like `power` / `power_dc` and\n  // reports a phantom `-target`/`-source` handle that was never rendered.\n  if (isPowerPortType(port.type)) {\n    return [baseHandle];\n  }\n\n  const normalizedDeviceType = String(options.deviceType ?? '').toLowerCase();\n  const face = options.face ?? 'front';\n\n  if (normalizedDeviceType === 'switch' || normalizedDeviceType === 'router') {\n    return [baseHandle, `${baseHandle}-target`];\n  }\n\n  if (normalizedDeviceType === 'patch-panel' || normalizedDeviceType === 'patch_panel') {\n    return face === 'rear'\n      ? [baseHandle, `${baseHandle}-target`]\n      : [baseHandle, `${baseHandle}-source`];\n  }\n\n  return [baseHandle];\n}\n\n/**\n * A rendered/stored handle id resolved back to the port that owns it.\n * @public\n */\nexport interface DescribedPortHandle {\n  /** The port the handle belongs to. */\n  port: DevicePort;\n  /** The port block (group) the port renders under. */\n  blockName: string;\n  /** The device face the block renders on. */\n  face: DeviceFace;\n}\n\n/**\n * Resolve a handle id back to the port that owns it — the inverse of\n * {@link buildPortHandleId} / {@link listPossiblePortHandleIds} over a node's\n * resolved port blocks. Directional suffixes and split-face aliases are\n * normalized before comparison (via {@link normalizeHandleId}), so ids taken\n * straight from edges match. Pass `options.sanitizeHandle` to match under an\n * additional app-side normalization (e.g. a `+`-insensitive comparison). Prefer\n * {@link handlesMatch} / {@link findEdgesForPortHandle} for whole-handle matching.\n * Returns `undefined` when no port on the node renders that handle.\n * @public\n */\nexport function describePortHandle(\n  nodeData: Pick<NetworkNodeDataBase, 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n  handle: string,\n  options: {\n    deviceType?: string;\n    sanitizeHandle?: (value: string) => string;\n  } = {},\n): DescribedPortHandle | undefined {\n  const sanitize = options.sanitizeHandle ?? ((value: string) => normalizeHandleId(value));\n  const normalizedRequestedHandle = sanitize(handle);\n  const { ports, rearPorts } = getResolvedPortBlocks(nodeData);\n\n  const findInBlocks = (blocks: PortBlock[], face: DeviceFace): DescribedPortHandle | undefined => {\n    for (const portBlock of blocks) {\n      for (const port of portBlock.ports) {\n        const possibleHandles = listPossiblePortHandleIds(portBlock.name, port, {\n          deviceType: options.deviceType,\n          face,\n        });\n        if (\n          possibleHandles.some(\n            (candidate) => sanitize(candidate) === normalizedRequestedHandle,\n          )\n        ) {\n          return { port, blockName: portBlock.name, face };\n        }\n      }\n    }\n    return undefined;\n  };\n\n  return findInBlocks(ports, 'front') ?? findInBlocks(rearPorts, 'rear');\n}\n\n/**\n * Validate that a handle id exists on node data after grouping resolution.\n * @public\n */\nexport function validatePortHandleOnNodeData(\n  nodeData: Pick<NetworkNodeDataBase, 'ports' | 'portGroups' | 'rearPortGroups'> | undefined,\n  handle: string,\n  options: {\n    deviceType?: string;\n    sanitizeHandle?: (value: string) => string;\n  } = {},\n): boolean {\n  return describePortHandle(nodeData, handle, options) !== undefined;\n}\n\n/**\n * Convert flat ports to grouped blocks using the same rules as renderers.\n * @public\n */\nexport function resolveDevicePortBlocksFromFlatPorts(\n  ports: DevicePort[],\n): { portGroups: PortBlock[]; rearPortGroups: PortBlock[] } {\n  return buildDevicePortGroupsFromPorts(ports);\n}\n","import type { DeviceBay, DeviceBaySlot, DeviceFace, PortPosition, TemplateBayKind } from \"../types\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isDeviceFace = (value: unknown): value is DeviceFace | \"back\" =>\n  value === \"front\" || value === \"rear\" || value === \"back\";\n\nconst isPortPosition = (value: unknown): value is PortPosition => {\n  if (!isRecord(value)) return false;\n  const { side, offsetPercent, offsetPx, xPercent, yPercent, widthPercent, heightPercent } =\n    value;\n  if (\n    side !== undefined &&\n    side !== \"top\" &&\n    side !== \"right\" &&\n    side !== \"bottom\" &&\n    side !== \"left\"\n  ) {\n    return false;\n  }\n  for (const n of [offsetPercent, offsetPx, xPercent, yPercent, widthPercent, heightPercent]) {\n    if (n !== undefined && typeof n !== \"number\") return false;\n  }\n  return true;\n};\n\nconst readBayKind = (value: unknown): TemplateBayKind | undefined => {\n  if (typeof value !== \"string\" || value.length === 0) return undefined;\n  // Keep unknown kinds so rack-config round trips stay lossless; the renderer\n  // treats unrecognised kinds as generic outlines.\n  return value as TemplateBayKind;\n};\n\nconst readDeviceBaySlot = (value: unknown): DeviceBaySlot | undefined => {\n  if (!isRecord(value)) return undefined;\n  if (typeof value.id !== \"string\" || value.id.length === 0) return undefined;\n  if (typeof value.label !== \"string\") return undefined;\n  if (value.position !== undefined && !isPortPosition(value.position)) return undefined;\n  return {\n    id: value.id,\n    label: value.label,\n    ...(value.position !== undefined ? { position: value.position } : {}),\n  };\n};\n\n/**\n * Narrow one unknown bay entry to {@link DeviceBay}.\n *\n * Requires non-empty `id`, `label`, and string `bayKind`. Malformed geometry\n * fails the entry; unrecognised `bayKind` strings are kept so a rack-config\n * round trip does not silently drop forward-compatible data.\n */\nexport function readDeviceBay(value: unknown): DeviceBay | undefined {\n  if (!isRecord(value)) return undefined;\n  if (typeof value.id !== \"string\" || value.id.length === 0) return undefined;\n  if (typeof value.label !== \"string\") return undefined;\n  const bayKind = readBayKind(value.bayKind);\n  if (!bayKind) return undefined;\n  if (value.face !== undefined && !isDeviceFace(value.face)) return undefined;\n  if (value.position !== undefined && !isPortPosition(value.position)) return undefined;\n\n  let slots: DeviceBaySlot[] | undefined;\n  if (value.slots !== undefined) {\n    if (!Array.isArray(value.slots)) return undefined;\n    slots = value.slots\n      .map(readDeviceBaySlot)\n      .filter((slot): slot is DeviceBaySlot => slot !== undefined);\n  }\n\n  return {\n    id: value.id,\n    label: value.label,\n    bayKind,\n    ...(value.face !== undefined ? { face: value.face } : {}),\n    ...(value.position !== undefined ? { position: value.position } : {}),\n    ...(slots !== undefined ? { slots } : {}),\n  };\n}\n\n/**\n * Read a device node's `bays` field.\n *\n * Node data reaches us through React Flow's `Record<string, unknown>` data\n * constraint, so `bays` arrives as `unknown` and has to be re-narrowed.\n * Malformed entries are dropped; recognised bays round-trip losslessly.\n *\n * @returns the validated bays array, or `undefined` when the field is\n * absent/not an array/contains no valid entries.\n */\nexport function readDeviceBays(bays: unknown): DeviceBay[] | undefined {\n  if (!Array.isArray(bays)) return undefined;\n  const validated = bays\n    .map(readDeviceBay)\n    .filter((bay): bay is DeviceBay => bay !== undefined);\n  return validated.length > 0 ? validated : undefined;\n}\n","import { NetworkNode, RackConfig, RackDevice, Width } from '../types';\nimport { validatePlacements } from './placement-validation';\nimport { calculateDevicePositionFromU, findNextAvailableUPosition } from './u-positioning';\nimport { isRackDeviceNodeType } from './rack-device-types';\nimport { getRackMountMode, isSplitFaceRackLayout, sharesMountedFace } from './rack-layout';\nimport { resolveNodeDevicePorts } from './port-contract';\nimport { readDeviceBays } from './device-bays';\n\nexport type RackMountedNodeType = 'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu';\n\n/**\n * Configuration options for building nodes from rack schema\n */\nexport interface RackSchemaOptions {\n  /** How to handle device placement conflicts */\n  conflictPolicy?: 'strict' | 'nearest';\n  /** Whether to validate all placements before creating nodes */\n  validatePlacements?: boolean;\n  /** Custom device type mapping from schema to NetworkNode type */\n  deviceTypeMapping?: Record<string, RackMountedNodeType>;\n}\n\n/**\n * Error thrown when device placement conflicts occur in strict mode\n */\nexport class DevicePlacementError extends Error {\n  /**\n   * Every placement problem found in the rack, one human-readable line each\n   * (conflicts and out-of-bounds). The positional fields describe the first.\n   */\n  public readonly issues: string[];\n\n  constructor(\n    public rackId: string,\n    public deviceId: string,\n    public uPosition: number,\n    public uHeight: number,\n    public conflictWith?: string,\n    issues?: string[]\n  ) {\n    const first = `Device placement conflict: ${deviceId} at U${uPosition}-${uPosition + uHeight - 1} in rack ${rackId}${\n      conflictWith ? ` conflicts with ${conflictWith}` : ''\n    }`;\n    super(\n      issues && issues.length > 1\n        ? `${issues.length} device placement problems in rack ${rackId}:\\n- ${issues.join('\\n- ')}`\n        : first\n    );\n    this.name = 'DevicePlacementError';\n    this.issues = issues && issues.length > 0 ? [...issues] : [first];\n  }\n}\n\n/**\n * Validation result for device placement\n */\nexport interface DevicePlacementValidation {\n  isValid: boolean;\n  conflicts: Array<{\n    deviceId: string;\n    uPosition: number;\n    uHeight: number;\n    conflictWith: string;\n  }>;\n  outOfBounds: Array<{\n    deviceId: string;\n    uPosition: number;\n    uHeight: number;\n    rackUHeight: number;\n  }>;\n}\n\n/**\n * Validate device placements within a rack schema\n * \n * Checks for out-of-bounds placements and device-to-device conflicts.\n * Uses the generic validatePlacements utility for consistent validation logic.\n * \n * @param rack - The rack configuration to validate\n * @param _options - Validation options (currently unused)\n * @returns Validation result with conflicts and out-of-bounds errors\n * @public\n * \n * @example\n * ```ts\n * const rack: RackConfig = {\n *   id: 'rack-1',\n *   units: 42,\n *   devices: [\n *     { id: 'dev-1', unit: 1, height: 2, ... },\n *     { id: 'dev-2', unit: 2, height: 1, ... } // Conflicts with dev-1\n *   ]\n * };\n * const result = validateRackDevicePlacements(rack);\n * if (!result.isValid) {\n *   console.log('Conflicts:', result.conflicts);\n * }\n * ```\n */\nexport function validateRackDevicePlacements(\n  rack: RackConfig,\n  _options: RackSchemaOptions = {}\n): DevicePlacementValidation {\n  const splitFaceLayout = isSplitFaceRackLayout(rack);\n\n  return validatePlacements({\n    items: rack.devices,\n    checkOutOfBounds: (device) => {\n      const deviceEnd = device.unit + device.height - 1;\n      // Below U1, a non-positive height, or a non-finite U is as invalid as\n      // overrunning the top of the rack.\n      if (\n        !Number.isFinite(device.unit) ||\n        !Number.isFinite(device.height) ||\n        device.unit < 1 ||\n        device.height < 1 ||\n        deviceEnd > rack.units\n      ) {\n        return {\n          isValid: false,\n          error: {\n            deviceId: device.id,\n            uPosition: device.unit,\n            uHeight: device.height,\n            rackUHeight: rack.units,\n          },\n        };\n      }\n      return null;\n    },\n    checkConflicts: (deviceA, deviceB) => {\n      const deviceAStart = deviceA.unit;\n      const deviceAEnd = deviceA.unit + deviceA.height - 1;\n      const deviceBStart = deviceB.unit;\n      const deviceBEnd = deviceB.unit + deviceB.height - 1;\n      \n      // Check for overlap using 1a rule (start to start+height-1)\n      if (deviceAStart <= deviceBEnd && deviceAEnd >= deviceBStart) {\n        if (splitFaceLayout) {\n          const mountModeA = getRackMountMode(deviceA);\n          const mountModeB = getRackMountMode(deviceB);\n          if (!sharesMountedFace(mountModeA, mountModeB)) {\n            return null;\n          }\n        }\n\n        return {\n          hasConflict: true,\n          conflict: {\n            deviceId: deviceA.id,\n            uPosition: deviceA.unit,\n            uHeight: deviceA.height,\n            conflictWith: deviceB.id,\n          },\n        };\n      }\n      return null;\n    },\n  });\n}\n\n/**\n * Convert a RackDevice to NetworkNode with proper U positioning\n */\nfunction createDeviceNode(\n  device: RackDevice,\n  rack: NetworkNode,\n  options: RackSchemaOptions = {}\n): NetworkNode<RackMountedNodeType> {\n  const { deviceTypeMapping = {} } = options;\n  \n  // Map device type to NetworkNode type\n  const nodeType: RackMountedNodeType = deviceTypeMapping[device.type] || 'device';\n  \n  // Calculate pixel position from U position\n  const position = calculateDevicePositionFromU(rack, device.unit, device.height, device);\n  \n  \n  return {\n    id: device.id,\n    type: nodeType,\n    position,\n    parentId: rack.id,\n    extent: 'parent',\n    data: {\n      label: device.name,\n      status: device.status || 'active',\n      deviceType: device.type,\n      uHeight: device.height,\n      uPosition: device.unit,\n      isRackMounted: true,\n      width: device.width || Width.FULL,\n      // face is controlled at node-level; default computed in DeviceNode when absent\n      role: device.role, // Pass through the role property\n      ports: device.ports,\n      portGroups: device.portGroups,\n      rearPortGroups: device.rearPortGroups ?? device.rearPorts,\n      // Bays authored on the rack config land on the same node-data field the\n      // renderer reads, so rackConfig.devices[].bays and data.bays are equivalent.\n      bays: device.bays,\n      connections: device.connections,\n      frontImageUrl: device.frontImageUrl,\n      rearImageUrl: device.rearImageUrl,\n      frontImageAttribution: device.frontImageAttribution,\n      rearImageAttribution: device.rearImageAttribution,\n      imageFit: device.imageFit,\n      imagePosition: device.imagePosition,\n      imageRepeat: device.imageRepeat,\n      stretchToFit: device.stretchToFit,\n      rackWidthPx: rack.data.rackWidthPx, // Pass through parent rack's custom width\n      rackLayout: rack.data.rackLayout,\n      rackFaceGapPx: rack.data.rackFaceGapPx,\n      mountMode: getRackMountMode(device),\n    },\n  };\n}\n\n/**\n * Convert a RackConfig to NetworkNode\n */\nfunction createRackNode(rack: RackConfig): NetworkNode {\n  return {\n    id: rack.id,\n    type: 'rack',\n    position: rack.position,\n    data: {\n      label: rack.name,\n      status: 'active',\n      uHeight: rack.units,\n      uNumberingDirection: rack.uNumberingDirection || 'bottom-up',\n      customHeaderText: rack.customHeaderText,\n      rackWidthPx: rack.rackWidthPx, // Pass through optional custom width\n      rackLayout: rack.rackLayout,\n      rackFaceGapPx: rack.rackFaceGapPx,\n    },\n  };\n}\n\n/**\n * Build NetworkNode array from RackConfig array with comprehensive validation\n * \n * Converts rack configurations to React Flow nodes with automatic validation\n * and conflict resolution. Supports strict and nearest conflict policies.\n * \n * @param racks - Array of rack configurations\n * @param options - Build options including conflict policy and validation settings\n * @returns Array of NetworkNode objects ready for React Flow\n * @throws {DevicePlacementError} If validation fails in strict mode\n * @public\n * \n * @example\n * ```ts\n * const racks: RackConfig[] = [{\n *   id: 'rack-1',\n *   name: 'Main Rack',\n *   units: 42,\n *   devices: [...]\n * }];\n * const nodes = buildNodesFromRackConfig(racks, {\n *   conflictPolicy: 'strict',\n *   validatePlacements: true\n * });\n * ```\n */\nexport function buildNodesFromRackConfig(\n  racks: RackConfig[],\n  options: RackSchemaOptions = {}\n): NetworkNode[] {\n  const { conflictPolicy = 'strict', validatePlacements = true } = options;\n  const nodes: NetworkNode[] = [];\n  const deviceNodes: NetworkNode[] = [];\n  \n  // Validate all racks first if requested\n  if (validatePlacements) {\n    for (const rack of racks) {\n      const validation = validateRackDevicePlacements(rack, options);\n      \n      if (!validation.isValid) {\n        if (conflictPolicy === 'strict') {\n          // Report all conflicts and out-of-bounds\n          const errors: string[] = [];\n          \n          validation.conflicts.forEach(conflict => {\n            errors.push(\n              `Device ${conflict.deviceId} at U${conflict.uPosition}-${conflict.uPosition + conflict.uHeight - 1} conflicts with ${conflict.conflictWith}`\n            );\n          });\n          \n          validation.outOfBounds.forEach(bound => {\n            errors.push(\n              bound.uPosition < 1 || bound.uHeight < 1\n                ? `Device ${bound.deviceId} at U${bound.uPosition} (height ${bound.uHeight}U) is outside the rack (U1-U${bound.rackUHeight})`\n                : `Device ${bound.deviceId} at U${bound.uPosition}-${bound.uPosition + bound.uHeight - 1} exceeds rack height (${bound.rackUHeight}U)`\n            );\n          });\n          \n          const firstConflict = validation.conflicts[0];\n          const firstBound = validation.outOfBounds[0];\n          throw new DevicePlacementError(\n            rack.id,\n            firstConflict?.deviceId ?? firstBound?.deviceId ?? 'unknown',\n            firstConflict?.uPosition ?? firstBound?.uPosition ?? 1,\n            firstConflict?.uHeight ?? firstBound?.uHeight ?? 1,\n            firstConflict?.conflictWith,\n            errors\n          );\n        }\n      }\n    }\n  }\n  \n  // Create rack nodes first (required for device parent references)\n  for (const rack of racks) {\n    const rackNode = createRackNode(rack);\n    nodes.push(rackNode);\n    \n    // Create device nodes for this rack.\n    for (const device of rack.devices) {\n      if (conflictPolicy === 'nearest') {\n        // createDeviceNode positions a device at its requested U but never throws\n        // on a conflict, so the 'nearest' policy must actively resolve the closest\n        // free U against the devices already placed in THIS rack (a throw-based\n        // catch here would be dead code).\n        const siblingNodes = deviceNodes.filter((n) => n.parentId === rackNode.id);\n        const probe = {\n          id: device.id,\n          type: 'device',\n          position: { x: 0, y: 0 },\n          parentId: rackNode.id,\n          data: {\n            label: device.name,\n            uHeight: device.height,\n            mountMode: getRackMountMode(device),\n          },\n        } as NetworkNode;\n        const availablePosition = findNextAvailableUPosition(\n          rackNode,\n          probe,\n          siblingNodes,\n          Number.isFinite(device.unit) ? Math.max(1, device.unit) : 1\n        );\n\n        if (availablePosition === undefined) {\n          throw new DevicePlacementError(rack.id, device.id, device.unit, device.height);\n        }\n        if (availablePosition !== device.unit) {\n          console.warn(\n            `Device ${device.id} moved from U${device.unit} to U${availablePosition} due to conflict`\n          );\n        }\n        const placedDevice =\n          availablePosition === device.unit\n            ? device\n            : { ...device, unit: availablePosition };\n        deviceNodes.push(createDeviceNode(placedDevice, rackNode, options));\n      } else {\n        // strict / default: place at the requested U. Strict conflicts already\n        // threw during validation above.\n        deviceNodes.push(createDeviceNode(device, rackNode, options));\n      }\n    }\n  }\n  \n  // Add device nodes after all rack nodes\n  nodes.push(...deviceNodes);\n  \n  return nodes;\n}\n\n/**\n * Create a RackConfig from existing NetworkNode data (reverse operation)\n */\nexport function createRackConfigFromNodes(nodes: NetworkNode[]): RackConfig[] {\n  const racks: RackConfig[] = [];\n  const rackNodes = nodes.filter(node => node.type === 'rack');\n  \n  for (const rackNode of rackNodes) {\n    const deviceNodes = nodes\n      .filter(node => isRackDeviceNodeType(node.type) && node.parentId === rackNode.id)\n      .sort((left, right) => {\n        const leftU = left.data.uPosition || 1;\n        const rightU = right.data.uPosition || 1;\n        if (leftU !== rightU) {\n          return leftU - rightU;\n        }\n        return left.id.localeCompare(right.id);\n      });\n    \n    const devices: RackDevice[] = deviceNodes.map(deviceNode => {\n      const flatPorts = resolveNodeDevicePorts(deviceNode.data);\n      return {\n        id: deviceNode.id,\n        name: deviceNode.data.label,\n        unit: deviceNode.data.uPosition || 1,\n        height: deviceNode.data.uHeight || 1,\n        type: (deviceNode.data.deviceType as RackDevice['type']) || 'server',\n        width: deviceNode.data.width === Width.FULL || deviceNode.data.width === Width.HALF\n          ? deviceNode.data.width\n          : undefined,\n        ports: flatPorts,\n        portGroups: Array.isArray(deviceNode.data.portGroups) ? deviceNode.data.portGroups : undefined,\n        rearPortGroups: Array.isArray(deviceNode.data.rearPortGroups) ? deviceNode.data.rearPortGroups : undefined,\n        bays: readDeviceBays(deviceNode.data.bays),\n        role: typeof deviceNode.data.role === 'string' ? deviceNode.data.role : undefined,\n        status: deviceNode.data.status,\n        connections: deviceNode.data.connections,\n        frontImageUrl: deviceNode.data.frontImageUrl,\n        rearImageUrl: deviceNode.data.rearImageUrl,\n        frontImageAttribution: deviceNode.data.frontImageAttribution,\n        rearImageAttribution: deviceNode.data.rearImageAttribution,\n        imageFit: deviceNode.data.imageFit,\n        imagePosition: deviceNode.data.imagePosition,\n        imageRepeat: deviceNode.data.imageRepeat,\n        stretchToFit: deviceNode.data.stretchToFit,\n        mountMode: getRackMountMode(deviceNode.data),\n      };\n    });\n    \n    racks.push({\n      id: rackNode.id,\n      name: rackNode.data.label,\n      position: rackNode.position,\n      units: rackNode.data.uHeight || 42,\n      uNumberingDirection: rackNode.data.uNumberingDirection || 'bottom-up',\n      customHeaderText: rackNode.data.customHeaderText,\n      rackLayout: rackNode.data.rackLayout,\n      rackFaceGapPx: rackNode.data.rackFaceGapPx,\n      devices,\n      rackWidthPx: rackNode.data.rackWidthPx, // Preserve custom width\n    });\n  }\n  \n  return racks;\n}\n\n/**\n * Update device U position in a rack schema\n */\nexport function updateDeviceUPosition(\n  racks: RackConfig[],\n  rackId: string,\n  deviceId: string,\n  newUPosition: number\n): RackConfig[] {\n  return racks.map(rack => {\n    if (rack.id !== rackId) return rack;\n    \n    return {\n      ...rack,\n      devices: rack.devices.map(device => {\n        if (device.id !== deviceId) return device;\n        return { ...device, unit: newUPosition };\n      }),\n    };\n  });\n}\n\n/**\n * Add a device to a rack schema\n */\nexport function addDeviceToRack(\n  racks: RackConfig[],\n  rackId: string,\n  device: RackDevice,\n  options: RackSchemaOptions = {}\n): RackConfig[] {\n  const { conflictPolicy = 'strict' } = options;\n  \n  return racks.map(rack => {\n    if (rack.id !== rackId) return rack;\n    \n    // Check for conflicts if in strict mode\n    if (conflictPolicy === 'strict') {\n      const validation = validateRackDevicePlacements(\n        { ...rack, devices: [...rack.devices, device] },\n        options\n      );\n      \n      if (!validation.isValid) {\n        throw new DevicePlacementError(rackId, device.id, device.unit, device.height);\n      }\n    }\n    \n    return {\n      ...rack,\n      devices: [...rack.devices, device],\n    };\n  });\n}\n\n/**\n * Remove a device from a rack schema\n */\nexport function removeDeviceFromRack(\n  racks: RackConfig[],\n  rackId: string,\n  deviceId: string\n): RackConfig[] {\n  return racks.map(rack => {\n    if (rack.id !== rackId) return rack;\n    \n    return {\n      ...rack,\n      devices: rack.devices.filter(device => device.id !== deviceId),\n    };\n  });\n}\n","import { NetworkNode, SpliceConfig, SpliceTrayConfig } from '../types';\nimport { validatePlacements } from './placement-validation';\nimport { calculateSplicePositionFromNumberWithData, findNextAvailableHolderPosition, HOLDERS_PER_TRAY, isHolderPositionAvailable } from './splice-positioning';\n\n/**\n * Configuration options for building nodes from splice schema\n */\nexport interface SpliceSchemaOptions {\n  /** How to handle splice placement conflicts */\n  conflictPolicy?: 'strict' | 'nearest';\n  /** Whether to validate all placements before creating nodes */\n  validatePlacements?: boolean;\n}\n\n/**\n * Error thrown when splice placement conflicts occur in strict mode\n */\nexport class SplicePlacementError extends Error {\n  constructor(\n    public trayId: string,\n    public spliceId: string,\n    public holder: number,\n    public conflictWith?: string\n  ) {\n    super(\n      `Splice placement conflict: ${spliceId} at holder ${holder} in tray ${trayId}${\n        conflictWith ? ` conflicts with ${conflictWith}` : ''\n      }`\n    );\n    this.name = 'SplicePlacementError';\n  }\n}\n\n/**\n * Validation result for splice placement\n */\nexport interface SplicePlacementValidation {\n  isValid: boolean;\n  conflicts: Array<{\n    spliceId: string;\n    holder: number;\n    conflictWith: string;\n  }>;\n  outOfBounds: Array<{\n    spliceId: string;\n    holder: number;\n    max: number;\n  }>;\n}\n\n/**\n * Validate splice placements within a tray schema\n * \n * Checks for out-of-bounds placements and splice-to-splice conflicts.\n * Uses the generic validatePlacements utility for consistent validation logic.\n * \n * @param tray - The splice tray configuration to validate\n * @param _options - Validation options (currently unused)\n * @returns Validation result with conflicts and out-of-bounds errors\n * @public\n * \n * @example\n * ```ts\n * const tray: SpliceTrayConfig = {\n *   id: 'tray-1',\n *   splices: [\n *     { id: 'splice-1', holder: 1, ... },\n *     { id: 'splice-2', holder: 1, ... } // Conflicts with splice-1\n *   ]\n * };\n * const result = validateSplicePlacements(tray);\n * if (!result.isValid) {\n *   console.log('Conflicts:', result.conflicts);\n * }\n * ```\n */\nexport function validateSplicePlacements(\n  tray: SpliceTrayConfig,\n  _options: SpliceSchemaOptions = {}\n): SplicePlacementValidation {\n  return validatePlacements({\n    items: tray.splices,\n    checkOutOfBounds: (splice) => {\n      if (splice.holder < 1 || splice.holder > HOLDERS_PER_TRAY) {\n        return {\n          isValid: false,\n          error: {\n            spliceId: splice.id,\n            holder: splice.holder,\n            max: HOLDERS_PER_TRAY,\n          },\n        };\n      }\n      return null;\n    },\n    checkConflicts: (spliceA, spliceB) => {\n      // Check if two splices are in the same holder position\n      if (spliceA.holder === spliceB.holder) {\n        return {\n          hasConflict: true,\n          conflict: {\n            spliceId: spliceA.id,\n            holder: spliceA.holder,\n            conflictWith: spliceB.id,\n          },\n        };\n      }\n      return null;\n    },\n  });\n}\n\n/**\n * Convert a SpliceConfig to NetworkNode with proper holder positioning\n */\nfunction createSpliceNode(\n  splice: SpliceConfig,\n  tray: NetworkNode\n): NetworkNode {\n  // Calculate pixel position from holder position, using mode-aware positioning\n  const isRibbonMode = splice.mode === 'ribbon';\n  const position = calculateSplicePositionFromNumberWithData(tray, splice.holder, isRibbonMode);\n  \n  return {\n    id: splice.id,\n    type: 'splice',\n    position,\n    parentId: tray.id,\n    extent: 'parent',\n    data: {\n      label: splice.name,\n      holderPosition: splice.holder,\n      lossDb: splice.lossDb,\n      mode: splice.mode || 'single',\n      ribbonFiberIds: splice.ribbonFiberIds,\n    },\n  };\n}\n\n/**\n * Convert a SpliceTrayConfig to NetworkNode\n */\nfunction createTrayNode(tray: SpliceTrayConfig): NetworkNode {\n  return {\n    id: tray.id,\n    type: 'splice-tray',\n    position: tray.position,\n    data: {\n      label: tray.name,\n      status: 'active',\n    },\n  };\n}\n\n/**\n * Build NetworkNode array from SpliceTrayConfig array with comprehensive validation\n * \n * Converts splice tray configurations to React Flow nodes with automatic validation\n * and conflict resolution. Supports strict and nearest conflict policies.\n * \n * @param trays - Array of splice tray configurations\n * @param options - Build options including conflict policy and validation settings\n * @returns Array of NetworkNode objects ready for React Flow\n * @throws {SplicePlacementError} If validation fails in strict mode\n * @public\n * \n * @example\n * ```ts\n * const trays: SpliceTrayConfig[] = [{\n *   id: 'tray-1',\n *   name: 'Tray 1',\n *   splices: [...]\n * }];\n * const nodes = buildNodesFromSpliceConfig(trays, {\n *   conflictPolicy: 'strict',\n *   validatePlacements: true\n * });\n * ```\n */\nexport function buildNodesFromSpliceConfig(\n  trays: SpliceTrayConfig[],\n  options: SpliceSchemaOptions = {}\n): NetworkNode[] {\n  const { conflictPolicy = 'strict', validatePlacements = true } = options;\n  const nodes: NetworkNode[] = [];\n  const spliceNodes: NetworkNode[] = [];\n  \n  // Validate all trays first if requested\n  if (validatePlacements) {\n    for (const tray of trays) {\n      const validation = validateSplicePlacements(tray, options);\n      \n      if (!validation.isValid) {\n        if (conflictPolicy === 'strict') {\n          // Report all conflicts and out-of-bounds\n          const errors: string[] = [];\n          \n          validation.conflicts.forEach(conflict => {\n            errors.push(\n              `Splice ${conflict.spliceId} at holder ${conflict.holder} conflicts with ${conflict.conflictWith}`\n            );\n          });\n          \n          validation.outOfBounds.forEach(bound => {\n            errors.push(\n              `Splice ${bound.spliceId} at holder ${bound.holder} is out of bounds (max: ${bound.max})`\n            );\n          });\n          \n          throw new SplicePlacementError(\n            tray.id,\n            validation.conflicts[0]?.spliceId || validation.outOfBounds[0]?.spliceId || 'unknown',\n            validation.conflicts[0]?.holder || validation.outOfBounds[0]?.holder || 1\n          );\n        }\n      }\n    }\n  }\n  \n  // Create tray nodes first (required for splice parent references)\n  for (const tray of trays) {\n    const trayNode = createTrayNode(tray);\n    nodes.push(trayNode);\n    \n    // Create splice nodes for this tray\n    for (const splice of tray.splices) {\n      try {\n        let resolvedHolder = splice.holder;\n        if (conflictPolicy === 'nearest') {\n          const placeholderSplice: NetworkNode = {\n            id: splice.id,\n            type: 'splice',\n            position: { x: 0, y: 0 },\n            data: { label: splice.name, holderPosition: splice.holder },\n          };\n          const allNodesForConflictCheck = [...nodes, ...spliceNodes];\n          if (!isHolderPositionAvailable(trayNode, placeholderSplice, splice.holder, allNodesForConflictCheck)) {\n            resolvedHolder = findNextAvailableHolderPosition(trayNode, placeholderSplice, allNodesForConflictCheck, splice.holder);\n          }\n        }\n        const adjustedSplice = resolvedHolder !== splice.holder ? { ...splice, holder: resolvedHolder } : splice;\n        const spliceNode = createSpliceNode(adjustedSplice, trayNode);\n        spliceNodes.push(spliceNode);\n      } catch (error) {\n        if (conflictPolicy === 'strict') {\n          throw error;\n        } else if (conflictPolicy === 'nearest') {\n          // Try to find nearest available position\n          // Pass all nodes (tray + existing splices) for proper conflict detection\n          const allNodesForConflictCheck = [...nodes, ...spliceNodes];\n          const availablePosition = findNextAvailableHolderPosition(\n            trayNode,\n            { id: splice.id, type: 'splice', position: { x: 0, y: 0 }, data: { label: splice.name, holderPosition: splice.holder } } as NetworkNode,\n            allNodesForConflictCheck,\n            splice.holder\n          );\n          \n          if (availablePosition !== splice.holder) {\n            console.warn(\n              `Splice ${splice.id} moved from holder ${splice.holder} to holder ${availablePosition} due to conflict`\n            );\n            \n            const adjustedSplice = { ...splice, holder: availablePosition };\n            const spliceNode = createSpliceNode(adjustedSplice, trayNode);\n            spliceNodes.push(spliceNode);\n          } else {\n            throw new SplicePlacementError(tray.id, splice.id, splice.holder);\n          }\n        }\n      }\n    }\n  }\n  \n  // Add splice nodes after all tray nodes\n  nodes.push(...spliceNodes);\n  \n  return nodes;\n}\n\n/**\n * Create a SpliceTrayConfig from existing NetworkNode data (reverse operation)\n */\nexport function createSpliceConfigFromNodes(nodes: NetworkNode[]): SpliceTrayConfig[] {\n  const trays: SpliceTrayConfig[] = [];\n  const trayNodes = nodes.filter(node => node.type === 'splice-tray');\n  \n  for (const trayNode of trayNodes) {\n    const spliceNodes = nodes.filter(\n      node => node.type === 'splice' && node.parentId === trayNode.id\n    );\n    \n    const splices: SpliceConfig[] = spliceNodes.map(spliceNode => ({\n      id: spliceNode.id,\n      name: spliceNode.data.label,\n      holder: spliceNode.data.holderPosition || 1,\n      lossDb: spliceNode.data.lossDb,\n      mode: spliceNode.data.mode || 'single',\n      ribbonFiberIds: spliceNode.data.ribbonFiberIds,\n    }));\n    \n    trays.push({\n      id: trayNode.id,\n      name: trayNode.data.label,\n      position: trayNode.position,\n      splices,\n    });\n  }\n  \n  return trays;\n}\n\n/**\n * Update splice holder position in a splice schema\n */\nexport function updateSpliceHolderPosition(\n  trays: SpliceTrayConfig[],\n  trayId: string,\n  spliceId: string,\n  newHolder: number\n): SpliceTrayConfig[] {\n  return trays.map(tray => {\n    if (tray.id !== trayId) return tray;\n    \n    return {\n      ...tray,\n      splices: tray.splices.map(splice => {\n        if (splice.id !== spliceId) return splice;\n        return { ...splice, holder: newHolder };\n      }),\n    };\n  });\n}\n\n/**\n * Add a splice to a tray schema\n */\nexport function addSpliceToTray(\n  trays: SpliceTrayConfig[],\n  trayId: string,\n  splice: SpliceConfig,\n  options: SpliceSchemaOptions = {}\n): SpliceTrayConfig[] {\n  const { conflictPolicy = 'strict' } = options;\n  \n  return trays.map(tray => {\n    if (tray.id !== trayId) return tray;\n    \n    // Check for conflicts if in strict mode\n    if (conflictPolicy === 'strict') {\n      const validation = validateSplicePlacements(\n        { ...tray, splices: [...tray.splices, splice] },\n        options\n      );\n      \n      if (!validation.isValid) {\n        throw new SplicePlacementError(trayId, splice.id, splice.holder);\n      }\n    }\n    \n    return {\n      ...tray,\n      splices: [...tray.splices, splice],\n    };\n  });\n}\n\n/**\n * Remove a splice from a tray schema\n */\nexport function removeSpliceFromTray(\n  trays: SpliceTrayConfig[],\n  trayId: string,\n  spliceId: string\n): SpliceTrayConfig[] {\n  return trays.map(tray => {\n    if (tray.id !== trayId) return tray;\n    \n    return {\n      ...tray,\n      splices: tray.splices.filter(splice => splice.id !== spliceId),\n    };\n  });\n}\n","import type { DiagramNodeKind, NetworkNode, NetworkNodeType } from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Node-id authority\n//\n// The inventory/netbox converters mint top-level node ids by prefixing the\n// domain id: `rack-<id>` (see inventory.ts inventoryRackToNetworkNode) and\n// `device-<id>` (inventoryDeviceToNetworkNode). Consumers were reverse-\n// engineering those prefixes in dozens of places. This module is the single\n// authority for building/parsing them, mirroring what handle-ids does for\n// handle strings.\n//\n// IMPORTANT: `node.type` is the authoritative discriminator for what a node IS\n// (see {@link getNodeKind}/{@link isRackNode}/{@link isDeviceNode}). The id\n// helpers here only cover the converter-minted prefix grammar — they must NOT\n// be used to decide a node's kind, and they do not understand compound consumer\n// grammars layered on top of the prefix (e.g. `device-<rackId>-<unitId>`): they\n// return everything after the prefix verbatim as the id.\n// ---------------------------------------------------------------------------\n\nconst RACK_NODE_ID_PREFIX = \"rack-\";\nconst DEVICE_NODE_ID_PREFIX = \"device-\";\n\n/**\n * Build the top-level node id for a rack: `rack-<rackId>` — the exact form the\n * inventory/netbox converters mint.\n *\n * @remarks\n * Use this instead of hand-writing the `rack-` prefix so consumer code stays in\n * lockstep with the converters. Pairs with {@link parseNodeId} and\n * {@link buildDeviceNodeId}. This is an id-string helper only; to decide whether\n * a node is a rack, use {@link isRackNode} (which reads `node.type`).\n *\n * @example\n * ```ts\n * buildRackNodeId(\"42\"); // \"rack-42\"\n * buildRackNodeId(42);   // \"rack-42\"\n * ```\n * @public\n */\nexport function buildRackNodeId(rackId: string | number): string {\n  return `${RACK_NODE_ID_PREFIX}${rackId}`;\n}\n\n/**\n * Build the top-level node id for a device: `device-<deviceId>` — the exact\n * form the inventory/netbox converters mint.\n *\n * @remarks\n * Consumers that layer a compound scheme on top (e.g.\n * `device-<rackId>-<unitId>`) can pass the already-composed id part as\n * `deviceId`; {@link parseNodeId} returns it back verbatim. This is an\n * id-string helper only; to decide whether a node is a device/equipment node,\n * use {@link isDeviceNode} (which reads `node.type`).\n *\n * @example\n * ```ts\n * buildDeviceNodeId(\"7\");             // \"device-7\"\n * buildDeviceNodeId(`${rackId}-${u}`); // \"device-<rackId>-<u>\"\n * ```\n * @public\n */\nexport function buildDeviceNodeId(deviceId: string | number): string {\n  return `${DEVICE_NODE_ID_PREFIX}${deviceId}`;\n}\n\n/**\n * A parsed converter-minted node id.\n * @public\n */\nexport interface ParsedNodeId {\n  /** Which converter prefix produced the id. */\n  kind: \"rack\" | \"device\";\n  /**\n   * Everything after the prefix, verbatim. For a compound consumer scheme\n   * (`device-<rackId>-<unitId>`) this is the whole `<rackId>-<unitId>` tail —\n   * decomposing it further is the consumer's concern.\n   */\n  id: string;\n}\n\n/**\n * Parse a converter-minted node id back into `{ kind, id }`, or `null` when the\n * id carries neither the `rack-` nor the `device-` prefix.\n *\n * @remarks\n * Total and prefix-only: it never throws and only recognizes the two\n * converter-minted prefixes. It deliberately does NOT classify fibre-scene node\n * ids (`fiber-3`, `tube-2`, `closure-1`, …) or bare ids — those return `null`.\n * `kind` is derived from the id STRING here purely to invert the builders; do\n * not use it as a node's semantic kind (that is `node.type`'s job — see\n * {@link getNodeKind}).\n *\n * @example\n * ```ts\n * parseNodeId(\"rack-42\");          // { kind: \"rack\", id: \"42\" }\n * parseNodeId(\"device-r1-u2\");     // { kind: \"device\", id: \"r1-u2\" }\n * parseNodeId(\"fiber-3\");          // null\n * ```\n * @public\n */\nexport function parseNodeId(\n  nodeId: string | null | undefined\n): ParsedNodeId | null {\n  if (!nodeId) return null;\n  if (nodeId.startsWith(RACK_NODE_ID_PREFIX)) {\n    return { kind: \"rack\", id: nodeId.slice(RACK_NODE_ID_PREFIX.length) };\n  }\n  if (nodeId.startsWith(DEVICE_NODE_ID_PREFIX)) {\n    return { kind: \"device\", id: nodeId.slice(DEVICE_NODE_ID_PREFIX.length) };\n  }\n  return null;\n}\n\n/** The `node.type`s the document model unifies as the `equipment` kind. */\nconst EQUIPMENT_NODE_TYPES: ReadonlySet<string> = new Set<NetworkNodeType>([\n  \"server\",\n  \"switch\",\n  \"router\",\n  \"device\",\n]);\n\n/** The `node.type`s the document model unifies as the `container` kind. */\nconst CONTAINER_NODE_TYPES: ReadonlySet<string> = new Set<NetworkNodeType>([\n  \"patch-panel\",\n  \"closure\",\n  \"splice-tray\",\n]);\n\n/**\n * Minimal shape read by the type-driven predicates — only `type` matters.\n *\n * @remarks\n * This is the parameter type of the public {@link getNodeKind},\n * {@link isRackNode} and {@link isDeviceNode}, so it is exported for consumers\n * that need to name it (wrapper helpers, generic utilities). Any object with a\n * `type` satisfies it — a full {@link NetworkNode}, a document node, or a bare\n * `{ type: \"rack\" }` literal.\n *\n * @public\n */\nexport type NodeTypeCarrier =\n  | Pick<NetworkNode, \"type\">\n  | { type?: string | null };\n\n/**\n * Map a node's `type` to its {@link DiagramNodeKind}, mirroring the document\n * adapter's authoritative discrimination (`toDiagramNodeKind`). Returns `null`\n * when `type` is missing/empty.\n *\n * @remarks\n * `node.type` — never the id string — is authoritative for what a node is. Use\n * this (and {@link isRackNode}/{@link isDeviceNode}) rather than sniffing id\n * prefixes. The mapping collapses `server`/`switch`/`router`/`device` into\n * `equipment`, `vertical-pdu` into `pdu`, `patch-panel`/`closure`/`splice-tray`\n * into `container`, and `fibre-split` into `splitter`; the remaining fibre/rack\n * types map 1:1. Subtype detail (the adapter's `subtype`) is intentionally not\n * returned — this is a kind-only helper.\n *\n * @example\n * ```ts\n * getNodeKind({ type: \"switch\" }); // \"equipment\"\n * getNodeKind({ type: \"rack\" });   // \"rack\"\n * getNodeKind({ type: undefined }); // null\n * ```\n * @public\n */\nexport function getNodeKind(node: NodeTypeCarrier): DiagramNodeKind | null {\n  const type = node?.type;\n  if (typeof type !== \"string\" || type.length === 0) return null;\n  if (type === \"vertical-pdu\") return \"pdu\";\n  if (EQUIPMENT_NODE_TYPES.has(type)) return \"equipment\";\n  if (CONTAINER_NODE_TYPES.has(type)) return \"container\";\n  if (type === \"fibre-split\") return \"splitter\";\n  // rack, fiber, tube, cable, multi-tube-cable, splice, coupler,\n  // fibre-flow-cable and fibre-flow-closure map straight through.\n  return type as DiagramNodeKind;\n}\n\n/**\n * True when a node is a rack (`node.type === \"rack\"`). Type-driven — never sniffs\n * the id.\n *\n * @remarks\n * Prefer this over `node.id.startsWith(\"rack-\")`: the id prefix is a converter\n * detail, whereas `node.type` is the authoritative discriminator and survives\n * the document round-trip.\n *\n * @example\n * ```ts\n * isRackNode({ type: \"rack\" });   // true\n * isRackNode({ type: \"device\" }); // false\n * ```\n * @public\n */\nexport function isRackNode(node: NodeTypeCarrier): boolean {\n  return node?.type === \"rack\";\n}\n\n/**\n * True when a node is a device/equipment node — i.e. its {@link getNodeKind} is\n * `equipment` (`server`, `switch`, `router` or `device`). Type-driven — never\n * sniffs the id.\n *\n * @remarks\n * This is broader than `node.type === \"device\"`: the document model unifies the\n * whole equipment family under one kind, and a rack-mounted switch or server is\n * as much a \"device node\" as one the inventory converter happened to mint with\n * `type: \"device\"`. If you need only the literal `device` type, compare\n * `node.type` directly. Note the asymmetry with {@link buildDeviceNodeId}: the\n * `device-` id prefix only covers converter-minted device ids, whereas this\n * predicate covers the semantic equipment family.\n *\n * @example\n * ```ts\n * isDeviceNode({ type: \"switch\" }); // true\n * isDeviceNode({ type: \"rack\" });   // false\n * ```\n * @public\n */\nexport function isDeviceNode(node: NodeTypeCarrier): boolean {\n  return getNodeKind(node) === \"equipment\";\n}\n","import type { NetworkEdge, NetworkNode } from '../types';\n\nexport type PathLayoutDirection = 'horizontal' | 'vertical';\n\nexport interface PathLayoutStepInput {\n  id: string;\n  type?: NetworkNode['type'];\n  data?: NetworkNode['data'];\n}\n\nexport interface PathLayoutEdgeInput {\n  id: string;\n  source: string;\n  target: string;\n  type?: NetworkEdge['type'];\n  data?: NetworkEdge['data'];\n}\n\nexport interface BuildPathLayoutOptions {\n  direction?: PathLayoutDirection;\n  spacing?: number;\n  origin?: { x: number; y: number };\n}\n\nconst DEFAULT_SPACING = 220;\n\n/**\n * Lay out a sequential path of nodes and connecting edges with deterministic spacing.\n * @public\n */\nexport function buildPathLayoutGraph(\n  steps: PathLayoutStepInput[],\n  edges: PathLayoutEdgeInput[],\n  options: BuildPathLayoutOptions = {},\n): { nodes: NetworkNode[]; edges: NetworkEdge[] } {\n  const direction = options.direction ?? 'horizontal';\n  const spacing = options.spacing ?? DEFAULT_SPACING;\n  const origin = options.origin ?? { x: 0, y: 0 };\n\n  const nodes = steps.map((step, index) => ({\n    id: step.id,\n    type: step.type ?? 'fiber',\n    position:\n      direction === 'vertical'\n        ? { x: origin.x, y: origin.y + index * spacing }\n        : { x: origin.x + index * spacing, y: origin.y },\n    data: step.data ?? { label: step.id },\n  })) as NetworkNode[];\n\n  const layoutEdges: NetworkEdge[] = edges.map((edge) => ({\n    id: edge.id,\n    source: edge.source,\n    target: edge.target,\n    type: edge.type ?? 'fiber',\n    data: edge.data,\n  }));\n\n  return { nodes, edges: layoutEdges };\n}\n","import { POWER_SOURCE_NODE_TYPES } from \"../core/connection-validation\";\nimport type { BreakerWayInstanceState, NetworkEdge, NetworkNode } from \"../types\";\nimport { isPowerInletHandleId, parseHandleId } from \"./handle-ids\";\n\n/**\n * Client-side power energisation overlay.\n *\n * Energisation is pure graph reachability over the **existing** power edges:\n * starting from designated source nodes (distribution-board incomers by\n * default), power flows out through **closed** breaker ways, onward through\n * power-distribution nodes (PDUs / UPS / downstream boards) and terminates at\n * consuming devices.\n *\n * CRITICAL INVARIANT (IEC 61970 / PRD workstream C4): breaker state never\n * alters the edge set. Opening or tripping a way changes only the returned\n * overlay — the caller's `nodes`/`edges` arrays are never mutated, and no edge\n * is added or removed. A de-energised edge is still an edge.\n *\n * The cascade semantics are promoted from the example-site `impactAnalysis`\n * prototype: the consumer side of a power edge is the side that is not a power\n * source, a device that loses every feed is `unpowered`, and a device that\n * loses some but not all of several feeds is `degraded`.\n *\n */\n\n/**\n * Per-device power state.\n *\n * - `powered` — every power feed into the device is energised.\n * - `degraded` — at least one, but not all, feeds are energised (lost\n *   redundancy on a multi-feed device).\n * - `unpowered` — no feed is energised.\n * @public\n */\nexport type DevicePowerState = \"powered\" | \"unpowered\" | \"degraded\";\n\n/**\n * Direction power travels across an edge, relative to the edge's own\n * `source`/`target` endpoints.\n * @public\n */\nexport type PowerFlowDirection = \"source-to-target\" | \"target-to-source\";\n\n/**\n * Energisation overlay for a single power edge.\n * @public\n */\nexport interface EdgeEnergization {\n  /** Whether power reaches this edge from a source through closed ways. */\n  energized: boolean;\n  /** Which way power flows; omitted when the edge is de-energised. */\n  flowDirection?: PowerFlowDirection;\n}\n\n/**\n * Options for {@link computeEnergization}.\n * @public\n */\nexport interface EnergizationOptions {\n  /**\n   * Explicit source node ids. When omitted, every `distribution-board` node is\n   * treated as a source (its incomers are assumed live) **provided it has no\n   * upstream power feed of its own**. Unfed PDU/UPS nodes are also sources,\n   * including those in independent components. An empty array disables all sources.\n   */\n  sources?: readonly string[];\n  /**\n   * Switch state assumed for a way that has no entry in the board's\n   * `wayState`. Defaults to `\"closed\"` — an unstated breaker is closed.\n   */\n  defaultWayState?: BreakerWayInstanceState[\"switchState\"];\n}\n\n/**\n * Result of {@link computeEnergization}.\n * @public\n */\nexport interface EnergizationResult {\n  /** Overlay keyed by edge id. Contains an entry for every power edge. */\n  edges: Record<string, EdgeEnergization>;\n  /** Power state keyed by node id, for every node that consumes power. */\n  devices: Record<string, DevicePowerState>;\n  /** Ids of nodes power reaches (sources included). */\n  energizedNodeIds: string[];\n}\n\nconst POWER_SOURCE_TYPE_SET = new Set<string>(POWER_SOURCE_NODE_TYPES);\n\nconst isPowerEdge = (edge: NetworkEdge): boolean =>\n  edge.type === \"power\" || (edge.data as { kind?: string } | undefined)?.kind === \"power\";\n\n/** A node that can pass power onward (board, PDU, UPS) rather than consume it. */\nconst isPowerSourceNode = (node: NetworkNode | undefined): boolean => {\n  if (!node) return false;\n  if (node.type && POWER_SOURCE_TYPE_SET.has(node.type)) return true;\n  const data = (node.data ?? {}) as { role?: unknown; deviceType?: unknown };\n  const role = String(data.role ?? \"\").toLowerCase();\n  const deviceType = String(data.deviceType ?? \"\").toLowerCase();\n  return role === \"pdu\" || role === \"ups\" || deviceType === \"pdu\" || deviceType === \"ups\";\n};\n\n/**\n * Whether an edge lands on a power-distribution node's supply inlet — the PDU\n * strip's `inlet` handle or a board's `incomer`. An inlet is the node's\n * upstream connection point, so the edge that lands on it feeds that node.\n */\nconst isInletEndpoint = (\n  node: NetworkNode | undefined,\n  handle: string | null | undefined\n): boolean => {\n  if (!node) return false;\n  if (node.type !== \"vertical-pdu\" && node.type !== \"distribution-board\") {\n    return false;\n  }\n  return isPowerInletHandleId(handle);\n};\n\n/**\n * The side of a power edge that consumes the power.\n *\n * An explicit inlet decides it outright: whichever end lands on a PDU/board\n * inlet is being fed. Otherwise wiring is normally source -\\> device, and\n * reversed edges are tolerated by checking which side is a power source.\n */\nconst powerConsumerOf = (edge: NetworkEdge, nodeById: Map<string, NetworkNode>): string => {\n  const sourceNode = nodeById.get(edge.source);\n  const targetNode = nodeById.get(edge.target);\n  if (isInletEndpoint(targetNode, edge.targetHandle)) return edge.target;\n  if (isInletEndpoint(sourceNode, edge.sourceHandle)) return edge.source;\n  if (isPowerSourceNode(targetNode) && !isPowerSourceNode(sourceNode)) {\n    return edge.source;\n  }\n  return edge.target;\n};\n\nconst wayIdOfHandle = (handleId: string | null | undefined): string | undefined => {\n  if (typeof handleId !== \"string\" || handleId.length === 0) return undefined;\n  const shape = parseHandleId(handleId).shape;\n  return shape.kind === \"breaker-way\" ? shape.wayId : undefined;\n};\n\n/**\n * Compute the power energisation overlay for a graph.\n *\n * @remarks\n * Pure and side-effect free: `nodes` and `edges` are read only. Only power\n * edges appear in {@link EnergizationResult.edges}; other edges are ignored.\n *\n * Blocking is strictly local to the way's own handle — opening way `w1` stops\n * propagation only for edges leaving the board through `way-w1`. Sibling ways\n * on the same board are unaffected.\n *\n * @example\n * ```ts\n * const { edges, devices } = computeEnergization(nodes, allEdges);\n * const painted = allEdges.map((e) =>\n *   edges[e.id] ? { ...e, data: { ...e.data, ...edges[e.id] } } : e\n * );\n * ```\n * @public\n */\nexport function computeEnergization(\n  nodes: readonly NetworkNode[],\n  edges: readonly NetworkEdge[],\n  options: EnergizationOptions = {}\n): EnergizationResult {\n  const nodeById = new Map<string, NetworkNode>(nodes.map((node) => [node.id, node]));\n  const powerEdges = edges.filter(isPowerEdge);\n\n  const edgesByNode = new Map<string, NetworkEdge[]>();\n  for (const edge of powerEdges) {\n    for (const endpoint of [edge.source, edge.target]) {\n      const list = edgesByNode.get(endpoint);\n      if (list) list.push(edge);\n      else edgesByNode.set(endpoint, [edge]);\n    }\n  }\n\n  // Feeds (incoming power edges) per consuming node.\n  const feedsByConsumer = new Map<string, NetworkEdge[]>();\n  for (const edge of powerEdges) {\n    const consumer = powerConsumerOf(edge, nodeById);\n    const list = feedsByConsumer.get(consumer);\n    if (list) list.push(edge);\n    else feedsByConsumer.set(consumer, [edge]);\n  }\n\n  const defaultWayState = options.defaultWayState ?? \"closed\";\n\n  /** Whether power may leave `node` through the given handle. */\n  const canLeaveThrough = (node: NetworkNode, handleId: string | null | undefined): boolean => {\n    // A strip's inlet is its upstream connection point, never an export: power\n    // arriving there reaches the outlets, and a de-energised circuit into the\n    // inlet leaves every outlet dead.\n    if (node.type === \"vertical-pdu\") return !isPowerInletHandleId(handleId);\n    if (node.type !== \"distribution-board\") return true;\n    const wayId = wayIdOfHandle(handleId);\n    // A board only exports power through its ways; other handles are incomers.\n    if (!wayId) return false;\n    const wayState = ((node.data ?? {}) as {\n      wayState?: Record<string, BreakerWayInstanceState>;\n    }).wayState;\n    const state = wayState?.[wayId]?.switchState ?? defaultWayState;\n    return state === \"closed\";\n  };\n\n  const sourceIds =\n    options.sources !== undefined\n      ? options.sources.filter((id) => nodeById.has(id))\n      : defaultSources(nodes, feedsByConsumer);\n\n  const sourceIdSet = new Set<string>(sourceIds);\n  const energizedNodes = new Set<string>();\n  const edgeState: Record<string, EdgeEnergization> = {};\n  for (const edge of powerEdges) {\n    edgeState[edge.id] = { energized: false };\n  }\n\n  const queue: string[] = [];\n  for (const id of sourceIds) {\n    if (!energizedNodes.has(id)) {\n      energizedNodes.add(id);\n      queue.push(id);\n    }\n  }\n\n  for (let head = 0; head < queue.length; head += 1) {\n    const nodeId = queue[head];\n    const node = nodeById.get(nodeId);\n    if (!node) continue;\n    // Only sources and pass-through distribution nodes propagate onward.\n    if (!isPowerSourceNode(node) && !sourceIdSet.has(nodeId)) continue;\n\n    for (const edge of edgesByNode.get(nodeId) ?? []) {\n      const leavingHandle = edge.source === nodeId ? edge.sourceHandle : edge.targetHandle;\n      const other = edge.source === nodeId ? edge.target : edge.source;\n      if (other === nodeId) continue;\n      // Never push power back up an incoming feed.\n      if (powerConsumerOf(edge, nodeById) === nodeId) continue;\n      if (!canLeaveThrough(node, leavingHandle)) continue;\n\n      edgeState[edge.id] = {\n        energized: true,\n        flowDirection: edge.source === nodeId ? \"source-to-target\" : \"target-to-source\",\n      };\n      if (!energizedNodes.has(other)) {\n        energizedNodes.add(other);\n        queue.push(other);\n      }\n    }\n  }\n\n  const devices: Record<string, DevicePowerState> = {};\n  for (const [consumerId, feeds] of feedsByConsumer) {\n    const live = feeds.filter((feed) => edgeState[feed.id]?.energized).length;\n    devices[consumerId] = live === 0 ? \"unpowered\" : live === feeds.length ? \"powered\" : \"degraded\";\n  }\n\n  return {\n    edges: edgeState,\n    devices,\n    energizedNodeIds: [...energizedNodes],\n  };\n}\n\nconst defaultSources = (\n  nodes: readonly NetworkNode[],\n  feedsByConsumer: Map<string, NetworkEdge[]>\n): string[] => {\n  // Every independent supply component needs a root, whether it begins at a\n  // board, UPS or PDU. A fed distribution node must never power itself.\n  return nodes\n    .filter((node) => isPowerSourceNode(node))\n    .filter((node) => (feedsByConsumer.get(node.id) ?? []).length === 0)\n    .map((node) => node.id);\n};\n\n/**\n * Apply an {@link EnergizationResult} to an edge list, returning a new array\n * whose power edges carry `energized` / `flowDirection` in `data`. Non-power\n * edges are returned untouched, and the edge set is unchanged — same length,\n * same ids, same order.\n * @public\n */\nexport function applyEnergizationToEdges(\n  edges: readonly NetworkEdge[],\n  result: EnergizationResult\n): NetworkEdge[] {\n  return edges.map((edge) => {\n    if (!Object.prototype.hasOwnProperty.call(result.edges, edge.id)) return edge;\n    const state = result.edges[edge.id];\n    if (!state) return edge;\n    if (\n      edge.data?.energized === state.energized &&\n      edge.data?.flowDirection === state.flowDirection\n    ) return edge;\n    const data = { ...(edge.data ?? {}), energized: state.energized };\n    delete data.flowDirection;\n    if (state.flowDirection) data.flowDirection = state.flowDirection;\n    return {\n      ...edge,\n      data,\n    } as NetworkEdge;\n  });\n}\n","import type { NetworkEdge } from \"../types\";\nimport { parseHandleId } from \"./handle-ids\";\n\n/**\n * The full power chain touching one node: every ancestor feeding it and every\n * descendant it feeds, expressed as the id sets the diagram-focus props\n * (`highlightedNodeIds` / `highlightedEdgeIds` / `highlightedBays`) consume.\n * @public\n */\nexport interface PowerChainSelection {\n  /** The start node plus every node on an upstream or downstream power path. */\n  nodeIds: string[];\n  /** Every power edge on those paths. */\n  edgeIds: string[];\n  /**\n   * Breaker ways the chain passes through, in the highlight store's\n   * `{ nodeId, bayId }` shape (`bayId` is the way id).\n   */\n  ways: Array<{ nodeId: string; wayId: string }>;\n}\n\nconst isPowerEdge = (edge: NetworkEdge): boolean =>\n  edge.type === \"power\" ||\n  (edge.data as { kind?: string } | undefined)?.kind === \"power\";\n\n/**\n * Collect the power chain around `startNodeId`: walk upstream through the\n * edges that feed it (transitively, to the sources) and downstream through\n * the edges it feeds (transitively, to the consumers). Ancestors' *other*\n * children and descendants' *other* parents are not pulled in — the result is\n * the electrical path through the start node, not its connected component.\n *\n * Only power edges (`type: \"power\"` or `data.kind: \"power\"`) are walked, so\n * the same graph can carry fibre/copper edges untouched. Cycles are tolerated.\n *\n * The `ways` list names every distribution-board breaker way the chain passes\n * through (parsed from `way-<id>` source handles), ready for\n * `highlightedBays`.\n * @public\n */\nexport function collectPowerChain(\n  edges: readonly NetworkEdge[],\n  startNodeId: string\n): PowerChainSelection {\n  const powerEdges = edges.filter(isPowerEdge);\n  const bySource = new Map<string, NetworkEdge[]>();\n  const byTarget = new Map<string, NetworkEdge[]>();\n  for (const edge of powerEdges) {\n    const sourceList = bySource.get(edge.source);\n    if (sourceList) sourceList.push(edge);\n    else bySource.set(edge.source, [edge]);\n    const targetList = byTarget.get(edge.target);\n    if (targetList) targetList.push(edge);\n    else byTarget.set(edge.target, [edge]);\n  }\n\n  const nodeIds = new Set<string>([startNodeId]);\n  const edgeIds = new Set<string>();\n  const wayKeys = new Set<string>();\n  const ways: Array<{ nodeId: string; wayId: string }> = [];\n\n  const recordWay = (edge: NetworkEdge): void => {\n    const shape = parseHandleId(edge.sourceHandle ?? undefined).shape;\n    if (shape.kind !== \"breaker-way\") return;\n    const key = `${edge.source}#${shape.wayId}`;\n    if (wayKeys.has(key)) return;\n    wayKeys.add(key);\n    ways.push({ nodeId: edge.source, wayId: shape.wayId });\n  };\n\n  const walk = (from: string, direction: \"upstream\" | \"downstream\"): void => {\n    const queue = [from];\n    const visited = new Set<string>([from]);\n    while (queue.length > 0) {\n      const nodeId = queue.pop() as string;\n      const nextEdges =\n        (direction === \"upstream\" ? byTarget : bySource).get(nodeId) ?? [];\n      for (const edge of nextEdges) {\n        edgeIds.add(edge.id);\n        recordWay(edge);\n        const nextNode = direction === \"upstream\" ? edge.source : edge.target;\n        nodeIds.add(nextNode);\n        if (!visited.has(nextNode)) {\n          visited.add(nextNode);\n          queue.push(nextNode);\n        }\n      }\n    }\n  };\n\n  walk(startNodeId, \"upstream\");\n  walk(startNodeId, \"downstream\");\n\n  return {\n    nodeIds: Array.from(nodeIds),\n    edgeIds: Array.from(edgeIds),\n    ways,\n  };\n}\n","import { NetworkNode } from '../types';\nimport { getRackNodePixelDimensions } from './rack-layout';\n\n/**\n * Calculate the bounding box of a rack node (header + body)\n * @param rack - The rack node\n * @returns Object with left, right, top, and bottom coordinates\n * @public\n */\nexport function getRackBounds(rack: NetworkNode): {\n  left: number;\n  right: number;\n  top: number;\n  bottom: number;\n} {\n  const { width: rackWidth, height: rackHeight } = getRackNodePixelDimensions(rack.data);\n  const rackLeft = rack.position.x;\n  const rackRight = rack.position.x + rackWidth;\n  const rackTop = rack.position.y;\n  const rackBottom = rack.position.y + rackHeight;\n\n  return {\n    left: rackLeft,\n    right: rackRight,\n    top: rackTop,\n    bottom: rackBottom,\n  };\n}\n\n/**\n * Check if a point is inside a rack's bounds\n * @param point - The point coordinates { x, y }\n * @param rack - The rack node\n * @returns True if the point is inside the rack\n * @public\n */\nexport function isPointInRack(point: { x: number; y: number }, rack: NetworkNode): boolean {\n  const bounds = getRackBounds(rack);\n  return (\n    point.x >= bounds.left &&\n    point.x <= bounds.right &&\n    point.y >= bounds.top &&\n    point.y <= bounds.bottom\n  );\n}\n\n/**\n * Find the nearest rack to a point from a list of racks\n * Only considers racks that contain the point\n * @param point - The point coordinates { x, y }\n * @param racks - Array of rack nodes\n * @returns The nearest rack node, or null if no rack contains the point\n * @public\n */\nexport function findNearestRack(\n  point: { x: number; y: number },\n  racks: NetworkNode[]\n): NetworkNode | null {\n  let nearestRack: NetworkNode | null = null;\n  let minDistance = Infinity;\n\n  for (const rack of racks) {\n    if (!isPointInRack(point, rack)) {\n      continue;\n    }\n\n    const distance = Math.sqrt(\n      Math.pow(point.x - rack.position.x, 2) + Math.pow(point.y - rack.position.y, 2)\n    );\n\n    if (distance < minDistance) {\n      minDistance = distance;\n      nearestRack = rack;\n    }\n  }\n\n  return nearestRack;\n}\n","import { useCallback } from 'react';\nimport { useStore } from '@xyflow/react';\nimport type { NetworkFlowEdge } from '../types';\nimport { edgesShallowEqual, useNodeEdges } from './use-node-edges';\nimport { getIncomingEdges } from '../utils/graph-index';\n\n/**\n * Edges connected to one node (source or target). Same scoped subscription\n * as `useNodeEdges`; alias aligned with xyflow v12 naming.\n */\nexport function useNodeConnections(nodeId: string): NetworkFlowEdge[] {\n  return useNodeEdges(nodeId);\n}\n\n/** Incoming edges for a node (where node is the target). */\nexport function useIncomingNodeConnections(nodeId: string): NetworkFlowEdge[] {\n  return useStore(\n    useCallback(\n      (state: { edges: unknown[] }) =>\n        getIncomingEdges(state.edges as NetworkFlowEdge[], nodeId),\n      [nodeId],\n    ),\n    edgesShallowEqual,\n  );\n}\n","/**\n * Template Schema V2 — unified device template model.\n *\n * Supports rackable devices (1U+), 0U/unrackable devices (vertical PDUs),\n * and hybrid front/rear pass-through devices.  Strictly more expressive than\n * NetBox device-type YAML while remaining import-compatible.\n *\n * @public\n */\n\nimport type { PortType, PortCableType, PortPosition, TemplateBayKind } from '../types';\n\nexport type { TemplateBayKind };\n\n/** Current schema version constant. */\nexport const TEMPLATE_SCHEMA_VERSION = 2;\n\n/** Device mount class — distinguishes rackable from 0U/standalone devices. */\nexport type MountClass = 'rackable' | 'zero-u';\n\n/** Device width within a rack (full 19\" or half 9.5\"). */\nexport type TemplateWidth = 'full' | 'half';\n\n/**\n * Individual port inside a template port block.\n * @public\n */\nexport interface TemplatePort {\n  /** Stable identifier for the port (must be unique within the template). */\n  id: string;\n  /** Human-readable label. */\n  label: string;\n  /** Port classification for styling and validation. */\n  type?: PortType;\n  /** Cable type hint. */\n  cableType?: PortCableType;\n  /** Power connector classification. */\n  connectorType?: 'C13' | 'C14' | 'C15' | 'C16' | 'C19' | 'C20' | 'C21' | 'C22' | 'AC';\n  /** Explicit handle placement. */\n  position?: PortPosition;\n  /**\n   * Containable capability for a port that is ALSO a bay — a transceiver cage.\n   * The cage is one element with one geometry box: the port keeps its cable\n   * handle (so endpoints survive optic swaps) and gains the ability to hold a\n   * FRU. `bayKind` is `'transceiver_cage'` for real cages, or one of the\n   * {@link TemplateBayKind} values when a slot both cables and contains.\n   * `accepts` optionally lists compatible FRU/optic form factors.\n   */\n  bay?: {\n    bayKind: TemplateBayKind | 'transceiver_cage';\n    accepts?: string[];\n  };\n}\n\n/**\n * A per-slot sub-rectangle inside a template bay (e.g. one socket in a bank).\n * @public\n */\nexport interface TemplateBaySlot {\n  /** Stable identifier for the slot (unique within its bay). */\n  id: string;\n  /** Human-readable label. */\n  label: string;\n  /** Slot centre + bounding box as percentages of the owning face frame. */\n  position?: PortPosition;\n}\n\n/**\n * Containable-only element on a template face: it HOLDS a field-replaceable\n * unit (drive, PSU, fan, line card, …) and never mounts a cable handle. Cages,\n * which are both containable and connectable, are expressed as ports with\n * `bay` metadata instead (see {@link TemplatePort}). Mirrors dc_designer's\n * `module_bay`/`device_bay` component rows.\n * @public\n */\nexport interface TemplateBay {\n  /** Stable identifier for the bay (unique within the template). */\n  id: string;\n  /** Human-readable label. */\n  label: string;\n  /** Bay classification. */\n  bayKind: TemplateBayKind;\n  /** Bay centre + bounding box as percentages of the owning face frame. */\n  position?: PortPosition;\n  /** Optional per-slot sub-rectangles. */\n  slots?: TemplateBaySlot[];\n}\n\n/**\n * Group of related ports on a single face.\n * @public\n */\nexport interface TemplatePortBlock {\n  /** Group name. */\n  name: string;\n  /** Ports in this group. */\n  ports: TemplatePort[];\n}\n\n/**\n * Per-face data container for front or rear.\n * @public\n */\nexport interface TemplateFaceData {\n  /** Port blocks on this face. */\n  portBlocks: TemplatePortBlock[];\n  /**\n   * Containable-only bays on this face (storage/psu/fan/line-card/…). Absent\n   * when the face has no bays, so bay-free templates keep their prior shape.\n   */\n  bays?: TemplateBay[];\n  /** Optional image URL for this face. */\n  imageUrl?: string;\n  /** Optional attribution for the image. */\n  imageAttribution?: string;\n}\n\n/**\n * Explicit front-to-rear pass-through link between ports.\n * @public\n */\nexport interface TemplateFrontRearPortLink {\n  /** Port ID on the front face. */\n  frontPortId: string;\n  /** Port ID on the rear face. */\n  rearPortId: string;\n}\n\n/**\n * Source provenance for an imported template.\n * @public\n */\nexport interface TemplateSource {\n  /**\n   * Source system kind. Recognised values are `'ai'` (detector pipeline),\n   * `'netbox'` (devicetype-library YAML seed), `'manual'` (hand-authored in the\n   * designer), and `'community'` (imported from a public template library).\n   * Left as an open `string` for forward-compatibility with future providers.\n   */\n  kind: string;\n  /** Source repository URL. */\n  repository?: string;\n  /** Path within the repository. */\n  path?: string;\n}\n\n/**\n * Canonical device template schema V2.\n * @public\n */\nexport interface DeviceTemplateV2 {\n  /** Schema version — always 2. */\n  templateSchemaVersion: 2;\n  /** Unique identifier (e.g. 'cisco-c9300-48p'). */\n  id: string;\n  /** URL-safe slug. */\n  slug: string;\n  /** Device manufacturer. */\n  manufacturer: string;\n  /** Device model. */\n  model: string;\n  /** Device role (e.g. 'switch', 'pdu', 'server'). */\n  role: string;\n  /** Mount class — rackable (1U+) or zero-u. */\n  mountClass: MountClass;\n  /** Height in rack units (0 for zero-u devices). */\n  uHeight: number;\n  /** Width within a rack. */\n  width: TemplateWidth;\n  /** Per-face port and image data. */\n  faces: {\n    front: TemplateFaceData;\n    rear: TemplateFaceData;\n  };\n  /** Explicit front-to-rear pass-through links. */\n  frontRearPortLinks: TemplateFrontRearPortLink[];\n  /** Optional image configuration. */\n  images?: {\n    front?: string | null;\n    rear?: string | null;\n  };\n  /** Source provenance. */\n  source?: TemplateSource;\n  /** Optional tags for search/filtering. */\n  tags?: string[];\n}\n\n/**\n * Single validation issue from the parser pipeline.\n * @public\n */\nexport interface TemplateValidationIssue {\n  /** Severity level. */\n  severity: 'error' | 'warning';\n  /** Machine-readable code. */\n  code: string;\n  /** Human-readable description. */\n  message: string;\n  /** Optional path to the problematic field. */\n  path?: string;\n}\n\n/**\n * Validation report emitted by the parser pipeline.\n * @public\n */\nexport interface TemplateValidationReport {\n  /** Whether the template is valid (no errors — warnings are allowed). */\n  valid: boolean;\n  /** List of issues found. */\n  issues: TemplateValidationIssue[];\n  /** Summary port counts per face. */\n  portCounts: {\n    front: number;\n    rear: number;\n  };\n  /**\n   * Summary containable-only bay counts per face (cages count as ports, not\n   * here). Optional so the field is purely additive to this public type; the\n   * parser and `validateDeviceTemplate` always populate it.\n   */\n  bayCounts?: {\n    front: number;\n    rear: number;\n  };\n}\n\n/**\n * Result of the full parser pipeline.\n * @public\n */\nexport interface TemplateParseResult {\n  /** Parsed template (present when valid). */\n  template: DeviceTemplateV2 | null;\n  /** Validation report. */\n  report: TemplateValidationReport;\n}\n","/**\n * NetBox → Template V2 parser pipeline.\n *\n * Five stages: Ingest → Normalize → Enrich → Validate → Export.\n *\n * **Port position semantics**: Positions use `xPercent` / `yPercent` (0-100)\n * to express where the port CENTER should be relative to the device face.\n * Raw imports may also use semantic edge strings like `top-left` or\n * `right-middle`; those are normalized into side/offset placements during\n * import. `xPercent` / `yPercent` remain the manual override path for\n * detailed templates.\n * The renderer (`DeviceNode`) converts these to pixel coordinates and clamps\n * them so the port stays fully inside the device bounds.  Clamp bounds are\n * computed from the **actual** rendered port dimensions (which vary by port\n * type — e.g., power C13/C19 connectors are larger than generic ethernet\n * handles).  Templates therefore do NOT need to account for port size; the\n * renderer handles that.\n *\n * @public\n */\n\nimport type { PortPosition, PortType, TemplateBayKind } from '../types';\nimport {\n  TEMPLATE_SCHEMA_VERSION,\n  type DeviceTemplateV2,\n  type MountClass,\n  type TemplateBay,\n  type TemplateBaySlot,\n  type TemplateFaceData,\n  type TemplateFrontRearPortLink,\n  type TemplateParseResult,\n  type TemplatePort,\n  type TemplatePortBlock,\n  type TemplateSource,\n  type TemplateValidationIssue,\n  type TemplateValidationReport,\n  type TemplateWidth,\n} from './template-schema';\nimport { normalizePortPositionInput } from './port-position';\n\n/** The nine containable bay kinds (cages are `transceiver_cage`, on ports). */\nconst TEMPLATE_BAY_KINDS: readonly TemplateBayKind[] = [\n  'storage_bay',\n  'psu_bay',\n  'fan_bay',\n  'pcie_slot',\n  'linecard_slot',\n  'supervisor_slot',\n  'fabric_slot',\n  'io_slot',\n  'other',\n];\n\n// ---------------------------------------------------------------------------\n// Public input type — what the parser accepts as raw NetBox-style data.\n// ---------------------------------------------------------------------------\n\n/**\n * Raw port entry from a NetBox-style template.\n * @public\n */\nexport interface RawTemplatePort {\n  id?: string;\n  name?: string;\n  label?: string;\n  type?: string;\n  connectorType?: string;\n  connector_type?: string;\n  position?: string | {\n    side?: string;\n    offsetPercent?: number;\n    offset_percent?: number;\n    offsetPx?: number;\n    offset_px?: number;\n    xPercent?: number;\n    x_percent?: number;\n    yPercent?: number;\n    y_percent?: number;\n    // Bounding-box percents — contractual for ports (cages) and reused by bays.\n    widthPercent?: number;\n    width_percent?: number;\n    heightPercent?: number;\n    height_percent?: number;\n  };\n  /** Containable capability for a cage port (camelCase). */\n  bay?: RawTemplatePortBay;\n  /** Containable capability for a cage port (snake_case alias). */\n  bay_metadata?: RawTemplatePortBay;\n}\n\n/**\n * Raw containable capability on a cage port (a port that is also a bay).\n * @public\n */\nexport interface RawTemplatePortBay {\n  bayKind?: string;\n  bay_kind?: string;\n  accepts?: string[];\n}\n\n/**\n * Raw per-slot sub-rectangle inside a bay.\n * @public\n */\nexport interface RawTemplateBaySlot {\n  id?: string;\n  name?: string;\n  label?: string;\n  position?: RawTemplatePort['position'];\n}\n\n/**\n * Raw containable-only bay from a NetBox-style template.\n * @public\n */\nexport interface RawTemplateBay {\n  id?: string;\n  name?: string;\n  label?: string;\n  bayKind?: string;\n  bay_kind?: string;\n  position?: RawTemplatePort['position'];\n  slots?: RawTemplateBaySlot[];\n}\n\n/**\n * Raw port block from a NetBox-style template.\n * @public\n */\nexport interface RawTemplatePortBlock {\n  name?: string;\n  ports?: RawTemplatePort[];\n}\n\n/**\n * Raw NetBox-style template input for the parser.\n * @public\n */\nexport interface RawNetboxTemplate {\n  id?: string;\n  slug?: string;\n  manufacturer?: string;\n  model?: string;\n  role?: string;\n  deviceType?: string;\n  device_type?: string;\n  uHeight?: number;\n  u_height?: number;\n  width?: string;\n  ports?: RawTemplatePortBlock[];\n  rearPorts?: RawTemplatePortBlock[];\n  rear_ports?: RawTemplatePortBlock[];\n  bays?: RawTemplateBay[];\n  rearBays?: RawTemplateBay[];\n  rear_bays?: RawTemplateBay[];\n  frontRearPortLinks?: Array<{ frontPortId?: string; rearPortId?: string; front_port_id?: string; rear_port_id?: string }>;\n  front_rear_port_links?: Array<{ frontPortId?: string; rearPortId?: string; front_port_id?: string; rear_port_id?: string }>;\n  frontImageUrl?: string;\n  front_image_url?: string;\n  rearImageUrl?: string;\n  rear_image_url?: string;\n  frontImageAttribution?: string;\n  rearImageAttribution?: string;\n  tags?: string[];\n  sourceRepository?: string;\n  source_repository?: string;\n  sourcePath?: string;\n  source_path?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v);\n\nconst toStr = (v: unknown): string =>\n  typeof v === 'string' ? v.trim() : '';\n\n/**\n * Text for a name/label/id field. YAML parses unquoted `name: 1` as a number,\n * so finite numbers are accepted and stringified rather than dropped.\n */\nconst toText = (v: unknown): string =>\n  typeof v === 'number' && Number.isFinite(v) ? String(v) : toStr(v);\n\nconst toFiniteNumber = (v: unknown): number | undefined => {\n  if (typeof v === 'number' && Number.isFinite(v)) return v;\n  if (typeof v === 'string' && v.trim().length > 0) {\n    const parsed = Number(v);\n    if (Number.isFinite(parsed)) return parsed;\n  }\n  return undefined;\n};\n\nconst TEMPLATE_CONNECTORS: readonly NonNullable<TemplatePort['connectorType']>[] = [\n  'C13',\n  'C14',\n  'C15',\n  'C16',\n  'C19',\n  'C20',\n  'C21',\n  'C22',\n  'AC',\n];\n\nconst normalizeConnector = (raw: unknown): TemplatePort['connectorType'] | undefined => {\n  const s = toStr(raw).toUpperCase();\n  return (TEMPLATE_CONNECTORS as readonly string[]).includes(s)\n    ? (s as TemplatePort['connectorType'])\n    : undefined;\n};\n\nconst normalizePortType = (rawType: string | undefined, connector?: TemplatePort['connectorType']): PortType => {\n  // The 10 A family (C13–C16, incl. the high-temperature C15/C16) and the 16 A\n  // family (C19–C22, incl. C21/C22) share the renderer's two power types.\n  if (connector === 'C13' || connector === 'C14' || connector === 'C15' || connector === 'C16') return 'power_c13';\n  if (connector === 'C19' || connector === 'C20' || connector === 'C21' || connector === 'C22') return 'power_c19';\n  if (connector === 'AC') return 'power_ac';\n\n  const n = (rawType ?? '').toLowerCase();\n  if (n.includes('c13')) return 'power_c13';\n  if (n.includes('c19')) return 'power_c19';\n  if (n.includes('power') || n.includes('pdu')) return 'power_ac';\n  if (n.includes('fiber') || n.includes('sfp') || n.includes('qsfp')) return 'fiber';\n  if (n.includes('console')) return 'console';\n  if (n.includes('mgmt') || n.includes('management')) return 'mgmt';\n  if (n.includes('usb')) return 'usb';\n  if (n.includes('vga') || n.includes('hd15') || n.includes('hd-15')) return 'vga';\n  if (\n    n.includes('serial') ||\n    n.includes('rs232') ||\n    n.includes('rs-232') ||\n    n.includes('db9') ||\n    n.includes('db-9')\n  ) {\n    return 'serial';\n  }\n  if (n.includes('rj45') || n.includes('rj-45')) return 'rj45';\n  return 'ethernet';\n};\n\nconst inferConnectorFromType = (portType: PortType): TemplatePort['connectorType'] | undefined => {\n  if (portType === 'power_c13') return 'C13';\n  if (portType === 'power_c19') return 'C19';\n  if (portType === 'power_ac') return 'AC';\n  return undefined;\n};\n\nconst normalizeRole = (raw: string): string =>\n  raw.toLowerCase().replace(/_/g, '-').trim();\n\nconst inferMountClass = (uHeight: number): MountClass =>\n  uHeight >= 1 ? 'rackable' : 'zero-u';\n\nconst normalizeWidth = (raw: string | undefined): TemplateWidth => {\n  const n = (raw ?? '').toLowerCase();\n  return n === 'half' ? 'half' : 'full';\n};\n\n/**\n * Generate a port id from its label. The slug is lossy (case-folded,\n * punctuation → `-`), so two labels such as `A1`/`a1` or `Gi1/0/1`/`Gi1-0-1`\n * slug identically; `usedIds` (every id already taken in the template, explicit\n * ids included) makes the result unique with a `-2`, `-3`, … suffix.\n */\nconst generatePortId = (\n  face: 'front' | 'rear',\n  blockIndex: number,\n  portIndex: number,\n  label: string,\n  usedIds?: Set<string>,\n): string => {\n  const safeLabel = label.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();\n  const base = `${face}-b${blockIndex}-${safeLabel || `p${portIndex}`}`;\n  if (!usedIds) return base;\n  let id = base;\n  for (let suffix = 2; usedIds.has(id); suffix += 1) {\n    id = `${base}-${suffix}`;\n  }\n  usedIds.add(id);\n  return id;\n};\n\nconst generateSlug = (manufacturer: string, model: string): string =>\n  `${manufacturer}-${model}`\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-|-$/g, '');\n\nconst generateBayId = (face: 'front' | 'rear', bayIndex: number, label: string): string => {\n  const safeLabel = label.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();\n  return `${face}-bay${bayIndex}-${safeLabel || `b${bayIndex}`}`;\n};\n\nconst normalizeBayKind = (raw: unknown): TemplateBayKind => {\n  const s = toStr(raw).toLowerCase().replace(/[\\s-]/g, '_');\n  // Intentional: an unknown kind on a containable-only bay falls back to 'other'\n  // (a generic bay), whereas an unknown kind on a PORT's bay metadata falls back\n  // to 'transceiver_cage' (see normalizePortBayMetadata) — a port that declares\n  // bay metadata is, by capability, a cage.\n  return (TEMPLATE_BAY_KINDS as readonly string[]).includes(s)\n    ? (s as TemplateBayKind)\n    : 'other';\n};\n\n/**\n * Warning for a non-object entry (e.g. a YAML `-` with no value → `null`) in a\n * port/bay list. The entry is skipped — the parser never throws on it.\n */\nfunction invalidEntryIssue(code: string, path: string, what: string): TemplateValidationIssue {\n  return {\n    severity: 'warning',\n    code,\n    message: `Skipped a ${what} entry that is not an object`,\n    path,\n  };\n}\n\nconst PERCENT_FIELDS = ['xPercent', 'yPercent', 'widthPercent', 'heightPercent'] as const;\n\n/**\n * Percent-geometry policy — the SAME as ports: authored geometry is stored\n * VERBATIM (never mutated at parse). Out-of-range values are surfaced as a\n * `BAY_GEOMETRY_OUT_OF_RANGE` WARNING (not an error, and not clamped); the\n * renderer clamps at draw so a slightly-off box still lands inside the face\n * frame. Returns only the issues raised — the position is left untouched.\n */\nfunction collectPercentRangeIssues(\n  position: PortPosition,\n  path: string,\n): TemplateValidationIssue[] {\n  const issues: TemplateValidationIssue[] = [];\n  for (const field of PERCENT_FIELDS) {\n    const value = position[field];\n    if (typeof value === 'number' && Number.isFinite(value) && (value < 0 || value > 100)) {\n      issues.push({\n        severity: 'warning',\n        code: 'BAY_GEOMETRY_OUT_OF_RANGE',\n        message: `Bay geometry ${field}=${value} is outside 0-100 (renderer clamps at draw)`,\n        path,\n      });\n    }\n  }\n  return issues;\n}\n\nfunction normalizeBaySlot(\n  rawSlot: RawTemplateBaySlot,\n  bayId: string,\n  slotIndex: number,\n): { slot: TemplateBaySlot; issues: TemplateValidationIssue[] } {\n  const label = toText(rawSlot.label) || toText(rawSlot.name) || `Slot ${slotIndex + 1}`;\n  const id = toText(rawSlot.id) || `${bayId}-slot${slotIndex}`;\n  const slot: TemplateBaySlot = { id, label };\n  const issues: TemplateValidationIssue[] = [];\n  if (rawSlot.position) {\n    const normalized = normalizePortPositionInput(rawSlot.position);\n    if (normalized) {\n      // Store verbatim; only report out-of-range geometry.\n      slot.position = normalized;\n      issues.push(...collectPercentRangeIssues(normalized, `bays.${bayId}.slots.${id}.position`));\n    }\n  }\n  return { slot, issues };\n}\n\nfunction normalizeBay(\n  rawBay: RawTemplateBay,\n  face: 'front' | 'rear',\n  bayIndex: number,\n): { bay: TemplateBay; issues: TemplateValidationIssue[] } {\n  const issues: TemplateValidationIssue[] = [];\n  const label = toText(rawBay.label) || toText(rawBay.name) || `Bay ${bayIndex + 1}`;\n  const id = toText(rawBay.id) || generateBayId(face, bayIndex, label);\n  const bay: TemplateBay = {\n    id,\n    label,\n    bayKind: normalizeBayKind(rawBay.bayKind ?? rawBay.bay_kind),\n  };\n\n  if (rawBay.position) {\n    const normalized = normalizePortPositionInput(rawBay.position);\n    if (normalized) {\n      // Store verbatim; only report out-of-range geometry.\n      bay.position = normalized;\n      issues.push(...collectPercentRangeIssues(normalized, `bays.${id}.position`));\n    }\n  }\n\n  if (Array.isArray(rawBay.slots) && rawBay.slots.length > 0) {\n    const slots: TemplateBaySlot[] = [];\n    rawBay.slots.forEach((rawSlot, slotIndex) => {\n      if (!isRecord(rawSlot)) {\n        issues.push(invalidEntryIssue('INVALID_BAY_SLOT', `bays.${id}.slots[${slotIndex}]`, 'bay slot'));\n        return;\n      }\n      const { slot, issues: slotIssues } = normalizeBaySlot(rawSlot, id, slotIndex);\n      slots.push(slot);\n      issues.push(...slotIssues);\n    });\n    bay.slots = slots;\n  }\n\n  return { bay, issues };\n}\n\nfunction normalizeFaceBays(\n  rawBays: RawTemplateBay[],\n  face: 'front' | 'rear',\n): { bays: TemplateBay[]; issues: TemplateValidationIssue[] } {\n  const bays: TemplateBay[] = [];\n  const issues: TemplateValidationIssue[] = [];\n  rawBays.forEach((rawBay, bayIndex) => {\n    if (!isRecord(rawBay)) {\n      issues.push(invalidEntryIssue('INVALID_BAY', `${face}.bays[${bayIndex}]`, 'bay'));\n      return;\n    }\n    const { bay, issues: bayIssues } = normalizeBay(rawBay, face, bayIndex);\n    bays.push(bay);\n    issues.push(...bayIssues);\n  });\n  return { bays, issues };\n}\n\nconst normalizePortBayMetadata = (\n  raw: RawTemplatePortBay | undefined,\n): TemplatePort['bay'] | undefined => {\n  if (!raw || typeof raw !== 'object') {\n    return undefined;\n  }\n  const rawKind = toStr(raw.bayKind ?? raw.bay_kind).toLowerCase().replace(/[\\s-]/g, '_');\n  // Intentional: unknown kind on a port's bay metadata defaults to\n  // 'transceiver_cage' (a port with bay metadata is a cage by capability),\n  // diverging from normalizeBayKind's 'other' default for containable-only bays.\n  const bayKind: TemplateBayKind | 'transceiver_cage' =\n    rawKind === 'transceiver_cage'\n      ? 'transceiver_cage'\n      : (TEMPLATE_BAY_KINDS as readonly string[]).includes(rawKind)\n        ? (rawKind as TemplateBayKind)\n        : 'transceiver_cage';\n  const accepts = Array.isArray(raw.accepts)\n    ? raw.accepts.map(toStr).filter(Boolean)\n    : undefined;\n  return accepts && accepts.length > 0 ? { bayKind, accepts } : { bayKind };\n};\n\n// ---------------------------------------------------------------------------\n// Stage A: Ingest\n// ---------------------------------------------------------------------------\n\ninterface IngestResult {\n  id: string;\n  slug: string;\n  manufacturer: string;\n  model: string;\n  role: string;\n  uHeight: number;\n  width: string | undefined;\n  frontBlocks: RawTemplatePortBlock[];\n  rearBlocks: RawTemplatePortBlock[];\n  frontBays: RawTemplateBay[];\n  rearBays: RawTemplateBay[];\n  rawLinks: Array<{ frontPortId?: string; rearPortId?: string }>;\n  frontImageUrl?: string;\n  rearImageUrl?: string;\n  frontImageAttribution?: string;\n  rearImageAttribution?: string;\n  tags: string[];\n  sourceRepository?: string;\n  sourcePath?: string;\n  issues: TemplateValidationIssue[];\n}\n\nfunction stageIngest(raw: RawNetboxTemplate): IngestResult {\n  const issues: TemplateValidationIssue[] = [];\n\n  const manufacturer = toText(raw.manufacturer);\n  const model = toText(raw.model);\n  const role = toStr(raw.role) || toStr(raw.deviceType) || toStr(raw.device_type) || 'device';\n  // NetBox's DeviceType.u_height defaults to 1.0 when a definition omits it;\n  // an explicit 0 still means a 0U device.\n  const uHeight = toFiniteNumber(raw.uHeight) ?? toFiniteNumber(raw.u_height) ?? 1;\n\n  if (!manufacturer) issues.push({ severity: 'warning', code: 'MISSING_MANUFACTURER', message: 'Template is missing manufacturer', path: 'manufacturer' });\n  if (!model) issues.push({ severity: 'warning', code: 'MISSING_MODEL', message: 'Template is missing model', path: 'model' });\n\n  const id = toStr(raw.id) || toStr(raw.slug) || generateSlug(manufacturer, model) || 'unknown';\n  const slug = toStr(raw.slug) || generateSlug(manufacturer, model) || id;\n\n  const frontBlocks = Array.isArray(raw.ports) ? raw.ports : [];\n  const rearBlocks = Array.isArray(raw.rearPorts) ? raw.rearPorts : (Array.isArray(raw.rear_ports) ? raw.rear_ports : []);\n\n  const frontBays = Array.isArray(raw.bays) ? raw.bays : [];\n  const rearBays = Array.isArray(raw.rearBays) ? raw.rearBays : (Array.isArray(raw.rear_bays) ? raw.rear_bays : []);\n\n  const rawLinks = Array.isArray(raw.frontRearPortLinks)\n    ? raw.frontRearPortLinks\n    : (Array.isArray(raw.front_rear_port_links) ? raw.front_rear_port_links : []);\n  const normalizedLinks = rawLinks.filter((link) => isRecord(link)).map((link) => ({\n    frontPortId: toStr(link.frontPortId) || toStr(link.front_port_id),\n    rearPortId: toStr(link.rearPortId) || toStr(link.rear_port_id),\n  }));\n\n  return {\n    id,\n    slug,\n    manufacturer,\n    model,\n    role,\n    uHeight,\n    width: toStr(raw.width) || undefined,\n    frontBlocks,\n    rearBlocks,\n    frontBays,\n    rearBays,\n    rawLinks: normalizedLinks,\n    frontImageUrl: toStr(raw.frontImageUrl) || toStr(raw.front_image_url) || undefined,\n    rearImageUrl: toStr(raw.rearImageUrl) || toStr(raw.rear_image_url) || undefined,\n    frontImageAttribution: toStr(raw.frontImageAttribution) || undefined,\n    rearImageAttribution: toStr(raw.rearImageAttribution) || undefined,\n    tags: Array.isArray(raw.tags) ? raw.tags.map(toStr).filter(Boolean) : [],\n    sourceRepository: toStr(raw.sourceRepository) || toStr(raw.source_repository) || undefined,\n    sourcePath: toStr(raw.sourcePath) || toStr(raw.source_path) || undefined,\n    issues,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Stage B: Normalize\n// ---------------------------------------------------------------------------\n\ninterface NormalizeResult {\n  role: string;\n  mountClass: MountClass;\n  uHeight: number;\n  width: TemplateWidth;\n  frontFace: TemplateFaceData;\n  rearFace: TemplateFaceData;\n  issues: TemplateValidationIssue[];\n}\n\nfunction normalizePortBlock(\n  rawBlock: RawTemplatePortBlock,\n  face: 'front' | 'rear',\n  blockIndex: number,\n  usedIds?: Set<string>,\n  issues?: TemplateValidationIssue[],\n): TemplatePortBlock {\n  const name = toText(rawBlock.name) || `Block ${blockIndex + 1}`;\n  const rawPorts = Array.isArray(rawBlock.ports) ? rawBlock.ports : [];\n  const ports: TemplatePort[] = [];\n  rawPorts.forEach((candidate: unknown, portIndex) => {\n    if (!isRecord(candidate)) {\n      issues?.push(invalidEntryIssue('INVALID_PORT', `${face}.ports[${blockIndex}].ports[${portIndex}]`, 'port'));\n      return;\n    }\n    const rawPort = candidate as RawTemplatePort;\n    const label = toText(rawPort.label) || toText(rawPort.name) || `Port ${portIndex + 1}`;\n    const rawConnector = normalizeConnector(rawPort.connectorType ?? rawPort.connector_type);\n    const portType = normalizePortType(toStr(rawPort.type), rawConnector);\n    const connectorType = rawConnector ?? inferConnectorFromType(portType);\n    const portId = toText(rawPort.id) || generatePortId(face, blockIndex, portIndex, label, usedIds);\n\n    const port: TemplatePort = {\n      id: portId,\n      label,\n      type: portType,\n      connectorType,\n    };\n\n    if (rawPort.position) {\n      const position = normalizePortPositionInput(rawPort.position);\n      if (position) {\n        port.position = position;\n      }\n    }\n\n    const bay = normalizePortBayMetadata(rawPort.bay ?? rawPort.bay_metadata);\n    if (bay) {\n      port.bay = bay;\n    }\n\n    ports.push(port);\n  });\n\n  return { name, ports };\n}\n\n/** Explicit (author-supplied) port ids across raw blocks, for id generation. */\nfunction collectExplicitPortIds(blocks: readonly unknown[], into: Set<string>): Set<string> {\n  for (const block of blocks) {\n    if (!isRecord(block) || !Array.isArray(block.ports)) continue;\n    for (const port of block.ports) {\n      if (!isRecord(port)) continue;\n      const id = toText(port.id);\n      if (id) into.add(id);\n    }\n  }\n  return into;\n}\n\nfunction buildFace(\n  portBlocks: TemplatePortBlock[],\n  bays: TemplateBay[],\n  imageUrl: string | undefined,\n  imageAttribution: string | undefined,\n): TemplateFaceData {\n  const face: TemplateFaceData = {\n    portBlocks,\n    imageUrl,\n    imageAttribution,\n  };\n  // Only attach `bays` when present so bay-free templates keep their prior shape.\n  if (bays.length > 0) {\n    face.bays = bays;\n  }\n  return face;\n}\n\n/**\n * Normalize one face's raw port blocks + bays into a `TemplateFaceData`. The\n * single normalization seam shared by the parse pipeline (`stageNormalize`) and\n * the standalone `validateDeviceTemplate` entry point, so port type /\n * connector / cage / bayKind normalization can never drift between the two.\n * Geometry is stored verbatim; out-of-range percents are reported, not clamped.\n */\nfunction normalizeFaceData(\n  rawBlocks: RawTemplatePortBlock[],\n  rawBays: RawTemplateBay[],\n  face: 'front' | 'rear',\n  imageUrl?: string,\n  imageAttribution?: string,\n  usedIds?: Set<string>,\n): { face: TemplateFaceData; issues: TemplateValidationIssue[] } {\n  const issues: TemplateValidationIssue[] = [];\n  const portBlocks: TemplatePortBlock[] = [];\n  rawBlocks.forEach((block, i) => {\n    if (!isRecord(block)) {\n      issues.push(invalidEntryIssue('INVALID_PORT_BLOCK', `${face}.ports[${i}]`, 'port block'));\n      return;\n    }\n    portBlocks.push(normalizePortBlock(block, face, i, usedIds, issues));\n  });\n  const { bays, issues: bayIssues } = normalizeFaceBays(rawBays, face);\n  issues.push(...bayIssues);\n  return {\n    face: buildFace(portBlocks, bays, imageUrl, imageAttribution),\n    issues,\n  };\n}\n\nfunction stageNormalize(ingested: IngestResult): NormalizeResult {\n  const role = normalizeRole(ingested.role);\n  const uHeight = ingested.uHeight;\n  const mountClass = inferMountClass(uHeight);\n  const width = normalizeWidth(ingested.width);\n\n  const usedIds = collectExplicitPortIds(\n    [...ingested.frontBlocks, ...ingested.rearBlocks],\n    new Set<string>(),\n  );\n  const front = normalizeFaceData(\n    ingested.frontBlocks,\n    ingested.frontBays,\n    'front',\n    ingested.frontImageUrl,\n    ingested.frontImageAttribution,\n    usedIds,\n  );\n  const rear = normalizeFaceData(\n    ingested.rearBlocks,\n    ingested.rearBays,\n    'rear',\n    ingested.rearImageUrl,\n    ingested.rearImageAttribution,\n    usedIds,\n  );\n\n  return {\n    role,\n    mountClass,\n    uHeight,\n    width,\n    frontFace: front.face,\n    rearFace: rear.face,\n    issues: [...front.issues, ...rear.issues],\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Stage C: Enrich\n// ---------------------------------------------------------------------------\n\ninterface EnrichResult {\n  frontFace: TemplateFaceData;\n  rearFace: TemplateFaceData;\n  mountClass: MountClass;\n  role: string;\n  issues: TemplateValidationIssue[];\n}\n\nfunction addFacePrefixesIfNeeded(\n  frontFace: TemplateFaceData,\n  rearFace: TemplateFaceData\n): { front: TemplateFaceData; rear: TemplateFaceData } {\n  const frontNames = new Set(frontFace.portBlocks.map((b) => b.name));\n  const rearNames = new Set(rearFace.portBlocks.map((b) => b.name));\n  const overlapping = [...frontNames].filter((name) => rearNames.has(name));\n\n  if (overlapping.length === 0) {\n    return { front: frontFace, rear: rearFace };\n  }\n\n  return {\n    front: {\n      ...frontFace,\n      portBlocks: frontFace.portBlocks.map((block) =>\n        overlapping.includes(block.name) ? { ...block, name: `Front · ${block.name}` } : block\n      ),\n    },\n    rear: {\n      ...rearFace,\n      portBlocks: rearFace.portBlocks.map((block) =>\n        overlapping.includes(block.name) ? { ...block, name: `Rear · ${block.name}` } : block\n      ),\n    },\n  };\n}\n\nfunction stageEnrich(normalized: NormalizeResult): EnrichResult {\n  const issues: TemplateValidationIssue[] = [];\n  const { front, rear } = addFacePrefixesIfNeeded(normalized.frontFace, normalized.rearFace);\n\n  let role = normalized.role;\n  if (!role || role === 'device') {\n    const hasPowerPorts = [...front.portBlocks, ...rear.portBlocks].some((block) =>\n      block.ports.some((port) => port.type && port.type.startsWith('power'))\n    );\n    if (hasPowerPorts && normalized.mountClass === 'zero-u') {\n      role = 'pdu';\n    }\n  }\n\n  return {\n    frontFace: front,\n    rearFace: rear,\n    mountClass: normalized.mountClass,\n    role,\n    issues,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Stage D: Validate\n// ---------------------------------------------------------------------------\n\nfunction countFacePorts(face: TemplateFaceData): number {\n  return face.portBlocks.reduce((total, block) => total + block.ports.length, 0);\n}\n\nfunction countFaceBays(face: TemplateFaceData): number {\n  return Array.isArray(face.bays) ? face.bays.length : 0;\n}\n\ntype PortLinkRef = { frontPortId?: string; rearPortId?: string };\n\n/**\n * The shared structural validator over two ALREADY-NORMALIZED faces: unique\n * port ids, pass-through link references, and port/bay counts. `rawCounts` (the\n * raw input port counts) is supplied only by the parse pipeline so the\n * count-parity guard can catch a parser drop; `validateDeviceTemplate` omits it\n * (its input IS the normalized form, so parity is vacuous).\n */\nfunction buildValidationReport(\n  frontFace: TemplateFaceData,\n  rearFace: TemplateFaceData,\n  links: PortLinkRef[],\n  rawCounts?: { front: number; rear: number },\n): TemplateValidationReport {\n  const issues: TemplateValidationIssue[] = [];\n\n  // Unique port IDs\n  const allPorts: Array<{ id: string; face: string }> = [];\n  for (const block of frontFace.portBlocks) {\n    for (const port of block.ports) {\n      allPorts.push({ id: port.id, face: 'front' });\n    }\n  }\n  for (const block of rearFace.portBlocks) {\n    for (const port of block.ports) {\n      allPorts.push({ id: port.id, face: 'rear' });\n    }\n  }\n\n  const idCounts = new Map<string, number>();\n  for (const { id } of allPorts) {\n    idCounts.set(id, (idCounts.get(id) ?? 0) + 1);\n  }\n  for (const [id, count] of idCounts) {\n    if (count > 1) {\n      issues.push({\n        severity: 'error',\n        code: 'DUPLICATE_PORT_ID',\n        message: `Duplicate port id \"${id}\" found ${count} times`,\n        path: `ports.${id}`,\n      });\n    }\n  }\n\n  const parsedFrontCount = countFacePorts(frontFace);\n  const parsedRearCount = countFacePorts(rearFace);\n\n  // Count parity against raw input (parse path only).\n  if (rawCounts) {\n    if (parsedFrontCount !== rawCounts.front) {\n      issues.push({\n        severity: 'error',\n        code: 'FRONT_PORT_COUNT_MISMATCH',\n        message: `Front port count mismatch: raw=${rawCounts.front}, parsed=${parsedFrontCount}`,\n        path: 'faces.front',\n      });\n    }\n    if (parsedRearCount !== rawCounts.rear) {\n      issues.push({\n        severity: 'error',\n        code: 'REAR_PORT_COUNT_MISMATCH',\n        message: `Rear port count mismatch: raw=${rawCounts.rear}, parsed=${parsedRearCount}`,\n        path: 'faces.rear',\n      });\n    }\n  }\n\n  // Validate pass-through links reference existing port IDs\n  const allPortIds = new Set(allPorts.map((p) => p.id));\n  const frontPortIds = new Set(allPorts.filter((p) => p.face === 'front').map((p) => p.id));\n  const rearPortIds = new Set(allPorts.filter((p) => p.face === 'rear').map((p) => p.id));\n\n  for (const link of links) {\n    if (link.frontPortId && !frontPortIds.has(link.frontPortId) && !allPortIds.has(link.frontPortId)) {\n      issues.push({\n        severity: 'warning',\n        code: 'PASS_THROUGH_FRONT_PORT_MISSING',\n        message: `Pass-through link references unknown front port \"${link.frontPortId}\"`,\n        path: 'frontRearPortLinks',\n      });\n    }\n    if (link.rearPortId && !rearPortIds.has(link.rearPortId) && !allPortIds.has(link.rearPortId)) {\n      issues.push({\n        severity: 'warning',\n        code: 'PASS_THROUGH_REAR_PORT_MISSING',\n        message: `Pass-through link references unknown rear port \"${link.rearPortId}\"`,\n        path: 'frontRearPortLinks',\n      });\n    }\n  }\n\n  const hasErrors = issues.some((i) => i.severity === 'error');\n\n  return {\n    valid: !hasErrors,\n    issues,\n    portCounts: {\n      front: parsedFrontCount,\n      rear: parsedRearCount,\n    },\n    bayCounts: {\n      front: countFaceBays(frontFace),\n      rear: countFaceBays(rearFace),\n    },\n  };\n}\n\n/**\n * Raw port count for the parity guard. Non-object entries are excluded — they\n * are reported as INVALID_* warnings and skipped, not silently dropped.\n */\nfunction countRawFacePorts(blocks: RawTemplatePortBlock[]): number {\n  return blocks.reduce(\n    (total, block) =>\n      total +\n      (isRecord(block) && Array.isArray(block.ports)\n        ? block.ports.filter((port) => isRecord(port)).length\n        : 0),\n    0,\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Stage E: Export / Assemble\n// ---------------------------------------------------------------------------\n\nfunction stageExport(\n  ingested: IngestResult,\n  normalized: NormalizeResult,\n  enriched: EnrichResult\n): DeviceTemplateV2 {\n  const links: TemplateFrontRearPortLink[] = ingested.rawLinks\n    .filter((link) => Boolean(link.frontPortId) && Boolean(link.rearPortId))\n    .map((link) => ({\n      frontPortId: link.frontPortId!,\n      rearPortId: link.rearPortId!,\n    }));\n\n  const source: TemplateSource | undefined = ingested.sourceRepository\n    ? {\n        kind: 'netbox',\n        repository: ingested.sourceRepository,\n        path: ingested.sourcePath,\n      }\n    : undefined;\n\n  return {\n    templateSchemaVersion: TEMPLATE_SCHEMA_VERSION,\n    id: ingested.id,\n    slug: ingested.slug,\n    manufacturer: ingested.manufacturer,\n    model: ingested.model,\n    role: enriched.role,\n    mountClass: enriched.mountClass,\n    uHeight: normalized.uHeight,\n    width: normalized.width,\n    faces: {\n      front: enriched.frontFace,\n      rear: enriched.rearFace,\n    },\n    frontRearPortLinks: links,\n    images: {\n      front: enriched.frontFace.imageUrl ?? null,\n      rear: enriched.rearFace.imageUrl ?? null,\n    },\n    source,\n    tags: ingested.tags.length > 0 ? ingested.tags : undefined,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a raw NetBox-style template through the full 5-stage pipeline.\n *\n * Returns a `TemplateParseResult` containing the validated V2 template\n * (or `null` if validation failed) and a full validation report.\n *\n * @public\n */\nexport function parseNetboxTemplate(raw: RawNetboxTemplate): TemplateParseResult {\n  // Stage A — a non-object input ingests as an empty template (warnings only).\n  const ingested = stageIngest(isRecord(raw) ? raw : {});\n\n  // Stage B\n  const normalized = stageNormalize(ingested);\n\n  // Stage C\n  const enriched = stageEnrich(normalized);\n\n  // Stage D\n  const report = buildValidationReport(enriched.frontFace, enriched.rearFace, ingested.rawLinks, {\n    front: countRawFacePorts(ingested.frontBlocks),\n    rear: countRawFacePorts(ingested.rearBlocks),\n  });\n  report.issues.push(...ingested.issues, ...normalized.issues, ...enriched.issues);\n\n  if (!report.valid) {\n    return { template: null, report };\n  }\n\n  // Stage E\n  const template = stageExport(ingested, normalized, enriched);\n\n  return { template, report };\n}\n\n// ---------------------------------------------------------------------------\n// validateDeviceTemplate — community-library CI entry point\n// ---------------------------------------------------------------------------\n\n/** Coerce an unknown face into raw port-block/bay arrays for re-normalization. */\nfunction coerceRawFace(raw: unknown): {\n  blocks: RawTemplatePortBlock[];\n  bays: RawTemplateBay[];\n  imageUrl?: string;\n  imageAttribution?: string;\n} {\n  const record = isRecord(raw) ? raw : {};\n  return {\n    blocks: Array.isArray(record.portBlocks)\n      ? (record.portBlocks as unknown as RawTemplatePortBlock[])\n      : [],\n    bays: Array.isArray(record.bays) ? (record.bays as unknown as RawTemplateBay[]) : [],\n    imageUrl: toStr(record.imageUrl) || undefined,\n    imageAttribution: toStr(record.imageAttribution) || undefined,\n  };\n}\n\n/**\n * Validate an already-normalized {@link DeviceTemplateV2} object (the shape the\n * designer emits and the community library hosts). Runs the SAME normalization\n * as the parse pipeline (the internal `normalizeFaceData` seam) — so an\n * un-normalized community submission (raw `bayKind`, un-canonical port `type`)\n * is repaired identically — then the shared structural validator (unique port ids,\n * pass-through link references, verbatim bay geometry ranges). Requires\n * `templateSchemaVersion === 2`.\n *\n * Root-entry only; the first-class CI entry point for a community template\n * library (netbox-community/devicetype-library model): run it over each\n * submitted `template.v2.json` to schema-check the PR. Returns the NORMALIZED\n * (unclamped — the renderer clamps geometry at draw) template when valid, or\n * `null` alongside the error report when not.\n *\n * @public\n */\nexport function validateDeviceTemplate(template: unknown): TemplateParseResult {\n  const fail = (issue: TemplateValidationIssue): TemplateParseResult => ({\n    template: null,\n    report: { valid: false, issues: [issue], portCounts: { front: 0, rear: 0 } },\n  });\n\n  if (!isRecord(template)) {\n    return fail({ severity: 'error', code: 'INVALID_TEMPLATE', message: 'Template must be an object', path: '' });\n  }\n  if (template.templateSchemaVersion !== TEMPLATE_SCHEMA_VERSION) {\n    return fail({\n      severity: 'error',\n      code: 'INVALID_SCHEMA_VERSION',\n      message: `templateSchemaVersion must be ${TEMPLATE_SCHEMA_VERSION}`,\n      path: 'templateSchemaVersion',\n    });\n  }\n\n  const rawFaces = isRecord(template.faces) ? template.faces : {};\n  const rawFront = coerceRawFace(rawFaces.front);\n  const rawRear = coerceRawFace(rawFaces.rear);\n\n  const usedIds = collectExplicitPortIds([...rawFront.blocks, ...rawRear.blocks], new Set<string>());\n  const front = normalizeFaceData(rawFront.blocks, rawFront.bays, 'front', rawFront.imageUrl, rawFront.imageAttribution, usedIds);\n  const rear = normalizeFaceData(rawRear.blocks, rawRear.bays, 'rear', rawRear.imageUrl, rawRear.imageAttribution, usedIds);\n\n  const links: PortLinkRef[] = Array.isArray(template.frontRearPortLinks)\n    ? (template.frontRearPortLinks as TemplateFrontRearPortLink[]).map((link) => ({\n        frontPortId: toStr(link?.frontPortId) || undefined,\n        rearPortId: toStr(link?.rearPortId) || undefined,\n      }))\n    : [];\n\n  // No rawCounts: the input IS the normalized form, so count-parity is vacuous.\n  const report = buildValidationReport(front.face, rear.face, links);\n  report.issues.push(...front.issues, ...rear.issues);\n  report.valid = !report.issues.some((issue) => issue.severity === 'error');\n\n  const normalizedTemplate: DeviceTemplateV2 = {\n    ...(template as unknown as DeviceTemplateV2),\n    faces: { front: front.face, rear: rear.face },\n  };\n\n  return { template: report.valid ? normalizedTemplate : null, report };\n}\n\n/**\n * Count the total number of ports on a given face of a V2 template.\n * @public\n */\nexport function countTemplateFacePorts(face: TemplateFaceData): number {\n  return countFacePorts(face);\n}\n\n/**\n * Validate that rendered handle counts match the template port counts.\n * Returns issues when counts diverge — used as a regression guardrail.\n *\n * @public\n */\nexport function validateRenderFidelity(\n  template: DeviceTemplateV2,\n  renderedFrontHandleCount: number,\n  renderedRearHandleCount: number\n): TemplateValidationIssue[] {\n  const issues: TemplateValidationIssue[] = [];\n  const expectedFront = countFacePorts(template.faces.front);\n  const expectedRear = countFacePorts(template.faces.rear);\n\n  if (renderedFrontHandleCount !== expectedFront) {\n    issues.push({\n      severity: 'error',\n      code: 'RENDER_FRONT_COUNT_MISMATCH',\n      message: `Rendered front handle count (${renderedFrontHandleCount}) does not match template (${expectedFront})`,\n      path: 'faces.front',\n    });\n  }\n  if (renderedRearHandleCount !== expectedRear) {\n    issues.push({\n      severity: 'error',\n      code: 'RENDER_REAR_COUNT_MISMATCH',\n      message: `Rendered rear handle count (${renderedRearHandleCount}) does not match template (${expectedRear})`,\n      path: 'faces.rear',\n    });\n  }\n\n  return issues;\n}\n"]}