@mp70/react-networks 0.2.0-beta.1 → 0.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,224 @@
1
1
  import React$1, { Component, ReactNode, ErrorInfo } from 'react';
2
- import { NodeTypes, EdgeTypes, Connection, Edge, ConnectionMode, FitViewOptions, NodeProps, EdgeProps } from 'reactflow';
2
+ import { Node, NodeTypes, EdgeTypes, Connection, Edge, ConnectionMode, ReactFlowInstance, Viewport, FitViewOptions, NodeProps, EdgeProps } from 'reactflow';
3
+
4
+ type FibreFlowCableKind = 'simple' | 'tube';
5
+ interface FibreFlowEndpoint {
6
+ tube?: number;
7
+ fiber: number;
8
+ }
9
+ interface FibreFlowSplice {
10
+ id: string;
11
+ from: FibreFlowEndpoint;
12
+ to: FibreFlowEndpoint;
13
+ lossDb?: number;
14
+ circuitId?: string;
15
+ label?: string;
16
+ color?: string;
17
+ }
18
+ interface FibreFlowCable {
19
+ id: string;
20
+ label: string;
21
+ kind?: FibreFlowCableKind;
22
+ fiberCount?: number;
23
+ tubeCount?: number;
24
+ fibersPerTube?: number;
25
+ tubeStartIndex?: number;
26
+ fiberStartIndex?: number;
27
+ tubeColorMap?: Record<number, string>;
28
+ jacketColor?: string;
29
+ distanceLabel?: string;
30
+ distanceMeters?: number;
31
+ }
32
+ interface FibreFlowClosure {
33
+ id: string;
34
+ chamberId?: string;
35
+ leftCable: FibreFlowCable;
36
+ rightCable: FibreFlowCable;
37
+ splices: FibreFlowSplice[];
38
+ }
39
+ interface FibreFlowCircuit {
40
+ id: string;
41
+ label: string;
42
+ color?: string;
43
+ }
44
+
45
+ interface NetBoxPortPosition {
46
+ side?: 'top' | 'right' | 'bottom' | 'left';
47
+ offsetPercent?: number;
48
+ offsetPx?: number;
49
+ xPercent?: number;
50
+ yPercent?: number;
51
+ }
52
+ interface NetBoxMappedPort {
53
+ id?: string;
54
+ label?: string;
55
+ face?: 'front' | 'rear';
56
+ group?: string;
57
+ type?: string;
58
+ cableType?: string;
59
+ connected?: boolean;
60
+ connectorType?: 'C13' | 'C19' | 'C14' | 'C20';
61
+ position?: NetBoxPortPosition;
62
+ }
63
+ interface NetBoxDeviceTransformOptions {
64
+ /** Include interface arrays as `ports`/`rearPorts` in generated nodes */
65
+ includeInterfacePorts?: boolean;
66
+ /** Include per-port position metadata when provided */
67
+ includePortPositions?: boolean;
68
+ /** Optional mapper for full control over interface->port mapping */
69
+ mapInterfaceToPort?: (args: {
70
+ device: NetBoxDevice;
71
+ iface: NetBoxInterface;
72
+ index: number;
73
+ }) => NetBoxMappedPort | null | undefined;
74
+ }
75
+ interface NetBoxDevice {
76
+ id: number;
77
+ name: string;
78
+ device_type: {
79
+ id: number;
80
+ model: string;
81
+ manufacturer: {
82
+ name: string;
83
+ };
84
+ };
85
+ device_role: {
86
+ name: string;
87
+ color: string;
88
+ };
89
+ site: {
90
+ name: string;
91
+ };
92
+ rack: {
93
+ id: number;
94
+ name: string;
95
+ } | null;
96
+ position: number | null;
97
+ face: 'front' | 'rear' | null;
98
+ status: {
99
+ label: string;
100
+ value: string;
101
+ color: string;
102
+ };
103
+ primary_ip4?: {
104
+ address: string;
105
+ };
106
+ primary_ip6?: {
107
+ address: string;
108
+ };
109
+ interfaces: NetBoxInterface[];
110
+ }
111
+ interface NetBoxInterface {
112
+ id: number;
113
+ name: string;
114
+ type: {
115
+ label: string;
116
+ value: string;
117
+ };
118
+ enabled: boolean;
119
+ mac_address: string | null;
120
+ mtu: number | null;
121
+ connected_endpoint_type: string | null;
122
+ connected_endpoint: {
123
+ device: {
124
+ id?: number;
125
+ name: string;
126
+ };
127
+ name: string;
128
+ } | null;
129
+ cable: {
130
+ id: number;
131
+ label: string;
132
+ type: string;
133
+ color: string;
134
+ } | null;
135
+ /** Optional face hint used by UI transforms */
136
+ face?: 'front' | 'rear' | null;
137
+ /** Optional display/ordering position from external enrichments */
138
+ position?: number | null;
139
+ /** Optional grouping hint used by UI transforms */
140
+ port_group?: string | null;
141
+ /** Optional explicit connector hint (e.g., C13/C19 for PDUs) */
142
+ connector_type?: 'C13' | 'C19' | 'C14' | 'C20' | null;
143
+ /** Optional transform layout metadata */
144
+ layout?: NetBoxMappedPort;
145
+ /** Optional custom fields payload from NetBox */
146
+ custom_fields?: Record<string, unknown>;
147
+ }
148
+ interface NetBoxRack {
149
+ id: number;
150
+ name: string;
151
+ site: {
152
+ name: string;
153
+ };
154
+ group: {
155
+ name: string;
156
+ } | null;
157
+ tenant: {
158
+ name: string;
159
+ } | null;
160
+ role: {
161
+ name: string;
162
+ color: string;
163
+ } | null;
164
+ status: {
165
+ label: string;
166
+ value: string;
167
+ color: string;
168
+ };
169
+ facility_id: string | null;
170
+ serial: string | null;
171
+ asset_tag: string | null;
172
+ type: {
173
+ label: string;
174
+ value: string;
175
+ };
176
+ width: {
177
+ label: string;
178
+ value: number;
179
+ };
180
+ u_height: number;
181
+ desc_units: boolean;
182
+ outer_width: number | null;
183
+ outer_depth: number | null;
184
+ weight: number | null;
185
+ max_weight: number | null;
186
+ weight_unit: string;
187
+ mounting_depth: number | null;
188
+ devices: NetBoxDevice[];
189
+ }
190
+ interface NetBoxCable {
191
+ id: number;
192
+ label: string;
193
+ type: {
194
+ label: string;
195
+ value: string;
196
+ };
197
+ status: {
198
+ label: string;
199
+ value: string;
200
+ color: string;
201
+ };
202
+ color: string;
203
+ length: number | null;
204
+ length_unit: string;
205
+ termination_a_type: string;
206
+ termination_a: {
207
+ device: {
208
+ id?: number;
209
+ name: string;
210
+ };
211
+ name: string;
212
+ };
213
+ termination_b_type: string;
214
+ termination_b: {
215
+ device: {
216
+ id?: number;
217
+ name: string;
218
+ };
219
+ name: string;
220
+ };
221
+ }
3
222
 
4
223
  /**
5
224
  * Rack device width configuration
@@ -21,7 +240,66 @@ declare enum Width {
21
240
  * @public
22
241
  */
23
242
  type PortType = 'ethernet' | 'fiber' | 'console' | 'mgmt' | 'usb' | 'power_ac' | 'power_c13' | 'power_c19';
243
+ /**
244
+ * Cable types used for connected network ports.
245
+ * @public
246
+ */
247
+ type PortCableType = 'smf' | 'cat6' | 'om3' | 'om4' | 'om5' | 'cat5e' | 'cat6a' | 'cat7' | 'fiber' | 'ethernet';
248
+ /**
249
+ * Device image fit options.
250
+ *
251
+ * Values mirror object-fit terms for API ergonomics and are mapped to valid
252
+ * background-size values by renderers where needed.
253
+ *
254
+ * @public
255
+ */
256
+ type DeviceImageFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
257
+ /**
258
+ * Device image repeat options.
259
+ * @public
260
+ */
261
+ type DeviceImageRepeat = 'no-repeat' | 'repeat' | 'repeat-x' | 'repeat-y';
262
+ /**
263
+ * Shared image configuration for rack-mounted devices and device nodes.
264
+ * @public
265
+ */
266
+ interface DeviceImageOptions {
267
+ /** Optional front image URL for device background (PNG, SVG, JPEG, etc.) */
268
+ frontImageUrl?: string;
269
+ /** Optional rear image URL for device background (PNG, SVG, JPEG, etc.) */
270
+ rearImageUrl?: string;
271
+ /** Optional attribution URL for front image (e.g. source/license link) */
272
+ frontImageAttribution?: string;
273
+ /** Optional attribution URL for rear image (e.g. source/license link) */
274
+ rearImageAttribution?: string;
275
+ /** Optional background sizing for device images */
276
+ imageFit?: DeviceImageFit;
277
+ /** Optional background position for device images */
278
+ imagePosition?: string;
279
+ /** Optional background repeat for device images */
280
+ imageRepeat?: DeviceImageRepeat;
281
+ /** When true, stretch device image to fill the device bounds (overrides imageFit) */
282
+ stretchToFit?: boolean;
283
+ }
284
+ /**
285
+ * Explicit handle placement for a device/PDU port.
286
+ * @public
287
+ */
288
+ interface PortPosition {
289
+ /** Handle side on the node */
290
+ side?: 'top' | 'right' | 'bottom' | 'left';
291
+ /** Side offset as percentage (0-100) */
292
+ offsetPercent?: number;
293
+ /** Side offset in pixels */
294
+ offsetPx?: number;
295
+ /** Absolute X position as percentage (0-100) */
296
+ xPercent?: number;
297
+ /** Absolute Y position as percentage (0-100) */
298
+ yPercent?: number;
299
+ }
24
300
  interface Port {
301
+ /** Stable handle identifier override (defaults to `label`) */
302
+ id?: string;
25
303
  /** Display label for the port */
26
304
  label: string;
27
305
  /** Whether the port is currently connected */
@@ -29,7 +307,11 @@ interface Port {
29
307
  /** Type of port for styling and validation */
30
308
  type?: PortType;
31
309
  /** Cable type for connection validation */
32
- cableType?: 'smf' | 'cat6' | 'om3' | 'om4' | 'om5' | 'cat5e' | 'cat6a' | 'cat7' | 'fiber' | 'ethernet';
310
+ cableType?: PortCableType;
311
+ /** Optional power connector type for rendering (mainly for PDU ports) */
312
+ connectorType?: 'C13' | 'C14' | 'C19' | 'C20';
313
+ /** Optional explicit handle placement */
314
+ position?: PortPosition;
33
315
  }
34
316
  /**
35
317
  * Group of related ports on a device
@@ -56,6 +338,16 @@ type DeviceStatus = 'active' | 'inactive' | 'maintenance';
56
338
  * @public
57
339
  */
58
340
  type DeviceView = 'front' | 'rear';
341
+ /**
342
+ * Rack rendering layout modes.
343
+ * @public
344
+ */
345
+ type RackLayoutMode = 'single-face' | 'split-face';
346
+ /**
347
+ * Rack mounting behavior for a device.
348
+ * @public
349
+ */
350
+ type RackMountMode = 'front' | 'rear' | 'both';
59
351
  /**
60
352
  * Power port side configuration
61
353
  * @public
@@ -67,125 +359,303 @@ type PowerSide = 'left' | 'right';
67
359
  */
68
360
  type UNumberingDirection = 'bottom-up' | 'top-down';
69
361
  /**
70
- * Network node data structure
362
+ * Shared node data fields available across node types.
363
+ * Specific node kinds can narrow these fields via `NetworkNodeDataByType`.
71
364
  * @public
72
365
  */
73
- interface NetworkNode {
74
- /** Unique identifier for the node */
75
- id: string;
366
+ interface NetworkNodeDataBase extends DeviceImageOptions {
367
+ /** Display label for the node */
368
+ label: string;
369
+ /** Current status of the node/device */
370
+ status?: DeviceStatus | string;
371
+ /** Port configurations for device-like nodes */
372
+ ports?: PortBlock[] | number;
373
+ /** Rear port configurations */
374
+ rearPorts?: PortBlock[];
375
+ /** List of connected node IDs */
376
+ connections?: string[];
377
+ /** NetBox integration ID */
378
+ netboxId?: number | string;
379
+ /** NetBox object type */
380
+ netboxType?: 'device' | 'rack' | 'interface' | 'cable' | string;
381
+ /** Inventory integration ID */
382
+ inventoryId?: number | string;
383
+ /** Inventory object type */
384
+ inventoryType?: 'device' | 'rack' | 'interface' | 'cable' | string;
385
+ /** Rack height in U units */
386
+ uHeight?: number;
387
+ /** Device position within rack in U units */
388
+ uPosition?: number;
389
+ /** Current front/rear view orientation */
390
+ view?: DeviceView;
391
+ /** Rack face orientation */
392
+ face?: DeviceView;
393
+ /** Rack rendering layout mode */
394
+ rackLayout?: RackLayoutMode;
395
+ /** Gap in pixels between front and rear faces in split-face mode */
396
+ rackFaceGapPx?: number;
397
+ /** Device mount behavior within a rack */
398
+ mountMode?: RackMountMode;
399
+ /** Device type specification */
400
+ deviceType?: string;
401
+ /** Device manufacturer */
402
+ manufacturer?: string;
403
+ /** Device role in network */
404
+ role?: string;
405
+ /** Device width configuration */
406
+ width?: Width | number;
407
+ /** Power port side for rear view */
408
+ powerSide?: PowerSide;
409
+ /** Vertical PDU height in centimeters */
410
+ heightCm?: number;
411
+ /** Number of AC power ports */
412
+ powerPorts?: number;
413
+ /** Optional explicit PDU connector sequence */
414
+ portTypes?: Array<'C13' | 'C19'>;
415
+ /** Optional grouped PDU ports (supports custom positions/types) */
416
+ pduPortGroups?: PortBlock[];
417
+ /** Vertical PDU width in pixels */
418
+ widthPx?: number;
419
+ /** Optional management unit position for vertical PDUs */
420
+ mgmt_unit?: 'top' | 'middle' | 'bottom';
421
+ /** U numbering direction for rack nodes */
422
+ uNumberingDirection?: UNumberingDirection;
423
+ /** Custom header text for rack nodes */
424
+ customHeaderText?: string;
425
+ /** Rack UI marker visibility */
426
+ showUMarkers?: boolean;
427
+ /** Holder position within tray (1-12) for splice nodes */
428
+ holderPosition?: number;
429
+ /** Loss value in dB for splice nodes */
430
+ lossDb?: number;
431
+ /** Splice mode */
432
+ mode?: 'single' | 'ribbon';
433
+ /** Legacy ribbon flag */
434
+ ribbon?: boolean;
435
+ /** Ribbon fiber IDs */
436
+ ribbonFiberIds?: number[];
437
+ /** Connector type for coupler nodes */
438
+ connector?: 'SC' | 'LC';
439
+ /** Number of fibers for coupler nodes (1, 2, or 4) */
440
+ couplerFiberCount?: 1 | 2 | 4;
441
+ /** Optional port labels for left side of coupler */
442
+ portLabelsLeft?: string[];
443
+ /** Optional port labels for right side of coupler */
444
+ portLabelsRight?: string[];
445
+ /** Slot position for couplers inside panels (1-based, vertical slots aligned to right edge) */
446
+ slotPosition?: number;
447
+ /** Width override for panel-like container nodes */
448
+ panelWidth?: number;
449
+ /** Height override for panel-like container nodes */
450
+ panelHeight?: number;
451
+ /** Width override for closure nodes */
452
+ closureWidth?: number;
453
+ /** Height override for closure nodes */
454
+ closureHeight?: number;
455
+ /** Optional rack width in pixels (for rack nodes, default: 300px) */
456
+ rackWidthPx?: number;
457
+ /** Number of rows for container nodes (patch-panel, closure) */
458
+ rows?: number;
459
+ /** Number of columns for container nodes (patch-panel, closure) */
460
+ cols?: number;
461
+ /** Number of trays for closure nodes */
462
+ trayCount?: number;
463
+ /** Header text for container nodes */
464
+ header?: string;
465
+ /** Type of fibre splitter: 'splitter' or 'WDM' (default: 'splitter') */
466
+ splitterType?: 'splitter' | 'WDM';
467
+ /** Number of input fibers on the left (1 or 2, default: 1) */
468
+ inputCount?: number;
469
+ /** Number of output fibers on the right (1-64, default: 8) */
470
+ outputCount?: number;
471
+ /** Tube index (1-based) */
472
+ tubeIndex?: number;
473
+ /** Tube count for multi-tube cables */
474
+ tubeCount?: number;
475
+ /** Tube start index for multi-tube cables */
476
+ tubeStartIndex?: number;
477
+ /** Number of fibers per tube */
478
+ fibersPerTube?: number;
479
+ /** Total fiber count for cables */
480
+ fiberCount?: number;
481
+ /** Cable identifier */
482
+ cableId?: string;
483
+ /** Primary node color override */
484
+ color?: string;
485
+ /** Rotation in degrees for rotatable nodes */
486
+ rotation?: number;
487
+ /** Tube handle orientation */
488
+ startOn?: 'left-center' | 'left' | 'right';
489
+ /** Rack metadata for integrations */
490
+ facilityId?: string | number | null;
491
+ /** Rack serial metadata */
492
+ serial?: string | null;
493
+ /** Asset tag metadata */
494
+ assetTag?: string | null;
495
+ /** Site metadata */
496
+ site?: string;
497
+ /** Tenant metadata */
498
+ tenant?: string;
499
+ /** Primary IPv4 metadata */
500
+ primaryIp4?: string;
501
+ /** Primary IPv6 metadata */
502
+ primaryIp6?: string;
503
+ /** Rack name metadata */
504
+ rack?: string | number | null;
505
+ /** Optional arrays used by inventory/netbox transforms */
506
+ racks?: unknown[];
507
+ devices?: unknown[];
508
+ cables?: unknown[];
509
+ /** UI color mode metadata */
510
+ colorMode?: 'light' | 'dark' | 'system';
511
+ /** Generic dimensions used by some helpers */
512
+ height?: number;
513
+ headerHeight?: number;
514
+ /** Optional precomputed map for handle-to-face resolution */
515
+ handleSideMap?: Record<string, DeviceView>;
516
+ /** Additional extension metadata */
517
+ metadata?: Record<string, unknown>;
518
+ }
519
+ /**
520
+ * Rack node data.
521
+ * @public
522
+ */
523
+ interface RackNodeData extends NetworkNodeDataBase {
524
+ ports?: number | PortBlock[];
525
+ }
526
+ /**
527
+ * Device-like node data (servers/switches/routers/generic device).
528
+ * @public
529
+ */
530
+ interface DeviceNodeData extends NetworkNodeDataBase {
531
+ ports?: PortBlock[] | number;
532
+ }
533
+ /**
534
+ * Fibre flow cable node data.
535
+ * @public
536
+ */
537
+ interface FibreFlowCableNodeData extends NetworkNodeDataBase {
538
+ cable: FibreFlowCable;
539
+ side: 'left' | 'right';
540
+ rowHeight: number;
541
+ columnWidth: number;
542
+ showTubeLabels: boolean;
543
+ showFiberNumbers: boolean;
544
+ layout: unknown;
545
+ colorMode?: 'dark' | 'light';
546
+ }
547
+ /**
548
+ * Fibre flow closure node data.
549
+ * @public
550
+ */
551
+ interface FibreFlowClosureNodeData extends NetworkNodeDataBase {
552
+ width?: number;
553
+ height?: number;
554
+ headerHeight?: number;
555
+ colorMode?: 'dark' | 'light';
556
+ }
557
+ /**
558
+ * Mapping from node type to corresponding data type.
559
+ * @public
560
+ */
561
+ interface NetworkNodeDataByType {
562
+ rack: RackNodeData;
563
+ switch: DeviceNodeData;
564
+ router: DeviceNodeData;
565
+ server: DeviceNodeData;
566
+ fiber: NetworkNodeDataBase;
567
+ 'patch-panel': NetworkNodeDataBase;
568
+ device: DeviceNodeData;
569
+ 'vertical-pdu': NetworkNodeDataBase;
570
+ splice: NetworkNodeDataBase;
571
+ 'splice-tray': NetworkNodeDataBase;
572
+ tube: NetworkNodeDataBase;
573
+ cable: NetworkNodeDataBase;
574
+ 'multi-tube-cable': NetworkNodeDataBase;
575
+ coupler: NetworkNodeDataBase;
576
+ closure: NetworkNodeDataBase;
577
+ 'fibre-split': NetworkNodeDataBase;
578
+ 'fibre-flow-cable': FibreFlowCableNodeData;
579
+ 'fibre-flow-closure': FibreFlowClosureNodeData;
580
+ }
581
+ /**
582
+ * Union of all node data variants.
583
+ * @public
584
+ */
585
+ type NetworkNodeData = NetworkNodeDataByType[NetworkNodeType];
586
+ /**
587
+ * Network node structure with data discriminated by `type`.
588
+ * @public
589
+ */
590
+ type NetworkNode<TType extends NetworkNodeType = NetworkNodeType, TExtra = unknown> = TType extends NetworkNodeType ? Node<NetworkNodeDataByType[TType] & TExtra, TType> & {
76
591
  /** Type of network node */
77
- type: NetworkNodeType;
78
- /** Position coordinates in the diagram */
79
- position: {
80
- x: number;
81
- y: number;
82
- };
83
- /** Parent node ID for React Flow sub-flows */
84
- parentId?: string;
85
- /** Node extent for sub-flows */
86
- extent?: 'parent' | [[number, number], [number, number]];
592
+ type: TType;
87
593
  /** Node data and configuration */
88
- data: {
89
- /** Display label for the node */
90
- label: string;
91
- /** Port configurations for the device */
92
- ports?: PortBlock[];
93
- /** List of connected node IDs */
94
- connections?: string[];
95
- /** Current status of the device */
96
- status?: DeviceStatus;
97
- /** NetBox integration ID */
98
- netboxId?: number;
99
- /** NetBox object type */
100
- netboxType?: 'device' | 'rack' | 'interface' | 'cable';
101
- /** Rack height in U units */
102
- uHeight?: number;
103
- /** Device position within rack in U units */
104
- uPosition?: number;
105
- /** Current view orientation */
106
- view?: DeviceView;
107
- /** Device type specification */
108
- deviceType?: string;
109
- /** Device manufacturer */
110
- manufacturer?: string;
111
- /** Device role in network */
112
- role?: string;
113
- /** Device width configuration */
114
- width?: Width;
115
- /** Rear port configurations */
116
- rearPorts?: PortBlock[];
117
- /** Power port side for rear view */
118
- powerSide?: PowerSide;
119
- /** Optional front image URL for device background (PNG, SVG, JPEG, etc.) */
120
- frontImageUrl?: string;
121
- /** Optional rear image URL for device background (PNG, SVG, JPEG, etc.) */
122
- rearImageUrl?: string;
123
- /** Optional attribution URL for front image (e.g. source/license link) */
124
- frontImageAttribution?: string;
125
- /** Optional attribution URL for rear image (e.g. source/license link) */
126
- rearImageAttribution?: string;
127
- /** Optional background sizing for device images */
128
- imageFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
129
- /** Optional background position for device images */
130
- imagePosition?: string;
131
- /** Optional background repeat for device images */
132
- imageRepeat?: 'no-repeat' | 'repeat' | 'repeat-x' | 'repeat-y';
133
- /** When true, stretch device image to fill the device bounds (overrides imageFit) */
134
- stretchToFit?: boolean;
135
- /** Vertical PDU height in centimeters */
136
- heightCm?: number;
137
- /** Number of AC power ports */
138
- powerPorts?: number;
139
- /** U numbering direction for rack nodes */
140
- uNumberingDirection?: UNumberingDirection;
141
- /** Custom header text for rack nodes */
142
- customHeaderText?: string;
143
- /** Holder position within tray (1-12) for splice nodes */
144
- holderPosition?: number;
145
- /** Loss value in dB for splice nodes */
146
- lossDb?: number;
147
- /** Connector type for coupler nodes */
148
- connector?: 'SC' | 'LC';
149
- /** Number of fibers for coupler nodes (1, 2, or 4) */
150
- couplerFiberCount?: 1 | 2 | 4;
151
- /** Optional port labels for left side of coupler */
152
- portLabelsLeft?: string[];
153
- /** Optional port labels for right side of coupler */
154
- portLabelsRight?: string[];
155
- /** Slot position for couplers inside panels (1-based, vertical slots aligned to right edge) */
156
- slotPosition?: number;
157
- /** Width override for container nodes (patch-panel, closure) */
158
- panelWidth?: number;
159
- /** Height override for container nodes (patch-panel, closure) */
160
- panelHeight?: number;
161
- /** Optional rack width in pixels (for rack nodes, default: 300px) */
162
- rackWidthPx?: number;
163
- /** Number of rows for container nodes (patch-panel, closure) */
164
- rows?: number;
165
- /** Number of columns for container nodes (patch-panel, closure) */
166
- cols?: number;
167
- /** Header text for container nodes */
168
- header?: string;
169
- /** Type of fibre splitter: 'splitter' or 'WDM' (default: 'splitter') */
170
- splitterType?: 'splitter' | 'WDM';
171
- /** Number of input fibers on the left (1 or 2, default: 1) */
172
- inputCount?: number;
173
- /** Number of output fibers on the right (1-64, default: 8) */
174
- outputCount?: number;
175
- /** Additional custom properties */
176
- [key: string]: any;
177
- };
178
- }
594
+ data: NetworkNodeDataByType[TType] & TExtra;
595
+ } : never;
179
596
  /**
180
597
  * Network edge types
181
598
  * @public
182
599
  */
183
600
  type NetworkEdgeType = 'fiber' | 'ethernet' | 'power' | 'smoothstep' | 'step' | 'thick-cable' | 'fibre-flow' | 'fibre-flow-link';
601
+ /**
602
+ * Edge data payload.
603
+ * @public
604
+ */
605
+ interface NetworkEdgeData {
606
+ /** Connection bandwidth */
607
+ bandwidth?: string;
608
+ /** Cable length */
609
+ length?: number;
610
+ /** Edge color for styling */
611
+ color?: string;
612
+ /** Edge kind for styling/validation */
613
+ kind?: 'fiber' | 'power' | 'network';
614
+ /** Whether this edge represents a ribbon fiber connection */
615
+ isRibbon?: boolean;
616
+ /** Array of fiber IDs for ribbon mode (6-12 fibers) */
617
+ ribbonFiberIds?: number[];
618
+ /** Highlight edge with a pulsing stroke */
619
+ highlight?: boolean;
620
+ /** Override highlight color (defaults to edge color) */
621
+ highlightColor?: string;
622
+ /** Override highlight max stroke width */
623
+ highlightWidth?: number;
624
+ /** Optional tag for grouping edges in demos */
625
+ traceTag?: string;
626
+ /** Legacy striped edge flag */
627
+ striped?: boolean;
628
+ /** Cable type metadata used by demos/helpers */
629
+ cableType?: PortCableType;
630
+ /** NetBox integration metadata */
631
+ netboxId?: number | string;
632
+ netboxType?: 'device' | 'rack' | 'interface' | 'cable' | string;
633
+ /** Inventory integration metadata */
634
+ inventoryId?: number | string;
635
+ inventoryType?: 'device' | 'rack' | 'interface' | 'cable' | string;
636
+ /** Status metadata */
637
+ status?: string;
638
+ /** Endpoint metadata */
639
+ terminationA?: string;
640
+ terminationB?: string;
641
+ /** Optional display label for the edge */
642
+ label?: string;
643
+ /** Optional opacity for styling (0–1) */
644
+ opacity?: number;
645
+ /** Optional power edge stroke width override */
646
+ powerStrokeWidth?: number;
647
+ /** Optional power edge stroke dasharray override */
648
+ powerStrokeDasharray?: string;
649
+ /** Optional power edge opacity override (0–1) */
650
+ powerOpacity?: number;
651
+ /** Additional extension metadata */
652
+ metadata?: Record<string, unknown>;
653
+ }
184
654
  /**
185
655
  * Network edge data structure
186
656
  * @public
187
657
  */
188
- interface NetworkEdge {
658
+ type NetworkEdge<TData extends NetworkEdgeData = NetworkEdgeData> = {
189
659
  /** Unique identifier for the edge */
190
660
  id: string;
191
661
  /** Source node ID */
@@ -199,31 +669,8 @@ interface NetworkEdge {
199
669
  /** Type of network connection */
200
670
  type?: NetworkEdgeType;
201
671
  /** Edge data and configuration */
202
- data?: {
203
- /** Connection bandwidth */
204
- bandwidth?: string;
205
- /** Cable length */
206
- length?: number;
207
- /** Edge color for styling */
208
- color?: string;
209
- /** Edge kind for styling/validation */
210
- kind?: 'fiber' | 'power' | 'network';
211
- /** Whether this edge represents a ribbon fiber connection */
212
- isRibbon?: boolean;
213
- /** Array of fiber IDs for ribbon mode (6-12 fibers) */
214
- ribbonFiberIds?: number[];
215
- /** Highlight edge with a pulsing stroke */
216
- highlight?: boolean;
217
- /** Override highlight color (defaults to edge color) */
218
- highlightColor?: string;
219
- /** Override highlight max stroke width */
220
- highlightWidth?: number;
221
- /** Optional tag for grouping edges in demos */
222
- traceTag?: string;
223
- /** Additional custom properties */
224
- [key: string]: any;
225
- };
226
- }
672
+ data?: TData;
673
+ };
227
674
  /**
228
675
  * Rack device types
229
676
  * @public
@@ -249,6 +696,10 @@ interface RackConfig {
249
696
  uNumberingDirection?: UNumberingDirection;
250
697
  /** Custom header text for rack */
251
698
  customHeaderText?: string;
699
+ /** Rack rendering layout mode */
700
+ rackLayout?: RackLayoutMode;
701
+ /** Gap in pixels between front and rear faces in split-face mode */
702
+ rackFaceGapPx?: number;
252
703
  /** Devices mounted in the rack */
253
704
  devices: RackDevice[];
254
705
  /** Optional rack width in pixels (default: 300px which represents standard 48.26cm/19" rack) */
@@ -258,7 +709,7 @@ interface RackConfig {
258
709
  * Device mounted in a rack
259
710
  * @public
260
711
  */
261
- interface RackDevice {
712
+ interface RackDevice extends DeviceImageOptions {
262
713
  /** Unique device identifier */
263
714
  id: string;
264
715
  /** Device display name */
@@ -271,30 +722,18 @@ interface RackDevice {
271
722
  type: RackDeviceType;
272
723
  /** Device width configuration */
273
724
  width?: Width;
725
+ /** Device mount behavior within a rack */
726
+ mountMode?: RackMountMode;
274
727
  /** Device role in network */
275
728
  role?: string;
729
+ /** Current status of the device */
730
+ status?: DeviceStatus | string;
276
731
  /** Front port configurations */
277
732
  ports?: PortBlock[];
278
733
  /** Rear port configurations */
279
734
  rearPorts?: PortBlock[];
280
735
  /** Power port side for rear view */
281
736
  powerSide?: PowerSide;
282
- /** Optional front image URL for device background (PNG, SVG, JPEG, etc.) */
283
- frontImageUrl?: string;
284
- /** Optional rear image URL for device background (PNG, SVG, JPEG, etc.) */
285
- rearImageUrl?: string;
286
- /** Optional attribution URL for front image (e.g. source/license link) */
287
- frontImageAttribution?: string;
288
- /** Optional attribution URL for rear image (e.g. source/license link) */
289
- rearImageAttribution?: string;
290
- /** Optional background sizing for device images */
291
- imageFit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
292
- /** Optional background position for device images */
293
- imagePosition?: string;
294
- /** Optional background repeat for device images */
295
- imageRepeat?: 'no-repeat' | 'repeat' | 'repeat-x' | 'repeat-y';
296
- /** When true, stretch device image to fill the device bounds (overrides imageFit) */
297
- stretchToFit?: boolean;
298
737
  /** Connected device IDs */
299
738
  connections?: string[];
300
739
  }
@@ -361,6 +800,139 @@ interface SpliceTrayConfig {
361
800
  /** Splices in this tray */
362
801
  splices: SpliceConfig[];
363
802
  }
803
+ /**
804
+ * Fine-grained interaction controls for network diagrams.
805
+ * @public
806
+ */
807
+ interface NetworkDiagramInteractionOptions {
808
+ /** Enable node dragging. */
809
+ nodesDraggable?: boolean;
810
+ /** Enable creating connections from node handles. */
811
+ nodesConnectable?: boolean;
812
+ /** Enable selecting nodes/edges. */
813
+ elementsSelectable?: boolean;
814
+ /** Enable keyboard/mouse focus on edges. */
815
+ edgesFocusable?: boolean;
816
+ /** Enable reconnecting existing edges. */
817
+ edgesReconnectable?: boolean;
818
+ /** Enable click-to-connect behavior on handles. */
819
+ connectOnClick?: boolean;
820
+ /** Enable panning by dragging the canvas. */
821
+ panOnDrag?: boolean;
822
+ /** Enable panning by scrolling. */
823
+ panOnScroll?: boolean;
824
+ /** Enable zoom via mouse wheel/trackpad scroll. */
825
+ zoomOnScroll?: boolean;
826
+ /** Enable pinch-zoom gestures. */
827
+ zoomOnPinch?: boolean;
828
+ /** Enable zoom on double click. */
829
+ zoomOnDoubleClick?: boolean;
830
+ /** Enable deleting selected edges with keyboard keys. */
831
+ allowKeyboardDelete?: boolean;
832
+ /** Show or hide the built-in React Flow controls widget. */
833
+ showControls?: boolean;
834
+ /** Show or hide the minimap. */
835
+ showMiniMap?: boolean;
836
+ /** Show or hide the interactive lock/unlock button in controls. */
837
+ showInteractiveControl?: boolean;
838
+ }
839
+ /**
840
+ * Selection action panel options rendered above selected racks/devices.
841
+ * @public
842
+ */
843
+ interface NetworkDiagramActionPanelOptions {
844
+ /** Enable or disable the floating action panel entirely. Defaults to true. */
845
+ enabled?: boolean;
846
+ /** Show or hide the front/rear toggle button. Defaults to true. */
847
+ showRearToggle?: boolean;
848
+ /** Show or hide the De-rack button for rack-mounted devices. Defaults to true. */
849
+ showDeRack?: boolean;
850
+ }
851
+ /**
852
+ * Serializable flow snapshot payload for save/restore helpers.
853
+ * @public
854
+ */
855
+ interface NetworkDiagramFlowObject {
856
+ /** Snapshot nodes */
857
+ nodes: NetworkNode[];
858
+ /** Snapshot edges */
859
+ edges: NetworkEdge[];
860
+ /** Optional viewport state */
861
+ viewport?: Viewport;
862
+ }
863
+ /**
864
+ * Options for exported diagram images.
865
+ * @public
866
+ */
867
+ interface NetworkDiagramToImageOptions {
868
+ /** Export width in px (clamped to 1-8192). */
869
+ width?: number;
870
+ /** Export height in px (clamped to 1-8192). */
871
+ height?: number;
872
+ backgroundColor?: string;
873
+ /** Export padding in px (clamped to 0-2048). */
874
+ padding?: number;
875
+ /** Device pixel ratio multiplier (clamped to 0.1-4). */
876
+ pixelRatio?: number;
877
+ /** JPEG/WebP quality (clamped to 0-1 when used). */
878
+ quality?: number;
879
+ filter?: (domNode: HTMLElement) => boolean;
880
+ }
881
+ /**
882
+ * Fit-view options that support targeting node IDs directly.
883
+ * @public
884
+ */
885
+ interface NetworkDiagramFitViewOptions extends Omit<FitViewOptions, 'nodes'> {
886
+ /** Optional node IDs to fit */
887
+ nodes?: string[];
888
+ }
889
+ /**
890
+ * Exposed methods for save/restore and export integrations.
891
+ * @public
892
+ */
893
+ interface NetworkDiagramFlowMethods {
894
+ toObject: () => NetworkDiagramFlowObject;
895
+ setViewport: (viewport: Viewport) => void;
896
+ setNodes: (nodes: NetworkNode[]) => void;
897
+ setEdges: (edges: NetworkEdge[]) => void;
898
+ toImage: (options?: NetworkDiagramToImageOptions) => Promise<string>;
899
+ getNodes: () => NetworkNode[];
900
+ getEdges: () => NetworkEdge[];
901
+ fitView: (options?: NetworkDiagramFitViewOptions) => void;
902
+ }
903
+ /**
904
+ * Store contract accepted by `NetworkDiagram`.
905
+ * @public
906
+ */
907
+ interface NetworkDiagramStoreApi {
908
+ getState: () => {
909
+ nodes: NetworkNode[];
910
+ edges: NetworkEdge[];
911
+ debug?: boolean;
912
+ };
913
+ setState: (partial: Partial<{
914
+ nodes: NetworkNode[];
915
+ edges: NetworkEdge[];
916
+ debug?: boolean;
917
+ }>) => void;
918
+ subscribe: (listener: (state: {
919
+ nodes: NetworkNode[];
920
+ edges: NetworkEdge[];
921
+ debug?: boolean;
922
+ }) => void) => () => void;
923
+ selectEdgeZIndex: (edge: NetworkEdge) => number;
924
+ validateConnection: (params: {
925
+ source: string | null;
926
+ target: string | null;
927
+ sourceHandle?: string | null;
928
+ targetHandle?: string | null;
929
+ }) => {
930
+ valid: boolean;
931
+ reason?: string;
932
+ };
933
+ selectNodeById: (id: string) => NetworkNode | undefined;
934
+ selectConnectedEdges: (nodeId: string) => NetworkEdge[];
935
+ }
364
936
  /**
365
937
  * Network diagram component props
366
938
  * @public
@@ -379,7 +951,7 @@ interface NetworkDiagramProps {
379
951
  /** Initial edges for uncontrolled mode */
380
952
  initialEdges?: NetworkEdge[];
381
953
  /** Headless store injection */
382
- store?: any;
954
+ store?: NetworkDiagramStoreApi;
383
955
  /** Enable debug mode */
384
956
  debug?: boolean;
385
957
  /** Callback when a node is clicked or selection changes (null when deselected) */
@@ -408,21 +980,14 @@ interface NetworkDiagramProps {
408
980
  style?: React.CSSProperties;
409
981
  /** If true, vertically align rack bottoms to the same baseline */
410
982
  alignRacksToBottom?: boolean;
983
+ /** Allow rack-mounted devices to move between racks/ungroup on drag and drop. Defaults to true. */
984
+ reAssignable?: boolean;
411
985
  /** Color mode for the diagram */
412
986
  colorMode?: 'light' | 'dark' | 'system';
413
987
  /** Callback when React Flow instance is initialized */
414
- onInit?: (instance: any) => void;
988
+ onInit?: (instance: ReactFlowInstance) => void;
415
989
  /** Callback to get React Flow methods for save/restore */
416
- onFlowMethods?: (methods: {
417
- toObject: () => any;
418
- setViewport: (viewport: any) => void;
419
- setNodes: (nodes: any[]) => void;
420
- setEdges: (edges: any[]) => void;
421
- toImage: (options?: any) => Promise<string>;
422
- getNodes: () => any[];
423
- getEdges: () => any[];
424
- fitView: (options?: any) => void;
425
- }) => void;
990
+ onFlowMethods?: (methods: NetworkDiagramFlowMethods) => void;
426
991
  /** Show download button */
427
992
  showDownloadButton?: boolean;
428
993
  /** Download button position */
@@ -433,6 +998,12 @@ interface NetworkDiagramProps {
433
998
  downloadButtonStyle?: React.CSSProperties;
434
999
  /** Use smoothstep edges for tube connections (both tube-to-cable and cable-to-tube). Default is fiber. */
435
1000
  useSmoothstepEdgesForTubes?: boolean;
1001
+ /** Force read-only behavior for the diagram (no drag/connect/delete/reconnect/pan/zoom edits). */
1002
+ readOnly?: boolean;
1003
+ /** Centralized interaction options for consistent edit/lock behavior. */
1004
+ interaction?: NetworkDiagramInteractionOptions;
1005
+ /** Configuration for the floating action panel shown on selected rack/device nodes. */
1006
+ actionPanel?: NetworkDiagramActionPanelOptions;
436
1007
  /** Enable connecting edges by clicking on handles. When true, increases handle sizes for better touch targets. Default is true. */
437
1008
  connectOnClick?: boolean;
438
1009
  /** Disable panning on drag. Default is false. */
@@ -650,47 +1221,6 @@ declare const ClosureNode: React$1.FC<NodeProps<NetworkNode['data']>>;
650
1221
  */
651
1222
  declare const FibreSplitNode: React$1.FC<NodeProps<NetworkNode['data']>>;
652
1223
 
653
- type FibreFlowCableKind = 'simple' | 'tube';
654
- interface FibreFlowEndpoint {
655
- tube?: number;
656
- fiber: number;
657
- }
658
- interface FibreFlowSplice {
659
- id: string;
660
- from: FibreFlowEndpoint;
661
- to: FibreFlowEndpoint;
662
- lossDb?: number;
663
- circuitId?: string;
664
- label?: string;
665
- color?: string;
666
- }
667
- interface FibreFlowCable {
668
- id: string;
669
- label: string;
670
- kind?: FibreFlowCableKind;
671
- fiberCount?: number;
672
- tubeCount?: number;
673
- fibersPerTube?: number;
674
- tubeStartIndex?: number;
675
- fiberStartIndex?: number;
676
- tubeColorMap?: Record<number, string>;
677
- jacketColor?: string;
678
- distanceLabel?: string;
679
- distanceMeters?: number;
680
- }
681
- interface FibreFlowClosure {
682
- id: string;
683
- chamberId?: string;
684
- leftCable: FibreFlowCable;
685
- rightCable: FibreFlowCable;
686
- splices: FibreFlowSplice[];
687
- }
688
- interface FibreFlowCircuit {
689
- id: string;
690
- label: string;
691
- color?: string;
692
- }
693
-
694
1224
  type FibreFlowLayoutDirection = 'horizontal' | 'vertical';
695
1225
  type FibreFlowColorMode = 'dark' | 'light';
696
1226
  interface FibreFlowMapProps {
@@ -709,6 +1239,7 @@ interface FibreFlowMapProps {
709
1239
  showCircuitId?: boolean;
710
1240
  className?: string;
711
1241
  style?: React$1.CSSProperties;
1242
+ readOnly?: boolean;
712
1243
  panOnDrag?: boolean;
713
1244
  panOnScroll?: boolean;
714
1245
  zoomOnScroll?: boolean;
@@ -857,6 +1388,34 @@ declare const inventoryToNetworkDiagram: (data: {
857
1388
  */
858
1389
  declare const generateLayout: (nodes: NetworkNode[]) => NetworkNode[];
859
1390
 
1391
+ /**
1392
+ * Convert NetBox rack data to NetworkNode
1393
+ */
1394
+ declare const netboxRackToNetworkNode: (rack: NetBoxRack) => NetworkNode;
1395
+ /**
1396
+ * Convert NetBox device data to NetworkNode
1397
+ */
1398
+ declare const netboxDeviceToNetworkNode: (device: NetBoxDevice, options?: NetBoxDeviceTransformOptions) => NetworkNode;
1399
+ /**
1400
+ * Convert NetBox cable data to NetworkEdge
1401
+ */
1402
+ declare const netboxCableToNetworkEdge: (cable: NetBoxCable) => NetworkEdge;
1403
+ /**
1404
+ * Convert NetBox data to network diagram format
1405
+ */
1406
+ declare const netboxToNetworkDiagram: (data: {
1407
+ racks: NetBoxRack[];
1408
+ devices: NetBoxDevice[];
1409
+ cables: NetBoxCable[];
1410
+ }, options?: NetBoxDeviceTransformOptions) => {
1411
+ nodes: NetworkNode[];
1412
+ edges: NetworkEdge[];
1413
+ };
1414
+ /**
1415
+ * Calculate U position for devices in a rack
1416
+ */
1417
+ declare const calculateUPositions: (devices: NetBoxDevice[], _rackHeight: number) => Map<number, number>;
1418
+
860
1419
  /**
861
1420
  * Snap a device position to the nearest U position within a rack
862
1421
  */
@@ -879,18 +1438,18 @@ declare const findNextAvailableUPosition: (rack: NetworkNode, device: NetworkNod
879
1438
  /**
880
1439
  * Calculate device position based on U position within a rack
881
1440
  */
882
- declare const calculateDevicePositionFromU: (rack: NetworkNode, uPosition: number, deviceHeight?: number) => {
1441
+ declare const calculateDevicePositionFromU: (rack: NetworkNode, uPosition: number, deviceHeight?: number, deviceData?: Pick<NetworkNode["data"], "mountMode">) => {
883
1442
  x: number;
884
1443
  y: number;
885
1444
  };
886
1445
  /**
887
1446
  * Update device U position and recalculate position
888
1447
  */
889
- declare const updateDeviceUPosition$1: (device: NetworkNode, rack: NetworkNode, newUPosition: number) => NetworkNode;
1448
+ declare const updateDeviceUPosition$1: (device: NetworkNode<"device" | "server" | "switch" | "router" | "patch-panel" | "vertical-pdu">, rack: NetworkNode, newUPosition: number) => NetworkNode<"device" | "server" | "switch" | "router" | "patch-panel" | "vertical-pdu">;
890
1449
  /**
891
1450
  * Validate and snap device to valid U position
892
1451
  */
893
- declare const validateAndSnapDevice: (device: NetworkNode, rack: NetworkNode) => NetworkNode;
1452
+ declare const validateAndSnapDevice: (device: NetworkNode<"device" | "server" | "switch" | "router" | "patch-panel" | "vertical-pdu">, rack: NetworkNode) => NetworkNode<"device" | "server" | "switch" | "router" | "patch-panel" | "vertical-pdu">;
894
1453
 
895
1454
  /**
896
1455
  * Snap a splice to the nearest holder position within a tray
@@ -1127,7 +1686,31 @@ declare function calculateCouplerSpacing(numCouplers: number, couplerHeight: num
1127
1686
  * @public
1128
1687
  */
1129
1688
  declare function calculateCouplerSlotSpacing(numCouplers: number, couplerHeight: number, availableHeight: number): number;
1689
+ /**
1690
+ * Calculate closure dimensions based on child nodes
1691
+ *
1692
+ * Automatically calculates the width and height of a closure based on its child nodes.
1693
+ * Supports special layouts for closures with tubes and trays.
1694
+ * Closure layout: [padding] [left tubes] [spacing] [trays] [spacing] [right tubes] [padding]
1695
+ *
1696
+ * @param closureNode - The closure node
1697
+ * @param childNodes - Array of child nodes within the closure
1698
+ * @returns Object with calculated width and height in pixels
1699
+ * @public
1700
+ *
1701
+ * @example
1702
+ * ```ts
1703
+ * const closure = { id: 'closure-1', type: 'closure', data: {} };
1704
+ * const children = [leftTubeNode, trayNode, rightTubeNode];
1705
+ * const dims = calculateClosureDimensions(closure, children);
1706
+ * ```
1707
+ */
1708
+ declare function calculateClosureDimensions(closureNode: NetworkNode, childNodes: NetworkNode[]): {
1709
+ width: number;
1710
+ height: number;
1711
+ };
1130
1712
 
1713
+ type RackMountedNodeType = 'device' | 'server' | 'switch' | 'router' | 'patch-panel' | 'vertical-pdu';
1131
1714
  /**
1132
1715
  * Configuration options for building nodes from rack schema
1133
1716
  */
@@ -1137,7 +1720,7 @@ interface RackSchemaOptions {
1137
1720
  /** Whether to validate all placements before creating nodes */
1138
1721
  validatePlacements?: boolean;
1139
1722
  /** Custom device type mapping from schema to NetworkNode type */
1140
- deviceTypeMapping?: Record<string, NetworkNode['type']>;
1723
+ deviceTypeMapping?: Record<string, RackMountedNodeType>;
1141
1724
  }
1142
1725
  /**
1143
1726
  * Error thrown when device placement conflicts occur in strict mode
@@ -1349,7 +1932,7 @@ declare function removeSpliceFromTray(trays: SpliceTrayConfig[], trayId: string,
1349
1932
  * Power connector utilities for consistent styling across devices and PDUs
1350
1933
  */
1351
1934
  interface PowerConnectorConfig {
1352
- type: 'C13' | 'C14' | 'C19' | 'C20';
1935
+ type: 'C13' | 'C14' | 'C19' | 'C20' | 'AC';
1353
1936
  color: string;
1354
1937
  borderColor: string;
1355
1938
  amperage: number;
@@ -1439,7 +2022,7 @@ declare function fiberSolidOrStriped(id: FiberColorId | number): React$1.CSSProp
1439
2022
  declare function getFiberColor(id: FiberColorId | number): string;
1440
2023
 
1441
2024
  /**
1442
- * Calculate the bounding box of a rack node
2025
+ * Calculate the bounding box of a rack node (header + body)
1443
2026
  * @param rack - The rack node
1444
2027
  * @returns Object with left, right, top, and bottom coordinates
1445
2028
  * @public
@@ -1495,8 +2078,10 @@ interface ReplaceEdgeOptions {
1495
2078
  edgeType: 'power' | 'fiber' | 'step' | 'smoothstep' | 'thick-cable';
1496
2079
  /** Function to calculate zIndex for the edge */
1497
2080
  selectEdgeZIndex: (edge: NetworkEdge) => number;
1498
- /** Whether to find edge by source or target */
2081
+ /** Which end of the connection to use (source or target) for finding the edge */
1499
2082
  findBy: 'source' | 'target';
2083
+ /** 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) */
2084
+ matchOn?: 'source' | 'target';
1500
2085
  /** Optional edge data to set on the new edge */
1501
2086
  data?: NetworkEdge['data'];
1502
2087
  }
@@ -1509,6 +2094,276 @@ interface ReplaceEdgeOptions {
1509
2094
  */
1510
2095
  declare function replaceEdge(options: ReplaceEdgeOptions): Edge[] | null;
1511
2096
 
2097
+ /**
2098
+ * Template Schema V2 — unified device template model.
2099
+ *
2100
+ * Supports rackable devices (1U+), 0U/unrackable devices (vertical PDUs),
2101
+ * and hybrid front/rear pass-through devices. Strictly more expressive than
2102
+ * NetBox device-type YAML while remaining import-compatible.
2103
+ *
2104
+ * @public
2105
+ */
2106
+
2107
+ /** Current schema version constant. */
2108
+ declare const TEMPLATE_SCHEMA_VERSION = 2;
2109
+ /** Device mount class — distinguishes rackable from 0U/standalone devices. */
2110
+ type MountClass = 'rackable' | 'zero-u';
2111
+ /** Device width within a rack (full 19" or half 9.5"). */
2112
+ type TemplateWidth = 'full' | 'half';
2113
+ /**
2114
+ * Individual port inside a template port block.
2115
+ * @public
2116
+ */
2117
+ interface TemplatePort {
2118
+ /** Stable identifier for the port (must be unique within the template). */
2119
+ id: string;
2120
+ /** Human-readable label. */
2121
+ label: string;
2122
+ /** Port classification for styling and validation. */
2123
+ type?: PortType;
2124
+ /** Cable type hint. */
2125
+ cableType?: PortCableType;
2126
+ /** Power connector classification. */
2127
+ connectorType?: 'C13' | 'C14' | 'C19' | 'C20' | 'AC';
2128
+ /** Explicit handle placement. */
2129
+ position?: PortPosition;
2130
+ }
2131
+ /**
2132
+ * Group of related ports on a single face.
2133
+ * @public
2134
+ */
2135
+ interface TemplatePortBlock {
2136
+ /** Group name. */
2137
+ name: string;
2138
+ /** Ports in this group. */
2139
+ ports: TemplatePort[];
2140
+ }
2141
+ /**
2142
+ * Per-face data container for front or rear.
2143
+ * @public
2144
+ */
2145
+ interface TemplateFaceData {
2146
+ /** Port blocks on this face. */
2147
+ portBlocks: TemplatePortBlock[];
2148
+ /** Optional image URL for this face. */
2149
+ imageUrl?: string;
2150
+ /** Optional attribution for the image. */
2151
+ imageAttribution?: string;
2152
+ }
2153
+ /**
2154
+ * Explicit front-to-rear pass-through link between ports.
2155
+ * @public
2156
+ */
2157
+ interface TemplateFrontRearPortLink {
2158
+ /** Port ID on the front face. */
2159
+ frontPortId: string;
2160
+ /** Port ID on the rear face. */
2161
+ rearPortId: string;
2162
+ }
2163
+ /**
2164
+ * Source provenance for an imported template.
2165
+ * @public
2166
+ */
2167
+ interface TemplateSource {
2168
+ /** Source system kind (e.g. 'netbox', 'manual'). */
2169
+ kind: string;
2170
+ /** Source repository URL. */
2171
+ repository?: string;
2172
+ /** Path within the repository. */
2173
+ path?: string;
2174
+ }
2175
+ /**
2176
+ * Canonical device template schema V2.
2177
+ * @public
2178
+ */
2179
+ interface DeviceTemplateV2 {
2180
+ /** Schema version — always 2. */
2181
+ templateSchemaVersion: 2;
2182
+ /** Unique identifier (e.g. 'cisco-c9300-48p'). */
2183
+ id: string;
2184
+ /** URL-safe slug. */
2185
+ slug: string;
2186
+ /** Device manufacturer. */
2187
+ manufacturer: string;
2188
+ /** Device model. */
2189
+ model: string;
2190
+ /** Device role (e.g. 'switch', 'pdu', 'server'). */
2191
+ role: string;
2192
+ /** Mount class — rackable (1U+) or zero-u. */
2193
+ mountClass: MountClass;
2194
+ /** Height in rack units (0 for zero-u devices). */
2195
+ uHeight: number;
2196
+ /** Width within a rack. */
2197
+ width: TemplateWidth;
2198
+ /** Per-face port and image data. */
2199
+ faces: {
2200
+ front: TemplateFaceData;
2201
+ rear: TemplateFaceData;
2202
+ };
2203
+ /** Explicit front-to-rear pass-through links. */
2204
+ frontRearPortLinks: TemplateFrontRearPortLink[];
2205
+ /** Optional image configuration. */
2206
+ images?: {
2207
+ front?: string | null;
2208
+ rear?: string | null;
2209
+ };
2210
+ /** Source provenance. */
2211
+ source?: TemplateSource;
2212
+ /** Optional tags for search/filtering. */
2213
+ tags?: string[];
2214
+ }
2215
+ /**
2216
+ * Single validation issue from the parser pipeline.
2217
+ * @public
2218
+ */
2219
+ interface TemplateValidationIssue {
2220
+ /** Severity level. */
2221
+ severity: 'error' | 'warning';
2222
+ /** Machine-readable code. */
2223
+ code: string;
2224
+ /** Human-readable description. */
2225
+ message: string;
2226
+ /** Optional path to the problematic field. */
2227
+ path?: string;
2228
+ }
2229
+ /**
2230
+ * Validation report emitted by the parser pipeline.
2231
+ * @public
2232
+ */
2233
+ interface TemplateValidationReport {
2234
+ /** Whether the template is valid (no errors — warnings are allowed). */
2235
+ valid: boolean;
2236
+ /** List of issues found. */
2237
+ issues: TemplateValidationIssue[];
2238
+ /** Summary counts per face. */
2239
+ portCounts: {
2240
+ front: number;
2241
+ rear: number;
2242
+ };
2243
+ }
2244
+ /**
2245
+ * Result of the full parser pipeline.
2246
+ * @public
2247
+ */
2248
+ interface TemplateParseResult {
2249
+ /** Parsed template (present when valid). */
2250
+ template: DeviceTemplateV2 | null;
2251
+ /** Validation report. */
2252
+ report: TemplateValidationReport;
2253
+ }
2254
+
2255
+ /**
2256
+ * NetBox → Template V2 parser pipeline.
2257
+ *
2258
+ * Five stages: Ingest → Normalize → Enrich → Validate → Export.
2259
+ *
2260
+ * **Port position semantics**: Positions use `xPercent` / `yPercent` (0-100)
2261
+ * to express where the port CENTER should be relative to the device face.
2262
+ * The renderer (`DeviceNode`) converts these to pixel coordinates and clamps
2263
+ * them so the port stays fully inside the device bounds. Clamp bounds are
2264
+ * computed from the **actual** rendered port dimensions (which vary by port
2265
+ * type — e.g., power C13/C19 connectors are larger than generic ethernet
2266
+ * handles). Templates therefore do NOT need to account for port size; the
2267
+ * renderer handles that.
2268
+ *
2269
+ * @public
2270
+ */
2271
+
2272
+ /**
2273
+ * Raw port entry from a NetBox-style template.
2274
+ * @public
2275
+ */
2276
+ interface RawTemplatePort {
2277
+ id?: string;
2278
+ name?: string;
2279
+ label?: string;
2280
+ type?: string;
2281
+ connectorType?: string;
2282
+ connector_type?: string;
2283
+ position?: {
2284
+ side?: string;
2285
+ offsetPercent?: number;
2286
+ offset_percent?: number;
2287
+ offsetPx?: number;
2288
+ offset_px?: number;
2289
+ xPercent?: number;
2290
+ x_percent?: number;
2291
+ yPercent?: number;
2292
+ y_percent?: number;
2293
+ };
2294
+ }
2295
+ /**
2296
+ * Raw port block from a NetBox-style template.
2297
+ * @public
2298
+ */
2299
+ interface RawTemplatePortBlock {
2300
+ name?: string;
2301
+ ports?: RawTemplatePort[];
2302
+ }
2303
+ /**
2304
+ * Raw NetBox-style template input for the parser.
2305
+ * @public
2306
+ */
2307
+ interface RawNetboxTemplate {
2308
+ id?: string;
2309
+ slug?: string;
2310
+ manufacturer?: string;
2311
+ model?: string;
2312
+ role?: string;
2313
+ deviceType?: string;
2314
+ device_type?: string;
2315
+ uHeight?: number;
2316
+ u_height?: number;
2317
+ width?: string;
2318
+ ports?: RawTemplatePortBlock[];
2319
+ rearPorts?: RawTemplatePortBlock[];
2320
+ rear_ports?: RawTemplatePortBlock[];
2321
+ frontRearPortLinks?: Array<{
2322
+ frontPortId?: string;
2323
+ rearPortId?: string;
2324
+ front_port_id?: string;
2325
+ rear_port_id?: string;
2326
+ }>;
2327
+ front_rear_port_links?: Array<{
2328
+ frontPortId?: string;
2329
+ rearPortId?: string;
2330
+ front_port_id?: string;
2331
+ rear_port_id?: string;
2332
+ }>;
2333
+ frontImageUrl?: string;
2334
+ front_image_url?: string;
2335
+ rearImageUrl?: string;
2336
+ rear_image_url?: string;
2337
+ frontImageAttribution?: string;
2338
+ rearImageAttribution?: string;
2339
+ tags?: string[];
2340
+ sourceRepository?: string;
2341
+ source_repository?: string;
2342
+ sourcePath?: string;
2343
+ source_path?: string;
2344
+ }
2345
+ /**
2346
+ * Parse a raw NetBox-style template through the full 5-stage pipeline.
2347
+ *
2348
+ * Returns a `TemplateParseResult` containing the validated V2 template
2349
+ * (or `null` if validation failed) and a full validation report.
2350
+ *
2351
+ * @public
2352
+ */
2353
+ declare function parseNetboxTemplate(raw: RawNetboxTemplate): TemplateParseResult;
2354
+ /**
2355
+ * Count the total number of ports on a given face of a V2 template.
2356
+ * @public
2357
+ */
2358
+ declare function countTemplateFacePorts(face: TemplateFaceData): number;
2359
+ /**
2360
+ * Validate that rendered handle counts match the template port counts.
2361
+ * Returns issues when counts diverge — used as a regression guardrail.
2362
+ *
2363
+ * @public
2364
+ */
2365
+ declare function validateRenderFidelity(template: DeviceTemplateV2, renderedFrontHandleCount: number, renderedRearHandleCount: number): TemplateValidationIssue[];
2366
+
1512
2367
  interface NetworkDiagramState {
1513
2368
  nodes: NetworkNode[];
1514
2369
  edges: NetworkEdge[];
@@ -1519,6 +2374,8 @@ interface ValidateConnectionParams {
1519
2374
  target: string | null;
1520
2375
  sourceHandle?: string | null;
1521
2376
  targetHandle?: string | null;
2377
+ /** Optional edge id to ignore during occupancy checks (used for reconnect). */
2378
+ ignoreEdgeId?: string;
1522
2379
  }
1523
2380
  interface ValidationResult {
1524
2381
  valid: boolean;
@@ -1542,4 +2399,4 @@ declare function useNetworkDiagram(store: NetworkDiagramStore): NetworkDiagramSt
1542
2399
  declare function useNodes(store: NetworkDiagramStore): NetworkNode[];
1543
2400
  declare function useEdges(store: NetworkDiagramStore): NetworkEdge[];
1544
2401
 
1545
- export { COUPLERS_PER_PANEL, COUPLER_SLOT_HEIGHT_PX, COUPLER_WIDTH_PX, CableNode, ClosureNode, CouplerNode, DeviceNode, DevicePlacementError, type DevicePlacementValidation, DiagramErrorBoundary, type DiagramErrorBoundaryProps, FIBER_COLORS_12, type FiberCable, type FiberColorId, FiberEdge, FiberNode, FibreCableWithTubesExpanded, type FibreFlowCable, type FibreFlowCableKind, type FibreFlowCircuit, type FibreFlowClosure, type FibreFlowEndpoint, FibreFlowMap, type FibreFlowMapProps, type FibreFlowSplice, FibreSplitNode, HANDLE_EXTENSION_PX, MultiTubeCableNode, NetworkDiagram, type NetworkDiagramProps, type NetworkEdge, type NetworkNode, PANEL_BORDER_WIDTH, PANEL_HEADER_HEIGHT, PANEL_HEIGHT_PX, PANEL_PADDING, PANEL_TUBE_TRAY_SPACING, PANEL_WIDTH_PX, POWER_CONNECTORS, PatchPanelNode, type Port, type PortBlock, type PortType, PowerEdge, RACK_HEADER_HEIGHT, RACK_WIDTH_PX, type RackConfig, type RackDevice, RackNode, type RackSchemaOptions, type ReplaceEdgeOptions, type SpliceConfig, SpliceNode, SplicePlacementError, type SplicePlacementValidation, type SpliceSchemaOptions, type SpliceTrayConfig, SpliceTrayNode, TubeNode, U_HEIGHT_PX, VerticalPDU, Width, addDeviceToRack, addSpliceToTray, baseColorFor, buildNodesFromRackConfig, buildNodesFromSpliceConfig, calculateCouplerRightX, calculateCouplerSlotSpacing, calculateCouplerSpacing, calculateDevicePositionFromU, calculatePanelDimensions, calculateSlotPositionFromY, calculateSplicePositionFromNumber, calculateYFromSlotPosition, createNetworkDiagramStore, createRackConfigFromNodes, createSpliceConfigFromNodes, fiberSolidOrStriped, findNearestRack, findNextAvailableHolderPosition, findNextAvailableUPosition, generateLayout, getConnectorConfig, getCouplerDimensions, getDeviceConnectorType, getFiberColor, getInnerContentWidth, getPDUPortType, getPanelHeight, getPanelWidth, getPowerPortStyle, getRackBounds, getStatusColor, getTrayDimensions, getTubeDimensions, inventoryCableToNetworkEdge, inventoryDeviceToNetworkNode, inventoryRackToNetworkNode, inventoryToNetworkDiagram, isHighPowerConnector, isPointInRack, isStriped, isUPositionAvailable, removeDeviceFromRack, removeSpliceFromTray, replaceEdge, snapToCouplerPosition, snapToSplicePosition, snapToUPosition, updateDeviceUPosition$1 as updateDeviceUPosition, updateDeviceUPosition as updateDeviceUPositionInSchema, updateSpliceHolderPosition, useEdges as useDiagramEdges, useNodes as useDiagramNodes, useNetworkDiagram, validateAndSnapDevice, validateRackDevicePlacements, validateSplicePlacements };
2402
+ export { COUPLERS_PER_PANEL, COUPLER_SLOT_HEIGHT_PX, COUPLER_WIDTH_PX, CableNode, ClosureNode, CouplerNode, type DeviceImageFit, type DeviceImageOptions, type DeviceImageRepeat, DeviceNode, DevicePlacementError, type DevicePlacementValidation, type DeviceTemplateV2, DiagramErrorBoundary, type DiagramErrorBoundaryProps, FIBER_COLORS_12, type FiberCable, type FiberColorId, FiberEdge, FiberNode, FibreCableWithTubesExpanded, type FibreFlowCable, type FibreFlowCableKind, type FibreFlowCableNodeData, type FibreFlowCircuit, type FibreFlowClosure, type FibreFlowClosureNodeData, type FibreFlowEndpoint, FibreFlowMap, type FibreFlowMapProps, type FibreFlowSplice, FibreSplitNode, HANDLE_EXTENSION_PX, type MountClass, MultiTubeCableNode, NetworkDiagram, type NetworkDiagramFitViewOptions, type NetworkDiagramFlowMethods, type NetworkDiagramFlowObject, type NetworkDiagramOptions, type NetworkDiagramProps, type NetworkDiagramStoreApi, type NetworkDiagramToImageOptions, type NetworkEdge, type NetworkEdgeData, type NetworkNode, type NetworkNodeData, type NetworkNodeDataBase, type NetworkNodeDataByType, PANEL_BORDER_WIDTH, PANEL_HEADER_HEIGHT, PANEL_HEIGHT_PX, PANEL_PADDING, PANEL_TUBE_TRAY_SPACING, PANEL_WIDTH_PX, POWER_CONNECTORS, PatchPanelNode, type Port, type PortBlock, type PortCableType, type PortType, PowerEdge, RACK_HEADER_HEIGHT, RACK_WIDTH_PX, type RackConfig, type RackDevice, type RackLayoutMode, type RackMountMode, RackNode, type RackSchemaOptions, type RawNetboxTemplate, type RawTemplatePort, type RawTemplatePortBlock, type ReplaceEdgeOptions, type SpliceConfig, SpliceNode, SplicePlacementError, type SplicePlacementValidation, type SpliceSchemaOptions, type SpliceTrayConfig, SpliceTrayNode, TEMPLATE_SCHEMA_VERSION, type TemplateFaceData, type TemplateFrontRearPortLink, type TemplateParseResult, type TemplatePort, type TemplatePortBlock, type TemplateSource, type TemplateValidationIssue, type TemplateValidationReport, type TemplateWidth, TubeNode, U_HEIGHT_PX, type ValidateConnectionParams, type ValidationResult, VerticalPDU, Width, addDeviceToRack, addSpliceToTray, baseColorFor, buildNodesFromRackConfig, buildNodesFromSpliceConfig, calculateClosureDimensions, calculateCouplerRightX, calculateCouplerSlotSpacing, calculateCouplerSpacing, calculateDevicePositionFromU, calculatePanelDimensions, calculateSlotPositionFromY, calculateSplicePositionFromNumber, calculateUPositions, calculateYFromSlotPosition, countTemplateFacePorts, createNetworkDiagramStore, createRackConfigFromNodes, createSpliceConfigFromNodes, fiberSolidOrStriped, findNearestRack, findNextAvailableHolderPosition, findNextAvailableUPosition, generateLayout, getConnectorConfig, getCouplerDimensions, getDeviceConnectorType, getFiberColor, getInnerContentWidth, getPDUPortType, getPanelHeight, getPanelWidth, getPowerPortStyle, getRackBounds, getStatusColor, getTrayDimensions, getTubeDimensions, inventoryCableToNetworkEdge, inventoryDeviceToNetworkNode, inventoryRackToNetworkNode, inventoryToNetworkDiagram, isHighPowerConnector, isPointInRack, isStriped, isUPositionAvailable, netboxCableToNetworkEdge, netboxDeviceToNetworkNode, netboxRackToNetworkNode, netboxToNetworkDiagram, parseNetboxTemplate, removeDeviceFromRack, removeSpliceFromTray, replaceEdge, snapToCouplerPosition, snapToSplicePosition, snapToUPosition, updateDeviceUPosition$1 as updateDeviceUPosition, updateDeviceUPosition as updateDeviceUPositionInSchema, updateSpliceHolderPosition, useEdges as useDiagramEdges, useNodes as useDiagramNodes, useNetworkDiagram, validateAndSnapDevice, validateRackDevicePlacements, validateRenderFidelity, validateSplicePlacements };