@cyvest/cyvest-vis 5.4.1 → 6.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/README.md +57 -50
- package/dist/index.cjs +537 -637
- package/dist/index.d.cts +49 -60
- package/dist/index.d.ts +49 -60
- package/dist/index.js +541 -636
- package/dist/styles.css +29 -86
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -31,30 +31,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CyvestGraph: () => CyvestGraph,
|
|
34
|
-
CyvestInvestigationView: () => CyvestInvestigationView,
|
|
35
34
|
CyvestObservablesView: () => CyvestObservablesView,
|
|
35
|
+
DARK_CYVEST_THEME: () => DARK_CYVEST_THEME,
|
|
36
36
|
DEFAULT_CYVEST_THEME: () => DEFAULT_CYVEST_THEME,
|
|
37
|
-
INVESTIGATION_ICON_NAME_MAP: () => INVESTIGATION_ICON_NAME_MAP,
|
|
38
37
|
OBSERVABLE_ICON_NAME_MAP: () => OBSERVABLE_ICON_NAME_MAP,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
computeForcePositions: () => computeForcePositions,
|
|
39
|
+
createForceLayout: () => createForceLayout,
|
|
40
|
+
getDefaultForceOptions: () => getDefaultForceOptions,
|
|
42
41
|
getLevelBackgroundColor: () => getLevelBackgroundColor,
|
|
43
42
|
getLevelColor: () => getLevelColor,
|
|
44
43
|
getObservableIconSvg: () => getObservableIconSvg,
|
|
45
44
|
lightenHexColor: () => lightenHexColor,
|
|
45
|
+
mixHexColor: () => mixHexColor,
|
|
46
|
+
startForceSimulation: () => startForceSimulation,
|
|
46
47
|
truncateLabel: () => truncateLabel
|
|
47
48
|
});
|
|
48
49
|
module.exports = __toCommonJS(index_exports);
|
|
49
50
|
|
|
50
|
-
// src/components/
|
|
51
|
-
var import_react4 = require("react");
|
|
52
|
-
|
|
53
|
-
// src/components/CyvestInvestigationView.tsx
|
|
51
|
+
// src/components/CyvestObservablesView.tsx
|
|
54
52
|
var import_react2 = require("react");
|
|
55
53
|
|
|
56
|
-
// src/adapters/
|
|
57
|
-
var
|
|
54
|
+
// src/adapters/observablesElements.ts
|
|
55
|
+
var import_cyvest_js = require("@cyvest/cyvest-js");
|
|
58
56
|
|
|
59
57
|
// src/icons/svg.ts
|
|
60
58
|
var ICON_PATHS = {
|
|
@@ -66,9 +64,7 @@ var ICON_PATHS = {
|
|
|
66
64
|
hash: '<line x1="4" y1="9" x2="20" y2="9"/><line x1="4" y1="15" x2="20" y2="15"/><line x1="10" y1="3" x2="8" y2="21"/><line x1="16" y1="3" x2="14" y2="21"/>',
|
|
67
65
|
flask: '<path d="M10 2v7.5a2 2 0 0 1-.2.9L4.7 20.5a1 1 0 0 0 .9 1.5h12.8a1 1 0 0 0 .9-1.5l-5.1-10.1a2 2 0 0 1-.2-.9V2"/><path d="M8.5 2h7"/><path d="M7 16h10"/>',
|
|
68
66
|
question: '<circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3"/><path d="M12 17h.01"/>',
|
|
69
|
-
|
|
70
|
-
check: '<rect x="7" y="3" width="10" height="4" rx="1.5"/><rect x="5" y="5" width="14" height="16" rx="2"/><path d="m9 14 2.5 2.5L16 12"/>',
|
|
71
|
-
tag: '<path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0L3.4 13.4a2 2 0 0 1 0-2.8l7.2-7.2a2 2 0 0 1 2.8 0l7.2 7.2a2 2 0 0 1 0 2.8z"/><circle cx="9" cy="9" r="1.2"/>'
|
|
67
|
+
finding: '<rect x="7" y="3" width="10" height="4" rx="1.5"/><rect x="5" y="5" width="14" height="16" rx="2"/><path d="m9 14 2.5 2.5L16 12"/>'
|
|
72
68
|
};
|
|
73
69
|
var OBSERVABLE_ICON_NAME_MAP = {
|
|
74
70
|
ipv4: "globe",
|
|
@@ -78,17 +74,18 @@ var OBSERVABLE_ICON_NAME_MAP = {
|
|
|
78
74
|
email: "mail",
|
|
79
75
|
hash: "hash",
|
|
80
76
|
file: "file",
|
|
81
|
-
artifact: "flask"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
artifact: "flask",
|
|
78
|
+
host: "domain",
|
|
79
|
+
process: "finding",
|
|
80
|
+
user: "mail",
|
|
81
|
+
command_line: "link",
|
|
82
|
+
cloud_resource: "globe"
|
|
87
83
|
};
|
|
88
84
|
function toSvgDataUri(iconName, color) {
|
|
89
85
|
const body = ICON_PATHS[iconName] ?? ICON_PATHS.question;
|
|
90
86
|
const svg = [
|
|
91
|
-
'<svg xmlns="http://www.w3.org/2000/svg"
|
|
87
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"',
|
|
88
|
+
' viewBox="0 0 24 24" fill="none"',
|
|
92
89
|
` stroke="${color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">`,
|
|
93
90
|
body,
|
|
94
91
|
"</svg>"
|
|
@@ -100,48 +97,93 @@ function getObservableIconSvg(observableType, options) {
|
|
|
100
97
|
const icon = OBSERVABLE_ICON_NAME_MAP[normalizedType] ?? "question";
|
|
101
98
|
return toSvgDataUri(icon, options?.color ?? "#314264");
|
|
102
99
|
}
|
|
103
|
-
function getInvestigationIconSvg(nodeType, options) {
|
|
104
|
-
const icon = INVESTIGATION_ICON_NAME_MAP[nodeType] ?? "question";
|
|
105
|
-
return toSvgDataUri(icon, options?.color ?? "#314264");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/utils/colors.ts
|
|
109
|
-
var import_cyvest_js = require("@cyvest/cyvest-js");
|
|
110
100
|
|
|
111
101
|
// src/types.ts
|
|
112
102
|
var DEFAULT_CYVEST_THEME = {
|
|
113
|
-
background: "#
|
|
114
|
-
gridColor: "#
|
|
115
|
-
panelBackground: "rgba(255, 255, 255, 0.
|
|
116
|
-
panelBorder: "#
|
|
117
|
-
panelText: "#
|
|
118
|
-
panelTextMuted: "#
|
|
119
|
-
accent: "#
|
|
120
|
-
edgeColor: "#
|
|
121
|
-
edgeSelectedColor: "#
|
|
122
|
-
fontFamily: "
|
|
103
|
+
background: "#f8fafc",
|
|
104
|
+
gridColor: "#e2e8f0",
|
|
105
|
+
panelBackground: "rgba(255, 255, 255, 0.97)",
|
|
106
|
+
panelBorder: "#e2e8f0",
|
|
107
|
+
panelText: "#0f172a",
|
|
108
|
+
panelTextMuted: "#64748b",
|
|
109
|
+
accent: "#334155",
|
|
110
|
+
edgeColor: "#cbd5e1",
|
|
111
|
+
edgeSelectedColor: "#475569",
|
|
112
|
+
fontFamily: "IBM Plex Sans, Segoe UI, Helvetica Neue, Arial, sans-serif",
|
|
113
|
+
nodeSurface: "#ffffff",
|
|
114
|
+
rootSurface: "#1e293b",
|
|
115
|
+
rootText: "#ffffff",
|
|
116
|
+
iconColor: "#314264",
|
|
117
|
+
iconMutedColor: "#475569",
|
|
118
|
+
levelSurfaceMix: "#ffffff",
|
|
119
|
+
levelSurfaceMixRatio: 0.94
|
|
120
|
+
};
|
|
121
|
+
var DARK_CYVEST_THEME = {
|
|
122
|
+
background: "#0f172a",
|
|
123
|
+
gridColor: "#1e293b",
|
|
124
|
+
panelBackground: "rgba(15, 23, 42, 0.97)",
|
|
125
|
+
panelBorder: "#334155",
|
|
126
|
+
panelText: "#e2e8f0",
|
|
127
|
+
panelTextMuted: "#94a3b8",
|
|
128
|
+
accent: "#cbd5e1",
|
|
129
|
+
edgeColor: "#475569",
|
|
130
|
+
edgeSelectedColor: "#cbd5e1",
|
|
131
|
+
fontFamily: "IBM Plex Sans, Segoe UI, Helvetica Neue, Arial, sans-serif",
|
|
132
|
+
nodeSurface: "#1e293b",
|
|
133
|
+
rootSurface: "#020617",
|
|
134
|
+
rootText: "#f8fafc",
|
|
135
|
+
iconColor: "#cbd5e1",
|
|
136
|
+
iconMutedColor: "#cbd5e1",
|
|
137
|
+
levelSurfaceMix: "#0f172a",
|
|
138
|
+
levelSurfaceMixRatio: 0.7
|
|
123
139
|
};
|
|
124
140
|
|
|
125
141
|
// src/utils/colors.ts
|
|
126
142
|
function getLevelColor(level) {
|
|
127
|
-
|
|
143
|
+
const colors = {
|
|
144
|
+
NONE: "#cbd5e1",
|
|
145
|
+
TRUSTED: "#94a3b8",
|
|
146
|
+
INFO: "#94a3b8",
|
|
147
|
+
SAFE: "#648b79",
|
|
148
|
+
NOTABLE: "#aa8958",
|
|
149
|
+
SUSPICIOUS: "#ad704b",
|
|
150
|
+
MALICIOUS: "#ad5555"
|
|
151
|
+
};
|
|
152
|
+
return colors[level] ?? colors.INFO;
|
|
128
153
|
}
|
|
129
154
|
function clampChannel(channel) {
|
|
130
155
|
return Math.max(0, Math.min(255, Math.round(channel)));
|
|
131
156
|
}
|
|
132
|
-
function
|
|
157
|
+
function parseHex(hex) {
|
|
133
158
|
const normalized = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
134
159
|
if (!/^[0-9a-fA-F]{6}$/.test(normalized)) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
return [
|
|
163
|
+
Number.parseInt(normalized.slice(0, 2), 16),
|
|
164
|
+
Number.parseInt(normalized.slice(2, 4), 16),
|
|
165
|
+
Number.parseInt(normalized.slice(4, 6), 16)
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
function mixHexColor(hex, target, ratio) {
|
|
169
|
+
const from = parseHex(hex);
|
|
170
|
+
const to = parseHex(target);
|
|
171
|
+
if (!from || !to) {
|
|
135
172
|
return hex;
|
|
136
173
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
const blue = Number.parseInt(normalized.slice(4, 6), 16);
|
|
140
|
-
const mix = (channel) => clampChannel(channel + (255 - channel) * ratio).toString(16).padStart(2, "0");
|
|
141
|
-
return `#${mix(red)}${mix(green)}${mix(blue)}`;
|
|
174
|
+
const mix = (a, b) => clampChannel(a + (b - a) * ratio).toString(16).padStart(2, "0");
|
|
175
|
+
return `#${mix(from[0], to[0])}${mix(from[1], to[1])}${mix(from[2], to[2])}`;
|
|
142
176
|
}
|
|
143
|
-
function
|
|
144
|
-
return
|
|
177
|
+
function lightenHexColor(hex, ratio) {
|
|
178
|
+
return mixHexColor(hex, "#ffffff", ratio);
|
|
179
|
+
}
|
|
180
|
+
function getLevelBackgroundColor(level, theme) {
|
|
181
|
+
const resolved = resolveTheme(theme);
|
|
182
|
+
return mixHexColor(
|
|
183
|
+
getLevelColor(level),
|
|
184
|
+
resolved.levelSurfaceMix,
|
|
185
|
+
resolved.levelSurfaceMixRatio
|
|
186
|
+
);
|
|
145
187
|
}
|
|
146
188
|
function resolveTheme(theme) {
|
|
147
189
|
return {
|
|
@@ -163,206 +205,106 @@ function truncateLabel(value, maxLength = 28, truncateMiddle = true) {
|
|
|
163
205
|
return `${value.slice(0, leftLength)}\u2026${value.slice(-rightLength)}`;
|
|
164
206
|
}
|
|
165
207
|
|
|
166
|
-
// src/adapters/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
labelFull: label,
|
|
185
|
-
level,
|
|
186
|
-
score,
|
|
187
|
-
borderColor,
|
|
188
|
-
fillColor: getLevelBackgroundColor(level),
|
|
189
|
-
icon: getInvestigationIconSvg(nodeType, { color: borderColor }),
|
|
190
|
-
width: dimensions.width,
|
|
191
|
-
height: dimensions.height,
|
|
192
|
-
shape: "round-rectangle",
|
|
193
|
-
borderWidth: 2
|
|
194
|
-
};
|
|
208
|
+
// src/adapters/observablesElements.ts
|
|
209
|
+
function findFallbackRootId(graph) {
|
|
210
|
+
if (graph.nodes.length === 0) {
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
214
|
+
for (const node of graph.nodes) {
|
|
215
|
+
incoming.set(node.id, 0);
|
|
216
|
+
}
|
|
217
|
+
for (const edge of graph.edges) {
|
|
218
|
+
incoming.set(edge.target, (incoming.get(edge.target) ?? 0) + 1);
|
|
219
|
+
}
|
|
220
|
+
const sourceCandidates = graph.nodes.filter((node) => (incoming.get(node.id) ?? 0) === 0);
|
|
221
|
+
if (sourceCandidates.length === 0) {
|
|
222
|
+
return graph.nodes[0]?.id;
|
|
223
|
+
}
|
|
224
|
+
sourceCandidates.sort((a, b) => b.score - a.score);
|
|
225
|
+
return sourceCandidates[0]?.id;
|
|
195
226
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
198
|
-
if (rootObservable) {
|
|
227
|
+
function getArrowShapes(edge) {
|
|
228
|
+
if (edge.direction === "bidirectional") {
|
|
199
229
|
return {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
score: rootObservable.score
|
|
230
|
+
sourceArrowShape: "triangle",
|
|
231
|
+
targetArrowShape: "triangle"
|
|
203
232
|
};
|
|
204
233
|
}
|
|
205
|
-
|
|
206
|
-
if (firstObservable) {
|
|
234
|
+
if (edge.direction === "inbound") {
|
|
207
235
|
return {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
score: firstObservable.score
|
|
236
|
+
sourceArrowShape: "triangle",
|
|
237
|
+
targetArrowShape: "none"
|
|
211
238
|
};
|
|
212
239
|
}
|
|
213
240
|
return {
|
|
214
|
-
value: investigation.investigation_name ?? investigation.investigation_id,
|
|
215
|
-
level: investigation.level,
|
|
216
|
-
score: investigation.score
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function getTagMap(tags) {
|
|
220
|
-
const map = /* @__PURE__ */ new Map();
|
|
221
|
-
for (const tag of Object.values(tags)) {
|
|
222
|
-
map.set(tag.name, tag);
|
|
223
|
-
}
|
|
224
|
-
return map;
|
|
225
|
-
}
|
|
226
|
-
function createEdge(id, source, target, relationshipType, edgeColor) {
|
|
227
|
-
const data = {
|
|
228
|
-
id,
|
|
229
|
-
relationshipType,
|
|
230
|
-
color: edgeColor,
|
|
231
|
-
width: 1.5,
|
|
232
241
|
sourceArrowShape: "none",
|
|
233
242
|
targetArrowShape: "triangle"
|
|
234
243
|
};
|
|
235
|
-
return {
|
|
236
|
-
group: "edges",
|
|
237
|
-
data: {
|
|
238
|
-
...data,
|
|
239
|
-
source,
|
|
240
|
-
target
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
244
|
}
|
|
244
|
-
function
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
check.score,
|
|
278
|
-
maxLabelLength
|
|
279
|
-
);
|
|
280
|
-
nodes.push({ group: "nodes", data: nodeData });
|
|
281
|
-
if (!checksInTags.has(check.key)) {
|
|
282
|
-
edges.push(
|
|
283
|
-
createEdge(
|
|
284
|
-
`inv-edge-root-check:${check.key}`,
|
|
285
|
-
ROOT_NODE_ID,
|
|
286
|
-
checkNodeId,
|
|
287
|
-
"contains-check",
|
|
288
|
-
edgeColor
|
|
289
|
-
)
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
const tagNames = /* @__PURE__ */ new Set();
|
|
294
|
-
for (const tag of allTags) {
|
|
295
|
-
tagNames.add(tag.name);
|
|
296
|
-
for (const ancestor of (0, import_cyvest_js2.getTagAncestors)(tag.name)) {
|
|
297
|
-
tagNames.add(ancestor);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
for (const tagName of tagNames) {
|
|
301
|
-
const tag = tagByName.get(tagName);
|
|
302
|
-
const tagLevel = tag?.direct_level ?? "INFO";
|
|
303
|
-
const tagScore = tag?.direct_score ?? 0;
|
|
304
|
-
const shortTagName = tagName.split(":").pop() ?? tagName;
|
|
305
|
-
nodes.push({
|
|
245
|
+
function buildObservablesElements(investigation, options) {
|
|
246
|
+
const graph = (0, import_cyvest_js.getObservableGraph)(investigation);
|
|
247
|
+
const rootObservable = (0, import_cyvest_js.getRootObservable)(investigation);
|
|
248
|
+
const rootId = rootObservable?.key ?? findFallbackRootId(graph);
|
|
249
|
+
const maxLabelLength = options?.maxLabelLength ?? 28;
|
|
250
|
+
const theme = resolveTheme(options?.theme);
|
|
251
|
+
const edgeColor = options?.edgeColor ?? theme.edgeColor;
|
|
252
|
+
const nodes = graph.nodes.map((node) => {
|
|
253
|
+
const isRoot = node.id === rootId;
|
|
254
|
+
const borderColor = getLevelColor(node.level);
|
|
255
|
+
const dimension = isRoot ? 52 : 38;
|
|
256
|
+
const iconColor = isRoot ? theme.rootText : theme.iconMutedColor;
|
|
257
|
+
const data = {
|
|
258
|
+
id: node.id,
|
|
259
|
+
nodeType: "observable",
|
|
260
|
+
labelShort: truncateLabel(node.value, maxLabelLength, true),
|
|
261
|
+
labelFull: node.value,
|
|
262
|
+
observableType: node.type,
|
|
263
|
+
level: node.level,
|
|
264
|
+
score: node.score,
|
|
265
|
+
isRoot,
|
|
266
|
+
whitelisted: node.whitelisted,
|
|
267
|
+
internal: node.internal,
|
|
268
|
+
shape: "ellipse",
|
|
269
|
+
width: dimension,
|
|
270
|
+
height: dimension,
|
|
271
|
+
borderWidth: isRoot ? 1 : 2,
|
|
272
|
+
borderColor: isRoot ? theme.rootSurface : borderColor,
|
|
273
|
+
fillColor: isRoot ? theme.rootSurface : node.internal ? theme.nodeSurface : getLevelBackgroundColor(node.level, theme),
|
|
274
|
+
icon: getObservableIconSvg(node.type, { color: iconColor }),
|
|
275
|
+
opacity: node.whitelisted ? 0.52 : 1
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
306
278
|
group: "nodes",
|
|
307
|
-
data
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
);
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
const parentName = parts.slice(0, -1).join(":");
|
|
332
|
-
edges.push(
|
|
333
|
-
createEdge(
|
|
334
|
-
`inv-edge-tag:${parentName}->${tagName}`,
|
|
335
|
-
`inv-tag:${parentName}`,
|
|
336
|
-
`inv-tag:${tagName}`,
|
|
337
|
-
"tag-hierarchy",
|
|
338
|
-
edgeColor
|
|
339
|
-
)
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
const checksByKey = new Map(
|
|
343
|
-
checks.map((check) => [check.key, check])
|
|
344
|
-
);
|
|
345
|
-
for (const tag of allTags) {
|
|
346
|
-
for (const checkKey of tag.checks) {
|
|
347
|
-
if (!checksByKey.has(checkKey)) {
|
|
348
|
-
continue;
|
|
279
|
+
data
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
const nodeIds = new Set(graph.nodes.map((node) => node.id));
|
|
283
|
+
const edges = graph.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)).map((edge, index) => {
|
|
284
|
+
const arrowShape = getArrowShapes(edge);
|
|
285
|
+
const data = {
|
|
286
|
+
id: `obs-edge-${index}-${edge.source}-${edge.target}-${edge.type}`,
|
|
287
|
+
relationshipType: edge.type,
|
|
288
|
+
direction: edge.direction,
|
|
289
|
+
color: edgeColor,
|
|
290
|
+
width: 1.15,
|
|
291
|
+
sourceArrowShape: arrowShape.sourceArrowShape,
|
|
292
|
+
targetArrowShape: arrowShape.targetArrowShape
|
|
293
|
+
};
|
|
294
|
+
return {
|
|
295
|
+
group: "edges",
|
|
296
|
+
data: {
|
|
297
|
+
...data,
|
|
298
|
+
source: edge.source,
|
|
299
|
+
target: edge.target
|
|
349
300
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
`inv-edge-tag-check:${tag.name}->${checkKey}`,
|
|
353
|
-
`inv-tag:${tag.name}`,
|
|
354
|
-
`inv-check:${checkKey}`,
|
|
355
|
-
"tag-check",
|
|
356
|
-
edgeColor
|
|
357
|
-
)
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
301
|
+
};
|
|
302
|
+
});
|
|
361
303
|
return [...nodes, ...edges];
|
|
362
304
|
}
|
|
363
305
|
|
|
364
306
|
// src/core/styles.ts
|
|
365
|
-
function
|
|
307
|
+
function createSharedStylesheet(theme) {
|
|
366
308
|
const resolved = resolveTheme(theme);
|
|
367
309
|
return [
|
|
368
310
|
{
|
|
@@ -373,34 +315,58 @@ function createObservablesStylesheet(theme) {
|
|
|
373
315
|
height: "data(height)",
|
|
374
316
|
label: "data(labelShort)",
|
|
375
317
|
color: resolved.panelText,
|
|
376
|
-
"font-size":
|
|
318
|
+
"font-size": 10.5,
|
|
377
319
|
"font-family": resolved.fontFamily,
|
|
378
320
|
"font-weight": 500,
|
|
321
|
+
"min-zoomed-font-size": 8,
|
|
379
322
|
"text-wrap": "none",
|
|
380
|
-
"text-max-width":
|
|
323
|
+
"text-max-width": 190,
|
|
381
324
|
"text-halign": "center",
|
|
382
325
|
"text-valign": "bottom",
|
|
383
|
-
"text-margin-y":
|
|
326
|
+
"text-margin-y": 9,
|
|
327
|
+
"text-background-color": resolved.panelBackground,
|
|
328
|
+
"text-background-opacity": 0.86,
|
|
329
|
+
"text-background-padding": 2,
|
|
330
|
+
"text-background-shape": "roundrectangle",
|
|
384
331
|
"background-color": "data(fillColor)",
|
|
385
332
|
"border-color": "data(borderColor)",
|
|
386
333
|
"border-width": "data(borderWidth)",
|
|
387
334
|
"background-image": "data(icon)",
|
|
388
335
|
"background-fit": "none",
|
|
389
|
-
"background-width": "
|
|
390
|
-
"background-height": "
|
|
336
|
+
"background-width": "48%",
|
|
337
|
+
"background-height": "48%",
|
|
391
338
|
"background-position-x": "50%",
|
|
392
339
|
"background-position-y": "50%",
|
|
393
340
|
"background-image-opacity": 1,
|
|
394
341
|
"background-opacity": 1,
|
|
395
342
|
opacity: "data(opacity)",
|
|
396
|
-
"overlay-opacity": 0
|
|
343
|
+
"overlay-opacity": 0,
|
|
344
|
+
"transition-property": "opacity, border-width, border-color, underlay-opacity, underlay-padding",
|
|
345
|
+
"transition-duration": 140
|
|
397
346
|
}
|
|
398
347
|
},
|
|
399
348
|
{
|
|
400
349
|
selector: "node:selected",
|
|
401
350
|
style: {
|
|
402
|
-
"border-color": resolved.
|
|
403
|
-
"border-width":
|
|
351
|
+
"border-color": resolved.edgeSelectedColor,
|
|
352
|
+
"border-width": 2.5,
|
|
353
|
+
"underlay-color": resolved.accent,
|
|
354
|
+
"underlay-opacity": 0.12,
|
|
355
|
+
"underlay-padding": 7
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
selector: "node.cyvest-focus",
|
|
360
|
+
style: {
|
|
361
|
+
"underlay-color": resolved.accent,
|
|
362
|
+
"underlay-opacity": 0.08,
|
|
363
|
+
"underlay-padding": 6
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
selector: ".cyvest-dimmed",
|
|
368
|
+
style: {
|
|
369
|
+
opacity: 0.16
|
|
404
370
|
}
|
|
405
371
|
},
|
|
406
372
|
{
|
|
@@ -413,169 +379,286 @@ function createObservablesStylesheet(theme) {
|
|
|
413
379
|
"target-arrow-shape": "data(targetArrowShape)",
|
|
414
380
|
"source-arrow-shape": "data(sourceArrowShape)",
|
|
415
381
|
"curve-style": "bezier",
|
|
416
|
-
"arrow-scale":
|
|
417
|
-
opacity: 0.
|
|
418
|
-
"overlay-opacity": 0
|
|
382
|
+
"arrow-scale": 0.62,
|
|
383
|
+
opacity: 0.78,
|
|
384
|
+
"overlay-opacity": 0,
|
|
385
|
+
"transition-property": "opacity, line-color, width",
|
|
386
|
+
"transition-duration": 140
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
selector: "edge.cyvest-focus",
|
|
391
|
+
style: {
|
|
392
|
+
width: 1.7,
|
|
393
|
+
"line-color": resolved.edgeSelectedColor,
|
|
394
|
+
"target-arrow-color": resolved.edgeSelectedColor,
|
|
395
|
+
"source-arrow-color": resolved.edgeSelectedColor,
|
|
396
|
+
opacity: 0.9
|
|
419
397
|
}
|
|
420
398
|
},
|
|
421
399
|
{
|
|
422
400
|
selector: "edge:selected",
|
|
423
401
|
style: {
|
|
402
|
+
width: 1.9,
|
|
424
403
|
"line-color": resolved.edgeSelectedColor,
|
|
425
404
|
"target-arrow-color": resolved.edgeSelectedColor,
|
|
426
405
|
"source-arrow-color": resolved.edgeSelectedColor,
|
|
427
406
|
label: "data(relationshipType)",
|
|
428
407
|
color: resolved.panelTextMuted,
|
|
429
|
-
"font-size":
|
|
408
|
+
"font-size": 9,
|
|
430
409
|
"font-family": resolved.fontFamily,
|
|
431
410
|
"text-background-color": resolved.panelBackground,
|
|
432
|
-
"text-background-opacity": 0.
|
|
411
|
+
"text-background-opacity": 0.94,
|
|
433
412
|
"text-background-padding": 2
|
|
434
413
|
}
|
|
435
414
|
}
|
|
436
415
|
];
|
|
437
416
|
}
|
|
438
|
-
function
|
|
439
|
-
const resolved = resolveTheme(theme);
|
|
417
|
+
function createObservablesStylesheet(theme) {
|
|
440
418
|
return [
|
|
419
|
+
...createSharedStylesheet(theme),
|
|
441
420
|
{
|
|
442
|
-
selector: "node",
|
|
421
|
+
selector: "node[?isRoot]",
|
|
443
422
|
style: {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
height: "data(height)",
|
|
447
|
-
label: "data(labelShort)",
|
|
448
|
-
color: resolved.panelText,
|
|
449
|
-
"font-size": 10,
|
|
450
|
-
"font-family": resolved.fontFamily,
|
|
451
|
-
"font-weight": 500,
|
|
452
|
-
"text-wrap": "none",
|
|
453
|
-
"text-max-width": 190,
|
|
454
|
-
"text-halign": "center",
|
|
455
|
-
"text-valign": "center",
|
|
456
|
-
"text-justification": "center",
|
|
457
|
-
"background-color": "data(fillColor)",
|
|
458
|
-
"border-color": "data(borderColor)",
|
|
459
|
-
"border-width": "data(borderWidth)",
|
|
460
|
-
"background-image": "data(icon)",
|
|
461
|
-
"background-fit": "none",
|
|
462
|
-
"background-width": "18px",
|
|
463
|
-
"background-height": "18px",
|
|
464
|
-
"background-position-x": "10px",
|
|
465
|
-
"background-position-y": "50%",
|
|
466
|
-
"background-image-opacity": 0.9,
|
|
467
|
-
"text-margin-x": 0,
|
|
468
|
-
"overlay-opacity": 0
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
{
|
|
472
|
-
selector: "node[nodeType = 'check']",
|
|
473
|
-
style: {
|
|
474
|
-
"text-halign": "center",
|
|
475
|
-
"background-position-x": "10px"
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
selector: "node:selected",
|
|
480
|
-
style: {
|
|
481
|
-
"border-color": resolved.accent,
|
|
482
|
-
"border-width": 3
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
selector: "edge",
|
|
487
|
-
style: {
|
|
488
|
-
width: "data(width)",
|
|
489
|
-
"line-color": "data(color)",
|
|
490
|
-
"target-arrow-color": "data(color)",
|
|
491
|
-
"source-arrow-color": "data(color)",
|
|
492
|
-
"target-arrow-shape": "data(targetArrowShape)",
|
|
493
|
-
"source-arrow-shape": "data(sourceArrowShape)",
|
|
494
|
-
"curve-style": "taxi",
|
|
495
|
-
"taxi-direction": "rightward",
|
|
496
|
-
"taxi-turn": "26px",
|
|
497
|
-
"arrow-scale": 0.95,
|
|
498
|
-
opacity: 0.9,
|
|
499
|
-
"overlay-opacity": 0
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
{
|
|
503
|
-
selector: "edge:selected",
|
|
504
|
-
style: {
|
|
505
|
-
"line-color": resolved.edgeSelectedColor,
|
|
506
|
-
"target-arrow-color": resolved.edgeSelectedColor,
|
|
507
|
-
"source-arrow-color": resolved.edgeSelectedColor
|
|
423
|
+
"font-weight": 700,
|
|
424
|
+
"text-margin-y": 11
|
|
508
425
|
}
|
|
509
426
|
}
|
|
510
427
|
];
|
|
511
428
|
}
|
|
512
429
|
|
|
513
|
-
// src/layout/
|
|
430
|
+
// src/layout/force.ts
|
|
431
|
+
var import_d3_force = require("d3-force");
|
|
514
432
|
var DEFAULT_OBSERVABLES_LAYOUT = {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
direction: "RIGHT",
|
|
525
|
-
spacingNodeNode: 50,
|
|
526
|
-
spacingEdgeNode: 30,
|
|
527
|
-
spacingBetweenLayers: 120,
|
|
528
|
-
padding: 56,
|
|
433
|
+
linkDistance: 118,
|
|
434
|
+
linkStrength: 0.72,
|
|
435
|
+
chargeStrength: -420,
|
|
436
|
+
collisionPadding: 30,
|
|
437
|
+
radialStep: 128,
|
|
438
|
+
radialStrength: 0.38,
|
|
439
|
+
centerStrength: 0.055,
|
|
440
|
+
iterations: 320,
|
|
441
|
+
padding: 72,
|
|
529
442
|
fit: true,
|
|
530
|
-
animate:
|
|
443
|
+
animate: true,
|
|
444
|
+
animationDuration: 420
|
|
531
445
|
};
|
|
532
|
-
function
|
|
533
|
-
return
|
|
446
|
+
function getDefaultForceOptions() {
|
|
447
|
+
return { ...DEFAULT_OBSERVABLES_LAYOUT };
|
|
534
448
|
}
|
|
535
|
-
function
|
|
449
|
+
function resolveOptions(overrides) {
|
|
536
450
|
return {
|
|
537
|
-
...
|
|
538
|
-
...overrides
|
|
539
|
-
extra: {
|
|
540
|
-
...getDefaultElkOptions(view).extra ?? {},
|
|
541
|
-
...overrides?.extra ?? {}
|
|
542
|
-
}
|
|
451
|
+
...getDefaultForceOptions(),
|
|
452
|
+
...overrides
|
|
543
453
|
};
|
|
544
454
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
547
|
-
|
|
455
|
+
function configureSimulation(nodes, links, options) {
|
|
456
|
+
return (0, import_d3_force.forceSimulation)(nodes).alpha(1).alphaDecay(1 - Math.pow(1e-3, 1 / options.iterations)).velocityDecay(0.34).force(
|
|
457
|
+
"link",
|
|
458
|
+
(0, import_d3_force.forceLink)(links).id((node) => node.id).distance((link) => {
|
|
459
|
+
const sourceDepth = typeof link.source === "string" ? 0 : link.source.depth;
|
|
460
|
+
const targetDepth = typeof link.target === "string" ? 0 : link.target.depth;
|
|
461
|
+
return options.linkDistance + Math.abs(sourceDepth - targetDepth) * 8;
|
|
462
|
+
}).strength(options.linkStrength)
|
|
463
|
+
).force("charge", (0, import_d3_force.forceManyBody)().strength(options.chargeStrength)).force(
|
|
464
|
+
"collision",
|
|
465
|
+
(0, import_d3_force.forceCollide)().radius((node) => node.radius + options.collisionPadding).strength(0.92).iterations(3)
|
|
466
|
+
).force(
|
|
467
|
+
"radial",
|
|
468
|
+
(0, import_d3_force.forceRadial)(
|
|
469
|
+
(node) => node.depth * options.radialStep,
|
|
470
|
+
0,
|
|
471
|
+
0
|
|
472
|
+
).strength((node) => node.isRoot ? 1 : options.radialStrength)
|
|
473
|
+
).force("center", (0, import_d3_force.forceCenter)(0, 0).strength(options.centerStrength)).force("x", (0, import_d3_force.forceX)(0).strength(options.centerStrength)).force("y", (0, import_d3_force.forceY)(0).strength(options.centerStrength));
|
|
474
|
+
}
|
|
475
|
+
function getNodeId(element) {
|
|
476
|
+
return String(element.data.id);
|
|
477
|
+
}
|
|
478
|
+
function getNodeRadius(element) {
|
|
479
|
+
const width = Number(element.data.width ?? 40);
|
|
480
|
+
const height = Number(element.data.height ?? width);
|
|
481
|
+
return Math.max(width, height) / 2;
|
|
482
|
+
}
|
|
483
|
+
function findRootId(nodes) {
|
|
484
|
+
const explicitRoot = nodes.find(
|
|
485
|
+
(node) => node.data.isRoot === true || node.data.nodeType === "root"
|
|
486
|
+
);
|
|
487
|
+
return explicitRoot ? getNodeId(explicitRoot) : nodes[0] ? getNodeId(nodes[0]) : void 0;
|
|
488
|
+
}
|
|
489
|
+
function calculateDepths(nodeIds, links, rootId) {
|
|
490
|
+
const depths = /* @__PURE__ */ new Map();
|
|
491
|
+
if (!rootId) {
|
|
492
|
+
return depths;
|
|
493
|
+
}
|
|
494
|
+
const neighbors = /* @__PURE__ */ new Map();
|
|
495
|
+
for (const id of nodeIds) {
|
|
496
|
+
neighbors.set(id, []);
|
|
497
|
+
}
|
|
498
|
+
for (const link of links) {
|
|
499
|
+
const source = String(link.source);
|
|
500
|
+
const target = String(link.target);
|
|
501
|
+
neighbors.get(source)?.push(target);
|
|
502
|
+
neighbors.get(target)?.push(source);
|
|
503
|
+
}
|
|
504
|
+
const queue = [rootId];
|
|
505
|
+
depths.set(rootId, 0);
|
|
506
|
+
while (queue.length > 0) {
|
|
507
|
+
const current = queue.shift();
|
|
508
|
+
if (!current) continue;
|
|
509
|
+
const nextDepth = (depths.get(current) ?? 0) + 1;
|
|
510
|
+
for (const neighbor of neighbors.get(current) ?? []) {
|
|
511
|
+
if (depths.has(neighbor)) continue;
|
|
512
|
+
depths.set(neighbor, nextDepth);
|
|
513
|
+
queue.push(neighbor);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const fallbackDepth = Math.max(1, ...depths.values()) + 1;
|
|
517
|
+
for (const nodeId of nodeIds) {
|
|
518
|
+
if (!depths.has(nodeId)) {
|
|
519
|
+
depths.set(nodeId, fallbackDepth);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return depths;
|
|
523
|
+
}
|
|
524
|
+
function computeForcePositions(elements, overrides) {
|
|
525
|
+
const options = resolveOptions(overrides);
|
|
526
|
+
const nodeElements = elements.filter((element) => element.group === "nodes");
|
|
527
|
+
const edgeElements = elements.filter((element) => element.group === "edges");
|
|
528
|
+
const nodeIds = nodeElements.map(getNodeId);
|
|
529
|
+
const rootId = findRootId(nodeElements);
|
|
530
|
+
const links = edgeElements.map((edge) => ({
|
|
531
|
+
source: String(edge.data.source),
|
|
532
|
+
target: String(edge.data.target)
|
|
533
|
+
}));
|
|
534
|
+
const depths = calculateDepths(nodeIds, links, rootId);
|
|
535
|
+
const nodes = nodeElements.map((element) => {
|
|
536
|
+
const id = getNodeId(element);
|
|
537
|
+
const isRoot = id === rootId;
|
|
538
|
+
return {
|
|
539
|
+
id,
|
|
540
|
+
radius: getNodeRadius(element),
|
|
541
|
+
depth: depths.get(id) ?? 1,
|
|
542
|
+
isRoot,
|
|
543
|
+
...isRoot ? { fx: 0, fy: 0 } : {}
|
|
544
|
+
};
|
|
545
|
+
});
|
|
546
|
+
const simulation = configureSimulation(nodes, links, options).stop();
|
|
547
|
+
for (let index = 0; index < options.iterations; index += 1) {
|
|
548
|
+
simulation.tick();
|
|
549
|
+
}
|
|
550
|
+
return Object.fromEntries(
|
|
551
|
+
nodes.map((node) => [
|
|
552
|
+
node.id,
|
|
553
|
+
{
|
|
554
|
+
x: Number.isFinite(node.x) ? node.x ?? 0 : 0,
|
|
555
|
+
y: Number.isFinite(node.y) ? node.y ?? 0 : 0
|
|
556
|
+
}
|
|
557
|
+
])
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
function startForceSimulation(cy, overrides) {
|
|
561
|
+
if (cy.nodes().empty()) {
|
|
548
562
|
return {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
padding: merged.padding ?? 56,
|
|
552
|
-
animate: merged.animate ?? false,
|
|
553
|
-
// Force horizontal left-to-right investigation flow.
|
|
554
|
-
rankDir: "LR",
|
|
555
|
-
rankSep: merged.spacingBetweenLayers ?? 120,
|
|
556
|
-
nodeSep: merged.spacingNodeNode ?? 50,
|
|
557
|
-
edgeSep: merged.spacingEdgeNode ?? 30,
|
|
558
|
-
...merged.extra ?? {}
|
|
563
|
+
reheat: () => void 0,
|
|
564
|
+
stop: () => void 0
|
|
559
565
|
};
|
|
560
566
|
}
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
567
|
+
const options = resolveOptions(overrides);
|
|
568
|
+
const cyNodes = cy.nodes();
|
|
569
|
+
const nodeIds = cyNodes.map((node) => node.id());
|
|
570
|
+
const rootNode = cyNodes.filter(
|
|
571
|
+
(node) => node.data("isRoot") === true || node.data("nodeType") === "root"
|
|
572
|
+
).first();
|
|
573
|
+
const rootId = (!rootNode.empty() ? rootNode : cyNodes.first()).id();
|
|
574
|
+
const links = cy.edges().map((edge) => ({
|
|
575
|
+
source: edge.source().id(),
|
|
576
|
+
target: edge.target().id()
|
|
577
|
+
}));
|
|
578
|
+
const depths = calculateDepths(nodeIds, links, rootId);
|
|
579
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
580
|
+
const nodes = cyNodes.map((node) => {
|
|
581
|
+
const position = node.position();
|
|
582
|
+
const forceNode = {
|
|
583
|
+
id: node.id(),
|
|
584
|
+
radius: Math.max(
|
|
585
|
+
Number(node.data("width") ?? 40),
|
|
586
|
+
Number(node.data("height") ?? 40)
|
|
587
|
+
) / 2,
|
|
588
|
+
depth: depths.get(node.id()) ?? 1,
|
|
589
|
+
isRoot: node.id() === rootId,
|
|
590
|
+
x: position.x,
|
|
591
|
+
y: position.y
|
|
592
|
+
};
|
|
593
|
+
if (forceNode.isRoot) {
|
|
594
|
+
forceNode.fx = position.x;
|
|
595
|
+
forceNode.fy = position.y;
|
|
596
|
+
}
|
|
597
|
+
nodeById.set(forceNode.id, forceNode);
|
|
598
|
+
return forceNode;
|
|
599
|
+
});
|
|
600
|
+
const simulation = configureSimulation(nodes, links, options).alpha(0.42).alphaTarget(0).on("tick", () => {
|
|
601
|
+
cy.batch(() => {
|
|
602
|
+
for (const forceNode of nodes) {
|
|
603
|
+
const cyNode = cy.getElementById(forceNode.id);
|
|
604
|
+
if (cyNode.empty() || cyNode.grabbed()) continue;
|
|
605
|
+
cyNode.position({
|
|
606
|
+
x: forceNode.x ?? 0,
|
|
607
|
+
y: forceNode.y ?? 0
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
const handleGrab = (event) => {
|
|
613
|
+
const forceNode = nodeById.get(event.target.id());
|
|
614
|
+
if (!forceNode) return;
|
|
615
|
+
const position = event.target.position();
|
|
616
|
+
forceNode.fx = position.x;
|
|
617
|
+
forceNode.fy = position.y;
|
|
618
|
+
simulation.alphaTarget(0.22).restart();
|
|
619
|
+
};
|
|
620
|
+
const handleDrag = (event) => {
|
|
621
|
+
const forceNode = nodeById.get(event.target.id());
|
|
622
|
+
if (!forceNode) return;
|
|
623
|
+
const position = event.target.position();
|
|
624
|
+
forceNode.fx = position.x;
|
|
625
|
+
forceNode.fy = position.y;
|
|
570
626
|
};
|
|
627
|
+
const handleFree = (event) => {
|
|
628
|
+
const forceNode = nodeById.get(event.target.id());
|
|
629
|
+
if (!forceNode) return;
|
|
630
|
+
if (!forceNode.isRoot) {
|
|
631
|
+
forceNode.fx = null;
|
|
632
|
+
forceNode.fy = null;
|
|
633
|
+
}
|
|
634
|
+
simulation.alphaTarget(0);
|
|
635
|
+
};
|
|
636
|
+
cy.on("grab", "node", handleGrab);
|
|
637
|
+
cy.on("drag", "node", handleDrag);
|
|
638
|
+
cy.on("free", "node", handleFree);
|
|
639
|
+
return {
|
|
640
|
+
reheat: () => {
|
|
641
|
+
simulation.alpha(0.72).alphaTarget(0).restart();
|
|
642
|
+
},
|
|
643
|
+
stop: () => {
|
|
644
|
+
simulation.stop();
|
|
645
|
+
cy.removeListener("grab", "node", handleGrab);
|
|
646
|
+
cy.removeListener("drag", "node", handleDrag);
|
|
647
|
+
cy.removeListener("free", "node", handleFree);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function createForceLayout(elements, overrides) {
|
|
652
|
+
const options = resolveOptions(overrides);
|
|
653
|
+
const positions = computeForcePositions(elements, overrides);
|
|
571
654
|
return {
|
|
572
|
-
name: "
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
655
|
+
name: "preset",
|
|
656
|
+
positions,
|
|
657
|
+
fit: options.fit,
|
|
658
|
+
padding: options.padding,
|
|
659
|
+
animate: options.animate,
|
|
660
|
+
animationDuration: options.animationDuration,
|
|
661
|
+
animationEasing: "ease-out-cubic"
|
|
579
662
|
};
|
|
580
663
|
}
|
|
581
664
|
|
|
@@ -584,19 +667,7 @@ var import_react = require("react");
|
|
|
584
667
|
|
|
585
668
|
// src/core/createCyInstance.ts
|
|
586
669
|
var import_cytoscape = __toESM(require("cytoscape"), 1);
|
|
587
|
-
var import_cytoscape_dagre = __toESM(require("cytoscape-dagre"), 1);
|
|
588
|
-
var import_cytoscape_elk = __toESM(require("cytoscape-elk"), 1);
|
|
589
|
-
var pluginRegistered = false;
|
|
590
|
-
function ensurePluginsRegistered() {
|
|
591
|
-
if (pluginRegistered) {
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
import_cytoscape.default.use(import_cytoscape_elk.default);
|
|
595
|
-
import_cytoscape.default.use(import_cytoscape_dagre.default);
|
|
596
|
-
pluginRegistered = true;
|
|
597
|
-
}
|
|
598
670
|
function createCyInstance(container) {
|
|
599
|
-
ensurePluginsRegistered();
|
|
600
671
|
return (0, import_cytoscape.default)({
|
|
601
672
|
container,
|
|
602
673
|
elements: [],
|
|
@@ -604,9 +675,9 @@ function createCyInstance(container) {
|
|
|
604
675
|
autoungrabify: false,
|
|
605
676
|
boxSelectionEnabled: false,
|
|
606
677
|
selectionType: "single",
|
|
607
|
-
wheelSensitivity: 0.
|
|
608
|
-
minZoom: 0.
|
|
609
|
-
maxZoom:
|
|
678
|
+
wheelSensitivity: 0.16,
|
|
679
|
+
minZoom: 0.16,
|
|
680
|
+
maxZoom: 3
|
|
610
681
|
});
|
|
611
682
|
}
|
|
612
683
|
|
|
@@ -633,10 +704,11 @@ function joinClassNames(...names) {
|
|
|
633
704
|
return names.filter(Boolean).join(" ");
|
|
634
705
|
}
|
|
635
706
|
var CytoscapeCanvas = ({
|
|
636
|
-
view,
|
|
637
707
|
elements,
|
|
638
708
|
stylesheet,
|
|
639
709
|
layout,
|
|
710
|
+
forceOptions,
|
|
711
|
+
physics = true,
|
|
640
712
|
width,
|
|
641
713
|
height,
|
|
642
714
|
className,
|
|
@@ -649,7 +721,12 @@ var CytoscapeCanvas = ({
|
|
|
649
721
|
const containerRef = (0, import_react.useRef)(null);
|
|
650
722
|
const cyRef = (0, import_react.useRef)(null);
|
|
651
723
|
const layoutRef = (0, import_react.useRef)(layout);
|
|
724
|
+
const forceOptionsRef = (0, import_react.useRef)(forceOptions);
|
|
725
|
+
const physicsRef = (0, import_react.useRef)(physics);
|
|
726
|
+
const simulationRef = (0, import_react.useRef)(null);
|
|
652
727
|
layoutRef.current = layout;
|
|
728
|
+
forceOptionsRef.current = forceOptions;
|
|
729
|
+
physicsRef.current = physics;
|
|
653
730
|
(0, import_react.useEffect)(() => {
|
|
654
731
|
if (!containerRef.current) {
|
|
655
732
|
return;
|
|
@@ -658,6 +735,8 @@ var CytoscapeCanvas = ({
|
|
|
658
735
|
cyRef.current = cy;
|
|
659
736
|
onCyReady?.(cy);
|
|
660
737
|
return () => {
|
|
738
|
+
simulationRef.current?.stop();
|
|
739
|
+
simulationRef.current = null;
|
|
661
740
|
cy.destroy();
|
|
662
741
|
cyRef.current = null;
|
|
663
742
|
};
|
|
@@ -667,8 +746,19 @@ var CytoscapeCanvas = ({
|
|
|
667
746
|
if (!cy) {
|
|
668
747
|
return;
|
|
669
748
|
}
|
|
670
|
-
|
|
749
|
+
simulationRef.current?.stop();
|
|
750
|
+
simulationRef.current = null;
|
|
751
|
+
const runner = cy.layout({
|
|
752
|
+
...layoutRef.current,
|
|
753
|
+
animate: false
|
|
754
|
+
});
|
|
671
755
|
runner.run();
|
|
756
|
+
if (physicsRef.current) {
|
|
757
|
+
simulationRef.current = startForceSimulation(
|
|
758
|
+
cy,
|
|
759
|
+
forceOptionsRef.current
|
|
760
|
+
);
|
|
761
|
+
}
|
|
672
762
|
}, []);
|
|
673
763
|
(0, import_react.useEffect)(() => {
|
|
674
764
|
const cy = cyRef.current;
|
|
@@ -695,7 +785,7 @@ var CytoscapeCanvas = ({
|
|
|
695
785
|
const data = node.data();
|
|
696
786
|
const rawLabel = data.labelFull ?? data.labelShort ?? node.id();
|
|
697
787
|
onNodeSelect({
|
|
698
|
-
view,
|
|
788
|
+
view: "observables",
|
|
699
789
|
nodeId: node.id(),
|
|
700
790
|
nodeType: typeof data.nodeType === "string" ? data.nodeType : "unknown",
|
|
701
791
|
label: String(rawLabel),
|
|
@@ -710,7 +800,7 @@ var CytoscapeCanvas = ({
|
|
|
710
800
|
const edge = event.target;
|
|
711
801
|
const data = edge.data();
|
|
712
802
|
onEdgeSelect({
|
|
713
|
-
view,
|
|
803
|
+
view: "observables",
|
|
714
804
|
edgeId: edge.id(),
|
|
715
805
|
sourceId: edge.source().id(),
|
|
716
806
|
targetId: edge.target().id(),
|
|
@@ -719,13 +809,35 @@ var CytoscapeCanvas = ({
|
|
|
719
809
|
element: edge
|
|
720
810
|
});
|
|
721
811
|
};
|
|
812
|
+
const handleNodeMouseOver = (event) => {
|
|
813
|
+
const node = event.target;
|
|
814
|
+
const neighborhood = node.closedNeighborhood();
|
|
815
|
+
cy.elements().addClass("cyvest-dimmed");
|
|
816
|
+
neighborhood.removeClass("cyvest-dimmed");
|
|
817
|
+
node.addClass("cyvest-focus");
|
|
818
|
+
node.connectedEdges().addClass("cyvest-focus");
|
|
819
|
+
};
|
|
820
|
+
const clearFocus = () => {
|
|
821
|
+
cy.elements().removeClass("cyvest-dimmed cyvest-focus");
|
|
822
|
+
};
|
|
823
|
+
const handleCanvasTap = (event) => {
|
|
824
|
+
if (event.target === cy) {
|
|
825
|
+
cy.elements().unselect();
|
|
826
|
+
}
|
|
827
|
+
};
|
|
722
828
|
cy.on("tap", "node", handleNodeTap);
|
|
723
829
|
cy.on("tap", "edge", handleEdgeTap);
|
|
830
|
+
cy.on("mouseover", "node", handleNodeMouseOver);
|
|
831
|
+
cy.on("mouseout", "node", clearFocus);
|
|
832
|
+
cy.on("tap", handleCanvasTap);
|
|
724
833
|
return () => {
|
|
725
834
|
cy.removeListener("tap", "node", handleNodeTap);
|
|
726
835
|
cy.removeListener("tap", "edge", handleEdgeTap);
|
|
836
|
+
cy.removeListener("mouseover", "node", handleNodeMouseOver);
|
|
837
|
+
cy.removeListener("mouseout", "node", clearFocus);
|
|
838
|
+
cy.removeListener("tap", handleCanvasTap);
|
|
727
839
|
};
|
|
728
|
-
}, [onEdgeSelect, onNodeSelect
|
|
840
|
+
}, [onEdgeSelect, onNodeSelect]);
|
|
729
841
|
const handleFit = (0, import_react.useCallback)(() => {
|
|
730
842
|
const cy = cyRef.current;
|
|
731
843
|
if (!cy) {
|
|
@@ -779,8 +891,8 @@ var CytoscapeCanvas = ({
|
|
|
779
891
|
type: "button",
|
|
780
892
|
className: "cyvest-toolbar__button",
|
|
781
893
|
onClick: runLayout,
|
|
782
|
-
title: "
|
|
783
|
-
"aria-label": "
|
|
894
|
+
title: "Reheat physics",
|
|
895
|
+
"aria-label": "Reheat physics",
|
|
784
896
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
785
897
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 1 1-2.64-6.36" }),
|
|
786
898
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 3v6h-6" })
|
|
@@ -807,156 +919,8 @@ var CytoscapeCanvas = ({
|
|
|
807
919
|
);
|
|
808
920
|
};
|
|
809
921
|
|
|
810
|
-
// src/components/CyvestInvestigationView.tsx
|
|
811
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
812
|
-
var CyvestInvestigationView = ({
|
|
813
|
-
investigation,
|
|
814
|
-
height = 500,
|
|
815
|
-
width = "100%",
|
|
816
|
-
className,
|
|
817
|
-
theme,
|
|
818
|
-
onCyReady,
|
|
819
|
-
onNodeSelect,
|
|
820
|
-
onEdgeSelect,
|
|
821
|
-
showToolbar = true,
|
|
822
|
-
layout,
|
|
823
|
-
maxLabelLength = 26
|
|
824
|
-
}) => {
|
|
825
|
-
const elements = (0, import_react2.useMemo)(
|
|
826
|
-
() => buildInvestigationElements(investigation, {
|
|
827
|
-
maxLabelLength,
|
|
828
|
-
edgeColor: theme?.edgeColor
|
|
829
|
-
}),
|
|
830
|
-
[investigation, maxLabelLength, theme?.edgeColor]
|
|
831
|
-
);
|
|
832
|
-
const stylesheet = (0, import_react2.useMemo)(
|
|
833
|
-
() => createInvestigationStylesheet(theme),
|
|
834
|
-
[theme]
|
|
835
|
-
);
|
|
836
|
-
const elkLayout = (0, import_react2.useMemo)(
|
|
837
|
-
() => createElkLayout("investigation", layout),
|
|
838
|
-
[layout]
|
|
839
|
-
);
|
|
840
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
841
|
-
CytoscapeCanvas,
|
|
842
|
-
{
|
|
843
|
-
view: "investigation",
|
|
844
|
-
elements,
|
|
845
|
-
stylesheet,
|
|
846
|
-
layout: elkLayout,
|
|
847
|
-
width,
|
|
848
|
-
height,
|
|
849
|
-
className,
|
|
850
|
-
theme,
|
|
851
|
-
onCyReady,
|
|
852
|
-
onNodeSelect,
|
|
853
|
-
onEdgeSelect,
|
|
854
|
-
showToolbar
|
|
855
|
-
}
|
|
856
|
-
);
|
|
857
|
-
};
|
|
858
|
-
|
|
859
922
|
// src/components/CyvestObservablesView.tsx
|
|
860
|
-
var
|
|
861
|
-
|
|
862
|
-
// src/adapters/observablesElements.ts
|
|
863
|
-
var import_cyvest_js3 = require("@cyvest/cyvest-js");
|
|
864
|
-
function findFallbackRootId(graph) {
|
|
865
|
-
if (graph.nodes.length === 0) {
|
|
866
|
-
return void 0;
|
|
867
|
-
}
|
|
868
|
-
const incoming = /* @__PURE__ */ new Map();
|
|
869
|
-
for (const node of graph.nodes) {
|
|
870
|
-
incoming.set(node.id, 0);
|
|
871
|
-
}
|
|
872
|
-
for (const edge of graph.edges) {
|
|
873
|
-
incoming.set(edge.target, (incoming.get(edge.target) ?? 0) + 1);
|
|
874
|
-
}
|
|
875
|
-
const sourceCandidates = graph.nodes.filter((node) => (incoming.get(node.id) ?? 0) === 0);
|
|
876
|
-
if (sourceCandidates.length === 0) {
|
|
877
|
-
return graph.nodes[0]?.id;
|
|
878
|
-
}
|
|
879
|
-
sourceCandidates.sort((a, b) => b.score - a.score);
|
|
880
|
-
return sourceCandidates[0]?.id;
|
|
881
|
-
}
|
|
882
|
-
function getArrowShapes(edge) {
|
|
883
|
-
if (edge.direction === "bidirectional") {
|
|
884
|
-
return {
|
|
885
|
-
sourceArrowShape: "triangle",
|
|
886
|
-
targetArrowShape: "triangle"
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
if (edge.direction === "inbound") {
|
|
890
|
-
return {
|
|
891
|
-
sourceArrowShape: "triangle",
|
|
892
|
-
targetArrowShape: "none"
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
return {
|
|
896
|
-
sourceArrowShape: "none",
|
|
897
|
-
targetArrowShape: "triangle"
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
function buildObservablesElements(investigation, options) {
|
|
901
|
-
const graph = (0, import_cyvest_js3.getObservableGraph)(investigation);
|
|
902
|
-
const rootObservable = (0, import_cyvest_js3.getRootObservable)(investigation);
|
|
903
|
-
const rootId = rootObservable?.key ?? findFallbackRootId(graph);
|
|
904
|
-
const maxLabelLength = options?.maxLabelLength ?? 28;
|
|
905
|
-
const edgeColor = options?.edgeColor ?? "#8a95aa";
|
|
906
|
-
const nodes = graph.nodes.map((node) => {
|
|
907
|
-
const isRoot = node.id === rootId;
|
|
908
|
-
const borderColor = getLevelColor(node.level);
|
|
909
|
-
const data = {
|
|
910
|
-
id: node.id,
|
|
911
|
-
nodeType: "observable",
|
|
912
|
-
labelShort: truncateLabel(node.value, maxLabelLength, true),
|
|
913
|
-
labelFull: node.value,
|
|
914
|
-
observableType: node.type,
|
|
915
|
-
level: node.level,
|
|
916
|
-
score: node.score,
|
|
917
|
-
isRoot,
|
|
918
|
-
whitelisted: node.whitelisted,
|
|
919
|
-
internal: node.internal,
|
|
920
|
-
shape: "ellipse",
|
|
921
|
-
width: 48,
|
|
922
|
-
height: 48,
|
|
923
|
-
borderWidth: 2,
|
|
924
|
-
borderColor,
|
|
925
|
-
fillColor: getLevelBackgroundColor(node.level),
|
|
926
|
-
icon: getObservableIconSvg(node.type, { color: borderColor }),
|
|
927
|
-
opacity: node.whitelisted ? 0.5 : 1
|
|
928
|
-
};
|
|
929
|
-
return {
|
|
930
|
-
group: "nodes",
|
|
931
|
-
data
|
|
932
|
-
};
|
|
933
|
-
});
|
|
934
|
-
const nodeIds = new Set(graph.nodes.map((node) => node.id));
|
|
935
|
-
const edges = graph.edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)).map((edge, index) => {
|
|
936
|
-
const arrowShape = getArrowShapes(edge);
|
|
937
|
-
const data = {
|
|
938
|
-
id: `obs-edge-${index}-${edge.source}-${edge.target}-${edge.type}`,
|
|
939
|
-
relationshipType: edge.type,
|
|
940
|
-
direction: edge.direction,
|
|
941
|
-
color: edgeColor,
|
|
942
|
-
width: 1.6,
|
|
943
|
-
sourceArrowShape: arrowShape.sourceArrowShape,
|
|
944
|
-
targetArrowShape: arrowShape.targetArrowShape
|
|
945
|
-
};
|
|
946
|
-
return {
|
|
947
|
-
group: "edges",
|
|
948
|
-
data: {
|
|
949
|
-
...data,
|
|
950
|
-
source: edge.source,
|
|
951
|
-
target: edge.target
|
|
952
|
-
}
|
|
953
|
-
};
|
|
954
|
-
});
|
|
955
|
-
return [...nodes, ...edges];
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// src/components/CyvestObservablesView.tsx
|
|
959
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
923
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
960
924
|
var CyvestObservablesView = ({
|
|
961
925
|
investigation,
|
|
962
926
|
height = 500,
|
|
@@ -966,32 +930,34 @@ var CyvestObservablesView = ({
|
|
|
966
930
|
onCyReady,
|
|
967
931
|
onNodeSelect,
|
|
968
932
|
onEdgeSelect,
|
|
933
|
+
physics = true,
|
|
969
934
|
showToolbar = true,
|
|
970
935
|
layout,
|
|
971
936
|
maxLabelLength = 28
|
|
972
937
|
}) => {
|
|
973
|
-
const elements = (0,
|
|
938
|
+
const elements = (0, import_react2.useMemo)(
|
|
974
939
|
() => buildObservablesElements(investigation, {
|
|
975
940
|
maxLabelLength,
|
|
976
|
-
|
|
941
|
+
theme
|
|
977
942
|
}),
|
|
978
|
-
[investigation, maxLabelLength, theme
|
|
943
|
+
[investigation, maxLabelLength, theme]
|
|
979
944
|
);
|
|
980
|
-
const stylesheet = (0,
|
|
945
|
+
const stylesheet = (0, import_react2.useMemo)(
|
|
981
946
|
() => createObservablesStylesheet(theme),
|
|
982
947
|
[theme]
|
|
983
948
|
);
|
|
984
|
-
const
|
|
985
|
-
() =>
|
|
986
|
-
[layout]
|
|
949
|
+
const forceLayout = (0, import_react2.useMemo)(
|
|
950
|
+
() => createForceLayout(elements, layout),
|
|
951
|
+
[elements, layout]
|
|
987
952
|
);
|
|
988
|
-
return /* @__PURE__ */ (0,
|
|
953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
989
954
|
CytoscapeCanvas,
|
|
990
955
|
{
|
|
991
|
-
view: "observables",
|
|
992
956
|
elements,
|
|
993
957
|
stylesheet,
|
|
994
|
-
layout:
|
|
958
|
+
layout: forceLayout,
|
|
959
|
+
forceOptions: layout,
|
|
960
|
+
physics,
|
|
995
961
|
width,
|
|
996
962
|
height,
|
|
997
963
|
className,
|
|
@@ -1005,10 +971,7 @@ var CyvestObservablesView = ({
|
|
|
1005
971
|
};
|
|
1006
972
|
|
|
1007
973
|
// src/components/CyvestGraph.tsx
|
|
1008
|
-
var
|
|
1009
|
-
function normalizeInitialView(view) {
|
|
1010
|
-
return view === "investigation" ? "investigation" : "observables";
|
|
1011
|
-
}
|
|
974
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1012
975
|
var CyvestGraph = ({
|
|
1013
976
|
investigation,
|
|
1014
977
|
height = 500,
|
|
@@ -1018,105 +981,42 @@ var CyvestGraph = ({
|
|
|
1018
981
|
onCyReady,
|
|
1019
982
|
onNodeSelect,
|
|
1020
983
|
onEdgeSelect,
|
|
1021
|
-
|
|
1022
|
-
showViewToggle = true,
|
|
1023
|
-
onViewChange,
|
|
984
|
+
physics = true,
|
|
1024
985
|
showToolbar = true,
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}),
|
|
1045
|
-
[width, height]
|
|
1046
|
-
);
|
|
1047
|
-
const handleViewChange = (0, import_react4.useCallback)((view) => {
|
|
1048
|
-
setActiveView(view);
|
|
1049
|
-
}, []);
|
|
1050
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style: containerStyle, children: [
|
|
1051
|
-
showViewToggle && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "cyvest-view-toggle", role: "tablist", "aria-label": "Graph view mode", children: [
|
|
1052
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1053
|
-
"button",
|
|
1054
|
-
{
|
|
1055
|
-
type: "button",
|
|
1056
|
-
className: activeView === "observables" ? "cyvest-view-toggle__button cyvest-view-toggle__button--active" : "cyvest-view-toggle__button",
|
|
1057
|
-
onClick: () => handleViewChange("observables"),
|
|
1058
|
-
role: "tab",
|
|
1059
|
-
"aria-selected": activeView === "observables",
|
|
1060
|
-
children: "Observables"
|
|
1061
|
-
}
|
|
1062
|
-
),
|
|
1063
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1064
|
-
"button",
|
|
1065
|
-
{
|
|
1066
|
-
type: "button",
|
|
1067
|
-
className: activeView === "investigation" ? "cyvest-view-toggle__button cyvest-view-toggle__button--active" : "cyvest-view-toggle__button",
|
|
1068
|
-
onClick: () => handleViewChange("investigation"),
|
|
1069
|
-
role: "tab",
|
|
1070
|
-
"aria-selected": activeView === "investigation",
|
|
1071
|
-
children: "Investigation"
|
|
1072
|
-
}
|
|
1073
|
-
)
|
|
1074
|
-
] }),
|
|
1075
|
-
activeView === "observables" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1076
|
-
CyvestObservablesView,
|
|
1077
|
-
{
|
|
1078
|
-
investigation,
|
|
1079
|
-
width: "100%",
|
|
1080
|
-
height: "100%",
|
|
1081
|
-
theme,
|
|
1082
|
-
onCyReady,
|
|
1083
|
-
onNodeSelect,
|
|
1084
|
-
onEdgeSelect,
|
|
1085
|
-
showToolbar,
|
|
1086
|
-
layout: observablesLayout,
|
|
1087
|
-
maxLabelLength: maxObservableLabelLength
|
|
1088
|
-
}
|
|
1089
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1090
|
-
CyvestInvestigationView,
|
|
1091
|
-
{
|
|
1092
|
-
investigation,
|
|
1093
|
-
width: "100%",
|
|
1094
|
-
height: "100%",
|
|
1095
|
-
theme,
|
|
1096
|
-
onCyReady,
|
|
1097
|
-
onNodeSelect,
|
|
1098
|
-
onEdgeSelect,
|
|
1099
|
-
showToolbar,
|
|
1100
|
-
layout: investigationLayout,
|
|
1101
|
-
maxLabelLength: maxInvestigationLabelLength
|
|
1102
|
-
}
|
|
1103
|
-
)
|
|
1104
|
-
] });
|
|
1105
|
-
};
|
|
986
|
+
layout,
|
|
987
|
+
maxLabelLength = 28
|
|
988
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
989
|
+
CyvestObservablesView,
|
|
990
|
+
{
|
|
991
|
+
investigation,
|
|
992
|
+
width,
|
|
993
|
+
height,
|
|
994
|
+
className,
|
|
995
|
+
theme,
|
|
996
|
+
onCyReady,
|
|
997
|
+
onNodeSelect,
|
|
998
|
+
onEdgeSelect,
|
|
999
|
+
physics,
|
|
1000
|
+
showToolbar,
|
|
1001
|
+
layout,
|
|
1002
|
+
maxLabelLength
|
|
1003
|
+
}
|
|
1004
|
+
);
|
|
1106
1005
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1107
1006
|
0 && (module.exports = {
|
|
1108
1007
|
CyvestGraph,
|
|
1109
|
-
CyvestInvestigationView,
|
|
1110
1008
|
CyvestObservablesView,
|
|
1009
|
+
DARK_CYVEST_THEME,
|
|
1111
1010
|
DEFAULT_CYVEST_THEME,
|
|
1112
|
-
INVESTIGATION_ICON_NAME_MAP,
|
|
1113
1011
|
OBSERVABLE_ICON_NAME_MAP,
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1012
|
+
computeForcePositions,
|
|
1013
|
+
createForceLayout,
|
|
1014
|
+
getDefaultForceOptions,
|
|
1117
1015
|
getLevelBackgroundColor,
|
|
1118
1016
|
getLevelColor,
|
|
1119
1017
|
getObservableIconSvg,
|
|
1120
1018
|
lightenHexColor,
|
|
1019
|
+
mixHexColor,
|
|
1020
|
+
startForceSimulation,
|
|
1121
1021
|
truncateLabel
|
|
1122
1022
|
});
|