@omiron33/omi-neuron-web 0.1.2 → 0.1.6
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/{NeuronWeb-DG5ICk2K.d.cts → NeuronWeb-DHP0heLZ.d.cts} +4 -2
- package/dist/{NeuronWeb-DbcYoeQE.d.ts → NeuronWeb-jS_bg9lu.d.ts} +4 -2
- package/dist/{chunk-KC7V76I3.cjs → chunk-U7K7KQZG.cjs} +320 -69
- package/dist/chunk-U7K7KQZG.cjs.map +1 -0
- package/dist/{chunk-GPDX3O37.js → chunk-YFJMQCGE.js} +321 -70
- package/dist/chunk-YFJMQCGE.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/visualization/index.cjs +6 -6
- package/dist/visualization/index.d.cts +4 -2
- package/dist/visualization/index.d.ts +4 -2
- package/dist/visualization/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-GPDX3O37.js.map +0 -1
- package/dist/chunk-KC7V76I3.cjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef, useState, useMemo, useEffect } from 'react';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
4
|
-
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
|
|
4
|
+
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/visualization/constants.ts
|
|
@@ -14,7 +14,7 @@ var DEFAULT_THEME = {
|
|
|
14
14
|
edgeActive: "#c6d4ff",
|
|
15
15
|
edgeSelected: "#ffffff",
|
|
16
16
|
labelText: "#ffffff",
|
|
17
|
-
labelBackground: "rgba(
|
|
17
|
+
labelBackground: "rgba(5, 6, 31, 0.8)"
|
|
18
18
|
},
|
|
19
19
|
typography: {
|
|
20
20
|
labelFontFamily: "system-ui, sans-serif",
|
|
@@ -33,8 +33,8 @@ var DEFAULT_THEME = {
|
|
|
33
33
|
edgeFlowSpeed: 1.2,
|
|
34
34
|
fogEnabled: true,
|
|
35
35
|
fogColor: "#020314",
|
|
36
|
-
fogNear:
|
|
37
|
-
fogFar:
|
|
36
|
+
fogNear: 24,
|
|
37
|
+
fogFar: 160
|
|
38
38
|
},
|
|
39
39
|
animation: {
|
|
40
40
|
focusDuration: 800,
|
|
@@ -52,8 +52,8 @@ var SceneManager = class {
|
|
|
52
52
|
this.container = container;
|
|
53
53
|
this.config = config;
|
|
54
54
|
this.scene = new THREE.Scene();
|
|
55
|
-
this.camera = new THREE.PerspectiveCamera(
|
|
56
|
-
this.renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
55
|
+
this.camera = new THREE.PerspectiveCamera(config.cameraFov ?? 52, 1, 0.1, 220);
|
|
56
|
+
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
57
57
|
this.labelRenderer = new CSS2DRenderer();
|
|
58
58
|
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
59
59
|
}
|
|
@@ -70,6 +70,7 @@ var SceneManager = class {
|
|
|
70
70
|
ambientLight = null;
|
|
71
71
|
keyLight = null;
|
|
72
72
|
fillLight = null;
|
|
73
|
+
resizeObserver = null;
|
|
73
74
|
initialize() {
|
|
74
75
|
const { cameraPosition, cameraTarget, backgroundColor } = this.config;
|
|
75
76
|
this.scene.background = new THREE.Color(backgroundColor);
|
|
@@ -84,9 +85,14 @@ var SceneManager = class {
|
|
|
84
85
|
this.controls.update();
|
|
85
86
|
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, this.config.pixelRatioCap));
|
|
86
87
|
this.renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
87
|
-
this.renderer.toneMapping = THREE.
|
|
88
|
-
this.renderer.toneMappingExposure = 1
|
|
88
|
+
this.renderer.toneMapping = THREE.NoToneMapping;
|
|
89
|
+
this.renderer.toneMappingExposure = 1;
|
|
89
90
|
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
91
|
+
this.renderer.domElement.style.position = "absolute";
|
|
92
|
+
this.renderer.domElement.style.top = "0";
|
|
93
|
+
this.renderer.domElement.style.left = "0";
|
|
94
|
+
this.renderer.domElement.style.width = "100%";
|
|
95
|
+
this.renderer.domElement.style.height = "100%";
|
|
90
96
|
this.labelRenderer.setSize(this.container.clientWidth, this.container.clientHeight);
|
|
91
97
|
this.labelRenderer.domElement.style.position = "absolute";
|
|
92
98
|
this.labelRenderer.domElement.style.top = "0";
|
|
@@ -101,11 +107,17 @@ var SceneManager = class {
|
|
|
101
107
|
this.initStarfield();
|
|
102
108
|
}
|
|
103
109
|
window.addEventListener("resize", this.resize);
|
|
110
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
111
|
+
this.resizeObserver = new ResizeObserver(() => this.resize());
|
|
112
|
+
this.resizeObserver.observe(this.container);
|
|
113
|
+
}
|
|
104
114
|
this.startAnimationLoop();
|
|
105
115
|
}
|
|
106
116
|
dispose() {
|
|
107
117
|
this.stopAnimationLoop();
|
|
108
118
|
window.removeEventListener("resize", this.resize);
|
|
119
|
+
this.resizeObserver?.disconnect();
|
|
120
|
+
this.resizeObserver = null;
|
|
109
121
|
this.renderer.dispose();
|
|
110
122
|
this.scene.clear();
|
|
111
123
|
this.container.innerHTML = "";
|
|
@@ -143,6 +155,7 @@ var SceneManager = class {
|
|
|
143
155
|
resize = () => {
|
|
144
156
|
const width = this.container.clientWidth;
|
|
145
157
|
const height = this.container.clientHeight;
|
|
158
|
+
if (!width || !height) return;
|
|
146
159
|
this.camera.aspect = width / height;
|
|
147
160
|
this.camera.updateProjectionMatrix();
|
|
148
161
|
this.renderer.setSize(width, height);
|
|
@@ -197,20 +210,17 @@ var SceneManager = class {
|
|
|
197
210
|
const geometry = new THREE.BufferGeometry();
|
|
198
211
|
const positions = new Float32Array(count * 3);
|
|
199
212
|
for (let i = 0; i < count; i += 1) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
|
|
204
|
-
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
|
|
205
|
-
positions[i * 3 + 2] = radius * Math.cos(phi);
|
|
213
|
+
positions[i * 3] = THREE.MathUtils.randFloatSpread(70);
|
|
214
|
+
positions[i * 3 + 1] = THREE.MathUtils.randFloatSpread(70);
|
|
215
|
+
positions[i * 3 + 2] = THREE.MathUtils.randFloatSpread(70);
|
|
206
216
|
}
|
|
207
217
|
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
|
208
218
|
const material = new THREE.PointsMaterial({
|
|
209
219
|
color: this.config.starfieldColor,
|
|
210
|
-
size: 0.
|
|
220
|
+
size: 0.22,
|
|
211
221
|
sizeAttenuation: true,
|
|
212
222
|
transparent: true,
|
|
213
|
-
opacity: 0.
|
|
223
|
+
opacity: 0.5,
|
|
214
224
|
depthWrite: false
|
|
215
225
|
});
|
|
216
226
|
this.starfield = new THREE.Points(geometry, material);
|
|
@@ -251,38 +261,53 @@ var NodeRenderer = class {
|
|
|
251
261
|
this.scene = scene;
|
|
252
262
|
this.config = config;
|
|
253
263
|
this.scene.add(this.group);
|
|
264
|
+
this.glowTexture = this.createGlowTexture();
|
|
265
|
+
if (config.labelOffset) {
|
|
266
|
+
this.labelOffset.set(...config.labelOffset);
|
|
267
|
+
}
|
|
254
268
|
}
|
|
255
269
|
group = new THREE.Group();
|
|
256
270
|
nodeStates = /* @__PURE__ */ new Map();
|
|
257
271
|
hoveredNodeId = null;
|
|
258
272
|
selectedNodeId = null;
|
|
273
|
+
glowTexture = null;
|
|
274
|
+
labelOffset = new THREE.Vector3(0, 0.65, 0);
|
|
259
275
|
renderNodes(nodes) {
|
|
260
276
|
this.clear();
|
|
277
|
+
const shouldRenderLabels = this.config.maxVisibleLabels > 0 && this.config.labelDistance > 0;
|
|
261
278
|
nodes.forEach((node) => {
|
|
262
279
|
const color = new THREE.Color(
|
|
263
280
|
this.config.domainColors[node.domain] ?? this.config.defaultColor
|
|
264
281
|
);
|
|
265
|
-
const
|
|
266
|
-
|
|
282
|
+
const material = new THREE.SpriteMaterial({
|
|
283
|
+
map: this.glowTexture ?? void 0,
|
|
267
284
|
color,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
emissiveIntensity: 1
|
|
285
|
+
transparent: true,
|
|
286
|
+
opacity: 0.78,
|
|
287
|
+
depthWrite: false
|
|
272
288
|
});
|
|
273
|
-
const
|
|
289
|
+
const sprite = new THREE.Sprite(material);
|
|
274
290
|
const position = new THREE.Vector3();
|
|
275
291
|
if (node.position) {
|
|
276
292
|
position.set(...node.position);
|
|
277
293
|
}
|
|
278
|
-
|
|
294
|
+
sprite.position.copy(position);
|
|
279
295
|
const tierScale = node.tier ? this.config.tierScales[node.tier] ?? 1 : 1;
|
|
280
296
|
const baseScale = this.config.baseScale * tierScale;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.group.add(
|
|
297
|
+
sprite.scale.setScalar(baseScale);
|
|
298
|
+
sprite.userData = { nodeId: node.id, nodeSlug: node.slug };
|
|
299
|
+
this.group.add(sprite);
|
|
300
|
+
let labelObject = null;
|
|
301
|
+
if (shouldRenderLabels) {
|
|
302
|
+
const labelElement = this.createLabelElement(node, color);
|
|
303
|
+
labelObject = new CSS2DObject(labelElement);
|
|
304
|
+
labelObject.position.copy(sprite.position).add(this.labelOffset);
|
|
305
|
+
this.scene.add(labelObject);
|
|
306
|
+
}
|
|
284
307
|
this.nodeStates.set(node.id, {
|
|
285
|
-
|
|
308
|
+
sprite,
|
|
309
|
+
material,
|
|
310
|
+
label: labelObject,
|
|
286
311
|
basePosition: position.clone(),
|
|
287
312
|
baseScale,
|
|
288
313
|
phase: Math.random() * Math.PI * 2,
|
|
@@ -298,7 +323,7 @@ var NodeRenderer = class {
|
|
|
298
323
|
if (!state) return;
|
|
299
324
|
if (updates.position) {
|
|
300
325
|
state.basePosition.set(...updates.position);
|
|
301
|
-
state.
|
|
326
|
+
state.sprite.position.set(...updates.position);
|
|
302
327
|
}
|
|
303
328
|
if (updates.tier) {
|
|
304
329
|
const tierScale = this.config.tierScales[updates.tier] ?? 1;
|
|
@@ -309,18 +334,24 @@ var NodeRenderer = class {
|
|
|
309
334
|
this.config.domainColors[updates.domain] ?? this.config.defaultColor
|
|
310
335
|
);
|
|
311
336
|
state.baseColor = color;
|
|
312
|
-
|
|
313
|
-
material.color = color;
|
|
314
|
-
material.emissive = color.clone().multiplyScalar(this.config.glowIntensity * 0.4);
|
|
337
|
+
state.material.color = color;
|
|
315
338
|
}
|
|
316
339
|
}
|
|
317
340
|
removeNode(nodeId) {
|
|
318
341
|
const state = this.nodeStates.get(nodeId);
|
|
319
342
|
if (!state) return;
|
|
320
|
-
|
|
343
|
+
if (state.label) {
|
|
344
|
+
this.scene.remove(state.label);
|
|
345
|
+
}
|
|
346
|
+
this.group.remove(state.sprite);
|
|
321
347
|
this.nodeStates.delete(nodeId);
|
|
322
348
|
}
|
|
323
349
|
clear() {
|
|
350
|
+
this.nodeStates.forEach((state) => {
|
|
351
|
+
if (state.label) {
|
|
352
|
+
this.scene.remove(state.label);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
324
355
|
this.group.clear();
|
|
325
356
|
this.nodeStates.clear();
|
|
326
357
|
this.hoveredNodeId = null;
|
|
@@ -328,29 +359,130 @@ var NodeRenderer = class {
|
|
|
328
359
|
}
|
|
329
360
|
showNodes(nodeIds) {
|
|
330
361
|
nodeIds.forEach((id) => {
|
|
331
|
-
const
|
|
332
|
-
if (
|
|
362
|
+
const state = this.nodeStates.get(id);
|
|
363
|
+
if (state) state.sprite.visible = true;
|
|
333
364
|
});
|
|
334
365
|
}
|
|
335
366
|
hideNodes(nodeIds) {
|
|
336
367
|
nodeIds.forEach((id) => {
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
368
|
+
const state = this.nodeStates.get(id);
|
|
369
|
+
if (state) state.sprite.visible = false;
|
|
339
370
|
});
|
|
340
371
|
}
|
|
341
372
|
setVisibleNodes(nodeIds) {
|
|
342
373
|
if (!nodeIds) {
|
|
343
374
|
this.nodeStates.forEach((state) => {
|
|
344
|
-
state.
|
|
375
|
+
state.sprite.visible = true;
|
|
345
376
|
});
|
|
346
377
|
return;
|
|
347
378
|
}
|
|
348
379
|
const visibleSet = new Set(nodeIds);
|
|
349
380
|
this.nodeStates.forEach((state, id) => {
|
|
350
|
-
state.
|
|
381
|
+
state.sprite.visible = visibleSet.has(id);
|
|
351
382
|
});
|
|
352
383
|
}
|
|
353
|
-
updateLabelVisibility() {
|
|
384
|
+
updateLabelVisibility(camera) {
|
|
385
|
+
if (this.config.maxVisibleLabels <= 0 || this.config.labelDistance <= 0) {
|
|
386
|
+
this.nodeStates.forEach((state) => {
|
|
387
|
+
if (state.label) state.label.visible = false;
|
|
388
|
+
});
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const entries = [];
|
|
392
|
+
this.nodeStates.forEach((state) => {
|
|
393
|
+
if (!state.label) return;
|
|
394
|
+
if (!state.sprite.visible) {
|
|
395
|
+
state.label.visible = false;
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const distance = camera.position.distanceTo(state.sprite.position);
|
|
399
|
+
entries.push({ state, distance });
|
|
400
|
+
});
|
|
401
|
+
entries.sort((a, b) => a.distance - b.distance);
|
|
402
|
+
entries.forEach((entry, index) => {
|
|
403
|
+
const visible = entry.distance <= this.config.labelDistance && index < this.config.maxVisibleLabels;
|
|
404
|
+
entry.state.label.visible = visible;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
createLabelElement(node, accent) {
|
|
408
|
+
const wrapper = document.createElement("div");
|
|
409
|
+
wrapper.style.borderRadius = "10px";
|
|
410
|
+
wrapper.style.border = "1px solid rgba(255, 255, 255, 0.12)";
|
|
411
|
+
wrapper.style.background = this.config.labelBackground;
|
|
412
|
+
wrapper.style.color = this.config.labelTextColor;
|
|
413
|
+
wrapper.style.fontFamily = this.config.labelFontFamily;
|
|
414
|
+
wrapper.style.fontSize = `${this.config.labelFontSize}px`;
|
|
415
|
+
wrapper.style.fontWeight = this.config.labelFontWeight;
|
|
416
|
+
wrapper.style.padding = "6px 8px";
|
|
417
|
+
wrapper.style.boxShadow = "0 10px 30px rgba(5, 10, 20, 0.35)";
|
|
418
|
+
wrapper.style.backdropFilter = "blur(10px)";
|
|
419
|
+
wrapper.style.pointerEvents = "none";
|
|
420
|
+
wrapper.style.maxWidth = "220px";
|
|
421
|
+
const isInsight = node.tier === "insight" || node.domain === "insight";
|
|
422
|
+
if (isInsight) {
|
|
423
|
+
const accentBright = accent.clone().lerp(new THREE.Color("#ffffff"), 0.35);
|
|
424
|
+
wrapper.style.border = `1px solid ${this.toRgba(accentBright, 0.6)}`;
|
|
425
|
+
wrapper.style.background = this.toRgba(accent, 0.2);
|
|
426
|
+
wrapper.style.color = this.toRgba(accentBright, 0.95);
|
|
427
|
+
}
|
|
428
|
+
const badgeRow = document.createElement("div");
|
|
429
|
+
badgeRow.style.display = "flex";
|
|
430
|
+
badgeRow.style.flexWrap = "wrap";
|
|
431
|
+
badgeRow.style.gap = "4px";
|
|
432
|
+
badgeRow.style.marginBottom = "4px";
|
|
433
|
+
const makeBadge = (text, tone) => {
|
|
434
|
+
const badge = document.createElement("span");
|
|
435
|
+
badge.textContent = text;
|
|
436
|
+
badge.style.display = "inline-flex";
|
|
437
|
+
badge.style.alignItems = "center";
|
|
438
|
+
badge.style.gap = "4px";
|
|
439
|
+
badge.style.borderRadius = "999px";
|
|
440
|
+
badge.style.padding = "2px 6px";
|
|
441
|
+
badge.style.fontSize = "0.6rem";
|
|
442
|
+
badge.style.fontWeight = "600";
|
|
443
|
+
badge.style.textTransform = "uppercase";
|
|
444
|
+
badge.style.letterSpacing = "0.2em";
|
|
445
|
+
if (tone === "accent") {
|
|
446
|
+
badge.style.border = `1px solid ${this.toRgba(accent, 0.6)}`;
|
|
447
|
+
badge.style.background = this.toRgba(accent, 0.4);
|
|
448
|
+
badge.style.color = "#f5f7ff";
|
|
449
|
+
} else {
|
|
450
|
+
badge.style.border = "1px solid rgba(255, 255, 255, 0.2)";
|
|
451
|
+
badge.style.background = "rgba(255, 255, 255, 0.08)";
|
|
452
|
+
badge.style.color = "rgba(255, 255, 255, 0.8)";
|
|
453
|
+
}
|
|
454
|
+
return badge;
|
|
455
|
+
};
|
|
456
|
+
if (isInsight) {
|
|
457
|
+
badgeRow.appendChild(makeBadge("Insight", "accent"));
|
|
458
|
+
const statusRaw = node.metadata?.status ?? node.metadata?.draftNodeStatus ?? node.metadata?.studyPathStatus;
|
|
459
|
+
if (statusRaw) {
|
|
460
|
+
const formatted = statusRaw.replace(/[_-]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
461
|
+
badgeRow.appendChild(makeBadge(formatted, "muted"));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (badgeRow.childElementCount > 0) {
|
|
465
|
+
wrapper.appendChild(badgeRow);
|
|
466
|
+
}
|
|
467
|
+
const title = document.createElement("div");
|
|
468
|
+
title.textContent = node.label;
|
|
469
|
+
title.style.fontSize = "0.8rem";
|
|
470
|
+
title.style.fontWeight = "600";
|
|
471
|
+
wrapper.appendChild(title);
|
|
472
|
+
if (node.ref) {
|
|
473
|
+
const reference = document.createElement("div");
|
|
474
|
+
reference.textContent = node.ref;
|
|
475
|
+
reference.style.fontSize = "0.65rem";
|
|
476
|
+
reference.style.opacity = "0.7";
|
|
477
|
+
wrapper.appendChild(reference);
|
|
478
|
+
}
|
|
479
|
+
return wrapper;
|
|
480
|
+
}
|
|
481
|
+
toRgba(color, alpha) {
|
|
482
|
+
const r = Math.round(color.r * 255);
|
|
483
|
+
const g = Math.round(color.g * 255);
|
|
484
|
+
const b = Math.round(color.b * 255);
|
|
485
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
354
486
|
}
|
|
355
487
|
highlightNode(nodeId) {
|
|
356
488
|
this.setHoveredNode(nodeId);
|
|
@@ -365,14 +497,14 @@ var NodeRenderer = class {
|
|
|
365
497
|
}
|
|
366
498
|
getNodePosition(nodeId) {
|
|
367
499
|
const state = this.nodeStates.get(nodeId);
|
|
368
|
-
return state ? state.
|
|
500
|
+
return state ? state.sprite.position.clone() : null;
|
|
369
501
|
}
|
|
370
502
|
getNodeObject(nodeId) {
|
|
371
503
|
const state = this.nodeStates.get(nodeId);
|
|
372
|
-
return state?.
|
|
504
|
+
return state?.sprite ?? null;
|
|
373
505
|
}
|
|
374
506
|
getNodeObjects() {
|
|
375
|
-
return Array.from(this.nodeStates.values()).map((state) => state.
|
|
507
|
+
return Array.from(this.nodeStates.values()).map((state) => state.sprite);
|
|
376
508
|
}
|
|
377
509
|
setHoveredNode(nodeId) {
|
|
378
510
|
if (this.hoveredNodeId === nodeId) return;
|
|
@@ -410,13 +542,16 @@ var NodeRenderer = class {
|
|
|
410
542
|
const drift = Math.sin(elapsed * this.config.ambientMotionSpeed + state.phase) * this.config.ambientMotionAmplitude;
|
|
411
543
|
const driftX = Math.cos(elapsed * this.config.ambientMotionSpeed * 0.6 + state.phase) * this.config.ambientMotionAmplitude * 0.45;
|
|
412
544
|
const driftZ = Math.sin(elapsed * this.config.ambientMotionSpeed * 0.4 + state.phase) * this.config.ambientMotionAmplitude * 0.35;
|
|
413
|
-
state.
|
|
545
|
+
state.sprite.position.set(
|
|
414
546
|
state.basePosition.x + driftX,
|
|
415
547
|
state.basePosition.y + drift,
|
|
416
548
|
state.basePosition.z + driftZ
|
|
417
549
|
);
|
|
418
550
|
} else {
|
|
419
|
-
state.
|
|
551
|
+
state.sprite.position.copy(state.basePosition);
|
|
552
|
+
}
|
|
553
|
+
if (state.label) {
|
|
554
|
+
state.label.position.copy(state.sprite.position).add(this.labelOffset);
|
|
420
555
|
}
|
|
421
556
|
const hoverScale = state.hovered ? this.config.hoverScale : 1;
|
|
422
557
|
const selectedScale = state.selected ? this.config.selectedScale : 1;
|
|
@@ -430,17 +565,42 @@ var NodeRenderer = class {
|
|
|
430
565
|
}
|
|
431
566
|
}
|
|
432
567
|
const targetScale = state.baseScale * hoverScale * selectedScale * (1 + pulseScale);
|
|
433
|
-
const currentScale = state.
|
|
568
|
+
const currentScale = state.sprite.scale.x;
|
|
434
569
|
const nextScale = currentScale + (targetScale - currentScale) * 0.18;
|
|
435
|
-
state.
|
|
436
|
-
const material = state.
|
|
437
|
-
const
|
|
438
|
-
|
|
570
|
+
state.sprite.scale.setScalar(nextScale);
|
|
571
|
+
const material = state.material;
|
|
572
|
+
const baseOpacity = 0.78;
|
|
573
|
+
const hoverOpacity = Math.min(0.95, baseOpacity + 0.12);
|
|
574
|
+
const selectedOpacity = 1;
|
|
575
|
+
material.opacity = state.selected ? selectedOpacity : state.hovered ? hoverOpacity : baseOpacity;
|
|
576
|
+
material.color.copy(state.baseColor);
|
|
577
|
+
if (state.selected) {
|
|
578
|
+
material.color.lerp(new THREE.Color("#ffffff"), 0.25);
|
|
579
|
+
}
|
|
439
580
|
});
|
|
440
581
|
}
|
|
441
582
|
dispose() {
|
|
442
583
|
this.clear();
|
|
443
584
|
this.scene.remove(this.group);
|
|
585
|
+
this.glowTexture?.dispose();
|
|
586
|
+
this.glowTexture = null;
|
|
587
|
+
}
|
|
588
|
+
createGlowTexture() {
|
|
589
|
+
const size = 256;
|
|
590
|
+
const canvas = document.createElement("canvas");
|
|
591
|
+
canvas.width = size;
|
|
592
|
+
canvas.height = size;
|
|
593
|
+
const ctx = canvas.getContext("2d");
|
|
594
|
+
if (!ctx) return null;
|
|
595
|
+
const gradient = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
|
|
596
|
+
gradient.addColorStop(0, "rgba(255,255,255,0.95)");
|
|
597
|
+
gradient.addColorStop(0.4, "rgba(255,255,255,0.45)");
|
|
598
|
+
gradient.addColorStop(1, "rgba(255,255,255,0)");
|
|
599
|
+
ctx.fillStyle = gradient;
|
|
600
|
+
ctx.fillRect(0, 0, size, size);
|
|
601
|
+
const texture = new THREE.CanvasTexture(canvas);
|
|
602
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
603
|
+
return texture;
|
|
444
604
|
}
|
|
445
605
|
};
|
|
446
606
|
var EdgeRenderer = class {
|
|
@@ -564,6 +724,35 @@ var EdgeRenderer = class {
|
|
|
564
724
|
|
|
565
725
|
// src/visualization/layouts/fuzzy-layout.ts
|
|
566
726
|
var GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));
|
|
727
|
+
var ATLAS_POSITION_OVERRIDES = {
|
|
728
|
+
uap: [6, 2, 2],
|
|
729
|
+
ez1: [2, 4, 0],
|
|
730
|
+
neph: [-3, 3, 2],
|
|
731
|
+
jude6: [-1, 1.5, 2.5],
|
|
732
|
+
"2p24": [1.2, 0.3, 2.6],
|
|
733
|
+
aiimg: [4.6, -1.6, 1.8],
|
|
734
|
+
rev13: [2.7, -2.8, 0.6],
|
|
735
|
+
xhuman: [0.4, -3.4, -1.8],
|
|
736
|
+
dan243: [-1.6, -2.1, -2.9],
|
|
737
|
+
llm: [3.4, 0.3, -2.4],
|
|
738
|
+
babel: [0.9, 1.4, -3.4],
|
|
739
|
+
warfare: [-4.6, 0.4, -1.5],
|
|
740
|
+
eph612: [-5.5, -1.7, 0.5],
|
|
741
|
+
berea: [-0.2, 4.6, -1.3],
|
|
742
|
+
pharm: [-3.1, -2.4, 1.2],
|
|
743
|
+
testsp: [5.4, 3.2, 1.6]
|
|
744
|
+
};
|
|
745
|
+
function generateSpherePosition(index, total, radius) {
|
|
746
|
+
if (total <= 1) {
|
|
747
|
+
return [0, 0, 0];
|
|
748
|
+
}
|
|
749
|
+
const offset = 2 / total;
|
|
750
|
+
const increment = Math.PI * (3 - Math.sqrt(5));
|
|
751
|
+
const y = 1 - index * offset;
|
|
752
|
+
const r = Math.sqrt(Math.max(0, 1 - y * y));
|
|
753
|
+
const phi = index * increment;
|
|
754
|
+
return [Math.cos(phi) * r * radius, y * radius, Math.sin(phi) * r * radius];
|
|
755
|
+
}
|
|
567
756
|
function hashString(input) {
|
|
568
757
|
let hash = 2166136261;
|
|
569
758
|
for (let i = 0; i < input.length; i += 1) {
|
|
@@ -585,17 +774,53 @@ function buildSeed(baseSeed, nodeKey) {
|
|
|
585
774
|
return mulberry32(hashString(`${baseSeed}:${nodeKey}`));
|
|
586
775
|
}
|
|
587
776
|
function applyFuzzyLayout(nodes, options = {}) {
|
|
588
|
-
const mode = options.mode ?? "
|
|
777
|
+
const mode = options.mode ?? "atlas";
|
|
589
778
|
if (mode === "positioned") {
|
|
590
779
|
return nodes;
|
|
591
780
|
}
|
|
592
|
-
const needsLayout =
|
|
593
|
-
if (!needsLayout) {
|
|
781
|
+
const needsLayout = nodes.some((node) => !node.position);
|
|
782
|
+
if (mode === "auto" && !needsLayout) {
|
|
594
783
|
return nodes;
|
|
595
784
|
}
|
|
785
|
+
const spread = options.spread ?? 1;
|
|
786
|
+
const overrides = { ...ATLAS_POSITION_OVERRIDES, ...options.overrides ?? {} };
|
|
787
|
+
if (mode === "atlas" || mode === "auto") {
|
|
788
|
+
const baseRadius2 = (options.radius ?? 12) * spread;
|
|
789
|
+
const insightRadius = (options.insightRadius ?? Math.max(5, baseRadius2 * 0.4)) * spread;
|
|
790
|
+
const canonicalNodes = nodes.filter(
|
|
791
|
+
(node) => node.tier !== "insight" && node.domain !== "insight"
|
|
792
|
+
);
|
|
793
|
+
const insightNodes = nodes.filter(
|
|
794
|
+
(node) => node.tier === "insight" || node.domain === "insight"
|
|
795
|
+
);
|
|
796
|
+
const canonicalPositions = /* @__PURE__ */ new Map();
|
|
797
|
+
const insightPositions = /* @__PURE__ */ new Map();
|
|
798
|
+
canonicalNodes.forEach((node, index) => {
|
|
799
|
+
canonicalPositions.set(
|
|
800
|
+
node.id,
|
|
801
|
+
generateSpherePosition(index, canonicalNodes.length, baseRadius2)
|
|
802
|
+
);
|
|
803
|
+
});
|
|
804
|
+
insightNodes.forEach((node, index) => {
|
|
805
|
+
insightPositions.set(
|
|
806
|
+
node.id,
|
|
807
|
+
generateSpherePosition(index, insightNodes.length || 1, insightRadius)
|
|
808
|
+
);
|
|
809
|
+
});
|
|
810
|
+
return nodes.map((node) => {
|
|
811
|
+
const override = overrides[node.id] ?? overrides[node.slug];
|
|
812
|
+
if (node.position && !override) {
|
|
813
|
+
return node;
|
|
814
|
+
}
|
|
815
|
+
if (override) {
|
|
816
|
+
return { ...node, position: [...override] };
|
|
817
|
+
}
|
|
818
|
+
const fallback = (node.tier === "insight" || node.domain === "insight" ? insightPositions.get(node.id) : canonicalPositions.get(node.id)) ?? [0, 0, 0];
|
|
819
|
+
return { ...node, position: fallback };
|
|
820
|
+
});
|
|
821
|
+
}
|
|
596
822
|
const baseSeed = options.seed ?? "omi-neuron-web";
|
|
597
823
|
const count = Math.max(nodes.length, 1);
|
|
598
|
-
const spread = options.spread ?? 1.2;
|
|
599
824
|
const baseRadius = (options.radius ?? Math.max(4, Math.sqrt(count) * 2.4)) * spread;
|
|
600
825
|
const jitter = (options.jitter ?? baseRadius * 0.12) * spread;
|
|
601
826
|
const zSpread = (options.zSpread ?? baseRadius * 0.6) * spread;
|
|
@@ -798,6 +1023,8 @@ function NeuronWeb({
|
|
|
798
1023
|
graphData,
|
|
799
1024
|
className,
|
|
800
1025
|
style,
|
|
1026
|
+
fullHeight,
|
|
1027
|
+
isFullScreen,
|
|
801
1028
|
isLoading,
|
|
802
1029
|
error,
|
|
803
1030
|
renderEmptyState,
|
|
@@ -837,17 +1064,18 @@ function NeuronWeb({
|
|
|
837
1064
|
}, [performanceMode, graphData.nodes.length]);
|
|
838
1065
|
const sceneManager = useSceneManager(containerRef, {
|
|
839
1066
|
backgroundColor: resolvedTheme.colors.background,
|
|
1067
|
+
cameraFov: 52,
|
|
840
1068
|
cameraPosition: [4, 8, 20],
|
|
841
1069
|
cameraTarget: [0, 0, 0],
|
|
842
1070
|
minZoom: 4,
|
|
843
1071
|
maxZoom: 42,
|
|
844
1072
|
enableStarfield: resolvedTheme.effects.starfieldEnabled,
|
|
845
|
-
starfieldCount: 1200,
|
|
1073
|
+
starfieldCount: resolvedPerformanceMode === "normal" ? 1200 : 700,
|
|
846
1074
|
starfieldColor: resolvedTheme.effects.starfieldColor,
|
|
847
1075
|
pixelRatioCap: 2,
|
|
848
|
-
ambientLightIntensity: 0.
|
|
849
|
-
keyLightIntensity:
|
|
850
|
-
fillLightIntensity: 0.
|
|
1076
|
+
ambientLightIntensity: 0.9,
|
|
1077
|
+
keyLightIntensity: 0.6,
|
|
1078
|
+
fillLightIntensity: 0.4,
|
|
851
1079
|
fogEnabled: resolvedTheme.effects.fogEnabled,
|
|
852
1080
|
fogColor: resolvedTheme.effects.fogColor,
|
|
853
1081
|
fogNear: resolvedTheme.effects.fogNear,
|
|
@@ -858,16 +1086,22 @@ function NeuronWeb({
|
|
|
858
1086
|
return new NodeRenderer(sceneManager.scene, {
|
|
859
1087
|
domainColors: resolvedTheme.colors.domainColors,
|
|
860
1088
|
defaultColor: resolvedTheme.colors.defaultDomainColor,
|
|
861
|
-
baseScale:
|
|
1089
|
+
baseScale: 1.15,
|
|
862
1090
|
tierScales: {
|
|
863
|
-
primary: 1.
|
|
864
|
-
secondary: 1.
|
|
865
|
-
tertiary:
|
|
866
|
-
insight: 1
|
|
1091
|
+
primary: 1.25,
|
|
1092
|
+
secondary: 1.1,
|
|
1093
|
+
tertiary: 0.95,
|
|
1094
|
+
insight: 1.05
|
|
867
1095
|
},
|
|
868
1096
|
glowIntensity: resolvedTheme.effects.glowEnabled ? resolvedTheme.effects.glowIntensity : 0,
|
|
869
|
-
labelDistance:
|
|
870
|
-
maxVisibleLabels:
|
|
1097
|
+
labelDistance: resolvedPerformanceMode === "normal" ? 26 : 0,
|
|
1098
|
+
maxVisibleLabels: resolvedPerformanceMode === "normal" ? 80 : 0,
|
|
1099
|
+
labelOffset: [0, 0.65, 0],
|
|
1100
|
+
labelFontFamily: resolvedTheme.typography.labelFontFamily,
|
|
1101
|
+
labelFontSize: resolvedTheme.typography.labelFontSize,
|
|
1102
|
+
labelFontWeight: resolvedTheme.typography.labelFontWeight,
|
|
1103
|
+
labelTextColor: resolvedTheme.colors.labelText,
|
|
1104
|
+
labelBackground: resolvedTheme.colors.labelBackground,
|
|
871
1105
|
ambientMotionEnabled: resolvedTheme.effects.ambientMotionEnabled && resolvedPerformanceMode === "normal",
|
|
872
1106
|
ambientMotionAmplitude: resolvedTheme.effects.ambientMotionAmplitude,
|
|
873
1107
|
ambientMotionSpeed: resolvedTheme.effects.ambientMotionSpeed,
|
|
@@ -883,7 +1117,7 @@ function NeuronWeb({
|
|
|
883
1117
|
defaultColor: resolvedTheme.colors.edgeDefault,
|
|
884
1118
|
activeColor: resolvedTheme.colors.edgeActive,
|
|
885
1119
|
selectedColor: resolvedTheme.colors.edgeSelected,
|
|
886
|
-
baseOpacity: 0.
|
|
1120
|
+
baseOpacity: 0.45,
|
|
887
1121
|
strengthOpacityScale: true,
|
|
888
1122
|
edgeFlowEnabled: resolvedTheme.effects.edgeFlowEnabled && resolvedPerformanceMode === "normal",
|
|
889
1123
|
edgeFlowSpeed: resolvedTheme.effects.edgeFlowSpeed
|
|
@@ -964,6 +1198,7 @@ function NeuronWeb({
|
|
|
964
1198
|
if (!sceneManager || !nodeRenderer || !edgeRenderer) return;
|
|
965
1199
|
return sceneManager.addFrameListener((delta, elapsed) => {
|
|
966
1200
|
nodeRenderer.update(delta, elapsed);
|
|
1201
|
+
nodeRenderer.updateLabelVisibility(sceneManager.camera);
|
|
967
1202
|
edgeRenderer.update(delta, elapsed);
|
|
968
1203
|
animationController?.update();
|
|
969
1204
|
if (hoverCardRef.current && hoveredNodeId) {
|
|
@@ -1084,14 +1319,30 @@ function NeuronWeb({
|
|
|
1084
1319
|
if (!resolvedNodes.length) {
|
|
1085
1320
|
return /* @__PURE__ */ jsx("div", { className, style, "aria-label": ariaLabel, children: renderEmptyState ? renderEmptyState() : /* @__PURE__ */ jsx("div", { children: "No data" }) });
|
|
1086
1321
|
}
|
|
1322
|
+
const resolvedStyle = {
|
|
1323
|
+
position: isFullScreen ? "fixed" : "relative",
|
|
1324
|
+
inset: isFullScreen ? 0 : void 0,
|
|
1325
|
+
width: isFullScreen ? "100vw" : "100%",
|
|
1326
|
+
height: isFullScreen ? "100vh" : "100%",
|
|
1327
|
+
minHeight: !isFullScreen && fullHeight ? "100vh" : void 0,
|
|
1328
|
+
overflow: "hidden",
|
|
1329
|
+
background: resolvedTheme.colors.background,
|
|
1330
|
+
...style
|
|
1331
|
+
};
|
|
1087
1332
|
return /* @__PURE__ */ jsxs(
|
|
1088
1333
|
"div",
|
|
1089
1334
|
{
|
|
1090
1335
|
className,
|
|
1091
|
-
style:
|
|
1336
|
+
style: resolvedStyle,
|
|
1092
1337
|
"aria-label": ariaLabel,
|
|
1093
1338
|
children: [
|
|
1094
|
-
/* @__PURE__ */ jsx(
|
|
1339
|
+
/* @__PURE__ */ jsx(
|
|
1340
|
+
"div",
|
|
1341
|
+
{
|
|
1342
|
+
ref: containerRef,
|
|
1343
|
+
style: { position: "absolute", inset: 0, width: "100%", height: "100%" }
|
|
1344
|
+
}
|
|
1345
|
+
),
|
|
1095
1346
|
hoverCardEnabled && hoveredNode && /* @__PURE__ */ jsx(
|
|
1096
1347
|
"div",
|
|
1097
1348
|
{
|
|
@@ -1190,5 +1441,5 @@ var ThemeEngine = class {
|
|
|
1190
1441
|
};
|
|
1191
1442
|
|
|
1192
1443
|
export { DEFAULT_THEME, NeuronWeb, SceneManager, ThemeEngine, applyFuzzyLayout };
|
|
1193
|
-
//# sourceMappingURL=chunk-
|
|
1194
|
-
//# sourceMappingURL=chunk-
|
|
1444
|
+
//# sourceMappingURL=chunk-YFJMQCGE.js.map
|
|
1445
|
+
//# sourceMappingURL=chunk-YFJMQCGE.js.map
|