@emblemvault/identity-mesh 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +475 -94
- package/dist/index.mjs +471 -91
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
IdentityMesh: () => IdentityMesh,
|
|
24
|
+
IdentityMeshRenderer: () => IdentityMeshRenderer,
|
|
24
25
|
defaultNodeTypes: () => defaultNodeTypes,
|
|
25
26
|
getBrowserIcon: () => getBrowserIcon,
|
|
26
27
|
getCountryFlag: () => getCountryFlag,
|
|
@@ -30,70 +31,8 @@ __export(index_exports, {
|
|
|
30
31
|
});
|
|
31
32
|
module.exports = __toCommonJS(index_exports);
|
|
32
33
|
|
|
33
|
-
// src/
|
|
34
|
-
var import_react2 = require("react");
|
|
35
|
-
|
|
36
|
-
// src/hooks/useMeshData.ts
|
|
34
|
+
// src/IdentityMeshRenderer.tsx
|
|
37
35
|
var import_react = require("react");
|
|
38
|
-
function useMeshData({
|
|
39
|
-
apiUrl,
|
|
40
|
-
vaultId,
|
|
41
|
-
visitorId,
|
|
42
|
-
onError
|
|
43
|
-
}) {
|
|
44
|
-
const [links, setLinks] = (0, import_react.useState)([]);
|
|
45
|
-
const [visitorDetails, setVisitorDetails] = (0, import_react.useState)({});
|
|
46
|
-
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
47
|
-
const [error, setError] = (0, import_react.useState)(null);
|
|
48
|
-
const fetchData = (0, import_react.useCallback)(async () => {
|
|
49
|
-
if (!apiUrl || !vaultId || !visitorId) {
|
|
50
|
-
setLoading(false);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
setLoading(true);
|
|
54
|
-
setError(null);
|
|
55
|
-
try {
|
|
56
|
-
const response = await fetch(apiUrl, {
|
|
57
|
-
method: "POST",
|
|
58
|
-
headers: {
|
|
59
|
-
"Content-Type": "application/json"
|
|
60
|
-
},
|
|
61
|
-
body: JSON.stringify({ vaultId, visitorId })
|
|
62
|
-
});
|
|
63
|
-
const data = await response.json();
|
|
64
|
-
if (!response.ok || !data.success) {
|
|
65
|
-
const err = new Error(data.error || `HTTP ${response.status}`);
|
|
66
|
-
setError(err);
|
|
67
|
-
onError == null ? void 0 : onError(err);
|
|
68
|
-
setLinks([]);
|
|
69
|
-
setVisitorDetails({});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (data.data) {
|
|
73
|
-
setLinks(data.data.links);
|
|
74
|
-
setVisitorDetails(data.data.visitorDetails);
|
|
75
|
-
}
|
|
76
|
-
} catch (err) {
|
|
77
|
-
const error2 = err instanceof Error ? err : new Error("Unknown error");
|
|
78
|
-
setError(error2);
|
|
79
|
-
onError == null ? void 0 : onError(error2);
|
|
80
|
-
setLinks([]);
|
|
81
|
-
setVisitorDetails({});
|
|
82
|
-
} finally {
|
|
83
|
-
setLoading(false);
|
|
84
|
-
}
|
|
85
|
-
}, [apiUrl, vaultId, visitorId, onError]);
|
|
86
|
-
(0, import_react.useEffect)(() => {
|
|
87
|
-
fetchData();
|
|
88
|
-
}, [fetchData]);
|
|
89
|
-
return {
|
|
90
|
-
links,
|
|
91
|
-
visitorDetails,
|
|
92
|
-
loading,
|
|
93
|
-
error,
|
|
94
|
-
refetch: fetchData
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
36
|
|
|
98
37
|
// src/utils.tsx
|
|
99
38
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -202,27 +141,33 @@ function renderNodeShape(nodeTypes, type, x, y, size, isHovered, isSelected) {
|
|
|
202
141
|
}
|
|
203
142
|
}
|
|
204
143
|
|
|
205
|
-
// src/
|
|
144
|
+
// src/IdentityMeshRenderer.tsx
|
|
206
145
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
207
|
-
function
|
|
208
|
-
links,
|
|
209
|
-
visitorDetails,
|
|
210
|
-
selectedVisitor,
|
|
211
|
-
onSelectVisitor,
|
|
212
|
-
nodeTypes,
|
|
213
|
-
height,
|
|
214
|
-
showLegend,
|
|
215
|
-
showIdentityCount
|
|
146
|
+
function IdentityMeshRenderer({
|
|
147
|
+
links = [],
|
|
148
|
+
visitorDetails = {},
|
|
149
|
+
selectedVisitor: externalSelectedVisitor,
|
|
150
|
+
onSelectVisitor: externalOnSelectVisitor,
|
|
151
|
+
nodeTypes: customNodeTypes,
|
|
152
|
+
height = 600,
|
|
153
|
+
showLegend = true,
|
|
154
|
+
showIdentityCount = true
|
|
216
155
|
}) {
|
|
217
|
-
const [hoveredNode, setHoveredNode] = (0,
|
|
218
|
-
const [animationPhase, setAnimationPhase] = (0,
|
|
219
|
-
(0,
|
|
156
|
+
const [hoveredNode, setHoveredNode] = (0, import_react.useState)(null);
|
|
157
|
+
const [animationPhase, setAnimationPhase] = (0, import_react.useState)(0);
|
|
158
|
+
const [internalSelectedVisitor, setInternalSelectedVisitor] = (0, import_react.useState)(null);
|
|
159
|
+
const selectedVisitor = externalSelectedVisitor != null ? externalSelectedVisitor : internalSelectedVisitor;
|
|
160
|
+
const onSelectVisitor = externalOnSelectVisitor != null ? externalOnSelectVisitor : setInternalSelectedVisitor;
|
|
161
|
+
const nodeTypes = (0, import_react.useMemo)(() => {
|
|
162
|
+
return { ...defaultNodeTypes, ...customNodeTypes };
|
|
163
|
+
}, [customNodeTypes]);
|
|
164
|
+
(0, import_react.useEffect)(() => {
|
|
220
165
|
const interval = setInterval(() => {
|
|
221
166
|
setAnimationPhase((p) => (p + 1) % 360);
|
|
222
167
|
}, 50);
|
|
223
168
|
return () => clearInterval(interval);
|
|
224
169
|
}, []);
|
|
225
|
-
const visitorGroups = (0,
|
|
170
|
+
const visitorGroups = (0, import_react.useMemo)(() => {
|
|
226
171
|
const groups = {};
|
|
227
172
|
links.forEach((link) => {
|
|
228
173
|
if (!groups[link.visitorId]) groups[link.visitorId] = [];
|
|
@@ -230,7 +175,7 @@ function MeshRenderer({
|
|
|
230
175
|
});
|
|
231
176
|
return groups;
|
|
232
177
|
}, [links]);
|
|
233
|
-
const identityLinks = (0,
|
|
178
|
+
const identityLinks = (0, import_react.useMemo)(() => {
|
|
234
179
|
const externalIdToVisitors = {};
|
|
235
180
|
links.forEach((link) => {
|
|
236
181
|
if (!externalIdToVisitors[link.externalId]) {
|
|
@@ -259,7 +204,7 @@ function MeshRenderer({
|
|
|
259
204
|
return pairs;
|
|
260
205
|
}, [links]);
|
|
261
206
|
const visitors = Object.keys(visitorGroups);
|
|
262
|
-
const visitorPositions = (0,
|
|
207
|
+
const visitorPositions = (0, import_react.useMemo)(() => {
|
|
263
208
|
const centerX = 400;
|
|
264
209
|
const centerY = 300;
|
|
265
210
|
const radius = Math.min(200, 80 + visitors.length * 25);
|
|
@@ -272,7 +217,7 @@ function MeshRenderer({
|
|
|
272
217
|
return acc;
|
|
273
218
|
}, {});
|
|
274
219
|
}, [visitors]);
|
|
275
|
-
const linkPositions = (0,
|
|
220
|
+
const linkPositions = (0, import_react.useMemo)(() => {
|
|
276
221
|
const positions = {};
|
|
277
222
|
Object.entries(visitorGroups).forEach(([visitorId, visitorLinks]) => {
|
|
278
223
|
const visitorPos = visitorPositions[visitorId];
|
|
@@ -571,6 +516,441 @@ function MeshRenderer({
|
|
|
571
516
|
] })
|
|
572
517
|
] });
|
|
573
518
|
}
|
|
519
|
+
|
|
520
|
+
// src/IdentityMesh.tsx
|
|
521
|
+
var import_react3 = require("react");
|
|
522
|
+
|
|
523
|
+
// src/hooks/useMeshData.ts
|
|
524
|
+
var import_react2 = require("react");
|
|
525
|
+
function useMeshData({
|
|
526
|
+
apiUrl,
|
|
527
|
+
vaultId,
|
|
528
|
+
visitorId,
|
|
529
|
+
onError
|
|
530
|
+
}) {
|
|
531
|
+
const [links, setLinks] = (0, import_react2.useState)([]);
|
|
532
|
+
const [visitorDetails, setVisitorDetails] = (0, import_react2.useState)({});
|
|
533
|
+
const [loading, setLoading] = (0, import_react2.useState)(true);
|
|
534
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
535
|
+
const fetchData = (0, import_react2.useCallback)(async () => {
|
|
536
|
+
if (!apiUrl || !vaultId || !visitorId) {
|
|
537
|
+
setLoading(false);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
setLoading(true);
|
|
541
|
+
setError(null);
|
|
542
|
+
try {
|
|
543
|
+
const response = await fetch(apiUrl, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: {
|
|
546
|
+
"Content-Type": "application/json"
|
|
547
|
+
},
|
|
548
|
+
body: JSON.stringify({ vaultId, visitorId })
|
|
549
|
+
});
|
|
550
|
+
const data = await response.json();
|
|
551
|
+
if (!response.ok || !data.success) {
|
|
552
|
+
const err = new Error(data.error || `HTTP ${response.status}`);
|
|
553
|
+
setError(err);
|
|
554
|
+
onError == null ? void 0 : onError(err);
|
|
555
|
+
setLinks([]);
|
|
556
|
+
setVisitorDetails({});
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (data.data) {
|
|
560
|
+
setLinks(data.data.links);
|
|
561
|
+
setVisitorDetails(data.data.visitorDetails);
|
|
562
|
+
}
|
|
563
|
+
} catch (err) {
|
|
564
|
+
const error2 = err instanceof Error ? err : new Error("Unknown error");
|
|
565
|
+
setError(error2);
|
|
566
|
+
onError == null ? void 0 : onError(error2);
|
|
567
|
+
setLinks([]);
|
|
568
|
+
setVisitorDetails({});
|
|
569
|
+
} finally {
|
|
570
|
+
setLoading(false);
|
|
571
|
+
}
|
|
572
|
+
}, [apiUrl, vaultId, visitorId, onError]);
|
|
573
|
+
(0, import_react2.useEffect)(() => {
|
|
574
|
+
fetchData();
|
|
575
|
+
}, [fetchData]);
|
|
576
|
+
return {
|
|
577
|
+
links,
|
|
578
|
+
visitorDetails,
|
|
579
|
+
loading,
|
|
580
|
+
error,
|
|
581
|
+
refetch: fetchData
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/IdentityMesh.tsx
|
|
586
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
587
|
+
function MeshRenderer({
|
|
588
|
+
links,
|
|
589
|
+
visitorDetails,
|
|
590
|
+
selectedVisitor,
|
|
591
|
+
onSelectVisitor,
|
|
592
|
+
nodeTypes,
|
|
593
|
+
height,
|
|
594
|
+
showLegend,
|
|
595
|
+
showIdentityCount
|
|
596
|
+
}) {
|
|
597
|
+
const [hoveredNode, setHoveredNode] = (0, import_react3.useState)(null);
|
|
598
|
+
const [animationPhase, setAnimationPhase] = (0, import_react3.useState)(0);
|
|
599
|
+
(0, import_react3.useEffect)(() => {
|
|
600
|
+
const interval = setInterval(() => {
|
|
601
|
+
setAnimationPhase((p) => (p + 1) % 360);
|
|
602
|
+
}, 50);
|
|
603
|
+
return () => clearInterval(interval);
|
|
604
|
+
}, []);
|
|
605
|
+
const visitorGroups = (0, import_react3.useMemo)(() => {
|
|
606
|
+
const groups = {};
|
|
607
|
+
links.forEach((link) => {
|
|
608
|
+
if (!groups[link.visitorId]) groups[link.visitorId] = [];
|
|
609
|
+
groups[link.visitorId].push(link);
|
|
610
|
+
});
|
|
611
|
+
return groups;
|
|
612
|
+
}, [links]);
|
|
613
|
+
const identityLinks = (0, import_react3.useMemo)(() => {
|
|
614
|
+
const externalIdToVisitors = {};
|
|
615
|
+
links.forEach((link) => {
|
|
616
|
+
if (!externalIdToVisitors[link.externalId]) {
|
|
617
|
+
externalIdToVisitors[link.externalId] = /* @__PURE__ */ new Set();
|
|
618
|
+
}
|
|
619
|
+
externalIdToVisitors[link.externalId].add(link.visitorId);
|
|
620
|
+
});
|
|
621
|
+
const pairs = [];
|
|
622
|
+
const seenPairs = /* @__PURE__ */ new Set();
|
|
623
|
+
Object.entries(externalIdToVisitors).forEach(([externalId, visitors2]) => {
|
|
624
|
+
var _a;
|
|
625
|
+
if (visitors2.size > 1) {
|
|
626
|
+
const visitorList = Array.from(visitors2);
|
|
627
|
+
for (let i = 0; i < visitorList.length; i++) {
|
|
628
|
+
for (let j = i + 1; j < visitorList.length; j++) {
|
|
629
|
+
const pairKey = [visitorList[i], visitorList[j]].sort().join("-");
|
|
630
|
+
if (!seenPairs.has(pairKey)) {
|
|
631
|
+
seenPairs.add(pairKey);
|
|
632
|
+
const linkType = ((_a = links.find((l) => l.externalId === externalId)) == null ? void 0 : _a.externalType) || "unknown";
|
|
633
|
+
pairs.push({ v1: visitorList[i], v2: visitorList[j], sharedId: externalId, type: linkType });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
return pairs;
|
|
640
|
+
}, [links]);
|
|
641
|
+
const visitors = Object.keys(visitorGroups);
|
|
642
|
+
const visitorPositions = (0, import_react3.useMemo)(() => {
|
|
643
|
+
const centerX = 400;
|
|
644
|
+
const centerY = 300;
|
|
645
|
+
const radius = Math.min(200, 80 + visitors.length * 25);
|
|
646
|
+
return visitors.reduce((acc, visitor, i) => {
|
|
647
|
+
const angle = i / visitors.length * Math.PI * 2 - Math.PI / 2;
|
|
648
|
+
acc[visitor] = {
|
|
649
|
+
x: centerX + Math.cos(angle) * radius,
|
|
650
|
+
y: centerY + Math.sin(angle) * radius
|
|
651
|
+
};
|
|
652
|
+
return acc;
|
|
653
|
+
}, {});
|
|
654
|
+
}, [visitors]);
|
|
655
|
+
const linkPositions = (0, import_react3.useMemo)(() => {
|
|
656
|
+
const positions = {};
|
|
657
|
+
Object.entries(visitorGroups).forEach(([visitorId, visitorLinks]) => {
|
|
658
|
+
const visitorPos = visitorPositions[visitorId];
|
|
659
|
+
if (!visitorPos) return;
|
|
660
|
+
const linkRadius = 60;
|
|
661
|
+
visitorLinks.forEach((link, i) => {
|
|
662
|
+
const angle = i / visitorLinks.length * Math.PI * 2 - Math.PI / 2;
|
|
663
|
+
positions[link.id] = {
|
|
664
|
+
x: visitorPos.x + Math.cos(angle) * linkRadius,
|
|
665
|
+
y: visitorPos.y + Math.sin(angle) * linkRadius,
|
|
666
|
+
type: link.externalType
|
|
667
|
+
};
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
return positions;
|
|
671
|
+
}, [visitorGroups, visitorPositions]);
|
|
672
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", width: "100%", height, overflow: "hidden" }, children: [
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "100%", height: "100%", style: { position: "absolute", top: 0, left: 0, opacity: 0.1 }, children: [
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pattern", { id: "mesh-grid", width: "40", height: "40", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M 40 0 L 0 0 0 40", fill: "none", stroke: "currentColor", strokeWidth: "0.5" }) }) }),
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "100%", height: "100%", fill: "url(#mesh-grid)" })
|
|
676
|
+
] }),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "100%", height: "100%", viewBox: "0 0 800 600", style: { position: "relative" }, children: [
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("radialGradient", { id: "mesh-centerGlow", children: [
|
|
679
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0%", stopColor: "rgba(0, 240, 255, 0.2)" }),
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "100%", stopColor: "rgba(0, 240, 255, 0)" })
|
|
681
|
+
] }) }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "400", cy: "300", r: "280", fill: "url(#mesh-centerGlow)" }),
|
|
683
|
+
identityLinks.map((link, idx) => {
|
|
684
|
+
const p1 = visitorPositions[link.v1];
|
|
685
|
+
const p2 = visitorPositions[link.v2];
|
|
686
|
+
if (!p1 || !p2) return null;
|
|
687
|
+
const midX = (p1.x + p2.x) / 2;
|
|
688
|
+
const midY = (p1.y + p2.y) / 2;
|
|
689
|
+
const dx = p2.x - p1.x;
|
|
690
|
+
const dy = p2.y - p1.y;
|
|
691
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
692
|
+
const curvature = Math.min(50, dist * 0.2);
|
|
693
|
+
const perpX = -dy / dist * curvature;
|
|
694
|
+
const perpY = dx / dist * curvature;
|
|
695
|
+
const ctrlX = midX + perpX;
|
|
696
|
+
const ctrlY = midY + perpY;
|
|
697
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
699
|
+
"path",
|
|
700
|
+
{
|
|
701
|
+
d: `M ${p1.x} ${p1.y} Q ${ctrlX} ${ctrlY} ${p2.x} ${p2.y}`,
|
|
702
|
+
fill: "none",
|
|
703
|
+
stroke: "#ff6b6b",
|
|
704
|
+
strokeWidth: 8,
|
|
705
|
+
strokeOpacity: 0.2,
|
|
706
|
+
style: { filter: "blur(4px)" }
|
|
707
|
+
}
|
|
708
|
+
),
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
710
|
+
"path",
|
|
711
|
+
{
|
|
712
|
+
d: `M ${p1.x} ${p1.y} Q ${ctrlX} ${ctrlY} ${p2.x} ${p2.y}`,
|
|
713
|
+
fill: "none",
|
|
714
|
+
stroke: "#ff6b6b",
|
|
715
|
+
strokeWidth: 3,
|
|
716
|
+
strokeOpacity: 0.8,
|
|
717
|
+
strokeDasharray: "8 4",
|
|
718
|
+
style: { strokeDashoffset: -animationPhase * 0.5 }
|
|
719
|
+
}
|
|
720
|
+
),
|
|
721
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${ctrlX}, ${ctrlY})`, children: [
|
|
722
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: -45, y: -12, width: 90, height: 24, rx: 12, fill: "rgba(255, 107, 107, 0.9)" }),
|
|
723
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("text", { x: 0, y: 4, textAnchor: "middle", fill: "white", fontSize: 9, fontWeight: "bold", children: [
|
|
724
|
+
"SAME ",
|
|
725
|
+
link.type === "wallet" ? "WALLET" : "VAULT"
|
|
726
|
+
] })
|
|
727
|
+
] }),
|
|
728
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { r: "4", fill: "#ff6b6b", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("animateMotion", { dur: "3s", repeatCount: "indefinite", path: `M ${p1.x} ${p1.y} Q ${ctrlX} ${ctrlY} ${p2.x} ${p2.y}` }) }),
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { r: "4", fill: "#ffd93d", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("animateMotion", { dur: "3s", repeatCount: "indefinite", begin: "1.5s", path: `M ${p1.x} ${p1.y} Q ${ctrlX} ${ctrlY} ${p2.x} ${p2.y}` }) })
|
|
730
|
+
] }, `identity-${idx}`);
|
|
731
|
+
}),
|
|
732
|
+
visitors.map(
|
|
733
|
+
(v1, i) => visitors.slice(i + 1).map((v2) => {
|
|
734
|
+
const p1 = visitorPositions[v1];
|
|
735
|
+
const p2 = visitorPositions[v2];
|
|
736
|
+
if (!p1 || !p2) return null;
|
|
737
|
+
const distance = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
|
|
738
|
+
const opacity = Math.max(0, 0.08 - distance / 3e3);
|
|
739
|
+
if (identityLinks.some((l) => l.v1 === v1 && l.v2 === v2 || l.v1 === v2 && l.v2 === v1)) return null;
|
|
740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
741
|
+
"line",
|
|
742
|
+
{
|
|
743
|
+
x1: p1.x,
|
|
744
|
+
y1: p1.y,
|
|
745
|
+
x2: p2.x,
|
|
746
|
+
y2: p2.y,
|
|
747
|
+
stroke: "rgba(0, 240, 255, 0.15)",
|
|
748
|
+
strokeWidth: 1,
|
|
749
|
+
strokeDasharray: "2 4",
|
|
750
|
+
style: { opacity, strokeDashoffset: animationPhase % 6 }
|
|
751
|
+
},
|
|
752
|
+
`mesh-${v1}-${v2}`
|
|
753
|
+
);
|
|
754
|
+
})
|
|
755
|
+
),
|
|
756
|
+
links.map((link) => {
|
|
757
|
+
const visitorPos = visitorPositions[link.visitorId];
|
|
758
|
+
const linkPos = linkPositions[link.id];
|
|
759
|
+
if (!visitorPos || !linkPos) return null;
|
|
760
|
+
const config = nodeTypes[link.externalType] || nodeTypes.session || defaultNodeTypes.session;
|
|
761
|
+
const isVisitorSelected = selectedVisitor === link.visitorId;
|
|
762
|
+
const isLinkHovered = hoveredNode === link.id;
|
|
763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
|
|
764
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
765
|
+
"line",
|
|
766
|
+
{
|
|
767
|
+
x1: visitorPos.x,
|
|
768
|
+
y1: visitorPos.y,
|
|
769
|
+
x2: linkPos.x,
|
|
770
|
+
y2: linkPos.y,
|
|
771
|
+
stroke: config.color,
|
|
772
|
+
strokeWidth: isVisitorSelected || isLinkHovered ? 2 : 1,
|
|
773
|
+
strokeOpacity: isVisitorSelected ? 0.8 : 0.3,
|
|
774
|
+
strokeDasharray: link.confidence && link.confidence < 0.9 ? "4 2" : "none"
|
|
775
|
+
}
|
|
776
|
+
),
|
|
777
|
+
isVisitorSelected && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { r: "3", fill: config.color, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("animateMotion", { dur: "2s", repeatCount: "indefinite", path: `M${visitorPos.x},${visitorPos.y} L${linkPos.x},${linkPos.y}` }) })
|
|
778
|
+
] }, `connection-${link.id}`);
|
|
779
|
+
}),
|
|
780
|
+
visitors.map((visitorId) => {
|
|
781
|
+
var _a, _b, _c, _d;
|
|
782
|
+
const pos = visitorPositions[visitorId];
|
|
783
|
+
if (!pos) return null;
|
|
784
|
+
const isSelected = selectedVisitor === visitorId;
|
|
785
|
+
const isHovered = hoveredNode === `visitor-${visitorId}`;
|
|
786
|
+
const linkCount = ((_a = visitorGroups[visitorId]) == null ? void 0 : _a.length) || 0;
|
|
787
|
+
const details = visitorDetails[visitorId];
|
|
788
|
+
const hasIdentityLink = identityLinks.some((l) => l.v1 === visitorId || l.v2 === visitorId);
|
|
789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
790
|
+
"g",
|
|
791
|
+
{
|
|
792
|
+
style: { cursor: "pointer" },
|
|
793
|
+
onMouseEnter: () => setHoveredNode(`visitor-${visitorId}`),
|
|
794
|
+
onMouseLeave: () => setHoveredNode(null),
|
|
795
|
+
onClick: () => onSelectVisitor(isSelected ? null : visitorId),
|
|
796
|
+
children: [
|
|
797
|
+
hasIdentityLink && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
798
|
+
"circle",
|
|
799
|
+
{
|
|
800
|
+
cx: pos.x,
|
|
801
|
+
cy: pos.y,
|
|
802
|
+
r: isSelected ? 42 : isHovered ? 40 : 36,
|
|
803
|
+
fill: "none",
|
|
804
|
+
stroke: "#ff6b6b",
|
|
805
|
+
strokeWidth: 2,
|
|
806
|
+
strokeDasharray: "6 3",
|
|
807
|
+
style: {
|
|
808
|
+
transform: `rotate(${-animationPhase}deg)`,
|
|
809
|
+
transformOrigin: `${pos.x}px ${pos.y}px`,
|
|
810
|
+
filter: "drop-shadow(0 0 8px rgba(255, 107, 107, 0.6))"
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
),
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
815
|
+
"circle",
|
|
816
|
+
{
|
|
817
|
+
cx: pos.x,
|
|
818
|
+
cy: pos.y,
|
|
819
|
+
r: isSelected ? 35 : isHovered ? 32 : 28,
|
|
820
|
+
fill: "none",
|
|
821
|
+
stroke: "rgba(0, 240, 255, 0.3)",
|
|
822
|
+
strokeWidth: 2,
|
|
823
|
+
strokeDasharray: "4 2",
|
|
824
|
+
style: {
|
|
825
|
+
transform: `rotate(${animationPhase}deg)`,
|
|
826
|
+
transformOrigin: `${pos.x}px ${pos.y}px`
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
),
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
831
|
+
"circle",
|
|
832
|
+
{
|
|
833
|
+
cx: pos.x,
|
|
834
|
+
cy: pos.y,
|
|
835
|
+
r: isSelected ? 24 : isHovered ? 22 : 18,
|
|
836
|
+
fill: "rgba(0, 20, 30, 0.95)",
|
|
837
|
+
stroke: "#00f0ff",
|
|
838
|
+
strokeWidth: isSelected ? 3 : 2,
|
|
839
|
+
style: {
|
|
840
|
+
filter: `drop-shadow(0 0 ${isSelected ? 20 : isHovered ? 15 : 10}px rgba(0, 240, 255, 0.6))`,
|
|
841
|
+
transition: "all 0.3s ease"
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
),
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: pos.x, y: pos.y + 5, textAnchor: "middle", fill: "#00f0ff", fontSize: isSelected ? 16 : 14, fontWeight: "bold", children: details ? getDeviceIcon((_b = details.fingerprint) == null ? void 0 : _b.platform, (_c = details.fingerprint) == null ? void 0 : _c.touchSupport) : linkCount }),
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: pos.x, y: pos.y + 48, textAnchor: "middle", fill: "rgba(255, 255, 255, 0.8)", fontSize: 10, fontFamily: "monospace", children: visitorId.length > 12 ? visitorId.slice(0, 12) + "..." : visitorId }),
|
|
847
|
+
((_d = details == null ? void 0 : details.location) == null ? void 0 : _d.country) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("text", { x: pos.x, y: pos.y + 60, textAnchor: "middle", fill: "rgba(255, 255, 255, 0.5)", fontSize: 9, children: [
|
|
848
|
+
getCountryFlag(details.location.country),
|
|
849
|
+
" ",
|
|
850
|
+
details.location.city || details.location.region
|
|
851
|
+
] })
|
|
852
|
+
]
|
|
853
|
+
},
|
|
854
|
+
`visitor-${visitorId}`
|
|
855
|
+
);
|
|
856
|
+
}),
|
|
857
|
+
links.map((link) => {
|
|
858
|
+
const pos = linkPositions[link.id];
|
|
859
|
+
if (!pos) return null;
|
|
860
|
+
const isHovered = hoveredNode === link.id;
|
|
861
|
+
const isVisitorSelected = selectedVisitor === link.visitorId;
|
|
862
|
+
const config = nodeTypes[link.externalType] || nodeTypes.session || defaultNodeTypes.session;
|
|
863
|
+
const isSharedId = identityLinks.some((l) => l.sharedId === link.externalId);
|
|
864
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
865
|
+
"g",
|
|
866
|
+
{
|
|
867
|
+
style: { cursor: "pointer", opacity: selectedVisitor && !isVisitorSelected ? 0.3 : 1 },
|
|
868
|
+
onMouseEnter: () => setHoveredNode(link.id),
|
|
869
|
+
onMouseLeave: () => setHoveredNode(null),
|
|
870
|
+
children: [
|
|
871
|
+
isSharedId && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
872
|
+
"circle",
|
|
873
|
+
{
|
|
874
|
+
cx: pos.x,
|
|
875
|
+
cy: pos.y,
|
|
876
|
+
r: 22,
|
|
877
|
+
fill: "none",
|
|
878
|
+
stroke: "#ff6b6b",
|
|
879
|
+
strokeWidth: 2,
|
|
880
|
+
strokeOpacity: 0.6,
|
|
881
|
+
strokeDasharray: "3 2"
|
|
882
|
+
}
|
|
883
|
+
),
|
|
884
|
+
renderNodeShape(nodeTypes, link.externalType, pos.x, pos.y, 14, isHovered, isVisitorSelected),
|
|
885
|
+
link.confidence && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
886
|
+
"circle",
|
|
887
|
+
{
|
|
888
|
+
cx: pos.x,
|
|
889
|
+
cy: pos.y,
|
|
890
|
+
r: 18,
|
|
891
|
+
fill: "none",
|
|
892
|
+
stroke: config.color,
|
|
893
|
+
strokeWidth: 2,
|
|
894
|
+
strokeOpacity: 0.3,
|
|
895
|
+
strokeDasharray: `${link.confidence * 113} 113`,
|
|
896
|
+
strokeLinecap: "round",
|
|
897
|
+
transform: `rotate(-90, ${pos.x}, ${pos.y})`
|
|
898
|
+
}
|
|
899
|
+
),
|
|
900
|
+
isHovered && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
|
|
901
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: pos.x - 70, y: pos.y - 60, width: 140, height: 48, rx: 6, fill: "rgba(0, 10, 20, 0.95)", stroke: config.color, strokeWidth: 1 }),
|
|
902
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("text", { x: pos.x, y: pos.y - 42, textAnchor: "middle", fill: config.color, fontSize: 10, fontWeight: "bold", children: [
|
|
903
|
+
isSharedId ? "\u{1F517} " : "",
|
|
904
|
+
config.label
|
|
905
|
+
] }),
|
|
906
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: pos.x, y: pos.y - 28, textAnchor: "middle", fill: "rgba(255, 255, 255, 0.7)", fontSize: 9, fontFamily: "monospace", children: link.externalId.length > 18 ? link.externalId.slice(0, 18) + "..." : link.externalId })
|
|
907
|
+
] })
|
|
908
|
+
]
|
|
909
|
+
},
|
|
910
|
+
`node-${link.id}`
|
|
911
|
+
);
|
|
912
|
+
})
|
|
913
|
+
] }),
|
|
914
|
+
showLegend && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
915
|
+
position: "absolute",
|
|
916
|
+
bottom: 20,
|
|
917
|
+
left: 20,
|
|
918
|
+
display: "flex",
|
|
919
|
+
gap: 16,
|
|
920
|
+
flexWrap: "wrap",
|
|
921
|
+
padding: "12px 16px",
|
|
922
|
+
background: "rgba(0, 10, 20, 0.9)",
|
|
923
|
+
borderRadius: 8,
|
|
924
|
+
border: "1px solid rgba(0, 240, 255, 0.2)"
|
|
925
|
+
}, children: [
|
|
926
|
+
Object.entries(nodeTypes).filter(([key]) => !["visitor", "device", "identity_link"].includes(key)).map(([key, config]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
927
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: 16, height: 16, viewBox: "0 0 24 24", children: [
|
|
928
|
+
config.shape === "hexagon" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "12,2 22,7 22,17 12,22 2,17 2,7", fill: config.color, fillOpacity: 0.3, stroke: config.color, strokeWidth: 2 }),
|
|
929
|
+
config.shape === "diamond" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "12,2 22,12 12,22 2,12", fill: config.color, fillOpacity: 0.3, stroke: config.color, strokeWidth: 2 }),
|
|
930
|
+
config.shape === "circle" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 12, cy: 12, r: 9, fill: config.color, fillOpacity: 0.3, stroke: config.color, strokeWidth: 2 }),
|
|
931
|
+
config.shape === "triangle" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "12,2 22,20 2,20", fill: config.color, fillOpacity: 0.3, stroke: config.color, strokeWidth: 2 })
|
|
932
|
+
] }),
|
|
933
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 10, color: "rgba(255, 255, 255, 0.7)" }, children: config.label })
|
|
934
|
+
] }, key)),
|
|
935
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
936
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { width: 20, height: 3, background: "#ff6b6b", borderRadius: 2 } }),
|
|
937
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 10, color: "rgba(255, 255, 255, 0.7)" }, children: "Same Identity" })
|
|
938
|
+
] })
|
|
939
|
+
] }),
|
|
940
|
+
showIdentityCount && identityLinks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
|
|
941
|
+
position: "absolute",
|
|
942
|
+
top: 20,
|
|
943
|
+
left: 20,
|
|
944
|
+
padding: "10px 14px",
|
|
945
|
+
background: "rgba(255, 107, 107, 0.15)",
|
|
946
|
+
borderRadius: 8,
|
|
947
|
+
border: "1px solid rgba(255, 107, 107, 0.4)"
|
|
948
|
+
}, children: [
|
|
949
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 10, color: "rgba(255, 255, 255, 0.6)", marginBottom: 4 }, children: "Cross-Device Identities" }),
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 20, fontWeight: "bold", color: "#ff6b6b" }, children: identityLinks.length })
|
|
951
|
+
] })
|
|
952
|
+
] });
|
|
953
|
+
}
|
|
574
954
|
function IdentityMesh({
|
|
575
955
|
apiUrl,
|
|
576
956
|
vaultId,
|
|
@@ -584,14 +964,14 @@ function IdentityMesh({
|
|
|
584
964
|
showLegend = true,
|
|
585
965
|
showIdentityCount = true
|
|
586
966
|
}) {
|
|
587
|
-
const [selectedVisitor, setSelectedVisitor] = (0,
|
|
967
|
+
const [selectedVisitor, setSelectedVisitor] = (0, import_react3.useState)(null);
|
|
588
968
|
const { links, visitorDetails, loading, error } = useMeshData({
|
|
589
969
|
apiUrl,
|
|
590
970
|
vaultId,
|
|
591
971
|
visitorId,
|
|
592
972
|
onError
|
|
593
973
|
});
|
|
594
|
-
const nodeTypes = (0,
|
|
974
|
+
const nodeTypes = (0, import_react3.useMemo)(() => {
|
|
595
975
|
const merged = { ...defaultNodeTypes };
|
|
596
976
|
if (theme == null ? void 0 : theme.nodeColors) {
|
|
597
977
|
Object.entries(theme.nodeColors).forEach(([key, value]) => {
|
|
@@ -612,8 +992,8 @@ function IdentityMesh({
|
|
|
612
992
|
...style
|
|
613
993
|
};
|
|
614
994
|
if (loading) {
|
|
615
|
-
return /* @__PURE__ */ (0,
|
|
616
|
-
/* @__PURE__ */ (0,
|
|
995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: { ...containerStyle, height, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center" }, children: [
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
|
|
617
997
|
width: 40,
|
|
618
998
|
height: 40,
|
|
619
999
|
border: "3px solid rgba(0, 240, 255, 0.3)",
|
|
@@ -622,24 +1002,24 @@ function IdentityMesh({
|
|
|
622
1002
|
animation: "spin 1s linear infinite",
|
|
623
1003
|
margin: "0 auto 12px"
|
|
624
1004
|
} }),
|
|
625
|
-
/* @__PURE__ */ (0,
|
|
626
|
-
/* @__PURE__ */ (0,
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }),
|
|
1006
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "rgba(255, 255, 255, 0.6)", fontSize: 14 }, children: "Loading mesh data..." })
|
|
627
1007
|
] }) });
|
|
628
1008
|
}
|
|
629
1009
|
if (error) {
|
|
630
|
-
return /* @__PURE__ */ (0,
|
|
631
|
-
/* @__PURE__ */ (0,
|
|
632
|
-
/* @__PURE__ */ (0,
|
|
633
|
-
/* @__PURE__ */ (0,
|
|
1010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: { ...containerStyle, height, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center", padding: 24 }, children: [
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 32, marginBottom: 12 }, children: "\u26A0\uFE0F" }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#ff6b6b", fontSize: 14, marginBottom: 8 }, children: "Failed to load mesh" }),
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "rgba(255, 255, 255, 0.5)", fontSize: 12 }, children: error.message })
|
|
634
1014
|
] }) });
|
|
635
1015
|
}
|
|
636
1016
|
if (links.length === 0) {
|
|
637
|
-
return /* @__PURE__ */ (0,
|
|
638
|
-
/* @__PURE__ */ (0,
|
|
639
|
-
/* @__PURE__ */ (0,
|
|
1017
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: { ...containerStyle, height, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center", padding: 24 }, children: [
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { fontSize: 32, marginBottom: 12 }, children: "\u{1F517}" }),
|
|
1019
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "rgba(255, 255, 255, 0.6)", fontSize: 14 }, children: "No connections found" })
|
|
640
1020
|
] }) });
|
|
641
1021
|
}
|
|
642
|
-
return /* @__PURE__ */ (0,
|
|
1022
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
643
1023
|
MeshRenderer,
|
|
644
1024
|
{
|
|
645
1025
|
links,
|
|
@@ -656,6 +1036,7 @@ function IdentityMesh({
|
|
|
656
1036
|
// Annotate the CommonJS export names for ESM import in node:
|
|
657
1037
|
0 && (module.exports = {
|
|
658
1038
|
IdentityMesh,
|
|
1039
|
+
IdentityMeshRenderer,
|
|
659
1040
|
defaultNodeTypes,
|
|
660
1041
|
getBrowserIcon,
|
|
661
1042
|
getCountryFlag,
|