force-3d-graph 1.2.6 → 1.3.7

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.
@@ -1,8 +1,8 @@
1
- var ot = Object.defineProperty;
2
- var nt = (d, e, s) => e in d ? ot(d, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : d[e] = s;
3
- var l = (d, e, s) => nt(d, typeof e != "symbol" ? e + "" : e, s);
4
- import * as y from "three";
5
- import { EventDispatcher as at, Vector3 as S, MOUSE as $, TOUCH as G, Spherical as ze, Quaternion as Te, Vector2 as T, Ray as rt, Plane as lt, MathUtils as ct } from "three";
1
+ var it = Object.defineProperty;
2
+ var ot = (h, e, s) => e in h ? it(h, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : h[e] = s;
3
+ var l = (h, e, s) => ot(h, typeof e != "symbol" ? e + "" : e, s);
4
+ import * as x from "three";
5
+ import { EventDispatcher as nt, Vector3 as S, MOUSE as j, TOUCH as $, Spherical as Se, Quaternion as ze, Vector2 as k, Ray as at, Plane as rt, MathUtils as lt } from "three";
6
6
  const P = {
7
7
  backgroundColor: 657930,
8
8
  cameraPosition: { x: 0, y: 0, z: 80 },
@@ -28,99 +28,100 @@ const P = {
28
28
  searchPlaceholder: "Search nodes or relationships...",
29
29
  initialViewMode: "3d",
30
30
  showViewToggle: !0,
31
+ showLegend: !0,
31
32
  targetFPS: 60,
32
33
  maxVisibleNodes: 1e4
33
34
  };
34
- var X = /* @__PURE__ */ ((d) => (d[d.HIGH = 0] = "HIGH", d[d.MEDIUM = 1] = "MEDIUM", d[d.LOW = 2] = "LOW", d))(X || {});
35
- function ht() {
36
- const d = document.createElement("div");
37
- return d.id = "force-graph-3d-container", d.style.cssText = `
35
+ var _ = /* @__PURE__ */ ((h) => (h[h.HIGH = 0] = "HIGH", h[h.MEDIUM = 1] = "MEDIUM", h[h.LOW = 2] = "LOW", h))(_ || {});
36
+ function ct() {
37
+ const h = document.createElement("div");
38
+ return h.id = "force-graph-3d-container", h.style.cssText = `
38
39
  width: 100%;
39
40
  height: 100%;
40
41
  position: absolute;
41
42
  top: 0;
42
43
  left: 0;
43
44
  overflow: hidden;
44
- `, document.body.appendChild(d), d;
45
+ `, document.body.appendChild(h), h;
45
46
  }
46
- function dt(d) {
47
- return d && d instanceof HTMLElement ? d : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ht());
47
+ function dt(h) {
48
+ return h && h instanceof HTMLElement ? h : (console.warn("[ForceGraph3D] No container provided, creating one automatically"), ct());
48
49
  }
49
- function ke(d) {
50
- const e = d.getBoundingClientRect();
50
+ function ke(h) {
51
+ const e = h.getBoundingClientRect();
51
52
  return {
52
53
  width: e.width || window.innerWidth,
53
54
  height: e.height || window.innerHeight
54
55
  };
55
56
  }
56
- function He(d) {
57
- if (!d || typeof d != "object")
57
+ function Fe(h) {
58
+ if (!h || typeof h != "object")
58
59
  return console.warn("[ForceGraph3D] Invalid node: must be an object"), !1;
59
- const e = d;
60
- return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !gt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
60
+ const e = h;
61
+ return typeof e.id != "string" || e.id.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node: id must be a non-empty string"), !1) : typeof e.label != "string" ? (console.warn("[ForceGraph3D] Invalid node: label must be a string"), !1) : e.color !== void 0 && typeof e.color != "number" ? (console.warn("[ForceGraph3D] Invalid node: color must be a number (hex)"), !1) : e.position !== void 0 && !pt(e.position) ? (console.warn("[ForceGraph3D] Invalid node: position must have x, y, z numbers"), !1) : !0;
61
62
  }
62
- function De(d) {
63
- if (!d || typeof d != "object")
63
+ function De(h) {
64
+ if (!h || typeof h != "object")
64
65
  return console.warn("[ForceGraph3D] Invalid edge: must be an object"), !1;
65
- const e = d;
66
+ const e = h;
66
67
  return typeof e.source != "string" || e.source.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: source must be a non-empty string"), !1) : typeof e.target != "string" || e.target.trim() === "" ? (console.warn("[ForceGraph3D] Invalid edge: target must be a non-empty string"), !1) : !0;
67
68
  }
68
- function pt(d) {
69
- return typeof d != "string" || d.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
69
+ function ht(h) {
70
+ return typeof h != "string" || h.trim() === "" ? (console.warn("[ForceGraph3D] Invalid node ID: must be a non-empty string"), !1) : !0;
70
71
  }
71
- function gt(d) {
72
- if (!d || typeof d != "object") return !1;
73
- const e = d;
72
+ function pt(h) {
73
+ if (!h || typeof h != "object") return !1;
74
+ const e = h;
74
75
  return typeof e.x == "number" && typeof e.y == "number" && typeof e.z == "number";
75
76
  }
76
- function D(d, e) {
77
- return d === e ? `${d}-${e}` : d < e ? `${d}-${e}` : `${e}-${d}`;
77
+ function Dt(h, e) {
78
+ return h === e ? `${h}-${e}` : h < e ? `${h}-${e}` : `${e}-${h}`;
78
79
  }
79
- const Pe = { type: "change" }, ce = { type: "start" }, Re = { type: "end" }, J = new rt(), Ie = new lt(), ut = Math.cos(70 * ct.DEG2RAD);
80
- class ft extends at {
80
+ const Te = { type: "change" }, le = { type: "start" }, Pe = { type: "end" }, Q = new at(), Le = new rt(), gt = Math.cos(70 * lt.DEG2RAD);
81
+ class ut extends nt {
81
82
  constructor(e, s) {
82
- super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: $.ROTATE, MIDDLE: $.DOLLY, RIGHT: $.PAN }, this.touches = { ONE: G.ROTATE, TWO: G.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
83
+ super(), this.object = e, this.domElement = s, this.domElement.style.touchAction = "none", this.enabled = !0, this.target = new S(), this.cursor = new S(), this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minTargetRadius = 0, this.maxTargetRadius = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = 0.05, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.panSpeed = 1, this.screenSpacePanning = !0, this.keyPanSpeed = 7, this.zoomToCursor = !1, this.autoRotate = !1, this.autoRotateSpeed = 2, this.keys = { LEFT: "ArrowLeft", UP: "ArrowUp", RIGHT: "ArrowRight", BOTTOM: "ArrowDown" }, this.mouseButtons = { LEFT: j.ROTATE, MIDDLE: j.DOLLY, RIGHT: j.PAN }, this.touches = { ONE: $.ROTATE, TWO: $.DOLLY_PAN }, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this._domElementKeyEvents = null, this.getPolarAngle = function() {
83
84
  return r.phi;
84
85
  }, this.getAzimuthalAngle = function() {
85
86
  return r.theta;
86
87
  }, this.getDistance = function() {
87
88
  return this.object.position.distanceTo(this.target);
88
- }, this.listenToKeyEvents = function(n) {
89
- n.addEventListener("keydown", re), this._domElementKeyEvents = n;
89
+ }, this.listenToKeyEvents = function(a) {
90
+ a.addEventListener("keydown", ae), this._domElementKeyEvents = a;
90
91
  }, this.stopListenToKeyEvents = function() {
91
- this._domElementKeyEvents.removeEventListener("keydown", re), this._domElementKeyEvents = null;
92
+ this._domElementKeyEvents.removeEventListener("keydown", ae), this._domElementKeyEvents = null;
92
93
  }, this.saveState = function() {
93
94
  t.target0.copy(t.target), t.position0.copy(t.object.position), t.zoom0 = t.object.zoom;
94
95
  }, this.reset = function() {
95
- t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Pe), t.update(), o = i.NONE;
96
+ t.target.copy(t.target0), t.object.position.copy(t.position0), t.object.zoom = t.zoom0, t.object.updateProjectionMatrix(), t.dispatchEvent(Te), t.update(), o = i.NONE;
96
97
  }, this.update = function() {
97
- const n = new S(), g = new Te().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), w = new S(), C = new Te(), F = new S(), z = 2 * Math.PI;
98
- return function(it = null) {
99
- const Se = t.object.position;
100
- n.copy(Se).sub(t.target), n.applyQuaternion(g), r.setFromVector3(n), t.autoRotate && o === i.NONE && B(Ae(it)), t.enableDamping ? (r.theta += c.theta * t.dampingFactor, r.phi += c.phi * t.dampingFactor) : (r.theta += c.theta, r.phi += c.phi);
101
- let I = t.minAzimuthAngle, L = t.maxAzimuthAngle;
102
- isFinite(I) && isFinite(L) && (I < -Math.PI ? I += z : I > Math.PI && (I -= z), L < -Math.PI ? L += z : L > Math.PI && (L -= z), I <= L ? r.theta = Math.max(I, Math.min(L, r.theta)) : r.theta = r.theta > (I + L) / 2 ? Math.max(I, r.theta) : Math.min(L, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && H || t.object.isOrthographicCamera ? r.radius = ne(r.radius) : r.radius = ne(r.radius * h), n.setFromSpherical(r), n.applyQuaternion(v), Se.copy(t.target).add(n), t.object.lookAt(t.target), t.enableDamping === !0 ? (c.theta *= 1 - t.dampingFactor, c.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (c.set(0, 0, 0), p.set(0, 0, 0));
103
- let le = !1;
104
- if (t.zoomToCursor && H) {
105
- let U = null;
98
+ const a = new S(), g = new ze().setFromUnitVectors(e.up, new S(0, 1, 0)), v = g.clone().invert(), M = new S(), C = new ze(), F = new S(), z = 2 * Math.PI;
99
+ return function(st = null) {
100
+ const Ne = t.object.position;
101
+ a.copy(Ne).sub(t.target), a.applyQuaternion(g), r.setFromVector3(a), t.autoRotate && o === i.NONE && Y(He(st)), t.enableDamping ? (r.theta += d.theta * t.dampingFactor, r.phi += d.phi * t.dampingFactor) : (r.theta += d.theta, r.phi += d.phi);
102
+ let R = t.minAzimuthAngle, I = t.maxAzimuthAngle;
103
+ isFinite(R) && isFinite(I) && (R < -Math.PI ? R += z : R > Math.PI && (R -= z), I < -Math.PI ? I += z : I > Math.PI && (I -= z), R <= I ? r.theta = Math.max(R, Math.min(I, r.theta)) : r.theta = r.theta > (R + I) / 2 ? Math.max(R, r.theta) : Math.min(I, r.theta)), r.phi = Math.max(t.minPolarAngle, Math.min(t.maxPolarAngle, r.phi)), r.makeSafe(), t.enableDamping === !0 ? t.target.addScaledVector(p, t.dampingFactor) : t.target.add(p), t.target.sub(t.cursor), t.target.clampLength(t.minTargetRadius, t.maxTargetRadius), t.target.add(t.cursor), t.zoomToCursor && D || t.object.isOrthographicCamera ? r.radius = oe(r.radius) : r.radius = oe(r.radius * c), a.setFromSpherical(r), a.applyQuaternion(v), Ne.copy(t.target).add(a), t.object.lookAt(t.target), t.enableDamping === !0 ? (d.theta *= 1 - t.dampingFactor, d.phi *= 1 - t.dampingFactor, p.multiplyScalar(1 - t.dampingFactor)) : (d.set(0, 0, 0), p.set(0, 0, 0));
104
+ let re = !1;
105
+ if (t.zoomToCursor && D) {
106
+ let K = null;
106
107
  if (t.object.isPerspectiveCamera) {
107
- const _ = n.length();
108
- U = ne(_ * h);
109
- const Q = _ - U;
110
- t.object.position.addScaledVector(Y, Q), t.object.updateMatrixWorld();
108
+ const U = a.length();
109
+ K = oe(U * c);
110
+ const Z = U - K;
111
+ t.object.position.addScaledVector(G, Z), t.object.updateMatrixWorld();
111
112
  } else if (t.object.isOrthographicCamera) {
112
- const _ = new S(k.x, k.y, 0);
113
- _.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), le = !0;
114
- const Q = new S(k.x, k.y, 0);
115
- Q.unproject(t.object), t.object.position.sub(Q).add(_), t.object.updateMatrixWorld(), U = n.length();
113
+ const U = new S(T.x, T.y, 0);
114
+ U.unproject(t.object), t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0;
115
+ const Z = new S(T.x, T.y, 0);
116
+ Z.unproject(t.object), t.object.position.sub(Z).add(U), t.object.updateMatrixWorld(), K = a.length();
116
117
  } else
117
118
  console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."), t.zoomToCursor = !1;
118
- U !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(U).add(t.object.position) : (J.origin.copy(t.object.position), J.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(J.direction)) < ut ? e.lookAt(t.target) : (Ie.setFromNormalAndCoplanarPoint(t.object.up, t.target), J.intersectPlane(Ie, t.target))));
119
- } else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / h)), t.object.updateProjectionMatrix(), le = !0);
120
- return h = 1, H = !1, le || w.distanceToSquared(t.object.position) > a || 8 * (1 - C.dot(t.object.quaternion)) > a || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Pe), w.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
119
+ K !== null && (this.screenSpacePanning ? t.target.set(0, 0, -1).transformDirection(t.object.matrix).multiplyScalar(K).add(t.object.position) : (Q.origin.copy(t.object.position), Q.direction.set(0, 0, -1).transformDirection(t.object.matrix), Math.abs(t.object.up.dot(Q.direction)) < gt ? e.lookAt(t.target) : (Le.setFromNormalAndCoplanarPoint(t.object.up, t.target), Q.intersectPlane(Le, t.target))));
120
+ } else t.object.isOrthographicCamera && (t.object.zoom = Math.max(t.minZoom, Math.min(t.maxZoom, t.object.zoom / c)), t.object.updateProjectionMatrix(), re = !0);
121
+ return c = 1, D = !1, re || M.distanceToSquared(t.object.position) > n || 8 * (1 - C.dot(t.object.quaternion)) > n || F.distanceToSquared(t.target) > 0 ? (t.dispatchEvent(Te), M.copy(t.object.position), C.copy(t.object.quaternion), F.copy(t.target), !0) : !1;
121
122
  };
122
123
  }(), this.dispose = function() {
123
- t.domElement.removeEventListener("contextmenu", Ce), t.domElement.removeEventListener("pointerdown", Me), t.domElement.removeEventListener("pointercancel", K), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", re), t._domElementKeyEvents = null);
124
+ t.domElement.removeEventListener("contextmenu", Ee), t.domElement.removeEventListener("pointerdown", ve), t.domElement.removeEventListener("pointercancel", B), t.domElement.removeEventListener("wheel", we), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B), t._domElementKeyEvents !== null && (t._domElementKeyEvents.removeEventListener("keydown", ae), t._domElementKeyEvents = null);
124
125
  };
125
126
  const t = this, i = {
126
127
  NONE: -1,
@@ -133,174 +134,174 @@ class ft extends at {
133
134
  TOUCH_DOLLY_ROTATE: 6
134
135
  };
135
136
  let o = i.NONE;
136
- const a = 1e-6, r = new ze(), c = new ze();
137
- let h = 1;
138
- const p = new S(), x = new T(), m = new T(), u = new T(), f = new T(), b = new T(), M = new T(), N = new T(), O = new T(), R = new T(), Y = new S(), k = new T();
139
- let H = !1;
140
- const E = [], q = {};
141
- let se = !1;
142
- function Ae(n) {
143
- return n !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * n : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
144
- }
145
- function W(n) {
146
- const g = Math.abs(n * 0.01);
137
+ const n = 1e-6, r = new Se(), d = new Se();
138
+ let c = 1;
139
+ const p = new S(), f = new k(), m = new k(), u = new k(), y = new k(), b = new k(), w = new k(), N = new k(), O = new k(), L = new k(), G = new S(), T = new k();
140
+ let D = !1;
141
+ const E = [], V = {};
142
+ let te = !1;
143
+ function He(a) {
144
+ return a !== null ? 2 * Math.PI / 60 * t.autoRotateSpeed * a : 2 * Math.PI / 60 / 60 * t.autoRotateSpeed;
145
+ }
146
+ function q(a) {
147
+ const g = Math.abs(a * 0.01);
147
148
  return Math.pow(0.95, t.zoomSpeed * g);
148
149
  }
149
- function B(n) {
150
- c.theta -= n;
150
+ function Y(a) {
151
+ d.theta -= a;
151
152
  }
152
- function Z(n) {
153
- c.phi -= n;
153
+ function W(a) {
154
+ d.phi -= a;
154
155
  }
155
- const he = function() {
156
- const n = new S();
157
- return function(v, w) {
158
- n.setFromMatrixColumn(w, 0), n.multiplyScalar(-v), p.add(n);
156
+ const ce = function() {
157
+ const a = new S();
158
+ return function(v, M) {
159
+ a.setFromMatrixColumn(M, 0), a.multiplyScalar(-v), p.add(a);
159
160
  };
160
161
  }(), de = function() {
161
- const n = new S();
162
- return function(v, w) {
163
- t.screenSpacePanning === !0 ? n.setFromMatrixColumn(w, 1) : (n.setFromMatrixColumn(w, 0), n.crossVectors(t.object.up, n)), n.multiplyScalar(v), p.add(n);
162
+ const a = new S();
163
+ return function(v, M) {
164
+ t.screenSpacePanning === !0 ? a.setFromMatrixColumn(M, 1) : (a.setFromMatrixColumn(M, 0), a.crossVectors(t.object.up, a)), a.multiplyScalar(v), p.add(a);
164
165
  };
165
- }(), A = function() {
166
- const n = new S();
167
- return function(v, w) {
166
+ }(), H = function() {
167
+ const a = new S();
168
+ return function(v, M) {
168
169
  const C = t.domElement;
169
170
  if (t.object.isPerspectiveCamera) {
170
171
  const F = t.object.position;
171
- n.copy(F).sub(t.target);
172
- let z = n.length();
173
- z *= Math.tan(t.object.fov / 2 * Math.PI / 180), he(2 * v * z / C.clientHeight, t.object.matrix), de(2 * w * z / C.clientHeight, t.object.matrix);
174
- } else t.object.isOrthographicCamera ? (he(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), de(w * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
172
+ a.copy(F).sub(t.target);
173
+ let z = a.length();
174
+ z *= Math.tan(t.object.fov / 2 * Math.PI / 180), ce(2 * v * z / C.clientHeight, t.object.matrix), de(2 * M * z / C.clientHeight, t.object.matrix);
175
+ } else t.object.isOrthographicCamera ? (ce(v * (t.object.right - t.object.left) / t.object.zoom / C.clientWidth, t.object.matrix), de(M * (t.object.top - t.object.bottom) / t.object.zoom / C.clientHeight, t.object.matrix)) : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."), t.enablePan = !1);
175
176
  };
176
177
  }();
177
- function ie(n) {
178
- t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h /= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
178
+ function se(a) {
179
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c /= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
179
180
  }
180
- function pe(n) {
181
- t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? h *= n : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
181
+ function he(a) {
182
+ t.object.isPerspectiveCamera || t.object.isOrthographicCamera ? c *= a : (console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."), t.enableZoom = !1);
182
183
  }
183
- function oe(n, g) {
184
+ function ie(a, g) {
184
185
  if (!t.zoomToCursor)
185
186
  return;
186
- H = !0;
187
- const v = t.domElement.getBoundingClientRect(), w = n - v.left, C = g - v.top, F = v.width, z = v.height;
188
- k.x = w / F * 2 - 1, k.y = -(C / z) * 2 + 1, Y.set(k.x, k.y, 1).unproject(t.object).sub(t.object.position).normalize();
187
+ D = !0;
188
+ const v = t.domElement.getBoundingClientRect(), M = a - v.left, C = g - v.top, F = v.width, z = v.height;
189
+ T.x = M / F * 2 - 1, T.y = -(C / z) * 2 + 1, G.set(T.x, T.y, 1).unproject(t.object).sub(t.object.position).normalize();
189
190
  }
190
- function ne(n) {
191
- return Math.max(t.minDistance, Math.min(t.maxDistance, n));
191
+ function oe(a) {
192
+ return Math.max(t.minDistance, Math.min(t.maxDistance, a));
192
193
  }
193
- function ge(n) {
194
- x.set(n.clientX, n.clientY);
194
+ function pe(a) {
195
+ f.set(a.clientX, a.clientY);
195
196
  }
196
- function je(n) {
197
- oe(n.clientX, n.clientX), N.set(n.clientX, n.clientY);
197
+ function Ae(a) {
198
+ ie(a.clientX, a.clientX), N.set(a.clientX, a.clientY);
198
199
  }
199
- function ue(n) {
200
- f.set(n.clientX, n.clientY);
200
+ function ge(a) {
201
+ y.set(a.clientX, a.clientY);
201
202
  }
202
- function $e(n) {
203
- m.set(n.clientX, n.clientY), u.subVectors(m, x).multiplyScalar(t.rotateSpeed);
203
+ function je(a) {
204
+ m.set(a.clientX, a.clientY), u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
204
205
  const g = t.domElement;
205
- B(2 * Math.PI * u.x / g.clientHeight), Z(2 * Math.PI * u.y / g.clientHeight), x.copy(m), t.update();
206
+ Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m), t.update();
206
207
  }
207
- function Ge(n) {
208
- O.set(n.clientX, n.clientY), R.subVectors(O, N), R.y > 0 ? ie(W(R.y)) : R.y < 0 && pe(W(R.y)), N.copy(O), t.update();
208
+ function $e(a) {
209
+ O.set(a.clientX, a.clientY), L.subVectors(O, N), L.y > 0 ? se(q(L.y)) : L.y < 0 && he(q(L.y)), N.copy(O), t.update();
209
210
  }
210
- function Ye(n) {
211
- b.set(n.clientX, n.clientY), M.subVectors(b, f).multiplyScalar(t.panSpeed), A(M.x, M.y), f.copy(b), t.update();
211
+ function Ge(a) {
212
+ b.set(a.clientX, a.clientY), w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b), t.update();
212
213
  }
213
- function Be(n) {
214
- oe(n.clientX, n.clientY), n.deltaY < 0 ? pe(W(n.deltaY)) : n.deltaY > 0 && ie(W(n.deltaY)), t.update();
214
+ function Ye(a) {
215
+ ie(a.clientX, a.clientY), a.deltaY < 0 ? he(q(a.deltaY)) : a.deltaY > 0 && se(q(a.deltaY)), t.update();
215
216
  }
216
- function Ke(n) {
217
+ function Be(a) {
217
218
  let g = !1;
218
- switch (n.code) {
219
+ switch (a.code) {
219
220
  case t.keys.UP:
220
- n.ctrlKey || n.metaKey || n.shiftKey ? Z(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, t.keyPanSpeed), g = !0;
221
+ a.ctrlKey || a.metaKey || a.shiftKey ? W(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, t.keyPanSpeed), g = !0;
221
222
  break;
222
223
  case t.keys.BOTTOM:
223
- n.ctrlKey || n.metaKey || n.shiftKey ? Z(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(0, -t.keyPanSpeed), g = !0;
224
+ a.ctrlKey || a.metaKey || a.shiftKey ? W(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(0, -t.keyPanSpeed), g = !0;
224
225
  break;
225
226
  case t.keys.LEFT:
226
- n.ctrlKey || n.metaKey || n.shiftKey ? B(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(t.keyPanSpeed, 0), g = !0;
227
+ a.ctrlKey || a.metaKey || a.shiftKey ? Y(2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(t.keyPanSpeed, 0), g = !0;
227
228
  break;
228
229
  case t.keys.RIGHT:
229
- n.ctrlKey || n.metaKey || n.shiftKey ? B(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : A(-t.keyPanSpeed, 0), g = !0;
230
+ a.ctrlKey || a.metaKey || a.shiftKey ? Y(-2 * Math.PI * t.rotateSpeed / t.domElement.clientHeight) : H(-t.keyPanSpeed, 0), g = !0;
230
231
  break;
231
232
  }
232
- g && (n.preventDefault(), t.update());
233
+ g && (a.preventDefault(), t.update());
233
234
  }
234
- function fe(n) {
235
+ function ue(a) {
235
236
  if (E.length === 1)
236
- x.set(n.pageX, n.pageY);
237
+ f.set(a.pageX, a.pageY);
237
238
  else {
238
- const g = j(n), v = 0.5 * (n.pageX + g.x), w = 0.5 * (n.pageY + g.y);
239
- x.set(v, w);
239
+ const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
240
+ f.set(v, M);
240
241
  }
241
242
  }
242
- function me(n) {
243
+ function fe(a) {
243
244
  if (E.length === 1)
244
- f.set(n.pageX, n.pageY);
245
+ y.set(a.pageX, a.pageY);
245
246
  else {
246
- const g = j(n), v = 0.5 * (n.pageX + g.x), w = 0.5 * (n.pageY + g.y);
247
- f.set(v, w);
247
+ const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
248
+ y.set(v, M);
248
249
  }
249
250
  }
250
- function ye(n) {
251
- const g = j(n), v = n.pageX - g.x, w = n.pageY - g.y, C = Math.sqrt(v * v + w * w);
251
+ function me(a) {
252
+ const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
252
253
  N.set(0, C);
253
254
  }
254
- function Ue(n) {
255
- t.enableZoom && ye(n), t.enablePan && me(n);
255
+ function Ke(a) {
256
+ t.enableZoom && me(a), t.enablePan && fe(a);
256
257
  }
257
- function _e(n) {
258
- t.enableZoom && ye(n), t.enableRotate && fe(n);
258
+ function Ue(a) {
259
+ t.enableZoom && me(a), t.enableRotate && ue(a);
259
260
  }
260
- function xe(n) {
261
+ function ye(a) {
261
262
  if (E.length == 1)
262
- m.set(n.pageX, n.pageY);
263
+ m.set(a.pageX, a.pageY);
263
264
  else {
264
- const v = j(n), w = 0.5 * (n.pageX + v.x), C = 0.5 * (n.pageY + v.y);
265
- m.set(w, C);
265
+ const v = A(a), M = 0.5 * (a.pageX + v.x), C = 0.5 * (a.pageY + v.y);
266
+ m.set(M, C);
266
267
  }
267
- u.subVectors(m, x).multiplyScalar(t.rotateSpeed);
268
+ u.subVectors(m, f).multiplyScalar(t.rotateSpeed);
268
269
  const g = t.domElement;
269
- B(2 * Math.PI * u.x / g.clientHeight), Z(2 * Math.PI * u.y / g.clientHeight), x.copy(m);
270
+ Y(2 * Math.PI * u.x / g.clientHeight), W(2 * Math.PI * u.y / g.clientHeight), f.copy(m);
270
271
  }
271
- function be(n) {
272
+ function xe(a) {
272
273
  if (E.length === 1)
273
- b.set(n.pageX, n.pageY);
274
+ b.set(a.pageX, a.pageY);
274
275
  else {
275
- const g = j(n), v = 0.5 * (n.pageX + g.x), w = 0.5 * (n.pageY + g.y);
276
- b.set(v, w);
276
+ const g = A(a), v = 0.5 * (a.pageX + g.x), M = 0.5 * (a.pageY + g.y);
277
+ b.set(v, M);
277
278
  }
278
- M.subVectors(b, f).multiplyScalar(t.panSpeed), A(M.x, M.y), f.copy(b);
279
+ w.subVectors(b, y).multiplyScalar(t.panSpeed), H(w.x, w.y), y.copy(b);
279
280
  }
280
- function ve(n) {
281
- const g = j(n), v = n.pageX - g.x, w = n.pageY - g.y, C = Math.sqrt(v * v + w * w);
282
- O.set(0, C), R.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), ie(R.y), N.copy(O);
283
- const F = (n.pageX + g.x) * 0.5, z = (n.pageY + g.y) * 0.5;
284
- oe(F, z);
281
+ function be(a) {
282
+ const g = A(a), v = a.pageX - g.x, M = a.pageY - g.y, C = Math.sqrt(v * v + M * M);
283
+ O.set(0, C), L.set(0, Math.pow(O.y / N.y, t.zoomSpeed)), se(L.y), N.copy(O);
284
+ const F = (a.pageX + g.x) * 0.5, z = (a.pageY + g.y) * 0.5;
285
+ ie(F, z);
285
286
  }
286
- function Xe(n) {
287
- t.enableZoom && ve(n), t.enablePan && be(n);
287
+ function _e(a) {
288
+ t.enableZoom && be(a), t.enablePan && xe(a);
288
289
  }
289
- function Ve(n) {
290
- t.enableZoom && ve(n), t.enableRotate && xe(n);
290
+ function Xe(a) {
291
+ t.enableZoom && be(a), t.enableRotate && ye(a);
291
292
  }
292
- function Me(n) {
293
- t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(n.pointerId), t.domElement.addEventListener("pointermove", ae), t.domElement.addEventListener("pointerup", K)), tt(n), n.pointerType === "touch" ? Je(n) : qe(n));
293
+ function ve(a) {
294
+ t.enabled !== !1 && (E.length === 0 && (t.domElement.setPointerCapture(a.pointerId), t.domElement.addEventListener("pointermove", ne), t.domElement.addEventListener("pointerup", B)), et(a), a.pointerType === "touch" ? Qe(a) : Ve(a));
294
295
  }
295
- function ae(n) {
296
- t.enabled !== !1 && (n.pointerType === "touch" ? et(n) : We(n));
296
+ function ne(a) {
297
+ t.enabled !== !1 && (a.pointerType === "touch" ? Je(a) : qe(a));
297
298
  }
298
- function K(n) {
299
- st(n), E.length === 0 && (t.domElement.releasePointerCapture(n.pointerId), t.domElement.removeEventListener("pointermove", ae), t.domElement.removeEventListener("pointerup", K)), t.dispatchEvent(Re), o = i.NONE;
299
+ function B(a) {
300
+ tt(a), E.length === 0 && (t.domElement.releasePointerCapture(a.pointerId), t.domElement.removeEventListener("pointermove", ne), t.domElement.removeEventListener("pointerup", B)), t.dispatchEvent(Pe), o = i.NONE;
300
301
  }
301
- function qe(n) {
302
+ function Ve(a) {
302
303
  let g;
303
- switch (n.button) {
304
+ switch (a.button) {
304
305
  case 0:
305
306
  g = t.mouseButtons.LEFT;
306
307
  break;
@@ -314,57 +315,57 @@ class ft extends at {
314
315
  g = -1;
315
316
  }
316
317
  switch (g) {
317
- case $.DOLLY:
318
+ case j.DOLLY:
318
319
  if (t.enableZoom === !1) return;
319
- je(n), o = i.DOLLY;
320
+ Ae(a), o = i.DOLLY;
320
321
  break;
321
- case $.ROTATE:
322
- if (n.ctrlKey || n.metaKey || n.shiftKey) {
322
+ case j.ROTATE:
323
+ if (a.ctrlKey || a.metaKey || a.shiftKey) {
323
324
  if (t.enablePan === !1) return;
324
- ue(n), o = i.PAN;
325
+ ge(a), o = i.PAN;
325
326
  } else {
326
327
  if (t.enableRotate === !1) return;
327
- ge(n), o = i.ROTATE;
328
+ pe(a), o = i.ROTATE;
328
329
  }
329
330
  break;
330
- case $.PAN:
331
- if (n.ctrlKey || n.metaKey || n.shiftKey) {
331
+ case j.PAN:
332
+ if (a.ctrlKey || a.metaKey || a.shiftKey) {
332
333
  if (t.enableRotate === !1) return;
333
- ge(n), o = i.ROTATE;
334
+ pe(a), o = i.ROTATE;
334
335
  } else {
335
336
  if (t.enablePan === !1) return;
336
- ue(n), o = i.PAN;
337
+ ge(a), o = i.PAN;
337
338
  }
338
339
  break;
339
340
  default:
340
341
  o = i.NONE;
341
342
  }
342
- o !== i.NONE && t.dispatchEvent(ce);
343
+ o !== i.NONE && t.dispatchEvent(le);
343
344
  }
344
- function We(n) {
345
+ function qe(a) {
345
346
  switch (o) {
346
347
  case i.ROTATE:
347
348
  if (t.enableRotate === !1) return;
348
- $e(n);
349
+ je(a);
349
350
  break;
350
351
  case i.DOLLY:
351
352
  if (t.enableZoom === !1) return;
352
- Ge(n);
353
+ $e(a);
353
354
  break;
354
355
  case i.PAN:
355
356
  if (t.enablePan === !1) return;
356
- Ye(n);
357
+ Ge(a);
357
358
  break;
358
359
  }
359
360
  }
360
- function we(n) {
361
- t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (n.preventDefault(), t.dispatchEvent(ce), Be(Ze(n)), t.dispatchEvent(Re));
361
+ function we(a) {
362
+ t.enabled === !1 || t.enableZoom === !1 || o !== i.NONE || (a.preventDefault(), t.dispatchEvent(le), Ye(We(a)), t.dispatchEvent(Pe));
362
363
  }
363
- function Ze(n) {
364
- const g = n.deltaMode, v = {
365
- clientX: n.clientX,
366
- clientY: n.clientY,
367
- deltaY: n.deltaY
364
+ function We(a) {
365
+ const g = a.deltaMode, v = {
366
+ clientX: a.clientX,
367
+ clientY: a.clientY,
368
+ deltaY: a.deltaY
368
369
  };
369
370
  switch (g) {
370
371
  case 1:
@@ -374,28 +375,28 @@ class ft extends at {
374
375
  v.deltaY *= 100;
375
376
  break;
376
377
  }
377
- return n.ctrlKey && !se && (v.deltaY *= 10), v;
378
+ return a.ctrlKey && !te && (v.deltaY *= 10), v;
378
379
  }
379
- function Qe(n) {
380
- n.key === "Control" && (se = !0, document.addEventListener("keyup", Ee, { passive: !0, capture: !0 }));
380
+ function Ze(a) {
381
+ a.key === "Control" && (te = !0, document.addEventListener("keyup", Me, { passive: !0, capture: !0 }));
381
382
  }
382
- function Ee(n) {
383
- n.key === "Control" && (se = !1, document.removeEventListener("keyup", Ee, { passive: !0, capture: !0 }));
383
+ function Me(a) {
384
+ a.key === "Control" && (te = !1, document.removeEventListener("keyup", Me, { passive: !0, capture: !0 }));
384
385
  }
385
- function re(n) {
386
- t.enabled === !1 || t.enablePan === !1 || Ke(n);
386
+ function ae(a) {
387
+ t.enabled === !1 || t.enablePan === !1 || Be(a);
387
388
  }
388
- function Je(n) {
389
- switch (Ne(n), E.length) {
389
+ function Qe(a) {
390
+ switch (Ce(a), E.length) {
390
391
  case 1:
391
392
  switch (t.touches.ONE) {
392
- case G.ROTATE:
393
+ case $.ROTATE:
393
394
  if (t.enableRotate === !1) return;
394
- fe(n), o = i.TOUCH_ROTATE;
395
+ ue(a), o = i.TOUCH_ROTATE;
395
396
  break;
396
- case G.PAN:
397
+ case $.PAN:
397
398
  if (t.enablePan === !1) return;
398
- me(n), o = i.TOUCH_PAN;
399
+ fe(a), o = i.TOUCH_PAN;
399
400
  break;
400
401
  default:
401
402
  o = i.NONE;
@@ -403,13 +404,13 @@ class ft extends at {
403
404
  break;
404
405
  case 2:
405
406
  switch (t.touches.TWO) {
406
- case G.DOLLY_PAN:
407
+ case $.DOLLY_PAN:
407
408
  if (t.enableZoom === !1 && t.enablePan === !1) return;
408
- Ue(n), o = i.TOUCH_DOLLY_PAN;
409
+ Ke(a), o = i.TOUCH_DOLLY_PAN;
409
410
  break;
410
- case G.DOLLY_ROTATE:
411
+ case $.DOLLY_ROTATE:
411
412
  if (t.enableZoom === !1 && t.enableRotate === !1) return;
412
- _e(n), o = i.TOUCH_DOLLY_ROTATE;
413
+ Ue(a), o = i.TOUCH_DOLLY_ROTATE;
413
414
  break;
414
415
  default:
415
416
  o = i.NONE;
@@ -418,56 +419,56 @@ class ft extends at {
418
419
  default:
419
420
  o = i.NONE;
420
421
  }
421
- o !== i.NONE && t.dispatchEvent(ce);
422
+ o !== i.NONE && t.dispatchEvent(le);
422
423
  }
423
- function et(n) {
424
- switch (Ne(n), o) {
424
+ function Je(a) {
425
+ switch (Ce(a), o) {
425
426
  case i.TOUCH_ROTATE:
426
427
  if (t.enableRotate === !1) return;
427
- xe(n), t.update();
428
+ ye(a), t.update();
428
429
  break;
429
430
  case i.TOUCH_PAN:
430
431
  if (t.enablePan === !1) return;
431
- be(n), t.update();
432
+ xe(a), t.update();
432
433
  break;
433
434
  case i.TOUCH_DOLLY_PAN:
434
435
  if (t.enableZoom === !1 && t.enablePan === !1) return;
435
- Xe(n), t.update();
436
+ _e(a), t.update();
436
437
  break;
437
438
  case i.TOUCH_DOLLY_ROTATE:
438
439
  if (t.enableZoom === !1 && t.enableRotate === !1) return;
439
- Ve(n), t.update();
440
+ Xe(a), t.update();
440
441
  break;
441
442
  default:
442
443
  o = i.NONE;
443
444
  }
444
445
  }
445
- function Ce(n) {
446
- t.enabled !== !1 && n.preventDefault();
446
+ function Ee(a) {
447
+ t.enabled !== !1 && a.preventDefault();
447
448
  }
448
- function tt(n) {
449
- E.push(n.pointerId);
449
+ function et(a) {
450
+ E.push(a.pointerId);
450
451
  }
451
- function st(n) {
452
- delete q[n.pointerId];
452
+ function tt(a) {
453
+ delete V[a.pointerId];
453
454
  for (let g = 0; g < E.length; g++)
454
- if (E[g] == n.pointerId) {
455
+ if (E[g] == a.pointerId) {
455
456
  E.splice(g, 1);
456
457
  return;
457
458
  }
458
459
  }
459
- function Ne(n) {
460
- let g = q[n.pointerId];
461
- g === void 0 && (g = new T(), q[n.pointerId] = g), g.set(n.pageX, n.pageY);
460
+ function Ce(a) {
461
+ let g = V[a.pointerId];
462
+ g === void 0 && (g = new k(), V[a.pointerId] = g), g.set(a.pageX, a.pageY);
462
463
  }
463
- function j(n) {
464
- const g = n.pointerId === E[0] ? E[1] : E[0];
465
- return q[g];
464
+ function A(a) {
465
+ const g = a.pointerId === E[0] ? E[1] : E[0];
466
+ return V[g];
466
467
  }
467
- t.domElement.addEventListener("contextmenu", Ce), t.domElement.addEventListener("pointerdown", Me), t.domElement.addEventListener("pointercancel", K), t.domElement.addEventListener("wheel", we, { passive: !1 }), document.addEventListener("keydown", Qe, { passive: !0, capture: !0 }), this.update();
468
+ t.domElement.addEventListener("contextmenu", Ee), t.domElement.addEventListener("pointerdown", ve), t.domElement.addEventListener("pointercancel", B), t.domElement.addEventListener("wheel", we, { passive: !1 }), document.addEventListener("keydown", Ze, { passive: !0, capture: !0 }), this.update();
468
469
  }
469
470
  }
470
- class mt {
471
+ class ft {
471
472
  constructor(e, s) {
472
473
  l(this, "scene");
473
474
  l(this, "camera");
@@ -475,35 +476,35 @@ class mt {
475
476
  l(this, "controls");
476
477
  l(this, "container");
477
478
  l(this, "resizeHandler");
478
- this.container = e, this.scene = new y.Scene(), this.scene.background = new y.Color(
479
+ this.container = e, this.scene = new x.Scene(), this.scene.background = new x.Color(
479
480
  s.backgroundColor ?? 657930
480
481
  );
481
482
  const { width: t, height: i } = ke(e), o = s.cameraFov ?? 75;
482
- this.camera = new y.PerspectiveCamera(o, t / i, 0.1, 2e3);
483
- const a = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
484
- this.camera.position.set(a.x, a.y, a.z), this.renderer = new y.WebGLRenderer({
483
+ this.camera = new x.PerspectiveCamera(o, t / i, 0.1, 2e3);
484
+ const n = s.cameraPosition ?? { x: 0, y: 0, z: 80 };
485
+ this.camera.position.set(n.x, n.y, n.z), this.renderer = new x.WebGLRenderer({
485
486
  antialias: !0,
486
487
  alpha: !0,
487
488
  powerPreference: "high-performance"
488
- }), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = y.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = y.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ft(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
489
+ }), this.renderer.setSize(t, i), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.renderer.toneMapping = x.ACESFilmicToneMapping, this.renderer.toneMappingExposure = 1, this.renderer.shadowMap.enabled = !0, this.renderer.shadowMap.type = x.PCFSoftShadowMap, e.appendChild(this.renderer.domElement), this.controls = new ut(this.camera, this.renderer.domElement), this.controls.enableDamping = !0, this.controls.dampingFactor = 0.05, this.controls.rotateSpeed = 0.8, this.controls.zoomSpeed = 1.2, this.controls.minDistance = 10, this.controls.maxDistance = 2e3, this.setupLighting(), this.resizeHandler = this.onWindowResize.bind(this), window.addEventListener("resize", this.resizeHandler);
489
490
  }
490
491
  /**
491
492
  * Sets up scene lighting for gradient glass on dark background
492
493
  */
493
494
  setupLighting() {
494
- const e = new y.AmbientLight(16777215, 0.4);
495
+ const e = new x.AmbientLight(16777215, 0.4);
495
496
  this.scene.add(e);
496
- const s = new y.DirectionalLight(16777215, 0.9);
497
+ const s = new x.DirectionalLight(16777215, 0.9);
497
498
  s.position.set(50, 60, 40), s.castShadow = !0, s.shadow.mapSize.width = 1024, s.shadow.mapSize.height = 1024, this.scene.add(s);
498
- const t = new y.DirectionalLight(16773344, 0.4);
499
+ const t = new x.DirectionalLight(16773344, 0.4);
499
500
  t.position.set(-50, 30, -40), this.scene.add(t);
500
- const i = new y.DirectionalLight(16777215, 0.3);
501
+ const i = new x.DirectionalLight(16777215, 0.3);
501
502
  i.position.set(0, -30, -50), this.scene.add(i);
502
- const o = new y.PointLight(16750950, 0.5, 150);
503
+ const o = new x.PointLight(16750950, 0.5, 150);
503
504
  o.position.set(40, 20, 40), this.scene.add(o);
504
- const a = new y.PointLight(16764057, 0.4, 150);
505
- a.position.set(-40, -20, 40), this.scene.add(a);
506
- const r = new y.PointLight(6724095, 0.2, 100);
505
+ const n = new x.PointLight(16764057, 0.4, 150);
506
+ n.position.set(-40, -20, 40), this.scene.add(n);
507
+ const r = new x.PointLight(6724095, 0.2, 100);
507
508
  r.position.set(0, 40, -40), this.scene.add(r);
508
509
  }
509
510
  /**
@@ -545,7 +546,7 @@ class mt {
545
546
  * Gets the camera's forward direction
546
547
  */
547
548
  getCameraDirection() {
548
- const e = new y.Vector3();
549
+ const e = new x.Vector3();
549
550
  return this.camera.getWorldDirection(e), e;
550
551
  }
551
552
  /**
@@ -558,7 +559,7 @@ class mt {
558
559
  }
559
560
  }
560
561
  }
561
- const V = class V {
562
+ const X = class X {
562
563
  constructor(e, s) {
563
564
  l(this, "sceneManager");
564
565
  l(this, "nodeFactory");
@@ -577,18 +578,18 @@ const V = class V {
577
578
  * Call this before adding nodes in bulk.
578
579
  */
579
580
  static setExpectedNodeCount(e) {
580
- V.expectedNodeCount = e;
581
+ X.expectedNodeCount = e;
581
582
  }
582
583
  /**
583
584
  * Adds a node to the graph
584
585
  * @returns true if added, false if node already exists or invalid
585
586
  */
586
587
  addNode(e, s = 0) {
587
- if (!He(e))
588
+ if (!Fe(e))
588
589
  return !1;
589
590
  if (this.nodes.has(e.id))
590
591
  return console.warn(`[ForceGraph3D] Node with id "${e.id}" already exists`), !1;
591
- const t = Math.max(50, Math.cbrt(V.expectedNodeCount) * 10), i = e.position ?? {
592
+ const t = Math.max(50, Math.cbrt(X.expectedNodeCount) * 10), i = e.position ?? {
592
593
  x: (Math.random() - 0.5) * t,
593
594
  y: (Math.random() - 0.5) * t,
594
595
  z: (Math.random() - 0.5) * t
@@ -597,11 +598,11 @@ const V = class V {
597
598
  position: i,
598
599
  velocity: { x: 0, y: 0, z: 0 },
599
600
  mass: 1
600
- }, a = this.nodeFactory.createNode(
601
+ }, n = this.nodeFactory.createNode(
601
602
  { ...e, position: i },
602
603
  s
603
604
  );
604
- return this.sceneManager.add(a.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, a), !0;
605
+ return this.sceneManager.add(n.group), this.nodes.set(e.id, o), this.nodeObjects.set(e.id, n), !0;
605
606
  }
606
607
  /**
607
608
  * Removes a node from the graph
@@ -692,16 +693,23 @@ const V = class V {
692
693
  }
693
694
  };
694
695
  // Scale spawn volume with expected graph size
695
- l(V, "expectedNodeCount", 100);
696
- let te = V;
697
- class yt {
696
+ l(X, "expectedNodeCount", 100);
697
+ let ee = X;
698
+ class mt {
698
699
  constructor(e, s, t) {
699
700
  l(this, "sceneManager");
700
701
  l(this, "nodeManager");
701
702
  l(this, "edgeFactory");
703
+ // Full relationship list (includes duplicates between same pair)
702
704
  l(this, "edges", []);
705
+ // One visual line per directed pair (source -> target)
703
706
  l(this, "edgeObjects", []);
707
+ // Tracks which directed pairs currently have at least one relationship
704
708
  l(this, "edgeKeySet", /* @__PURE__ */ new Set());
709
+ // Maps directed pair key to the relationship list for that pair
710
+ l(this, "edgeGroups", /* @__PURE__ */ new Map());
711
+ // Maps directed pair key to the corresponding visual line
712
+ l(this, "edgeObjectMap", /* @__PURE__ */ new Map());
705
713
  l(this, "highlightedEdgeKey", null);
706
714
  this.sceneManager = e, this.nodeManager = s, this.edgeFactory = t;
707
715
  }
@@ -709,12 +717,12 @@ class yt {
709
717
  * Checks if an edge exists
710
718
  */
711
719
  hasEdge(e, s) {
712
- const t = D(e, s);
720
+ const t = this.createDirectedEdgeKey(e, s);
713
721
  return this.edgeKeySet.has(t);
714
722
  }
715
723
  /**
716
724
  * Adds an edge to the graph
717
- * @returns true if added, false if edge already exists or nodes don't exist
725
+ * @returns true if added, false if invalid or nodes don't exist
718
726
  */
719
727
  addEdge(e) {
720
728
  if (!De(e))
@@ -723,54 +731,78 @@ class yt {
723
731
  return console.warn(`[ForceGraph3D] Source node "${e.source}" does not exist`), !1;
724
732
  if (!this.nodeManager.hasNode(e.target))
725
733
  return console.warn(`[ForceGraph3D] Target node "${e.target}" does not exist`), !1;
726
- const s = D(e.source, e.target);
727
- if (this.edgeKeySet.has(s))
728
- return console.warn(`[ForceGraph3D] Edge "${e.source}" -> "${e.target}" already exists`), !1;
729
- const t = this.nodeManager.getNode(e.source), i = this.nodeManager.getNode(e.target), o = this.edgeFactory.createEdge(
734
+ const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target), i = this.createDirectedEdgeKey(e.source, e.target);
735
+ this.edges.push(e);
736
+ const o = this.edgeGroups.get(i);
737
+ if (o) {
738
+ o.push(e);
739
+ const r = this.edgeObjectMap.get(i);
740
+ return r && this.updateEdgeUserData(r, o, s, t), !0;
741
+ }
742
+ const n = this.edgeFactory.createEdge(
730
743
  e,
744
+ s,
731
745
  t,
732
- i,
733
- t.position,
734
- i.position
746
+ s.position,
747
+ t.position
735
748
  );
736
- return this.sceneManager.add(o.line), this.edges.push(e), this.edgeObjects.push(o), this.edgeKeySet.add(s), !0;
749
+ return this.sceneManager.add(n.line), this.edgeGroups.set(i, [e]), this.edgeObjectMap.set(i, n), this.edgeObjects.push(n), this.edgeKeySet.add(i), this.updateEdgeUserData(n, [e], s, t), !0;
737
750
  }
738
751
  /**
739
752
  * Removes an edge from the graph
753
+ * Removes one relationship instance for source -> target.
740
754
  * @returns true if removed, false if not found
741
755
  */
742
756
  removeEdge(e, s) {
743
- const t = D(e, s);
757
+ const t = this.createDirectedEdgeKey(e, s);
744
758
  if (!this.edgeKeySet.has(t))
745
759
  return !1;
746
760
  const i = this.edges.findIndex(
747
- (a) => D(a.source, a.target) === t
761
+ (c) => c.source === e && c.target === s
748
762
  );
749
763
  if (i === -1)
750
764
  return !1;
751
- const o = this.edgeObjects[i];
752
- return this.sceneManager.remove(o.line), this.edgeFactory.disposeEdge(o), this.edges.splice(i, 1), this.edgeObjects.splice(i, 1), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null), !0;
765
+ const o = this.edges[i];
766
+ this.edges.splice(i, 1);
767
+ const n = this.edgeGroups.get(t);
768
+ if (!n)
769
+ return !0;
770
+ const r = n.findIndex((c) => c === o);
771
+ if (r !== -1)
772
+ n.splice(r, 1);
773
+ else {
774
+ const c = n.findIndex((p) => p.source === e && p.target === s);
775
+ c !== -1 && n.splice(c, 1);
776
+ }
777
+ const d = this.edgeObjectMap.get(t);
778
+ if (!d)
779
+ return !0;
780
+ if (n.length === 0) {
781
+ this.sceneManager.remove(d.line), this.edgeFactory.disposeEdge(d);
782
+ const c = this.edgeObjects.findIndex((p) => p === d);
783
+ c !== -1 && this.edgeObjects.splice(c, 1), this.edgeGroups.delete(t), this.edgeObjectMap.delete(t), this.edgeKeySet.delete(t), this.highlightedEdgeKey === t && (this.highlightedEdgeKey = null);
784
+ } else {
785
+ const c = this.nodeManager.getNode(e), p = this.nodeManager.getNode(s);
786
+ c && p && this.updateEdgeUserData(d, n, c, p);
787
+ }
788
+ return !0;
753
789
  }
754
790
  /**
755
791
  * Highlights an edge
756
792
  */
757
793
  highlightEdge(e, s) {
758
- const t = D(e, s);
794
+ const t = this.createDirectedEdgeKey(e, s);
759
795
  this.highlightedEdgeKey && this.highlightedEdgeKey !== t && this.unhighlightCurrentEdge();
760
- const i = this.edges.findIndex(
761
- (o) => D(o.source, o.target) === t
762
- );
763
- i !== -1 && (this.edgeFactory.highlightEdge(this.edgeObjects[i]), this.highlightedEdgeKey = t);
796
+ const i = this.edgeObjectMap.get(t);
797
+ i && (this.edgeFactory.highlightEdge(i), this.highlightedEdgeKey = t);
764
798
  }
765
799
  /**
766
800
  * Unhighlights the currently highlighted edge
767
801
  */
768
802
  unhighlightCurrentEdge() {
769
803
  if (!this.highlightedEdgeKey) return;
770
- const e = this.edges.findIndex(
771
- (s) => D(s.source, s.target) === this.highlightedEdgeKey
772
- );
773
- e !== -1 && this.edgeFactory.unhighlightEdge(this.edgeObjects[e]), this.highlightedEdgeKey = null;
804
+ const e = this.edgeObjectMap.get(this.highlightedEdgeKey);
805
+ e && this.edgeFactory.unhighlightEdge(e), this.highlightedEdgeKey = null;
774
806
  }
775
807
  /**
776
808
  * Removes all edges connected to a node
@@ -802,12 +834,12 @@ class yt {
802
834
  * Updates all edge positions based on current node positions
803
835
  */
804
836
  updateEdgePositions() {
805
- this.edgeObjects.forEach((e, s) => {
806
- const t = this.edges[s], i = this.nodeManager.getNode(t.source), o = this.nodeManager.getNode(t.target);
807
- i && o && this.edgeFactory.updateEdgePositions(
837
+ this.edgeObjects.forEach((e) => {
838
+ const s = this.nodeManager.getNode(e.source), t = this.nodeManager.getNode(e.target);
839
+ s && t && this.edgeFactory.updateEdgePositions(
808
840
  e,
809
- i.position,
810
- o.position
841
+ s.position,
842
+ t.position
811
843
  );
812
844
  });
813
845
  }
@@ -835,7 +867,29 @@ class yt {
835
867
  clear() {
836
868
  this.edgeObjects.forEach((e) => {
837
869
  this.sceneManager.remove(e.line), this.edgeFactory.disposeEdge(e);
838
- }), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.highlightedEdgeKey = null;
870
+ }), this.edges = [], this.edgeObjects = [], this.edgeKeySet.clear(), this.edgeGroups.clear(), this.edgeObjectMap.clear(), this.highlightedEdgeKey = null;
871
+ }
872
+ /**
873
+ * Creates a directed key (source -> target)
874
+ */
875
+ createDirectedEdgeKey(e, s) {
876
+ return `${e}->${s}`;
877
+ }
878
+ /**
879
+ * Syncs relationship metadata to the line for hover/click UI
880
+ */
881
+ updateEdgeUserData(e, s, t, i) {
882
+ const o = s[0];
883
+ e.line.userData = {
884
+ ...e.line.userData,
885
+ source: e.source,
886
+ target: e.target,
887
+ edge: o,
888
+ relationships: [...s],
889
+ relationshipCount: s.length,
890
+ sourceNode: t,
891
+ targetNode: i
892
+ };
839
893
  }
840
894
  /**
841
895
  * Dispose resources
@@ -844,7 +898,7 @@ class yt {
844
898
  this.clear();
845
899
  }
846
900
  }
847
- class Le {
901
+ class Re {
848
902
  constructor(e, s, t = {}) {
849
903
  l(this, "nodes");
850
904
  l(this, "edges");
@@ -880,17 +934,19 @@ class Le {
880
934
  const e = /* @__PURE__ */ new Map();
881
935
  for (const i of this.edges)
882
936
  e.set(i.source, (e.get(i.source) || 0) + 1), e.set(i.target, (e.get(i.target) || 0) + 1);
937
+ console.log(`[ForceGraph3D] initializeNodeMassAndPin: ${this.edges.length} edges, ${this.nodes.size} nodes, ${e.size} unique endpoints`);
883
938
  let s = 0, t = null;
884
939
  for (const [i, o] of e) {
885
940
  o > s && (s = o, t = i);
886
- const a = this.nodes.get(i);
887
- a && (a.mass = 1 + Math.log2(1 + o));
941
+ const n = this.nodes.get(i);
942
+ n && (n.mass = 1 + Math.log2(1 + o));
888
943
  }
889
- if (t && s > 10) {
944
+ if (console.log(`[ForceGraph3D] Hub detected: id="${t}", degree=${s}, threshold=10`), t && s > 10) {
890
945
  this.pinnedNodeId = t;
891
946
  const i = this.nodes.get(t);
892
- i && (i.position.x = 0, i.position.y = 0, i.position.z = 0, i.velocity.x = 0, i.velocity.y = 0, i.velocity.z = 0);
893
- }
947
+ i ? (i.position.x = 0, i.position.y = 0, i.position.z = 0, i.velocity.x = 0, i.velocity.y = 0, i.velocity.z = 0, console.log(`[ForceGraph3D] ✅ Hub node "${t}" pinned at origin`)) : console.warn(`[ForceGraph3D] ⚠️ Hub node "${t}" found in edges but NOT in nodes map!`);
948
+ } else
949
+ console.log(`[ForceGraph3D] No hub pinned (maxDegree=${s} <= 10 or hubId=${t})`);
894
950
  }
895
951
  /**
896
952
  * Computes effective repulsion strength scaled by node count.
@@ -915,13 +971,13 @@ class Le {
915
971
  const e = Array.from(this.nodes.values()), s = e.length, t = this.getEffectiveRepulsion();
916
972
  for (let i = 0; i < s; i++) {
917
973
  const o = e[i];
918
- for (let a = i + 1; a < s; a++) {
919
- const r = e[a], c = r.position.x - o.position.x, h = r.position.y - o.position.y, p = r.position.z - o.position.z;
920
- let x = c * c + h * h + p * p;
921
- if (x > this.REPULSION_CUTOFF_SQ) continue;
922
- x < 0.01 && (x = 0.01);
923
- const m = Math.sqrt(x), u = t * this.alpha / x, f = c / m * u, b = h / m * u, M = p / m * u;
924
- o.velocity.x -= f / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= M / o.mass, r.velocity.x += f / r.mass, r.velocity.y += b / r.mass, r.velocity.z += M / r.mass;
974
+ for (let n = i + 1; n < s; n++) {
975
+ const r = e[n], d = r.position.x - o.position.x, c = r.position.y - o.position.y, p = r.position.z - o.position.z;
976
+ let f = d * d + c * c + p * p;
977
+ if (f > this.REPULSION_CUTOFF_SQ) continue;
978
+ f < 0.01 && (f = 0.01);
979
+ const m = Math.sqrt(f), u = t * this.alpha / f, y = d / m * u, b = c / m * u, w = p / m * u;
980
+ o.velocity.x -= y / o.mass, o.velocity.y -= b / o.mass, o.velocity.z -= w / o.mass, r.velocity.x += y / r.mass, r.velocity.y += b / r.mass, r.velocity.z += w / r.mass;
925
981
  }
926
982
  }
927
983
  }
@@ -929,7 +985,7 @@ class Le {
929
985
  * Barnes-Hut approximation - O(n log n)
930
986
  */
931
987
  calculateRepulsionBarnesHut() {
932
- const e = Array.from(this.nodes.values()), s = new xt(e);
988
+ const e = Array.from(this.nodes.values()), s = new yt(e);
933
989
  for (const t of e)
934
990
  this.calculateForceFromOctree(t, s.root);
935
991
  }
@@ -942,26 +998,26 @@ class Le {
942
998
  return;
943
999
  }
944
1000
  if (s.mass === 0) return;
945
- const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z, a = t * t + i * i + o * o;
946
- if (a > this.REPULSION_CUTOFF_SQ) return;
947
- const r = Math.sqrt(a), c = this.getEffectiveRepulsion();
1001
+ const t = s.centerOfMass.x - e.position.x, i = s.centerOfMass.y - e.position.y, o = s.centerOfMass.z - e.position.z, n = t * t + i * i + o * o;
1002
+ if (n > this.REPULSION_CUTOFF_SQ) return;
1003
+ const r = Math.sqrt(n), d = this.getEffectiveRepulsion();
948
1004
  if (r > 0 && s.size / r < this.barnesHutTheta) {
949
- const h = Math.max(a, 0.01), p = c * this.alpha * s.mass / h;
1005
+ const c = Math.max(n, 0.01), p = d * this.alpha * s.mass / c;
950
1006
  e.velocity.x -= t / r * p / e.mass, e.velocity.y -= i / r * p / e.mass, e.velocity.z -= o / r * p / e.mass;
951
1007
  } else
952
- for (const h of s.children)
953
- h && this.calculateForceFromOctree(e, h);
1008
+ for (const c of s.children)
1009
+ c && this.calculateForceFromOctree(e, c);
954
1010
  }
955
1011
  /**
956
1012
  * Apply repulsion between two nodes (with cutoff)
957
1013
  */
958
1014
  applyRepulsionBetween(e, s) {
959
1015
  const t = s.position.x - e.position.x, i = s.position.y - e.position.y, o = s.position.z - e.position.z;
960
- let a = t * t + i * i + o * o;
961
- if (a > this.REPULSION_CUTOFF_SQ) return;
962
- a < 0.01 && (a = 0.01);
963
- const r = Math.sqrt(a), h = this.getEffectiveRepulsion() * this.alpha / a;
964
- e.velocity.x -= t / r * h / e.mass, e.velocity.y -= i / r * h / e.mass, e.velocity.z -= o / r * h / e.mass;
1016
+ let n = t * t + i * i + o * o;
1017
+ if (n > this.REPULSION_CUTOFF_SQ) return;
1018
+ n < 0.01 && (n = 0.01);
1019
+ const r = Math.sqrt(n), c = this.getEffectiveRepulsion() * this.alpha / n;
1020
+ e.velocity.x -= t / r * c / e.mass, e.velocity.y -= i / r * c / e.mass, e.velocity.z -= o / r * c / e.mass;
965
1021
  }
966
1022
  /**
967
1023
  * Calculate attraction forces along edges
@@ -971,10 +1027,10 @@ class Le {
971
1027
  for (const t of this.edges) {
972
1028
  const i = this.nodes.get(t.source), o = this.nodes.get(t.target);
973
1029
  if (!i || !o) continue;
974
- const a = o.position.x - i.position.x, r = o.position.y - i.position.y, c = o.position.z - i.position.z, h = Math.sqrt(a * a + r * r + c * c);
975
- if (h < 0.01) continue;
976
- const x = (h - 15) * this.attractionStrength * s * this.alpha, m = a / h * x, u = r / h * x, f = c / h * x;
977
- i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += f / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= f / o.mass;
1030
+ const n = o.position.x - i.position.x, r = o.position.y - i.position.y, d = o.position.z - i.position.z, c = Math.sqrt(n * n + r * r + d * d);
1031
+ if (c < 0.01) continue;
1032
+ const f = (c - 15) * this.attractionStrength * s * this.alpha, m = n / c * f, u = r / c * f, y = d / c * f;
1033
+ i.velocity.x += m / i.mass, i.velocity.y += u / i.mass, i.velocity.z += y / i.mass, o.velocity.x -= m / o.mass, o.velocity.y -= u / o.mass, o.velocity.z -= y / o.mass;
978
1034
  }
979
1035
  }
980
1036
  /**
@@ -1031,7 +1087,7 @@ class Le {
1031
1087
  e.repulsionStrength !== void 0 && (this.repulsionStrength = e.repulsionStrength), e.attractionStrength !== void 0 && (this.attractionStrength = e.attractionStrength), e.damping !== void 0 && (this.damping = e.damping);
1032
1088
  }
1033
1089
  }
1034
- class xt {
1090
+ class yt {
1035
1091
  constructor(e) {
1036
1092
  l(this, "root");
1037
1093
  const s = this.calculateBounds(e);
@@ -1067,54 +1123,54 @@ class xt {
1067
1123
  };
1068
1124
  if (e.length === 1 || t > 20) {
1069
1125
  let u = 0;
1070
- const f = { x: 0, y: 0, z: 0 };
1126
+ const y = { x: 0, y: 0, z: 0 };
1071
1127
  for (const b of e)
1072
- u += b.mass, f.x += b.position.x * b.mass, f.y += b.position.y * b.mass, f.z += b.position.z * b.mass;
1073
- return u > 0 && (f.x /= u, f.y /= u, f.z /= u), {
1128
+ u += b.mass, y.x += b.position.x * b.mass, y.y += b.position.y * b.mass, y.z += b.position.z * b.mass;
1129
+ return u > 0 && (y.x /= u, y.y /= u, y.z /= u), {
1074
1130
  bounds: s,
1075
1131
  size: i,
1076
- centerOfMass: f,
1132
+ centerOfMass: y,
1077
1133
  mass: u,
1078
1134
  isLeaf: !0,
1079
1135
  node: e[0],
1080
1136
  children: []
1081
1137
  };
1082
1138
  }
1083
- const o = (s.min.x + s.max.x) / 2, a = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, c = [[], [], [], [], [], [], [], []];
1139
+ const o = (s.min.x + s.max.x) / 2, n = (s.min.y + s.max.y) / 2, r = (s.min.z + s.max.z) / 2, d = [[], [], [], [], [], [], [], []];
1084
1140
  for (const u of e) {
1085
- const f = (u.position.x >= o ? 1 : 0) + (u.position.y >= a ? 2 : 0) + (u.position.z >= r ? 4 : 0);
1086
- c[f].push(u);
1087
- }
1088
- const h = [
1089
- { min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: a, z: r } },
1090
- { min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: a, z: r } },
1091
- { min: { x: s.min.x, y: a, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
1092
- { min: { x: o, y: a, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
1093
- { min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y: a, z: s.max.z } },
1094
- { min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y: a, z: s.max.z } },
1095
- { min: { x: s.min.x, y: a, z: r }, max: { x: o, y: s.max.y, z: s.max.z } },
1096
- { min: { x: o, y: a, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
1141
+ const y = (u.position.x >= o ? 1 : 0) + (u.position.y >= n ? 2 : 0) + (u.position.z >= r ? 4 : 0);
1142
+ d[y].push(u);
1143
+ }
1144
+ const c = [
1145
+ { min: { x: s.min.x, y: s.min.y, z: s.min.z }, max: { x: o, y: n, z: r } },
1146
+ { min: { x: o, y: s.min.y, z: s.min.z }, max: { x: s.max.x, y: n, z: r } },
1147
+ { min: { x: s.min.x, y: n, z: s.min.z }, max: { x: o, y: s.max.y, z: r } },
1148
+ { min: { x: o, y: n, z: s.min.z }, max: { x: s.max.x, y: s.max.y, z: r } },
1149
+ { min: { x: s.min.x, y: s.min.y, z: r }, max: { x: o, y: n, z: s.max.z } },
1150
+ { min: { x: o, y: s.min.y, z: r }, max: { x: s.max.x, y: n, z: s.max.z } },
1151
+ { min: { x: s.min.x, y: n, z: r }, max: { x: o, y: s.max.y, z: s.max.z } },
1152
+ { min: { x: o, y: n, z: r }, max: { x: s.max.x, y: s.max.y, z: s.max.z } }
1097
1153
  ], p = [];
1098
- let x = 0;
1154
+ let f = 0;
1099
1155
  const m = { x: 0, y: 0, z: 0 };
1100
1156
  for (let u = 0; u < 8; u++)
1101
- if (c[u].length > 0) {
1102
- const f = this.buildTree(c[u], h[u], t + 1);
1103
- p.push(f), x += f.mass, m.x += f.centerOfMass.x * f.mass, m.y += f.centerOfMass.y * f.mass, m.z += f.centerOfMass.z * f.mass;
1157
+ if (d[u].length > 0) {
1158
+ const y = this.buildTree(d[u], c[u], t + 1);
1159
+ p.push(y), f += y.mass, m.x += y.centerOfMass.x * y.mass, m.y += y.centerOfMass.y * y.mass, m.z += y.centerOfMass.z * y.mass;
1104
1160
  } else
1105
1161
  p.push(null);
1106
- return x > 0 && (m.x /= x, m.y /= x, m.z /= x), {
1162
+ return f > 0 && (m.x /= f, m.y /= f, m.z /= f), {
1107
1163
  bounds: s,
1108
1164
  size: i,
1109
1165
  centerOfMass: m,
1110
- mass: x,
1166
+ mass: f,
1111
1167
  isLeaf: !1,
1112
1168
  node: null,
1113
1169
  children: p
1114
1170
  };
1115
1171
  }
1116
1172
  }
1117
- class bt {
1173
+ class xt {
1118
1174
  constructor(e, s, t, i = 60) {
1119
1175
  l(this, "sceneManager");
1120
1176
  l(this, "animationId", null);
@@ -1179,7 +1235,7 @@ class bt {
1179
1235
  this.stop();
1180
1236
  }
1181
1237
  }
1182
- class vt {
1238
+ class bt {
1183
1239
  constructor() {
1184
1240
  l(this, "envMap", null);
1185
1241
  l(this, "materialCache", /* @__PURE__ */ new Map());
@@ -1220,7 +1276,7 @@ class vt {
1220
1276
  for (const i of t) {
1221
1277
  const o = document.createElement("canvas");
1222
1278
  o.width = 256, o.height = 256;
1223
- const a = o.getContext("2d"), r = a.createRadialGradient(
1279
+ const n = o.getContext("2d"), r = n.createRadialGradient(
1224
1280
  256 / 2,
1225
1281
  256 / 2,
1226
1282
  0,
@@ -1228,15 +1284,15 @@ class vt {
1228
1284
  256 / 2,
1229
1285
  256 * 0.8
1230
1286
  );
1231
- r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), a.fillStyle = r, a.fillRect(0, 0, 256, 256);
1232
- const c = a.getImageData(0, 0, 256, 256);
1233
- for (let h = 0; h < c.data.length; h += 4) {
1287
+ r.addColorStop(0, i.colors[0]), r.addColorStop(0.5, i.colors[1]), r.addColorStop(1, i.colors[2]), n.fillStyle = r, n.fillRect(0, 0, 256, 256);
1288
+ const d = n.getImageData(0, 0, 256, 256);
1289
+ for (let c = 0; c < d.data.length; c += 4) {
1234
1290
  const p = (Math.random() - 0.5) * 5;
1235
- c.data[h] = Math.min(255, Math.max(0, c.data[h] + p)), c.data[h + 1] = Math.min(255, Math.max(0, c.data[h + 1] + p)), c.data[h + 2] = Math.min(255, Math.max(0, c.data[h + 2] + p));
1291
+ d.data[c] = Math.min(255, Math.max(0, d.data[c] + p)), d.data[c + 1] = Math.min(255, Math.max(0, d.data[c + 1] + p)), d.data[c + 2] = Math.min(255, Math.max(0, d.data[c + 2] + p));
1236
1292
  }
1237
- a.putImageData(c, 0, 0), s.push(o);
1293
+ n.putImageData(d, 0, 0), s.push(o);
1238
1294
  }
1239
- this.envMap = new y.CubeTexture(s.map((i) => {
1295
+ this.envMap = new x.CubeTexture(s.map((i) => {
1240
1296
  const o = new Image();
1241
1297
  return o.src = i.toDataURL(), o;
1242
1298
  })), this.envMap.needsUpdate = !0;
@@ -1255,11 +1311,11 @@ class vt {
1255
1311
  const t = "glass-single";
1256
1312
  if (this.materialCache.has(t))
1257
1313
  return this.materialCache.get(t).clone();
1258
- const i = new y.Color(16750950), o = new y.ShaderMaterial({
1314
+ const i = new x.Color(16750950), o = new x.ShaderMaterial({
1259
1315
  uniforms: {
1260
1316
  uColor: { value: i },
1261
1317
  uEnvMap: { value: this.envMap },
1262
- uGlowColor: { value: new y.Color(16777215) },
1318
+ uGlowColor: { value: new x.Color(16777215) },
1263
1319
  uGlowIntensity: { value: 0.8 },
1264
1320
  uReflectivity: { value: 0.4 },
1265
1321
  uFresnelPower: { value: 2.5 }
@@ -1325,9 +1381,9 @@ class vt {
1325
1381
  }
1326
1382
  `,
1327
1383
  transparent: !0,
1328
- side: y.FrontSide,
1384
+ side: x.FrontSide,
1329
1385
  depthWrite: !0,
1330
- blending: y.NormalBlending
1386
+ blending: x.NormalBlending
1331
1387
  });
1332
1388
  return this.materialCache.set(t, o), o.clone();
1333
1389
  }
@@ -1335,7 +1391,7 @@ class vt {
1335
1391
  * Creates material for edges (light color for dark background)
1336
1392
  */
1337
1393
  createEdgeMaterial(e = 6710886, s = 0.4) {
1338
- return new y.LineBasicMaterial({
1394
+ return new x.LineBasicMaterial({
1339
1395
  color: e,
1340
1396
  transparent: !0,
1341
1397
  opacity: s,
@@ -1346,7 +1402,7 @@ class vt {
1346
1402
  * Creates highlighted edge material
1347
1403
  */
1348
1404
  createHighlightedEdgeMaterial() {
1349
- return new y.LineBasicMaterial({
1405
+ return new x.LineBasicMaterial({
1350
1406
  color: 16750950,
1351
1407
  // Tangerine highlight
1352
1408
  transparent: !1,
@@ -1360,10 +1416,10 @@ class vt {
1360
1416
  createLabelMaterial(e, s = 24) {
1361
1417
  const t = document.createElement("canvas"), i = t.getContext("2d");
1362
1418
  i.font = `600 ${s}px Inter, -apple-system, sans-serif`;
1363
- const a = i.measureText(e).width;
1364
- t.width = Math.max(128, a + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
1365
- const r = new y.CanvasTexture(t);
1366
- return r.needsUpdate = !0, new y.SpriteMaterial({
1419
+ const n = i.measureText(e).width;
1420
+ t.width = Math.max(128, n + 24), t.height = s + 20, i.clearRect(0, 0, t.width, t.height), i.font = `600 ${s}px Inter, -apple-system, sans-serif`, i.textAlign = "center", i.textBaseline = "middle", i.shadowColor = "rgba(0, 0, 0, 0.8)", i.shadowBlur = 4, i.shadowOffsetX = 1, i.shadowOffsetY = 1, i.fillStyle = "rgba(255, 255, 255, 0.95)", i.fillText(e, t.width / 2, t.height / 2);
1421
+ const r = new x.CanvasTexture(t);
1422
+ return r.needsUpdate = !0, new x.SpriteMaterial({
1367
1423
  map: r,
1368
1424
  transparent: !0,
1369
1425
  depthTest: !1,
@@ -1383,7 +1439,7 @@ class vt {
1383
1439
  this.materialCache.forEach((e) => e.dispose()), this.materialCache.clear(), this.envMap && this.envMap.dispose();
1384
1440
  }
1385
1441
  }
1386
- class Mt {
1442
+ class vt {
1387
1443
  constructor(e, s = 2, t = [32, 16, 8], i = 16750950) {
1388
1444
  l(this, "materialFactory");
1389
1445
  l(this, "geometryCache", /* @__PURE__ */ new Map());
@@ -1400,7 +1456,7 @@ class Mt {
1400
1456
  const t = `lod-${s}`;
1401
1457
  this.geometryCache.set(
1402
1458
  t,
1403
- new y.SphereGeometry(this.nodeRadius, e, e)
1459
+ new x.SphereGeometry(this.nodeRadius, e, e)
1404
1460
  );
1405
1461
  });
1406
1462
  }
@@ -1415,21 +1471,21 @@ class Mt {
1415
1471
  * Creates a node visual (glass ball + label)
1416
1472
  */
1417
1473
  createNode(e, s = 0) {
1418
- const t = new y.Group();
1474
+ const t = new x.Group();
1419
1475
  t.name = `node-${e.id}`, t.userData = { nodeId: e.id, nodeData: e };
1420
1476
  const i = this.getGeometry(s), o = this.materialFactory.createGlassMaterial(
1421
1477
  e.color ?? this.defaultNodeColor
1422
- ), a = new y.Mesh(i, o);
1423
- a.castShadow = !0, a.receiveShadow = !0, t.add(a);
1424
- const r = this.materialFactory.createLabelMaterial(e.label), c = new y.Sprite(r);
1425
- return c.position.y = this.nodeRadius + 1.5, c.scale.set(4, 1, 1), t.add(c), e.position && t.position.set(
1478
+ ), n = new x.Mesh(i, o);
1479
+ n.castShadow = !0, n.receiveShadow = !0, t.add(n);
1480
+ const r = this.materialFactory.createLabelMaterial(e.label), d = new x.Sprite(r);
1481
+ return d.position.y = this.nodeRadius + 1.5, d.scale.set(4, 1, 1), t.add(d), e.position && t.position.set(
1426
1482
  e.position.x,
1427
1483
  e.position.y,
1428
1484
  e.position.z
1429
1485
  ), {
1430
1486
  group: t,
1431
- sphere: a,
1432
- label: c,
1487
+ sphere: n,
1488
+ label: d,
1433
1489
  lodLevel: s
1434
1490
  };
1435
1491
  }
@@ -1445,19 +1501,19 @@ class Mt {
1445
1501
  * Updates the color of a node
1446
1502
  */
1447
1503
  updateNodeColor(e, s) {
1448
- e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
1504
+ e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.sphere.material = this.materialFactory.createGlassMaterial(s);
1449
1505
  }
1450
1506
  /**
1451
1507
  * Updates the label of a node
1452
1508
  */
1453
1509
  updateNodeLabel(e, s) {
1454
- e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
1510
+ e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose()), e.label.material = this.materialFactory.createLabelMaterial(s);
1455
1511
  }
1456
1512
  /**
1457
1513
  * Disposes a node's resources
1458
1514
  */
1459
1515
  disposeNode(e) {
1460
- e.sphere.material instanceof y.Material && e.sphere.material.dispose(), e.label.material instanceof y.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
1516
+ e.sphere.material instanceof x.Material && e.sphere.material.dispose(), e.label.material instanceof x.SpriteMaterial && (e.label.material.map && e.label.material.map.dispose(), e.label.material.dispose());
1461
1517
  }
1462
1518
  /**
1463
1519
  * Dispose factory resources
@@ -1494,7 +1550,7 @@ class wt {
1494
1550
  * Creates an edge line between two positions
1495
1551
  */
1496
1552
  createEdge(e, s, t, i, o) {
1497
- const a = new y.BufferGeometry(), r = new Float32Array([
1553
+ const n = new x.BufferGeometry(), r = new Float32Array([
1498
1554
  i.x,
1499
1555
  i.y,
1500
1556
  i.z,
@@ -1502,16 +1558,16 @@ class wt {
1502
1558
  o.y,
1503
1559
  o.z
1504
1560
  ]);
1505
- a.setAttribute("position", new y.BufferAttribute(r, 3));
1506
- const c = this.getDefaultMaterial().clone(), h = new y.Line(a, c);
1507
- return h.name = `edge-${e.source}-${e.target}`, h.userData = {
1561
+ n.setAttribute("position", new x.BufferAttribute(r, 3));
1562
+ const d = this.getDefaultMaterial().clone(), c = new x.Line(n, d);
1563
+ return c.name = `edge-${e.source}-${e.target}`, c.userData = {
1508
1564
  source: e.source,
1509
1565
  target: e.target,
1510
1566
  edge: e,
1511
1567
  sourceNode: s,
1512
1568
  targetNode: t
1513
- }, h.frustumCulled = !0, {
1514
- line: h,
1569
+ }, c.frustumCulled = !0, {
1570
+ line: c,
1515
1571
  source: e.source,
1516
1572
  target: e.target
1517
1573
  };
@@ -1520,13 +1576,13 @@ class wt {
1520
1576
  * Highlights an edge
1521
1577
  */
1522
1578
  highlightEdge(e) {
1523
- e.line.material instanceof y.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
1579
+ e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getHighlightMaterial().clone();
1524
1580
  }
1525
1581
  /**
1526
1582
  * Resets an edge to default appearance
1527
1583
  */
1528
1584
  unhighlightEdge(e) {
1529
- e.line.material instanceof y.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
1585
+ e.line.material instanceof x.Material && e.line.material.dispose(), e.line.material = this.getDefaultMaterial().clone();
1530
1586
  }
1531
1587
  /**
1532
1588
  * Updates an edge's positions
@@ -1539,7 +1595,7 @@ class wt {
1539
1595
  * Disposes an edge's resources
1540
1596
  */
1541
1597
  disposeEdge(e) {
1542
- e.line.geometry.dispose(), e.line.material instanceof y.Material && e.line.material.dispose();
1598
+ e.line.geometry.dispose(), e.line.material instanceof x.Material && e.line.material.dispose();
1543
1599
  }
1544
1600
  /**
1545
1601
  * Dispose factory resources
@@ -1548,7 +1604,7 @@ class wt {
1548
1604
  this.defaultMaterial && this.defaultMaterial.dispose(), this.highlightMaterial && this.highlightMaterial.dispose();
1549
1605
  }
1550
1606
  }
1551
- class Et {
1607
+ class Mt {
1552
1608
  constructor(e, s = [50, 100, 200], t = !0) {
1553
1609
  l(this, "camera");
1554
1610
  l(this, "lodDistances");
@@ -1560,9 +1616,9 @@ class Et {
1560
1616
  */
1561
1617
  getLODLevel(e) {
1562
1618
  if (!this.enabled)
1563
- return X.HIGH;
1619
+ return _.HIGH;
1564
1620
  const s = e.x - this.camera.position.x, t = e.y - this.camera.position.y, i = e.z - this.camera.position.z, o = Math.sqrt(s * s + t * t + i * i);
1565
- return o < this.lodDistances[0] ? X.HIGH : o < this.lodDistances[1] ? X.MEDIUM : X.LOW;
1621
+ return o < this.lodDistances[0] ? _.HIGH : o < this.lodDistances[1] ? _.MEDIUM : _.LOW;
1566
1622
  }
1567
1623
  /**
1568
1624
  * Checks if a node should be visible based on distance
@@ -1584,13 +1640,13 @@ class Et {
1584
1640
  this.enabled = e;
1585
1641
  }
1586
1642
  }
1587
- class Ct {
1643
+ class Et {
1588
1644
  constructor(e, s = !0) {
1589
1645
  l(this, "camera");
1590
1646
  l(this, "frustum");
1591
1647
  l(this, "projScreenMatrix");
1592
1648
  l(this, "enabled");
1593
- this.camera = e, this.frustum = new y.Frustum(), this.projScreenMatrix = new y.Matrix4(), this.enabled = s;
1649
+ this.camera = e, this.frustum = new x.Frustum(), this.projScreenMatrix = new x.Matrix4(), this.enabled = s;
1594
1650
  }
1595
1651
  /**
1596
1652
  * Updates the frustum from the camera
@@ -1606,7 +1662,7 @@ class Ct {
1606
1662
  */
1607
1663
  isPointVisible(e) {
1608
1664
  if (!this.enabled) return !0;
1609
- const s = new y.Vector3(e.x, e.y, e.z);
1665
+ const s = new x.Vector3(e.x, e.y, e.z);
1610
1666
  return this.frustum.containsPoint(s);
1611
1667
  }
1612
1668
  /**
@@ -1614,8 +1670,8 @@ class Ct {
1614
1670
  */
1615
1671
  isSphereVisible(e, s) {
1616
1672
  if (!this.enabled) return !0;
1617
- const t = new y.Sphere(
1618
- new y.Vector3(e.x, e.y, e.z),
1673
+ const t = new x.Sphere(
1674
+ new x.Vector3(e.x, e.y, e.z),
1619
1675
  s
1620
1676
  );
1621
1677
  return this.frustum.intersectsSphere(t);
@@ -1625,14 +1681,14 @@ class Ct {
1625
1681
  */
1626
1682
  isLineVisible(e, s) {
1627
1683
  if (!this.enabled) return !0;
1628
- const t = new y.Vector3(e.x, e.y, e.z), i = new y.Vector3(s.x, s.y, s.z);
1684
+ const t = new x.Vector3(e.x, e.y, e.z), i = new x.Vector3(s.x, s.y, s.z);
1629
1685
  if (this.frustum.containsPoint(t) || this.frustum.containsPoint(i))
1630
1686
  return !0;
1631
- const o = new y.Vector3(
1687
+ const o = new x.Vector3(
1632
1688
  (e.x + s.x) / 2,
1633
1689
  (e.y + s.y) / 2,
1634
1690
  (e.z + s.z) / 2
1635
- ), a = o.distanceTo(t), r = new y.Sphere(o, a);
1691
+ ), n = o.distanceTo(t), r = new x.Sphere(o, n);
1636
1692
  return this.frustum.intersectsSphere(r);
1637
1693
  }
1638
1694
  /**
@@ -1642,7 +1698,7 @@ class Ct {
1642
1698
  this.enabled = e;
1643
1699
  }
1644
1700
  }
1645
- class Nt {
1701
+ class Ct {
1646
1702
  constructor(e, s) {
1647
1703
  l(this, "sceneManager");
1648
1704
  l(this, "raycaster");
@@ -1656,7 +1712,7 @@ class Nt {
1656
1712
  l(this, "hoveredEdgeKey", null);
1657
1713
  l(this, "nodeObjects", []);
1658
1714
  l(this, "edgeObjects", []);
1659
- this.sceneManager = e, this.container = s, this.raycaster = new y.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new y.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
1715
+ this.sceneManager = e, this.container = s, this.raycaster = new x.Raycaster(), this.raycaster.params.Line = { threshold: 1.5 }, this.mouse = new x.Vector2(), this.handleClick = this.handleClick.bind(this), this.handleMouseMove = this.handleMouseMove.bind(this), s.addEventListener("click", this.handleClick), s.addEventListener("mousemove", this.handleMouseMove);
1660
1716
  }
1661
1717
  /**
1662
1718
  * Updates the list of node objects to raycast against
@@ -1745,13 +1801,17 @@ class Nt {
1745
1801
  const t = this.raycaster.intersectObjects(this.edgeObjects, !1);
1746
1802
  if (t.length > 0) {
1747
1803
  const i = t[0].object, o = i.userData;
1748
- if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode))
1804
+ if (o != null && o.edge && (o != null && o.sourceNode) && (o != null && o.targetNode)) {
1805
+ const n = Array.isArray(o.relationships) ? o.relationships : [o.edge];
1749
1806
  return {
1750
1807
  edge: o.edge,
1808
+ relationships: n,
1809
+ relationshipCount: typeof o.relationshipCount == "number" ? o.relationshipCount : n.length,
1751
1810
  sourceNode: o.sourceNode,
1752
1811
  targetNode: o.targetNode,
1753
1812
  edgeLine: i
1754
1813
  };
1814
+ }
1755
1815
  }
1756
1816
  return null;
1757
1817
  }
@@ -1764,11 +1824,11 @@ class Nt {
1764
1824
  this.mouse.x = (e - t.left) / t.width * 2 - 1, this.mouse.y = -((s - t.top) / t.height) * 2 + 1, this.raycaster.setFromCamera(this.mouse, this.sceneManager.camera);
1765
1825
  const i = this.raycaster.intersectObjects(this.nodeObjects, !0);
1766
1826
  if (i.length > 0) {
1767
- let a = i[0].object;
1768
- for (; a; ) {
1769
- if ((o = a.userData) != null && o.nodeId)
1770
- return a.userData.nodeId;
1771
- a = a.parent;
1827
+ let n = i[0].object;
1828
+ for (; n; ) {
1829
+ if ((o = n.userData) != null && o.nodeId)
1830
+ return n.userData.nodeId;
1831
+ n = n.parent;
1772
1832
  }
1773
1833
  }
1774
1834
  return null;
@@ -1780,7 +1840,7 @@ class Nt {
1780
1840
  this.container.removeEventListener("click", this.handleClick), this.container.removeEventListener("mousemove", this.handleMouseMove);
1781
1841
  }
1782
1842
  }
1783
- class St {
1843
+ class Nt {
1784
1844
  constructor(e) {
1785
1845
  l(this, "container");
1786
1846
  l(this, "panel", null);
@@ -1803,17 +1863,15 @@ class St {
1803
1863
  width: 280px;
1804
1864
  max-height: 80vh;
1805
1865
  overflow-y: auto;
1806
- background: rgba(15, 15, 25, 0.85);
1866
+ background: rgba(255, 255, 255, 0.08);
1807
1867
  backdrop-filter: blur(20px);
1808
1868
  -webkit-backdrop-filter: blur(20px);
1809
- border: 1px solid rgba(255, 255, 255, 0.1);
1810
- border-radius: 16px;
1869
+ border: 1px solid rgba(255, 255, 255, 0.12);
1870
+ border-radius: 12px;
1811
1871
  padding: 24px;
1812
1872
  color: white;
1813
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
1814
- box-shadow:
1815
- 0 8px 32px rgba(0, 0, 0, 0.4),
1816
- inset 0 0 0 1px rgba(255, 255, 255, 0.05);
1873
+ font-family: inherit;
1874
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
1817
1875
  z-index: 1000;
1818
1876
  opacity: 0;
1819
1877
  pointer-events: none;
@@ -1857,8 +1915,8 @@ class St {
1857
1915
  this.onExpand(this.currentNodeId, r);
1858
1916
  }
1859
1917
  });
1860
- const a = this.panel.querySelector('[data-action="close"]');
1861
- a && a.addEventListener("click", () => {
1918
+ const n = this.panel.querySelector('[data-action="close"]');
1919
+ n && n.addEventListener("click", () => {
1862
1920
  this.hide();
1863
1921
  }), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
1864
1922
  }
@@ -1871,8 +1929,8 @@ class St {
1871
1929
  <style>
1872
1930
  .force-graph-panel h2 {
1873
1931
  margin: 0 0 16px 0;
1874
- font-size: 20px;
1875
- font-weight: 600;
1932
+ font-size: 19px;
1933
+ font-weight: 650;
1876
1934
  letter-spacing: -0.5px;
1877
1935
  display: flex;
1878
1936
  align-items: center;
@@ -1890,13 +1948,13 @@ class St {
1890
1948
  height: 12px;
1891
1949
  border-radius: 50%;
1892
1950
  background: ${t};
1893
- box-shadow: 0 0 10px ${t}80;
1951
+ box-shadow: 0 0 12px ${t}80;
1894
1952
  }
1895
1953
  .force-graph-panel .info-row {
1896
1954
  display: flex;
1897
1955
  justify-content: space-between;
1898
1956
  padding: 8px 0;
1899
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
1957
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
1900
1958
  font-size: 13px;
1901
1959
  }
1902
1960
  .force-graph-panel .info-row:last-child {
@@ -1916,19 +1974,24 @@ class St {
1916
1974
  }
1917
1975
  .force-graph-panel .neighbors-section {
1918
1976
  margin-top: 16px;
1977
+ padding: 10px;
1978
+ background: rgba(255, 255, 255, 0.04);
1979
+ border: 1px solid rgba(255, 255, 255, 0.08);
1980
+ border-radius: 10px;
1919
1981
  }
1920
1982
  .force-graph-panel .neighbors-title {
1921
1983
  font-size: 12px;
1922
1984
  text-transform: uppercase;
1923
1985
  letter-spacing: 1px;
1924
- color: rgba(255, 255, 255, 0.5);
1986
+ color: rgba(255, 255, 255, 0.55);
1925
1987
  margin-bottom: 8px;
1926
1988
  }
1927
1989
  .force-graph-panel .neighbor-chip {
1928
1990
  display: inline-block;
1929
1991
  padding: 4px 10px;
1930
1992
  margin: 2px;
1931
- background: rgba(255, 255, 255, 0.1);
1993
+ background: rgba(255, 255, 255, 0.08);
1994
+ border: 1px solid rgba(255, 255, 255, 0.12);
1932
1995
  border-radius: 12px;
1933
1996
  font-size: 12px;
1934
1997
  }
@@ -1938,7 +2001,7 @@ class St {
1938
2001
  }
1939
2002
  .force-graph-panel .depth-label {
1940
2003
  font-size: 12px;
1941
- color: rgba(255, 255, 255, 0.6);
2004
+ color: rgba(255, 255, 255, 0.65);
1942
2005
  margin-bottom: 6px;
1943
2006
  text-transform: uppercase;
1944
2007
  letter-spacing: 0.5px;
@@ -1946,9 +2009,9 @@ class St {
1946
2009
  .force-graph-panel select {
1947
2010
  width: 100%;
1948
2011
  padding: 8px 12px;
1949
- background: rgba(255, 255, 255, 0.1);
1950
- border: 1px solid rgba(255, 255, 255, 0.15);
1951
- border-radius: 6px;
2012
+ background: rgba(255, 255, 255, 0.08);
2013
+ border: 1px solid rgba(255, 255, 255, 0.12);
2014
+ border-radius: 8px;
1952
2015
  color: white;
1953
2016
  font-size: 13px;
1954
2017
  cursor: pointer;
@@ -1956,10 +2019,11 @@ class St {
1956
2019
  transition: all 0.2s ease;
1957
2020
  }
1958
2021
  .force-graph-panel select:hover {
1959
- background: rgba(255, 255, 255, 0.15);
2022
+ background: rgba(255, 255, 255, 0.12);
1960
2023
  }
1961
2024
  .force-graph-panel select:focus {
1962
- border-color: rgba(96, 165, 250, 0.5);
2025
+ border-color: rgba(255, 153, 102, 0.5);
2026
+ box-shadow: 0 0 20px rgba(255, 153, 102, 0.15);
1963
2027
  }
1964
2028
  .force-graph-panel .btn-row {
1965
2029
  display: flex;
@@ -1969,28 +2033,33 @@ class St {
1969
2033
  .force-graph-panel button {
1970
2034
  flex: 1;
1971
2035
  padding: 10px 16px;
1972
- border: none;
2036
+ border: 1px solid rgba(255, 255, 255, 0.12);
1973
2037
  border-radius: 8px;
1974
2038
  font-size: 13px;
1975
- font-weight: 500;
2039
+ font-weight: 600;
1976
2040
  cursor: pointer;
1977
2041
  transition: all 0.2s ease;
1978
2042
  }
1979
2043
  .force-graph-panel .btn-expand {
1980
- background: linear-gradient(135deg, #60a5fa, #a78bfa);
2044
+ background: linear-gradient(135deg, rgba(255, 153, 102, 0.3), rgba(255, 102, 153, 0.2));
2045
+ border-color: rgba(255, 153, 102, 0.35);
1981
2046
  color: white;
1982
2047
  }
1983
2048
  .force-graph-panel .btn-expand:hover {
1984
2049
  transform: translateY(-1px);
1985
- box-shadow: 0 4px 12px rgba(96, 165, 250, 0.4);
2050
+ box-shadow: 0 0 15px rgba(255, 153, 102, 0.2);
1986
2051
  }
1987
2052
  .force-graph-panel .btn-close {
1988
- background: rgba(255, 255, 255, 0.1);
2053
+ background: rgba(255, 255, 255, 0.08);
1989
2054
  color: rgba(255, 255, 255, 0.8);
1990
2055
  }
1991
2056
  .force-graph-panel .btn-close:hover {
1992
- background: rgba(255, 255, 255, 0.15);
2057
+ background: rgba(255, 255, 255, 0.12);
1993
2058
  }
2059
+ .force-graph-panel::-webkit-scrollbar { width: 6px; }
2060
+ .force-graph-panel::-webkit-scrollbar-track { background: transparent; }
2061
+ .force-graph-panel::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
2062
+ .force-graph-panel::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); }
1994
2063
  </style>
1995
2064
 
1996
2065
  <h2>
@@ -2066,7 +2135,7 @@ class St {
2066
2135
  this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
2067
2136
  }
2068
2137
  }
2069
- class zt {
2138
+ class St {
2070
2139
  constructor(e) {
2071
2140
  l(this, "container");
2072
2141
  l(this, "panel", null);
@@ -2089,17 +2158,15 @@ class zt {
2089
2158
  width: 300px;
2090
2159
  max-height: 80vh;
2091
2160
  overflow-y: auto;
2092
- background: rgba(15, 15, 25, 0.85);
2161
+ background: rgba(255, 255, 255, 0.08);
2093
2162
  backdrop-filter: blur(20px);
2094
2163
  -webkit-backdrop-filter: blur(20px);
2095
- border: 1px solid rgba(255, 255, 255, 0.1);
2096
- border-radius: 16px;
2164
+ border: 1px solid rgba(255, 255, 255, 0.12);
2165
+ border-radius: 12px;
2097
2166
  padding: 24px;
2098
2167
  color: white;
2099
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
2100
- box-shadow:
2101
- 0 8px 32px rgba(0, 0, 0, 0.4),
2102
- inset 0 0 0 1px rgba(255, 255, 255, 0.05);
2168
+ font-family: inherit;
2169
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
2103
2170
  z-index: 1000;
2104
2171
  opacity: 0;
2105
2172
  pointer-events: none;
@@ -2127,29 +2194,34 @@ class zt {
2127
2194
  /**
2128
2195
  * Shows the panel with edge/relationship information
2129
2196
  */
2130
- show(e, s, t) {
2197
+ show(e, s, t, i = [e]) {
2131
2198
  if (!this.panel) return;
2132
2199
  this.currentEdgeKey = `${e.source}-${e.target}`;
2133
- let i;
2134
- this.panelTemplate ? i = this.panelTemplate(e, s, t) : i = this.generateDefaultContent(e, s, t), this.panel.innerHTML = i;
2135
- const o = this.panel.querySelector('[data-action="close"]');
2136
- o && o.addEventListener("click", () => {
2200
+ let o;
2201
+ this.panelTemplate ? o = this.panelTemplate(e, s, t) : o = this.generateDefaultContent(e, s, t, i), this.panel.innerHTML = o;
2202
+ const n = this.panel.querySelector('[data-action="close"]');
2203
+ n && n.addEventListener("click", () => {
2137
2204
  this.hide(), this.onClose && this.onClose();
2138
2205
  });
2139
- const a = this.panel.querySelector('[data-action="goto-source"]');
2140
- a && this.onNodeClick && a.addEventListener("click", () => {
2206
+ const r = this.panel.querySelector('[data-action="goto-source"]');
2207
+ r && this.onNodeClick && r.addEventListener("click", () => {
2141
2208
  this.onNodeClick && this.onNodeClick(e.source);
2142
2209
  });
2143
- const r = this.panel.querySelector('[data-action="goto-target"]');
2144
- r && this.onNodeClick && r.addEventListener("click", () => {
2210
+ const d = this.panel.querySelector('[data-action="goto-target"]');
2211
+ d && this.onNodeClick && d.addEventListener("click", () => {
2145
2212
  this.onNodeClick && this.onNodeClick(e.target);
2146
2213
  }), this.panel.style.opacity = "1", this.panel.style.pointerEvents = "auto", this.panel.style.transform = "translateY(-50%) translateX(0)", this.visible = !0;
2147
2214
  }
2148
2215
  /**
2149
2216
  * Generates default panel content
2150
2217
  */
2151
- generateDefaultContent(e, s, t) {
2152
- const i = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", o = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", a = e.relationship || "connected to";
2218
+ generateDefaultContent(e, s, t, i = [e]) {
2219
+ const o = s.color ? `#${s.color.toString(16).padStart(6, "0")}` : "#ff9966", n = t.color ? `#${t.color.toString(16).padStart(6, "0")}` : "#ff9966", r = this.getRelationshipLabel(e), d = i.length > 0 ? i : [e], c = this.summarizeRelationships(d), p = c.slice(0, 10).map((m) => `
2220
+ <div class="relationship-item">
2221
+ <span class="relationship-item-label">${this.escapeHtml(m.label)}</span>
2222
+ <span class="relationship-item-count">${m.count}</span>
2223
+ </div>
2224
+ `).join(""), f = c.length - Math.min(c.length, 10);
2153
2225
  return `
2154
2226
  <style>
2155
2227
  .force-graph-edge-panel .panel-header {
@@ -2159,28 +2231,72 @@ class zt {
2159
2231
  margin-bottom: 20px;
2160
2232
  }
2161
2233
  .force-graph-edge-panel .panel-title {
2162
- font-size: 14px;
2234
+ font-size: 12px;
2163
2235
  text-transform: uppercase;
2164
2236
  letter-spacing: 1px;
2165
- color: rgba(255, 255, 255, 0.6);
2237
+ color: rgba(255, 255, 255, 0.55);
2166
2238
  margin: 0;
2167
2239
  }
2168
2240
  .force-graph-edge-panel .relationship-section {
2169
- background: rgba(255, 255, 255, 0.05);
2241
+ background: rgba(255, 255, 255, 0.04);
2242
+ border: 1px solid rgba(255, 255, 255, 0.08);
2170
2243
  border-radius: 12px;
2171
2244
  padding: 16px;
2172
2245
  text-align: center;
2173
- margin-bottom: 20px;
2246
+ margin-bottom: 14px;
2174
2247
  }
2175
2248
  .force-graph-edge-panel .relationship-label {
2176
2249
  font-size: 18px;
2177
2250
  font-weight: 600;
2178
- color: #a78bfa;
2251
+ color: rgba(255, 153, 102, 0.95);
2179
2252
  letter-spacing: 0.5px;
2180
2253
  }
2254
+ .force-graph-edge-panel .relationship-count {
2255
+ margin-top: 6px;
2256
+ font-size: 11px;
2257
+ text-transform: uppercase;
2258
+ letter-spacing: 0.8px;
2259
+ color: rgba(255, 255, 255, 0.6);
2260
+ }
2261
+ .force-graph-edge-panel .relationship-list {
2262
+ margin-bottom: 16px;
2263
+ max-height: 190px;
2264
+ overflow-y: auto;
2265
+ padding-right: 4px;
2266
+ }
2267
+ .force-graph-edge-panel .relationship-item {
2268
+ display: flex;
2269
+ justify-content: space-between;
2270
+ gap: 12px;
2271
+ padding: 8px 10px;
2272
+ background: rgba(255, 255, 255, 0.04);
2273
+ border: 1px solid rgba(255, 255, 255, 0.08);
2274
+ border-radius: 8px;
2275
+ margin-bottom: 6px;
2276
+ }
2277
+ .force-graph-edge-panel .relationship-item-label {
2278
+ font-size: 12px;
2279
+ color: rgba(255, 255, 255, 0.88);
2280
+ overflow: hidden;
2281
+ text-overflow: ellipsis;
2282
+ white-space: nowrap;
2283
+ }
2284
+ .force-graph-edge-panel .relationship-item-count {
2285
+ font-size: 12px;
2286
+ font-weight: 700;
2287
+ color: rgba(255, 153, 102, 0.95);
2288
+ min-width: 24px;
2289
+ text-align: right;
2290
+ }
2291
+ .force-graph-edge-panel .relationship-more {
2292
+ font-size: 11px;
2293
+ color: rgba(255, 255, 255, 0.45);
2294
+ text-align: center;
2295
+ margin-top: 2px;
2296
+ }
2181
2297
  .force-graph-edge-panel .node-card {
2182
- background: rgba(255, 255, 255, 0.05);
2183
- border: 1px solid rgba(255, 255, 255, 0.1);
2298
+ background: rgba(255, 255, 255, 0.06);
2299
+ border: 1px solid rgba(255, 255, 255, 0.12);
2184
2300
  border-radius: 12px;
2185
2301
  padding: 14px;
2186
2302
  margin-bottom: 12px;
@@ -2189,8 +2305,9 @@ class zt {
2189
2305
  }
2190
2306
  .force-graph-edge-panel .node-card:hover {
2191
2307
  background: rgba(255, 255, 255, 0.1);
2192
- border-color: rgba(255, 255, 255, 0.2);
2308
+ border-color: rgba(255, 153, 102, 0.35);
2193
2309
  transform: translateX(4px);
2310
+ box-shadow: 0 0 15px rgba(255, 153, 102, 0.15);
2194
2311
  }
2195
2312
  .force-graph-edge-panel .node-card-header {
2196
2313
  display: flex;
@@ -2227,18 +2344,18 @@ class zt {
2227
2344
  .force-graph-edge-panel .btn-close {
2228
2345
  width: 100%;
2229
2346
  padding: 12px 16px;
2230
- border: none;
2347
+ border: 1px solid rgba(255, 255, 255, 0.12);
2231
2348
  border-radius: 8px;
2232
2349
  font-size: 13px;
2233
- font-weight: 500;
2350
+ font-weight: 600;
2234
2351
  cursor: pointer;
2235
2352
  transition: all 0.2s ease;
2236
- background: rgba(255, 255, 255, 0.1);
2353
+ background: rgba(255, 255, 255, 0.08);
2237
2354
  color: rgba(255, 255, 255, 0.8);
2238
2355
  margin-top: 16px;
2239
2356
  }
2240
2357
  .force-graph-edge-panel .btn-close:hover {
2241
- background: rgba(255, 255, 255, 0.15);
2358
+ background: rgba(255, 255, 255, 0.12);
2242
2359
  }
2243
2360
  .force-graph-edge-panel .hint-text {
2244
2361
  font-size: 11px;
@@ -2246,6 +2363,10 @@ class zt {
2246
2363
  text-align: center;
2247
2364
  margin-top: 8px;
2248
2365
  }
2366
+ .force-graph-edge-panel .relationship-list::-webkit-scrollbar { width: 6px; }
2367
+ .force-graph-edge-panel .relationship-list::-webkit-scrollbar-track { background: transparent; }
2368
+ .force-graph-edge-panel .relationship-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; }
2369
+ .force-graph-edge-panel .relationship-list::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); }
2249
2370
  </style>
2250
2371
 
2251
2372
  <div class="panel-header">
@@ -2253,13 +2374,19 @@ class zt {
2253
2374
  </div>
2254
2375
 
2255
2376
  <div class="relationship-section">
2256
- <span class="relationship-label">${this.escapeHtml(a)}</span>
2377
+ <span class="relationship-label">${this.escapeHtml(r)}</span>
2378
+ <div class="relationship-count">${d.length} relationships</div>
2379
+ </div>
2380
+
2381
+ <div class="relationship-list">
2382
+ ${p}
2383
+ ${f > 0 ? `<div class="relationship-more">+ ${f} more</div>` : ""}
2257
2384
  </div>
2258
2385
 
2259
2386
  <div class="node-card" data-action="goto-source" title="Click to focus on ${this.escapeHtml(s.label)}">
2260
2387
  <div class="node-type">Source</div>
2261
2388
  <div class="node-card-header">
2262
- <span class="color-dot" style="background: ${i}; box-shadow: 0 0 8px ${i}80;"></span>
2389
+ <span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
2263
2390
  <span class="node-label">${this.escapeHtml(s.label)}</span>
2264
2391
  </div>
2265
2392
  </div>
@@ -2269,7 +2396,7 @@ class zt {
2269
2396
  <div class="node-card" data-action="goto-target" title="Click to focus on ${this.escapeHtml(t.label)}">
2270
2397
  <div class="node-type">Target</div>
2271
2398
  <div class="node-card-header">
2272
- <span class="color-dot" style="background: ${o}; box-shadow: 0 0 8px ${o}80;"></span>
2399
+ <span class="color-dot" style="background: ${n}; box-shadow: 0 0 8px ${n}80;"></span>
2273
2400
  <span class="node-label">${this.escapeHtml(t.label)}</span>
2274
2401
  </div>
2275
2402
  </div>
@@ -2286,6 +2413,17 @@ class zt {
2286
2413
  const s = document.createElement("div");
2287
2414
  return s.textContent = e, s.innerHTML;
2288
2415
  }
2416
+ getRelationshipLabel(e) {
2417
+ return e.relationship || e.label || "connected to";
2418
+ }
2419
+ summarizeRelationships(e) {
2420
+ const s = /* @__PURE__ */ new Map();
2421
+ for (const t of e) {
2422
+ const i = this.getRelationshipLabel(t);
2423
+ s.set(i, (s.get(i) || 0) + 1);
2424
+ }
2425
+ return Array.from(s.entries()).map(([t, i]) => ({ label: t, count: i })).sort((t, i) => i.count - t.count);
2426
+ }
2289
2427
  /**
2290
2428
  * Hides the panel
2291
2429
  */
@@ -2311,7 +2449,7 @@ class zt {
2311
2449
  this.panel && this.panel.parentNode && this.panel.parentNode.removeChild(this.panel), this.panel = null;
2312
2450
  }
2313
2451
  }
2314
- class Tt {
2452
+ class zt {
2315
2453
  constructor() {
2316
2454
  l(this, "tooltip", null);
2317
2455
  l(this, "visible", !1);
@@ -2358,28 +2496,51 @@ class Tt {
2358
2496
  positionTooltip(e, s) {
2359
2497
  if (!this.tooltip) return;
2360
2498
  const t = this.tooltip.getBoundingClientRect(), i = window.innerWidth, o = window.innerHeight;
2361
- let a = e + 15, r = s + 15;
2362
- a + t.width > i - 10 && (a = e - t.width - 15), r + t.height > o - 10 && (r = s - t.height - 15), a < 10 && (a = 10), r < 10 && (r = 10), this.tooltip.style.left = `${a}px`, this.tooltip.style.top = `${r}px`;
2499
+ let n = e + 15, r = s + 15;
2500
+ n + t.width > i - 10 && (n = e - t.width - 15), r + t.height > o - 10 && (r = s - t.height - 15), n < 10 && (n = 10), r < 10 && (r = 10), this.tooltip.style.left = `${n}px`, this.tooltip.style.top = `${r}px`;
2363
2501
  }
2364
2502
  /**
2365
2503
  * Shows the tooltip with edge info
2366
2504
  */
2367
- show(e, s, t, i, o) {
2505
+ show(e, s, t, i, o, n = [e]) {
2368
2506
  if (!this.tooltip) return;
2369
- const a = e.relationship || "connected to";
2370
- this.tooltip.innerHTML = `
2371
- <div style="display: flex; flex-direction: column; gap: 4px;">
2372
- <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2373
- <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
2374
- </div>
2375
- <div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
2376
- ↳ ${this.escapeHtml(a)}
2507
+ const r = n.length > 0 ? n : [e], d = this.summarizeRelationships(r), c = r.length;
2508
+ if (c <= 1) {
2509
+ const p = this.getRelationshipLabel(r[0]);
2510
+ this.tooltip.innerHTML = `
2511
+ <div style="display: flex; flex-direction: column; gap: 4px;">
2512
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2513
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(s.label)}</span>
2514
+ </div>
2515
+ <div style="color: rgba(255, 255, 255, 0.6); font-style: italic; font-size: 12px; padding-left: 8px;">
2516
+ ↳ ${this.escapeHtml(p)}
2517
+ </div>
2518
+ <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2519
+ <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
2520
+ </div>
2377
2521
  </div>
2378
- <div style="display: flex; align-items: center; gap: 6px; flex-wrap: wrap;">
2379
- <span style="font-weight: 600; color: #ff9966;">${this.escapeHtml(t.label)}</span>
2522
+ `;
2523
+ } else {
2524
+ const p = d.slice(0, 4), f = d.length - p.length, m = p.map((u) => `<div style="display:flex; justify-content:space-between; gap:10px; font-size:12px; color: rgba(255,255,255,0.8);">
2525
+ <span style="overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${this.escapeHtml(u.label)}</span>
2526
+ <span style="color: rgba(255,153,102,0.9); font-weight:600;">${u.count}</span>
2527
+ </div>`).join("");
2528
+ this.tooltip.innerHTML = `
2529
+ <div style="display:flex; flex-direction:column; gap:8px; min-width:220px;">
2530
+ <div style="font-size:13px; font-weight:600; color:#ff9966;">
2531
+ ${this.escapeHtml(s.label)} → ${this.escapeHtml(t.label)}
2532
+ </div>
2533
+ <div style="font-size:11px; letter-spacing:0.4px; text-transform:uppercase; color: rgba(255,255,255,0.6);">
2534
+ ${c} relationships
2535
+ </div>
2536
+ <div style="display:flex; flex-direction:column; gap:4px;">
2537
+ ${m}
2538
+ </div>
2539
+ ${f > 0 ? `<div style="font-size:11px; color: rgba(255,255,255,0.45);">+ ${f} more</div>` : ""}
2380
2540
  </div>
2381
- </div>
2382
- `, this.positionTooltip(i, o), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
2541
+ `;
2542
+ }
2543
+ this.positionTooltip(i, o), this.tooltip.style.opacity = "1", this.tooltip.style.transform = "translateY(0)", this.visible = !0;
2383
2544
  }
2384
2545
  /**
2385
2546
  * Updates tooltip position (called externally on mouse move)
@@ -2406,6 +2567,17 @@ class Tt {
2406
2567
  const s = document.createElement("div");
2407
2568
  return s.textContent = e, s.innerHTML;
2408
2569
  }
2570
+ getRelationshipLabel(e) {
2571
+ return e.relationship || e.label || "connected to";
2572
+ }
2573
+ summarizeRelationships(e) {
2574
+ const s = /* @__PURE__ */ new Map();
2575
+ for (const t of e) {
2576
+ const i = this.getRelationshipLabel(t);
2577
+ s.set(i, (s.get(i) || 0) + 1);
2578
+ }
2579
+ return Array.from(s.entries()).map(([t, i]) => ({ label: t, count: i })).sort((t, i) => i.count - t.count);
2580
+ }
2409
2581
  /**
2410
2582
  * Dispose the tooltip
2411
2583
  */
@@ -2558,24 +2730,24 @@ class kt {
2558
2730
  }
2559
2731
  let i = "";
2560
2732
  s.length > 0 && (i += '<div class="f3d-search-section-header">Nodes</div>', s.slice(0, 10).forEach((o) => {
2561
- const a = o.type || "Node";
2733
+ const n = o.type || "Node";
2562
2734
  i += `
2563
2735
  <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.id)}">
2564
2736
  <div class="f3d-result-label">${this.escapeHtml(o.label)}</div>
2565
- <div class="f3d-result-type">${this.escapeHtml(a)}</div>
2737
+ <div class="f3d-result-type">${this.escapeHtml(n)}</div>
2566
2738
  </div>
2567
2739
  `;
2568
- }), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: o, sourceNode: a, targetNode: r }) => {
2740
+ }), s.length > 10 && (i += `<div class="f3d-no-results">+ ${s.length - 10} more nodes</div>`)), t.length > 0 && (i += '<div class="f3d-search-section-header">Relationships</div>', t.slice(0, 5).forEach(({ edge: o, sourceNode: n, targetNode: r }) => {
2569
2741
  i += `
2570
2742
  <div class="f3d-search-result-item" data-node-id="${this.escapeHtml(o.source)}">
2571
- <div class="f3d-result-label">${this.escapeHtml(a.label)} → ${this.escapeHtml(r.label)}</div>
2743
+ <div class="f3d-result-label">${this.escapeHtml(n.label)} → ${this.escapeHtml(r.label)}</div>
2572
2744
  <div class="f3d-result-relationship">${this.escapeHtml(o.relationship || "connected")}</div>
2573
2745
  </div>
2574
2746
  `;
2575
2747
  }), t.length > 5 && (i += `<div class="f3d-no-results">+ ${t.length - 5} more relationships</div>`)), this.searchResults.innerHTML = i, this.searchResults.style.display = "block", this.searchResults.querySelectorAll(".f3d-search-result-item").forEach((o) => {
2576
2748
  o.addEventListener("click", () => {
2577
- const a = o.dataset.nodeId;
2578
- a && (this.onResultClick(a), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
2749
+ const n = o.dataset.nodeId;
2750
+ n && (this.onResultClick(n), this.searchResults && (this.searchResults.style.display = "none"), this.searchInput && this.searchInput.blur());
2579
2751
  });
2580
2752
  });
2581
2753
  }
@@ -2587,7 +2759,7 @@ class kt {
2587
2759
  this.searchContainer && this.searchContainer.parentNode && this.searchContainer.parentNode.removeChild(this.searchContainer);
2588
2760
  }
2589
2761
  }
2590
- class Pt {
2762
+ class Tt {
2591
2763
  constructor(e, s) {
2592
2764
  l(this, "container");
2593
2765
  l(this, "toggleContainer", null);
@@ -2679,7 +2851,90 @@ class Pt {
2679
2851
  this.toggleContainer && this.toggleContainer.parentNode && this.toggleContainer.parentNode.removeChild(this.toggleContainer);
2680
2852
  }
2681
2853
  }
2682
- const Rt = {
2854
+ class Pt {
2855
+ constructor(e) {
2856
+ l(this, "container");
2857
+ l(this, "legendContainer", null);
2858
+ l(this, "nodeCountElement", null);
2859
+ l(this, "linkCountElement", null);
2860
+ this.container = e, this.init();
2861
+ }
2862
+ init() {
2863
+ this.createLegendUI(), this.injectStyles();
2864
+ }
2865
+ createLegendUI() {
2866
+ this.legendContainer = document.createElement("div"), this.legendContainer.className = "f3d-legend-container";
2867
+ const e = document.createElement("div");
2868
+ e.className = "f3d-legend-title", e.textContent = "Graph";
2869
+ const s = this.createStatRow("Nodes"), t = this.createStatRow("Links");
2870
+ this.nodeCountElement = s.valueElement, this.linkCountElement = t.valueElement, this.legendContainer.appendChild(e), this.legendContainer.appendChild(s.rowElement), this.legendContainer.appendChild(t.rowElement), this.container.appendChild(this.legendContainer);
2871
+ }
2872
+ createStatRow(e) {
2873
+ const s = document.createElement("div");
2874
+ s.className = "f3d-legend-row";
2875
+ const t = document.createElement("span");
2876
+ t.className = "f3d-legend-label", t.textContent = e;
2877
+ const i = document.createElement("span");
2878
+ return i.className = "f3d-legend-value", i.textContent = "0", s.appendChild(t), s.appendChild(i), { rowElement: s, valueElement: i };
2879
+ }
2880
+ injectStyles() {
2881
+ const e = "f3d-legend-styles";
2882
+ if (document.getElementById(e))
2883
+ return;
2884
+ const s = document.createElement("style");
2885
+ s.id = e, s.textContent = `
2886
+ .f3d-legend-container {
2887
+ position: absolute;
2888
+ right: 20px;
2889
+ bottom: 20px;
2890
+ z-index: 100;
2891
+ min-width: 140px;
2892
+ padding: 10px 12px;
2893
+ display: flex;
2894
+ flex-direction: column;
2895
+ gap: 8px;
2896
+ background: rgba(255, 255, 255, 0.08);
2897
+ backdrop-filter: blur(20px);
2898
+ -webkit-backdrop-filter: blur(20px);
2899
+ border: 1px solid rgba(255, 255, 255, 0.12);
2900
+ border-radius: 12px;
2901
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
2902
+ color: white;
2903
+ font-family: inherit;
2904
+ pointer-events: none;
2905
+ }
2906
+ .f3d-legend-title {
2907
+ font-size: 10px;
2908
+ letter-spacing: 1px;
2909
+ text-transform: uppercase;
2910
+ font-weight: 700;
2911
+ color: rgba(255, 255, 255, 0.55);
2912
+ }
2913
+ .f3d-legend-row {
2914
+ display: flex;
2915
+ align-items: center;
2916
+ justify-content: space-between;
2917
+ gap: 12px;
2918
+ }
2919
+ .f3d-legend-label {
2920
+ font-size: 12px;
2921
+ color: rgba(255, 255, 255, 0.75);
2922
+ }
2923
+ .f3d-legend-value {
2924
+ font-size: 13px;
2925
+ font-weight: 700;
2926
+ color: rgba(255, 153, 102, 0.95);
2927
+ }
2928
+ `, document.head.appendChild(s);
2929
+ }
2930
+ updateCounts(e, s) {
2931
+ this.nodeCountElement && (this.nodeCountElement.textContent = e.toString()), this.linkCountElement && (this.linkCountElement.textContent = s.toString());
2932
+ }
2933
+ dispose() {
2934
+ this.legendContainer && this.legendContainer.parentNode && this.legendContainer.parentNode.removeChild(this.legendContainer);
2935
+ }
2936
+ }
2937
+ const Lt = {
2683
2938
  backgroundColor: "#0a0a0a",
2684
2939
  gridColor: "rgba(255, 255, 255, 0.03)",
2685
2940
  nodeRadius: 24,
@@ -2692,7 +2947,7 @@ const Rt = {
2692
2947
  damping: 0.85
2693
2948
  // Fast energy dissipation
2694
2949
  };
2695
- class It {
2950
+ class Rt {
2696
2951
  constructor(e, s = {}) {
2697
2952
  l(this, "container");
2698
2953
  l(this, "canvas");
@@ -2719,7 +2974,7 @@ class It {
2719
2974
  l(this, "isSimulating", !0);
2720
2975
  // Resize handler
2721
2976
  l(this, "resizeHandler");
2722
- this.container = e, this.options = { ...Rt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
2977
+ this.container = e, this.options = { ...Lt, ...s }, this.canvas = document.createElement("canvas"), this.canvas.style.position = "absolute", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.width = "100%", this.canvas.style.height = "100%", this.canvas.style.cursor = "grab", e.appendChild(this.canvas);
2723
2978
  const t = this.canvas.getContext("2d");
2724
2979
  if (!t)
2725
2980
  throw new Error("Failed to get 2D context");
@@ -2732,21 +2987,21 @@ class It {
2732
2987
  setupInteractions() {
2733
2988
  this.canvas.addEventListener("wheel", (e) => {
2734
2989
  e.preventDefault();
2735
- const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1, a = Math.max(0.1, Math.min(5, this.transform.scale * o)), r = a / this.transform.scale;
2736
- this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale = a;
2990
+ const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = e.deltaY > 0 ? 0.9 : 1.1, n = Math.max(0.1, Math.min(5, this.transform.scale * o)), r = n / this.transform.scale;
2991
+ this.transform.x = t - (t - this.transform.x) * r, this.transform.y = i - (i - this.transform.y) * r, this.transform.scale = n;
2737
2992
  }), this.canvas.addEventListener("mousedown", (e) => {
2738
- const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i), a = this.findNodeAt(o.x, o.y);
2739
- this.dragStartPos = { x: e.clientX, y: e.clientY }, a ? (this.isDragging = !0, this.draggedNode = a, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
2993
+ const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i), n = this.findNodeAt(o.x, o.y);
2994
+ this.dragStartPos = { x: e.clientX, y: e.clientY }, n ? (this.isDragging = !0, this.draggedNode = n, this.canvas.style.cursor = "grabbing", this.isSimulating = !0) : (this.isPanning = !0, this.canvas.style.cursor = "grabbing"), this.lastMousePos = { x: e.clientX, y: e.clientY };
2740
2995
  }), this.canvas.addEventListener("mousemove", (e) => {
2741
2996
  const s = this.canvas.getBoundingClientRect(), t = e.clientX - s.left, i = e.clientY - s.top, o = this.screenToWorld(t, i);
2742
2997
  if (this.isDragging && this.draggedNode)
2743
2998
  this.draggedNode.x = o.x, this.draggedNode.y = o.y, this.draggedNode.vx = 0, this.draggedNode.vy = 0;
2744
2999
  else if (this.isPanning) {
2745
- const a = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
2746
- this.transform.x += a, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
3000
+ const n = e.clientX - this.lastMousePos.x, r = e.clientY - this.lastMousePos.y;
3001
+ this.transform.x += n, this.transform.y += r, this.lastMousePos = { x: e.clientX, y: e.clientY };
2747
3002
  } else {
2748
- const a = this.findNodeAt(o.x, o.y);
2749
- if (a !== this.hoveredNode && (this.hoveredNode = a, a ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(a.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !a) {
3003
+ const n = this.findNodeAt(o.x, o.y);
3004
+ if (n !== this.hoveredNode && (this.hoveredNode = n, n ? (this.hoveredEdge && (this.hoveredEdge = null, this.options.onEdgeHover && this.options.onEdgeHover(null)), this.canvas.style.cursor = "pointer", this.options.onNodeHover && this.options.onNodeHover(n.data)) : this.options.onNodeHover && this.options.onNodeHover(null)), !n) {
2750
3005
  const r = this.findEdgeAt(o.x, o.y);
2751
3006
  r !== this.hoveredEdge && (this.hoveredEdge = r, this.canvas.style.cursor = r ? "pointer" : "grab", this.options.onEdgeHover && this.options.onEdgeHover(r ? r.data : null, e));
2752
3007
  }
@@ -2756,7 +3011,7 @@ class It {
2756
3011
  if (this.isDragging && this.draggedNode)
2757
3012
  i && this.options.onNodeClick && (this.selectedNode = this.draggedNode, this.options.onNodeClick(this.draggedNode.data));
2758
3013
  else if (i) {
2759
- const o = this.canvas.getBoundingClientRect(), a = this.screenToWorld(e.clientX - o.left, e.clientY - o.top), r = this.findEdgeAt(a.x, a.y);
3014
+ const o = this.canvas.getBoundingClientRect(), n = this.screenToWorld(e.clientX - o.left, e.clientY - o.top), r = this.findEdgeAt(n.x, n.y);
2760
3015
  r && this.options.onEdgeClick && this.options.onEdgeClick(r.data);
2761
3016
  }
2762
3017
  this.isDragging = !1, this.isPanning = !1, this.draggedNode = null, this.canvas.style.cursor = this.hoveredNode ? "pointer" : "grab";
@@ -2780,12 +3035,12 @@ class It {
2780
3035
  }
2781
3036
  findEdgeAt(e, s) {
2782
3037
  for (const i of this.edges) {
2783
- const o = this.nodes.get(i.source), a = this.nodes.get(i.target);
2784
- if (!o || !a) continue;
2785
- const r = a.x - o.x, c = a.y - o.y, h = r * r + c * c;
2786
- if (h === 0) continue;
2787
- const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * c) / h)), x = o.x + p * r, m = o.y + p * c, u = e - x, f = s - m;
2788
- if (Math.sqrt(u * u + f * f) < 12)
3038
+ const o = this.nodes.get(i.source), n = this.nodes.get(i.target);
3039
+ if (!o || !n) continue;
3040
+ const r = n.x - o.x, d = n.y - o.y, c = r * r + d * d;
3041
+ if (c === 0) continue;
3042
+ const p = Math.max(0, Math.min(1, ((e - o.x) * r + (s - o.y) * d) / c)), f = o.x + p * r, m = o.y + p * d, u = e - f, y = s - m;
3043
+ if (Math.sqrt(u * u + y * y) < 12)
2789
3044
  return i;
2790
3045
  }
2791
3046
  return null;
@@ -2802,29 +3057,29 @@ class It {
2802
3057
  const t = 60, i = 5;
2803
3058
  let o = 0;
2804
3059
  for (let r = 0; r < s; r++)
2805
- for (let c = r + 1; c < s; c++) {
2806
- const h = e[r], p = e[c];
2807
- let x = p.x - h.x, m = p.y - h.y, u = Math.sqrt(x * x + m * m);
3060
+ for (let d = r + 1; d < s; d++) {
3061
+ const c = e[r], p = e[d];
3062
+ let f = p.x - c.x, m = p.y - c.y, u = Math.sqrt(f * f + m * m);
2808
3063
  if (u < t * 3) {
2809
3064
  u < 1 && (u = 1);
2810
- const f = this.options.repulsionStrength / (u * u), b = x / u * f, M = m / u * f;
2811
- h.vx -= b, h.vy -= M, p.vx += b, p.vy += M;
3065
+ const y = this.options.repulsionStrength / (u * u), b = f / u * y, w = m / u * y;
3066
+ c.vx -= b, c.vy -= w, p.vx += b, p.vy += w;
2812
3067
  }
2813
3068
  }
2814
- const a = 80;
3069
+ const n = 80;
2815
3070
  for (const r of this.edges) {
2816
- const c = this.nodes.get(r.source), h = this.nodes.get(r.target);
2817
- if (!c || !h) continue;
2818
- let p = h.x - c.x, x = h.y - c.y, m = Math.sqrt(p * p + x * x);
3071
+ const d = this.nodes.get(r.source), c = this.nodes.get(r.target);
3072
+ if (!d || !c) continue;
3073
+ let p = c.x - d.x, f = c.y - d.y, m = Math.sqrt(p * p + f * f);
2819
3074
  m < 1 && (m = 1);
2820
- const f = (m - a) * this.options.attractionStrength, b = p / m * f, M = x / m * f;
2821
- c.vx += b, c.vy += M, h.vx -= b, h.vy -= M;
3075
+ const y = (m - n) * this.options.attractionStrength, b = p / m * y, w = f / m * y;
3076
+ d.vx += b, d.vy += w, c.vx -= b, c.vy -= w;
2822
3077
  }
2823
3078
  for (const r of e) {
2824
3079
  if (this.draggedNode === r) continue;
2825
3080
  r.vx *= this.options.damping, r.vy *= this.options.damping;
2826
- const c = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
2827
- c > i && (r.vx = r.vx / c * i, r.vy = r.vy / c * i), r.x += r.vx, r.y += r.vy, o += r.vx * r.vx + r.vy * r.vy;
3081
+ const d = Math.sqrt(r.vx * r.vx + r.vy * r.vy);
3082
+ d > i && (r.vx = r.vx / d * i, r.vy = r.vy / d * i), r.x += r.vx, r.y += r.vy, o += r.vx * r.vx + r.vy * r.vy;
2828
3083
  }
2829
3084
  o < 0.01 && !this.draggedNode && (this.isSimulating = !1);
2830
3085
  }
@@ -2833,49 +3088,49 @@ class It {
2833
3088
  e.fillStyle = this.options.backgroundColor, e.fillRect(0, 0, s, t), this.renderGrid(s, t), e.save(), e.translate(this.transform.x, this.transform.y), e.scale(this.transform.scale, this.transform.scale), this.renderEdges(), this.renderNodes(), e.restore();
2834
3089
  }
2835
3090
  renderGrid(e, s) {
2836
- const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, a = this.transform.x % i, r = this.transform.y % i;
3091
+ const t = this.ctx, i = 40 * this.transform.scale, o = 1.5, n = this.transform.x % i, r = this.transform.y % i;
2837
3092
  t.fillStyle = this.options.gridColor;
2838
- for (let c = a; c < e; c += i)
2839
- for (let h = r; h < s; h += i)
2840
- t.beginPath(), t.arc(c, h, o, 0, Math.PI * 2), t.fill();
3093
+ for (let d = n; d < e; d += i)
3094
+ for (let c = r; c < s; c += i)
3095
+ t.beginPath(), t.arc(d, c, o, 0, Math.PI * 2), t.fill();
2841
3096
  }
2842
3097
  renderEdges() {
2843
3098
  const e = this.ctx;
2844
3099
  for (const s of this.edges) {
2845
3100
  const t = this.nodes.get(s.source), i = this.nodes.get(s.target);
2846
3101
  if (!t || !i) continue;
2847
- const o = s === this.hoveredEdge, a = e.createLinearGradient(t.x, t.y, i.x, i.y);
2848
- o ? (a.addColorStop(0, "rgba(255, 153, 102, 0.8)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), a.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (a.addColorStop(0, "rgba(255, 153, 102, 0.3)"), a.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), a.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = a, e.stroke();
3102
+ const o = s === this.hoveredEdge, n = e.createLinearGradient(t.x, t.y, i.x, i.y);
3103
+ o ? (n.addColorStop(0, "rgba(255, 153, 102, 0.8)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.5)"), n.addColorStop(1, "rgba(102, 153, 255, 0.8)"), e.lineWidth = 3) : (n.addColorStop(0, "rgba(255, 153, 102, 0.3)"), n.addColorStop(0.5, "rgba(255, 255, 255, 0.15)"), n.addColorStop(1, "rgba(102, 153, 255, 0.3)"), e.lineWidth = 1.5), e.beginPath(), e.moveTo(t.x, t.y), e.lineTo(i.x, i.y), e.strokeStyle = n, e.stroke();
2849
3104
  }
2850
3105
  }
2851
3106
  renderNodes() {
2852
3107
  const e = this.ctx;
2853
3108
  for (const s of this.nodes.values()) {
2854
- const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), a = s.radius * (t ? 1.1 : 1);
3109
+ const t = s === this.hoveredNode, i = s === this.selectedNode, o = this.hoveredEdge && (s.data.id === this.hoveredEdge.source || s.data.id === this.hoveredEdge.target), n = s.radius * (t ? 1.1 : 1);
2855
3110
  if (t || i || o) {
2856
- const f = e.createRadialGradient(
3111
+ const y = e.createRadialGradient(
2857
3112
  s.x,
2858
3113
  s.y,
2859
- a * 0.5,
3114
+ n * 0.5,
2860
3115
  s.x,
2861
3116
  s.y,
2862
- a * 2
3117
+ n * 2
2863
3118
  ), b = t || i ? 0.4 : 0.25;
2864
- f.addColorStop(0, `rgba(255, 153, 102, ${b})`), f.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = f, e.beginPath(), e.arc(s.x, s.y, a * 2, 0, Math.PI * 2), e.fill();
3119
+ y.addColorStop(0, `rgba(255, 153, 102, ${b})`), y.addColorStop(1, "rgba(255, 153, 102, 0)"), e.fillStyle = y, e.beginPath(), e.arc(s.x, s.y, n * 2, 0, Math.PI * 2), e.fill();
2865
3120
  }
2866
3121
  const r = e.createRadialGradient(
2867
- s.x - a * 0.3,
2868
- s.y - a * 0.3,
3122
+ s.x - n * 0.3,
3123
+ s.y - n * 0.3,
2869
3124
  0,
2870
3125
  s.x,
2871
3126
  s.y,
2872
- a
2873
- ), c = s.color >> 16 & 255, h = s.color >> 8 & 255, p = s.color & 255;
2874
- r.addColorStop(0, `rgba(${Math.min(255, c + 60)}, ${Math.min(255, h + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${c}, ${h}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, c - 40)}, ${Math.max(0, h - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, a, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - a * 0.25, s.y - a * 0.25, a * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
2875
- const x = a * 1.6;
3127
+ n
3128
+ ), d = s.color >> 16 & 255, c = s.color >> 8 & 255, p = s.color & 255;
3129
+ r.addColorStop(0, `rgba(${Math.min(255, d + 60)}, ${Math.min(255, c + 60)}, ${Math.min(255, p + 60)}, 0.95)`), r.addColorStop(0.7, `rgba(${d}, ${c}, ${p}, 0.9)`), r.addColorStop(1, `rgba(${Math.max(0, d - 40)}, ${Math.max(0, c - 40)}, ${Math.max(0, p - 40)}, 0.85)`), e.beginPath(), e.arc(s.x, s.y, n, 0, Math.PI * 2), e.fillStyle = r, e.fill(), e.strokeStyle = "rgba(255, 255, 255, 0.2)", e.lineWidth = 1, e.stroke(), e.beginPath(), e.arc(s.x - n * 0.25, s.y - n * 0.25, n * 0.3, 0, Math.PI * 2), e.fillStyle = "rgba(255, 255, 255, 0.15)", e.fill(), e.fillStyle = "white", e.font = "600 11px Inter, -apple-system, BlinkMacSystemFont, sans-serif", e.textAlign = "center", e.textBaseline = "middle";
3130
+ const f = n * 1.6;
2876
3131
  let m = s.label, u = e.measureText(m).width;
2877
- if (u > x) {
2878
- for (; u > x && m.length > 3; )
3132
+ if (u > f) {
3133
+ for (; u > f && m.length > 3; )
2879
3134
  m = m.slice(0, -1), u = e.measureText(m + "...").width;
2880
3135
  m += "...";
2881
3136
  }
@@ -2978,11 +3233,11 @@ class It {
2978
3233
  focusOnNode(e) {
2979
3234
  const s = this.nodes.get(e);
2980
3235
  if (!s) return;
2981
- const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, a = this.transform.y, r = 500, c = performance.now(), h = () => {
2982
- const p = performance.now() - c, x = Math.min(p / r, 1), m = 1 - Math.pow(1 - x, 3);
2983
- this.transform.x = o + (t - o) * m, this.transform.y = a + (i - a) * m, x < 1 ? requestAnimationFrame(h) : this.selectedNode = s;
3236
+ const t = this.canvas.width / 2 / (window.devicePixelRatio || 1) - s.x * this.transform.scale, i = this.canvas.height / 2 / (window.devicePixelRatio || 1) - s.y * this.transform.scale, o = this.transform.x, n = this.transform.y, r = 500, d = performance.now(), c = () => {
3237
+ const p = performance.now() - d, f = Math.min(p / r, 1), m = 1 - Math.pow(1 - f, 3);
3238
+ this.transform.x = o + (t - o) * m, this.transform.y = n + (i - n) * m, f < 1 ? requestAnimationFrame(c) : this.selectedNode = s;
2984
3239
  };
2985
- h();
3240
+ c();
2986
3241
  }
2987
3242
  /**
2988
3243
  * Updates node positions from 3D data
@@ -3050,6 +3305,7 @@ class Ht {
3050
3305
  l(this, "edgeTooltipManager");
3051
3306
  l(this, "searchManager", null);
3052
3307
  l(this, "viewToggleManager", null);
3308
+ l(this, "legendManager", null);
3053
3309
  // 2D Renderer
3054
3310
  l(this, "forceGraph2D", null);
3055
3311
  // Event system
@@ -3059,7 +3315,7 @@ class Ht {
3059
3315
  l(this, "devControls", null);
3060
3316
  l(this, "viewMode", "3d");
3061
3317
  l(this, "graphData", null);
3062
- this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new vt(), this.nodeFactory = new Mt(
3318
+ this.options = { ...P, ...s }, this.container = dt(e), this.materialFactory = new bt(), this.nodeFactory = new vt(
3063
3319
  this.materialFactory,
3064
3320
  this.options.nodeRadius ?? P.nodeRadius,
3065
3321
  this.options.lodSegments ?? P.lodSegments,
@@ -3068,14 +3324,14 @@ class Ht {
3068
3324
  this.materialFactory,
3069
3325
  this.options.edgeColor ?? P.edgeColor,
3070
3326
  this.options.edgeOpacity ?? P.edgeOpacity
3071
- ), this.sceneManager = new mt(this.container, this.options), this.lodManager = new Et(
3327
+ ), this.sceneManager = new ft(this.container, this.options), this.lodManager = new Mt(
3072
3328
  this.sceneManager.camera,
3073
3329
  this.options.lodDistances ?? P.lodDistances,
3074
3330
  this.options.enableLOD ?? P.enableLOD
3075
- ), this.frustumCuller = new Ct(
3331
+ ), this.frustumCuller = new Et(
3076
3332
  this.sceneManager.camera,
3077
3333
  this.options.enableEdgeCulling ?? P.enableEdgeCulling
3078
- ), this.nodeManager = new te(this.sceneManager, this.nodeFactory), this.edgeManager = new yt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Le(
3334
+ ), this.nodeManager = new ee(this.sceneManager, this.nodeFactory), this.edgeManager = new mt(this.sceneManager, this.nodeManager, this.edgeFactory), this.graphEngine = new Re(
3079
3335
  this.nodeManager.getAllNodes(),
3080
3336
  this.edgeManager.getAllEdges(),
3081
3337
  {
@@ -3085,12 +3341,12 @@ class Ht {
3085
3341
  useBarnesHut: this.options.useBarnesHut,
3086
3342
  barnesHutTheta: this.options.barnesHutTheta
3087
3343
  }
3088
- ), this.rendererManager = new bt(
3344
+ ), this.rendererManager = new xt(
3089
3345
  this.sceneManager,
3090
3346
  () => this.onSimulate(),
3091
3347
  () => this.onRender(),
3092
3348
  this.options.targetFPS ?? P.targetFPS
3093
- ), this.raycasterManager = new Nt(this.sceneManager, this.container), this.panelManager = new St(this.container), this.edgePanelManager = new zt(this.container), this.edgeTooltipManager = new Tt(), this.edgePanelManager.setNodeClickCallback((t) => {
3349
+ ), this.raycasterManager = new Ct(this.sceneManager, this.container), this.panelManager = new Nt(this.container), this.edgePanelManager = new St(this.container), this.edgeTooltipManager = new zt(), this.edgePanelManager.setNodeClickCallback((t) => {
3094
3350
  this.edgePanelManager.hide(), this.focusOnNode(t), setTimeout(() => {
3095
3351
  this.showNodePanel(t);
3096
3352
  }, 400);
@@ -3105,12 +3361,12 @@ class Ht {
3105
3361
  this.showNodePanel(t);
3106
3362
  }, 400);
3107
3363
  }
3108
- })), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Pt(this.container, {
3364
+ })), this.options.showViewToggle !== !1 && (this.viewMode = this.options.initialViewMode || "3d", this.viewToggleManager = new Tt(this.container, {
3109
3365
  initialMode: this.viewMode,
3110
3366
  onViewChange: (t) => {
3111
3367
  this.switchView(t);
3112
3368
  }
3113
- })), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
3369
+ })), this.options.showLegend !== !1 && (this.legendManager = new Pt(this.container), this.updateLegendCounts()), this.setupCallbacks(), this.rendererManager.start(), this.initialized = !0, this.emit("ready");
3114
3370
  }
3115
3371
  /**
3116
3372
  * Sets up internal callbacks
@@ -3133,7 +3389,14 @@ class Ht {
3133
3389
  if (e) {
3134
3390
  this.edgeManager.highlightEdge(e.edge.source, e.edge.target);
3135
3391
  const s = this.container.getBoundingClientRect(), t = s.left + s.width / 2, i = s.top + s.height / 2;
3136
- this.edgeTooltipManager.show(e.edge, e.sourceNode, e.targetNode, t, i), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
3392
+ this.edgeTooltipManager.show(
3393
+ e.edge,
3394
+ e.sourceNode,
3395
+ e.targetNode,
3396
+ t,
3397
+ i,
3398
+ e.relationships
3399
+ ), this.options.onEdgeHover && this.options.onEdgeHover(e.edge, e.sourceNode, e.targetNode), this.emit("edgeHover", e.edge, e.sourceNode, e.targetNode);
3137
3400
  } else
3138
3401
  this.edgeManager.unhighlightCurrentEdge(), this.edgeTooltipManager.hide(), this.options.onEdgeHover && this.options.onEdgeHover(null, null, null), this.emit("edgeHover", null, null, null);
3139
3402
  }
@@ -3149,7 +3412,7 @@ class Ht {
3149
3412
  * Handles edge click
3150
3413
  */
3151
3414
  onEdgeClick(e) {
3152
- this.panelManager.hide(), this.edgeTooltipManager.hide(), this.edgeManager.highlightEdge(e.edge.source, e.edge.target), this.edgePanelManager.show(e.edge, e.sourceNode, e.targetNode), this.focusOnEdge(e.edge.source, e.edge.target), this.emit("edgeClick", e.edge, e.sourceNode, e.targetNode);
3415
+ this.panelManager.hide(), this.edgeTooltipManager.hide(), this.edgeManager.highlightEdge(e.edge.source, e.edge.target), this.edgePanelManager.show(e.edge, e.sourceNode, e.targetNode, e.relationships), this.focusOnEdge(e.edge.source, e.edge.target), this.emit("edgeClick", e.edge, e.sourceNode, e.targetNode);
3153
3416
  }
3154
3417
  /**
3155
3418
  * Called every simulation step
@@ -3169,6 +3432,12 @@ class Ht {
3169
3432
  onRender() {
3170
3433
  this.frustumCuller.update(), this.raycasterManager.setNodeObjects(this.nodeManager.getAllNodeObjects()), this.raycasterManager.setEdgeObjects(this.edgeManager.getAllEdgeLines());
3171
3434
  }
3435
+ /**
3436
+ * Updates bottom-right legend counts
3437
+ */
3438
+ updateLegendCounts() {
3439
+ this.legendManager && this.legendManager.updateCounts(this.getNodeCount(), this.getEdgeCount());
3440
+ }
3172
3441
  // ==========================================================================
3173
3442
  // Public API
3174
3443
  // ==========================================================================
@@ -3176,8 +3445,8 @@ class Ht {
3176
3445
  * Sets the graph data
3177
3446
  */
3178
3447
  setData(e) {
3179
- var a;
3180
- const s = (a = e.data) != null && a.nodes ? e.data : e, i = (s.edges || s.links || []).map((r) => ({
3448
+ var n;
3449
+ const s = (n = e.data) != null && n.nodes ? e.data : e, i = (s.edges || s.links || []).map((r) => ({
3181
3450
  ...r,
3182
3451
  relationship: r.relationship || r.label || "related_to"
3183
3452
  })), o = {
@@ -3185,14 +3454,14 @@ class Ht {
3185
3454
  edges: i
3186
3455
  };
3187
3456
  if (this.graphData = o, this.edgeManager.clear(), this.nodeManager.clear(), o.nodes && Array.isArray(o.nodes)) {
3188
- te.setExpectedNodeCount(o.nodes.length);
3457
+ ee.setExpectedNodeCount(o.nodes.length);
3189
3458
  for (const r of o.nodes)
3190
3459
  this.addNode(r);
3191
3460
  }
3192
3461
  if (o.edges && Array.isArray(o.edges))
3193
3462
  for (const r of o.edges)
3194
3463
  this.addEdge(r);
3195
- this.graphEngine = new Le(
3464
+ this.graphEngine = new Re(
3196
3465
  this.nodeManager.getAllNodes(),
3197
3466
  this.edgeManager.getAllEdges(),
3198
3467
  {
@@ -3202,28 +3471,28 @@ class Ht {
3202
3471
  useBarnesHut: this.options.useBarnesHut,
3203
3472
  barnesHutTheta: this.options.barnesHutTheta
3204
3473
  }
3205
- ), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o);
3474
+ ), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.setData(o), this.updateLegendCounts();
3206
3475
  }
3207
3476
  /**
3208
3477
  * Adds a node to the graph
3209
3478
  * @returns true if added, false if node already exists or invalid
3210
3479
  */
3211
3480
  addNode(e) {
3212
- if (!He(e))
3481
+ if (!Fe(e))
3213
3482
  return !1;
3214
3483
  const s = this.nodeManager.addNode(e);
3215
- return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e)), s;
3484
+ return s && (this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addNode(e), this.options.onNodeAdd && this.options.onNodeAdd(e), this.emit("nodeAdd", e), this.updateLegendCounts()), s;
3216
3485
  }
3217
3486
  /**
3218
3487
  * Removes a node from the graph
3219
3488
  * @returns true if removed, false if not found
3220
3489
  */
3221
3490
  removeNode(e) {
3222
- if (!pt(e))
3491
+ if (!ht(e))
3223
3492
  return !1;
3224
3493
  this.edgeManager.removeEdgesForNode(e);
3225
3494
  const s = this.nodeManager.removeNode(e);
3226
- return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide()), s;
3495
+ return s && (this.graphEngine.restart(), this.options.onNodeRemove && this.options.onNodeRemove(e), this.emit("nodeRemove", e), this.panelManager.getCurrentNodeId() === e && this.panelManager.hide(), this.updateLegendCounts()), s;
3227
3496
  }
3228
3497
  /**
3229
3498
  * Updates a node's properties
@@ -3233,13 +3502,13 @@ class Ht {
3233
3502
  }
3234
3503
  /**
3235
3504
  * Adds an edge to the graph
3236
- * @returns true if added, false if edge already exists or nodes don't exist
3505
+ * @returns true if added, false if invalid or nodes don't exist
3237
3506
  */
3238
3507
  addEdge(e) {
3239
3508
  if (!De(e))
3240
3509
  return !1;
3241
3510
  const s = this.edgeManager.addEdge(e);
3242
- return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e)), s;
3511
+ return s && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.graphEngine.restart(), this.forceGraph2D && this.forceGraph2D.addEdge(e), this.options.onEdgeAdd && this.options.onEdgeAdd(e), this.emit("edgeAdd", e), this.updateLegendCounts()), s;
3243
3512
  }
3244
3513
  /**
3245
3514
  * Removes an edge from the graph
@@ -3247,7 +3516,7 @@ class Ht {
3247
3516
  */
3248
3517
  removeEdge(e, s) {
3249
3518
  const t = this.edgeManager.removeEdge(e, s);
3250
- return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s })), t;
3519
+ return t && (this.graphEngine.setEdges(this.edgeManager.getAllEdges()), this.options.onEdgeRemove && this.options.onEdgeRemove({ source: e, target: s }), this.emit("edgeRemove", { source: e, target: s }), this.updateLegendCounts()), t;
3251
3520
  }
3252
3521
  /**
3253
3522
  * Expands a node by fetching more data
@@ -3262,11 +3531,11 @@ class Ht {
3262
3531
  try {
3263
3532
  const o = await i(e, s);
3264
3533
  if (o.nodes && Array.isArray(o.nodes))
3265
- for (const a of o.nodes)
3266
- this.addNode(a);
3534
+ for (const n of o.nodes)
3535
+ this.addNode(n);
3267
3536
  if (o.edges && Array.isArray(o.edges))
3268
- for (const a of o.edges)
3269
- this.addEdge(a);
3537
+ for (const n of o.edges)
3538
+ this.addEdge(n);
3270
3539
  return this.panelManager.hide(), this.emit("expand", e, o), !0;
3271
3540
  } catch (o) {
3272
3541
  return console.error("[ForceGraph3D] Error expanding node:", o), !1;
@@ -3316,13 +3585,13 @@ class Ht {
3316
3585
  console.warn(`[ForceGraph3D] Node "${e}" not found`);
3317
3586
  return;
3318
3587
  }
3319
- const i = t.position, o = this.sceneManager.camera, a = this.sceneManager.controls, r = o.position.clone().sub(a.target).normalize(), c = {
3588
+ const i = t.position, o = this.sceneManager.camera, n = this.sceneManager.controls, r = o.position.clone().sub(n.target).normalize(), d = {
3320
3589
  x: i.x + r.x * s,
3321
3590
  y: i.y + r.y * s,
3322
3591
  z: i.z + r.z * s
3323
- }, h = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: a.target.x, y: a.target.y, z: a.target.z }, x = 800, m = performance.now(), u = () => {
3324
- const f = performance.now() - m, b = Math.min(f / x, 1), M = 1 - Math.pow(1 - b, 3);
3325
- o.position.x = h.x + (c.x - h.x) * M, o.position.y = h.y + (c.y - h.y) * M, o.position.z = h.z + (c.z - h.z) * M, a.target.x = p.x + (i.x - p.x) * M, a.target.y = p.y + (i.y - p.y) * M, a.target.z = p.z + (i.z - p.z) * M, a.update(), b < 1 && requestAnimationFrame(u);
3592
+ }, c = { x: o.position.x, y: o.position.y, z: o.position.z }, p = { x: n.target.x, y: n.target.y, z: n.target.z }, f = 800, m = performance.now(), u = () => {
3593
+ const y = performance.now() - m, b = Math.min(y / f, 1), w = 1 - Math.pow(1 - b, 3);
3594
+ o.position.x = c.x + (d.x - c.x) * w, o.position.y = c.y + (d.y - c.y) * w, o.position.z = c.z + (d.z - c.z) * w, n.target.x = p.x + (i.x - p.x) * w, n.target.y = p.y + (i.y - p.y) * w, n.target.z = p.z + (i.z - p.z) * w, n.update(), b < 1 && requestAnimationFrame(u);
3326
3595
  };
3327
3596
  u();
3328
3597
  }
@@ -3336,19 +3605,19 @@ class Ht {
3336
3605
  console.warn(`[ForceGraph3D] Could not find nodes for edge "${e}" -> "${s}"`);
3337
3606
  return;
3338
3607
  }
3339
- const a = this.sceneManager.camera, r = this.sceneManager.controls, c = {
3608
+ const n = this.sceneManager.camera, r = this.sceneManager.controls, d = {
3340
3609
  x: (i.position.x + o.position.x) / 2,
3341
3610
  y: (i.position.y + o.position.y) / 2,
3342
3611
  z: (i.position.z + o.position.z) / 2
3343
- }, h = o.position.x - i.position.x, p = o.position.y - i.position.y, x = o.position.z - i.position.z, m = Math.sqrt(h * h + p * p + x * x), u = Math.max(m * t, 40), f = a.position.clone().sub(r.target).normalize(), b = {
3344
- x: c.x + f.x * u,
3345
- y: c.y + f.y * u,
3346
- z: c.z + f.z * u
3347
- }, M = { x: a.position.x, y: a.position.y, z: a.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, R = performance.now(), Y = () => {
3348
- const k = performance.now() - R, H = Math.min(k / O, 1), E = 1 - Math.pow(1 - H, 3);
3349
- a.position.x = M.x + (b.x - M.x) * E, a.position.y = M.y + (b.y - M.y) * E, a.position.z = M.z + (b.z - M.z) * E, r.target.x = N.x + (c.x - N.x) * E, r.target.y = N.y + (c.y - N.y) * E, r.target.z = N.z + (c.z - N.z) * E, r.update(), H < 1 && requestAnimationFrame(Y);
3612
+ }, c = o.position.x - i.position.x, p = o.position.y - i.position.y, f = o.position.z - i.position.z, m = Math.sqrt(c * c + p * p + f * f), u = Math.max(m * t, 40), y = n.position.clone().sub(r.target).normalize(), b = {
3613
+ x: d.x + y.x * u,
3614
+ y: d.y + y.y * u,
3615
+ z: d.z + y.z * u
3616
+ }, w = { x: n.position.x, y: n.position.y, z: n.position.z }, N = { x: r.target.x, y: r.target.y, z: r.target.z }, O = 800, L = performance.now(), G = () => {
3617
+ const T = performance.now() - L, D = Math.min(T / O, 1), E = 1 - Math.pow(1 - D, 3);
3618
+ n.position.x = w.x + (b.x - w.x) * E, n.position.y = w.y + (b.y - w.y) * E, n.position.z = w.z + (b.z - w.z) * E, r.target.x = N.x + (d.x - N.x) * E, r.target.y = N.y + (d.y - N.y) * E, r.target.z = N.z + (d.z - N.z) * E, r.update(), D < 1 && requestAnimationFrame(G);
3350
3619
  };
3351
- Y();
3620
+ G();
3352
3621
  }
3353
3622
  /**
3354
3623
  * Shows the info panel for a specific node
@@ -3371,9 +3640,9 @@ class Ht {
3371
3640
  return [];
3372
3641
  const s = e.toLowerCase().trim(), t = this.nodeManager.getAllNodes(), i = [];
3373
3642
  return t.forEach((o) => {
3374
- var h, p, x;
3375
- const a = (h = o.label) == null ? void 0 : h.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), c = (x = o.type) == null ? void 0 : x.toLowerCase().includes(s);
3376
- (a || r || c) && i.push(o);
3643
+ var c, p, f;
3644
+ const n = (c = o.label) == null ? void 0 : c.toLowerCase().includes(s), r = (p = o.id) == null ? void 0 : p.toLowerCase().includes(s), d = (f = o.type) == null ? void 0 : f.toLowerCase().includes(s);
3645
+ (n || r || d) && i.push(o);
3377
3646
  }), i;
3378
3647
  }
3379
3648
  /**
@@ -3385,10 +3654,10 @@ class Ht {
3385
3654
  if (!e || e.trim() === "")
3386
3655
  return [];
3387
3656
  const s = e.toLowerCase().trim(), t = this.edgeManager.getAllEdges(), i = [];
3388
- for (const a of t)
3389
- if ((o = a.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
3390
- const c = this.nodeManager.getNode(a.source), h = this.nodeManager.getNode(a.target);
3391
- c && h && i.push({ edge: a, sourceNode: c, targetNode: h });
3657
+ for (const n of t)
3658
+ if ((o = n.relationship) == null ? void 0 : o.toLowerCase().includes(s)) {
3659
+ const d = this.nodeManager.getNode(n.source), c = this.nodeManager.getNode(n.target);
3660
+ d && c && i.push({ edge: n, sourceNode: d, targetNode: c });
3392
3661
  }
3393
3662
  return i;
3394
3663
  }
@@ -3421,7 +3690,7 @@ class Ht {
3421
3690
  * Switches between 2D and 3D view modes
3422
3691
  */
3423
3692
  switchView(e) {
3424
- this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new It(this.container, {
3693
+ this.viewMode !== e && (this.viewMode = e, this.panelManager.hide(), this.edgePanelManager.hide(), this.edgeTooltipManager.hide(), e === "2d" ? (this.sceneManager.renderer.domElement.style.display = "none", this.rendererManager.stop(), this.forceGraph2D ? (this.forceGraph2D.show(), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())) : (this.forceGraph2D = new Rt(this.container, {
3425
3694
  backgroundColor: "#0a0a0a",
3426
3695
  nodeRadius: 24,
3427
3696
  onNodeClick: (s) => {
@@ -3433,19 +3702,31 @@ class Ht {
3433
3702
  onEdgeHover: (s, t) => {
3434
3703
  if (s && t) {
3435
3704
  const i = this.nodeManager.getNode(s.source), o = this.nodeManager.getNode(s.target);
3436
- i && o && this.edgeTooltipManager.show(
3437
- s,
3438
- i,
3439
- o,
3440
- t.clientX,
3441
- t.clientY
3442
- );
3705
+ if (i && o) {
3706
+ const n = this.edgeManager.getAllEdges().filter((r) => r.source === s.source && r.target === s.target);
3707
+ this.edgeTooltipManager.show(
3708
+ s,
3709
+ i,
3710
+ o,
3711
+ t.clientX,
3712
+ t.clientY,
3713
+ n.length > 0 ? n : [s]
3714
+ );
3715
+ }
3443
3716
  } else
3444
3717
  this.edgeTooltipManager.hide();
3445
3718
  },
3446
3719
  onEdgeClick: (s) => {
3447
3720
  const t = this.nodeManager.getNode(s.source), i = this.nodeManager.getNode(s.target);
3448
- t && i && this.edgePanelManager.show(s, t, i);
3721
+ if (t && i) {
3722
+ const o = this.edgeManager.getAllEdges().filter((n) => n.source === s.source && n.target === s.target);
3723
+ this.edgePanelManager.show(
3724
+ s,
3725
+ t,
3726
+ i,
3727
+ o.length > 0 ? o : [s]
3728
+ );
3729
+ }
3449
3730
  }
3450
3731
  }), this.graphData && (this.forceGraph2D.setData(this.graphData), this.forceGraph2D.syncFrom3D(this.nodeManager.getAllNodes())))) : (this.forceGraph2D && this.forceGraph2D.hide(), this.sceneManager.renderer.domElement.style.display = "block", this.rendererManager.start()), this.emit("viewChange", e));
3451
3732
  }
@@ -3546,18 +3827,18 @@ class Ht {
3546
3827
  const i = parseFloat(t.value) / 100;
3547
3828
  this.setPhysicsParams({ damping: i }), this.devControls.querySelector("#dev-damping-val").textContent = i.toFixed(2);
3548
3829
  }), setInterval(() => {
3549
- const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"), a = this.devControls.querySelector("#dev-fps");
3550
- i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()), a && (a.textContent = this.rendererManager.getFPS().toString());
3830
+ const i = this.devControls.querySelector("#dev-node-count"), o = this.devControls.querySelector("#dev-edge-count"), n = this.devControls.querySelector("#dev-fps");
3831
+ i && (i.textContent = this.getNodeCount().toString()), o && (o.textContent = this.getEdgeCount().toString()), n && (n.textContent = this.rendererManager.getFPS().toString());
3551
3832
  }, 500);
3552
3833
  }
3553
3834
  /**
3554
3835
  * Destroys the graph and releases all resources
3555
3836
  */
3556
3837
  destroy() {
3557
- this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
3838
+ this.rendererManager.dispose(), this.panelManager.dispose(), this.raycasterManager.dispose(), this.edgeTooltipManager.dispose(), this.searchManager && this.searchManager.dispose(), this.viewToggleManager && this.viewToggleManager.dispose(), this.legendManager && this.legendManager.dispose(), this.forceGraph2D && this.forceGraph2D.dispose(), this.edgeManager.dispose(), this.nodeManager.dispose(), this.nodeFactory.dispose(), this.materialFactory.dispose(), this.sceneManager.dispose(), this.devControls && this.devControls.parentNode && this.devControls.parentNode.removeChild(this.devControls), this.eventCallbacks.clear(), this.initialized = !1;
3558
3839
  }
3559
3840
  }
3560
- const Oe = [
3841
+ const Ie = [
3561
3842
  "Alpha",
3562
3843
  "Beta",
3563
3844
  "Gamma",
@@ -3588,7 +3869,7 @@ const Oe = [
3588
3869
  "Link",
3589
3870
  "Point",
3590
3871
  "Vertex"
3591
- ], ee = [
3872
+ ], J = [
3592
3873
  "connects to",
3593
3874
  "links with",
3594
3875
  "relates to",
@@ -3599,7 +3880,7 @@ const Oe = [
3599
3880
  "partners with",
3600
3881
  "collaborates with",
3601
3882
  "supports"
3602
- ], Fe = [
3883
+ ], Oe = [
3603
3884
  16777215,
3604
3885
  // White
3605
3886
  16750950,
@@ -3611,14 +3892,14 @@ const Oe = [
3611
3892
  16746564
3612
3893
  // Darker tangerine
3613
3894
  ];
3614
- function Dt(d = 30) {
3895
+ function At(h = 30) {
3615
3896
  const e = [], s = [];
3616
- for (let i = 0; i < d; i++) {
3617
- const o = i < Oe.length ? Oe[i] : `Node ${i + 1}`;
3897
+ for (let i = 0; i < h; i++) {
3898
+ const o = i < Ie.length ? Ie[i] : `Node ${i + 1}`;
3618
3899
  e.push({
3619
3900
  id: `node-${i}`,
3620
3901
  label: o,
3621
- color: Fe[i % Fe.length],
3902
+ color: Oe[i % Oe.length],
3622
3903
  position: {
3623
3904
  x: (Math.random() - 0.5) * 60,
3624
3905
  y: (Math.random() - 0.5) * 60,
@@ -3626,52 +3907,52 @@ function Dt(d = 30) {
3626
3907
  }
3627
3908
  });
3628
3909
  }
3629
- for (let i = 1; i < d; i++) {
3910
+ for (let i = 1; i < h; i++) {
3630
3911
  const o = Math.floor(Math.random() * i);
3631
3912
  s.push({
3632
3913
  source: `node-${i}`,
3633
3914
  target: `node-${o}`,
3634
- relationship: ee[Math.floor(Math.random() * ee.length)]
3915
+ relationship: J[Math.floor(Math.random() * J.length)]
3635
3916
  });
3636
3917
  }
3637
- const t = Math.floor(d * 0.5);
3918
+ const t = Math.floor(h * 0.5);
3638
3919
  for (let i = 0; i < t; i++) {
3639
- const o = Math.floor(Math.random() * d);
3640
- let a = Math.floor(Math.random() * d);
3641
- o === a && (a = (a + 1) % d);
3642
- const r = `node-${o}`, c = `node-${a}`;
3920
+ const o = Math.floor(Math.random() * h);
3921
+ let n = Math.floor(Math.random() * h);
3922
+ o === n && (n = (n + 1) % h);
3923
+ const r = `node-${o}`, d = `node-${n}`;
3643
3924
  s.some(
3644
- (p) => p.source === r && p.target === c || p.source === c && p.target === r
3925
+ (p) => p.source === r && p.target === d || p.source === d && p.target === r
3645
3926
  ) || s.push({
3646
3927
  source: r,
3647
- target: c,
3648
- relationship: ee[Math.floor(Math.random() * ee.length)]
3928
+ target: d,
3929
+ relationship: J[Math.floor(Math.random() * J.length)]
3649
3930
  });
3650
3931
  }
3651
3932
  return { nodes: e, edges: s };
3652
3933
  }
3653
- function At(d = 1e3) {
3654
- const e = [], s = [], t = Math.ceil(d / 50), i = [];
3934
+ function jt(h = 1e3) {
3935
+ const e = [], s = [], t = Math.ceil(h / 50), i = [];
3655
3936
  for (let o = 0; o < t; o++)
3656
3937
  i.push({
3657
3938
  x: (Math.random() - 0.5) * 200,
3658
3939
  y: (Math.random() - 0.5) * 200,
3659
3940
  z: (Math.random() - 0.5) * 200
3660
3941
  });
3661
- for (let o = 0; o < d; o++) {
3662
- const a = i[o % t];
3942
+ for (let o = 0; o < h; o++) {
3943
+ const n = i[o % t];
3663
3944
  e.push({
3664
3945
  id: `node-${o}`,
3665
3946
  label: `N${o}`,
3666
3947
  position: {
3667
- x: a.x + (Math.random() - 0.5) * 40,
3668
- y: a.y + (Math.random() - 0.5) * 40,
3669
- z: a.z + (Math.random() - 0.5) * 40
3948
+ x: n.x + (Math.random() - 0.5) * 40,
3949
+ y: n.y + (Math.random() - 0.5) * 40,
3950
+ z: n.z + (Math.random() - 0.5) * 40
3670
3951
  }
3671
3952
  });
3672
3953
  }
3673
- for (let o = 1; o < d; o++) {
3674
- const a = Math.floor(o / 50) * 50, r = a === 0 ? Math.floor(Math.random() * o) : a + Math.floor(Math.random() * Math.min(o - a, 50));
3954
+ for (let o = 1; o < h; o++) {
3955
+ const n = Math.floor(o / 50) * 50, r = n === 0 ? Math.floor(Math.random() * o) : n + Math.floor(Math.random() * Math.min(o - n, 50));
3675
3956
  s.push({
3676
3957
  source: `node-${o}`,
3677
3958
  target: `node-${Math.min(r, o - 1)}`,
@@ -3679,9 +3960,9 @@ function At(d = 1e3) {
3679
3960
  });
3680
3961
  }
3681
3962
  for (let o = 1; o < t; o++) {
3682
- const a = o * 50, r = (o - 1) * 50 + Math.floor(Math.random() * 50);
3963
+ const n = o * 50, r = (o - 1) * 50 + Math.floor(Math.random() * 50);
3683
3964
  s.push({
3684
- source: `node-${a}`,
3965
+ source: `node-${n}`,
3685
3966
  target: `node-${r}`,
3686
3967
  relationship: "bridges to"
3687
3968
  });
@@ -3691,11 +3972,11 @@ function At(d = 1e3) {
3691
3972
  export {
3692
3973
  P as DEFAULT_OPTIONS,
3693
3974
  Ht as ForceGraph3D,
3694
- X as LODLevel,
3695
- D as createEdgeKey,
3696
- At as generateLargeSampleData,
3697
- Dt as generateSampleData,
3975
+ _ as LODLevel,
3976
+ Dt as createEdgeKey,
3977
+ jt as generateLargeSampleData,
3978
+ At as generateSampleData,
3698
3979
  De as validateEdgeData,
3699
- He as validateNodeData
3980
+ Fe as validateNodeData
3700
3981
  };
3701
3982
  //# sourceMappingURL=force-3d-graph.js.map