@omiron33/omi-neuron-web 0.2.16 → 0.2.19
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 +36 -1
- package/dist/{NeuronWeb-CZgKU1-X.d.ts → NeuronWeb-CnamAVi_.d.ts} +24 -3
- package/dist/{NeuronWeb-JVrxQksC.d.cts → NeuronWeb-DkNLfI1I.d.cts} +24 -3
- package/dist/{chunk-MVSWEBGR.cjs → chunk-OUO3CKBM.cjs} +169 -12
- package/dist/chunk-OUO3CKBM.cjs.map +1 -0
- package/dist/{chunk-MEQRF25L.js → chunk-YWCTYBMF.js} +170 -13
- package/dist/chunk-YWCTYBMF.js.map +1 -0
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/visualization/index.cjs +6 -6
- package/dist/visualization/index.d.cts +2 -2
- package/dist/visualization/index.d.ts +2 -2
- package/dist/visualization/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MEQRF25L.js.map +0 -1
- package/dist/chunk-MVSWEBGR.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -686,7 +686,7 @@ export interface NeuronWebProps {
|
|
|
686
686
|
|
|
687
687
|
Props currently used inside `NeuronWeb` (others are reserved for future use):
|
|
688
688
|
|
|
689
|
-
- Used: `graphData`, `className`, `style`, `fullHeight`, `isFullScreen`, `isLoading`, `error`, `renderEmptyState`, `renderLoadingState`, `ariaLabel`, `theme`, `layout`, `renderNodeHover`, `renderNodeDetail`, `hoverCard`, `clickCard`, `clickZoom`, `cardsMode`, `onNodeHover`, `onNodeClick`, `onNodeDoubleClick`, `onNodeFocused`, `onBackgroundClick`, `performanceMode`, `focusNodeSlug`, `onFocusConsumed`, `visibleNodeSlugs`.
|
|
689
|
+
- Used: `graphData`, `className`, `style`, `fullHeight`, `isFullScreen`, `isLoading`, `error`, `renderEmptyState`, `renderLoadingState`, `ariaLabel`, `theme`, `layout`, `renderNodeHover`, `renderNodeDetail`, `hoverCard`, `clickCard`, `clickZoom`, `cardsMode`, `onNodeHover`, `onNodeClick`, `onNodeDoubleClick`, `onNodeFocused`, `onBackgroundClick`, `performanceMode`, `focusNodeSlug`, `onFocusConsumed`, `visibleNodeSlugs`, `studyPathRequest`, `onStudyPathComplete`.
|
|
690
690
|
- Used: `cameraFit` (auto-fit bounds to a viewport fraction).
|
|
691
691
|
- Reserved (declared but not used in the component yet): `selectedNode`, `onEdgeClick`, `onCameraChange`, `studyPathRequest`, `onStudyPathComplete`, `domainColors`, `graphData.storyBeats`.
|
|
692
692
|
|
|
@@ -847,6 +847,37 @@ Semantics:
|
|
|
847
847
|
<NeuronWeb graphData={graphData} visibleNodeSlugs={[]} />;
|
|
848
848
|
```
|
|
849
849
|
|
|
850
|
+
### Study path playback (follow the path between nodes)
|
|
851
|
+
|
|
852
|
+
Use `studyPathRequest` to step through an ordered list of nodes. Each step:
|
|
853
|
+
- selects the node
|
|
854
|
+
- tweens the camera to the node (if `clickZoom.enabled`)
|
|
855
|
+
- highlights the edge between the current and next step
|
|
856
|
+
|
|
857
|
+
```tsx
|
|
858
|
+
<NeuronWeb
|
|
859
|
+
graphData={graphData}
|
|
860
|
+
studyPathRequest={{
|
|
861
|
+
steps: [
|
|
862
|
+
{ nodeSlug: 'uap', label: 'Start' },
|
|
863
|
+
{ nodeSlug: 'neph', label: 'Next' },
|
|
864
|
+
{ nodeSlug: 'jude6', label: 'Finish' },
|
|
865
|
+
],
|
|
866
|
+
stepDurationMs: 4200,
|
|
867
|
+
}}
|
|
868
|
+
onStudyPathComplete={() => console.log('study path done')}
|
|
869
|
+
/>
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
Fallback form (two-step path):
|
|
873
|
+
|
|
874
|
+
```tsx
|
|
875
|
+
<NeuronWeb
|
|
876
|
+
graphData={graphData}
|
|
877
|
+
studyPathRequest={{ fromNodeId: 'uap', toNodeId: 'neph' }}
|
|
878
|
+
/>
|
|
879
|
+
```
|
|
880
|
+
|
|
850
881
|
### Click cards + click zoom
|
|
851
882
|
|
|
852
883
|
Enable a persistent card on click and optional zoom-to-node behavior:
|
|
@@ -859,6 +890,10 @@ Enable a persistent card on click and optional zoom-to-node behavior:
|
|
|
859
890
|
/>
|
|
860
891
|
```
|
|
861
892
|
|
|
893
|
+
Orbit pivot behavior:
|
|
894
|
+
- On pointer down, the orbit target shifts to the cursor (or the node under it),
|
|
895
|
+
so rotating after a focus doesn’t stay locked to the previously focused node.
|
|
896
|
+
|
|
862
897
|
### Card mode (global override)
|
|
863
898
|
|
|
864
899
|
`cardsMode` lets you force card behavior irrespective of `hoverCard.enabled` or `clickCard.enabled`
|
|
@@ -6,9 +6,30 @@ interface NeuronStoryBeat {
|
|
|
6
6
|
label: string;
|
|
7
7
|
nodeIds: string[];
|
|
8
8
|
}
|
|
9
|
+
interface StudyPathStep {
|
|
10
|
+
nodeSlug?: string;
|
|
11
|
+
nodeId?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
summary?: string;
|
|
14
|
+
}
|
|
9
15
|
interface StudyPathRequest {
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Ordered steps to follow (slugs or ids). When provided, this takes precedence.
|
|
18
|
+
*/
|
|
19
|
+
steps?: StudyPathStep[];
|
|
20
|
+
/**
|
|
21
|
+
* Optional label for the study path (consumer UI usage).
|
|
22
|
+
*/
|
|
23
|
+
label?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Time to hold each step before advancing (ms). Defaults to 4200.
|
|
26
|
+
*/
|
|
27
|
+
stepDurationMs?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Minimal fallback when steps are not provided.
|
|
30
|
+
*/
|
|
31
|
+
fromNodeId?: string;
|
|
32
|
+
toNodeId?: string;
|
|
12
33
|
}
|
|
13
34
|
interface NeuronWebTheme {
|
|
14
35
|
colors: {
|
|
@@ -137,6 +158,6 @@ interface NeuronWebProps {
|
|
|
137
158
|
ariaLabel?: string;
|
|
138
159
|
}
|
|
139
160
|
|
|
140
|
-
declare function NeuronWeb({ graphData, className, style, fullHeight, isFullScreen, isLoading, error, focusNodeSlug, onFocusConsumed, visibleNodeSlugs, renderEmptyState, renderLoadingState, ariaLabel, theme, layout, cameraFit, cardsMode, clickCard, clickZoom, renderNodeHover, renderNodeDetail, hoverCard, onNodeHover, onNodeClick, onNodeDoubleClick, onNodeFocused, onBackgroundClick, performanceMode, }: NeuronWebProps): React__default.ReactElement;
|
|
161
|
+
declare function NeuronWeb({ graphData, className, style, fullHeight, isFullScreen, isLoading, error, focusNodeSlug, onFocusConsumed, visibleNodeSlugs, renderEmptyState, renderLoadingState, ariaLabel, theme, layout, cameraFit, cardsMode, clickCard, clickZoom, studyPathRequest, onStudyPathComplete, renderNodeHover, renderNodeDetail, hoverCard, onNodeHover, onNodeClick, onNodeDoubleClick, onNodeFocused, onBackgroundClick, performanceMode, }: NeuronWebProps): React__default.ReactElement;
|
|
141
162
|
|
|
142
163
|
export { type CameraFitOptions as C, type HoverCardOptions as H, type NeuronWebTheme as N, type NeuronWebThemeOverride as a, type NeuronLayoutOptions as b, NeuronWeb as c, type NeuronWebProps as d, type NeuronLayoutMode as e, type ClickCardOptions as f, type ClickZoomOptions as g, type CardsMode as h };
|
|
@@ -6,9 +6,30 @@ interface NeuronStoryBeat {
|
|
|
6
6
|
label: string;
|
|
7
7
|
nodeIds: string[];
|
|
8
8
|
}
|
|
9
|
+
interface StudyPathStep {
|
|
10
|
+
nodeSlug?: string;
|
|
11
|
+
nodeId?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
summary?: string;
|
|
14
|
+
}
|
|
9
15
|
interface StudyPathRequest {
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Ordered steps to follow (slugs or ids). When provided, this takes precedence.
|
|
18
|
+
*/
|
|
19
|
+
steps?: StudyPathStep[];
|
|
20
|
+
/**
|
|
21
|
+
* Optional label for the study path (consumer UI usage).
|
|
22
|
+
*/
|
|
23
|
+
label?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Time to hold each step before advancing (ms). Defaults to 4200.
|
|
26
|
+
*/
|
|
27
|
+
stepDurationMs?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Minimal fallback when steps are not provided.
|
|
30
|
+
*/
|
|
31
|
+
fromNodeId?: string;
|
|
32
|
+
toNodeId?: string;
|
|
12
33
|
}
|
|
13
34
|
interface NeuronWebTheme {
|
|
14
35
|
colors: {
|
|
@@ -137,6 +158,6 @@ interface NeuronWebProps {
|
|
|
137
158
|
ariaLabel?: string;
|
|
138
159
|
}
|
|
139
160
|
|
|
140
|
-
declare function NeuronWeb({ graphData, className, style, fullHeight, isFullScreen, isLoading, error, focusNodeSlug, onFocusConsumed, visibleNodeSlugs, renderEmptyState, renderLoadingState, ariaLabel, theme, layout, cameraFit, cardsMode, clickCard, clickZoom, renderNodeHover, renderNodeDetail, hoverCard, onNodeHover, onNodeClick, onNodeDoubleClick, onNodeFocused, onBackgroundClick, performanceMode, }: NeuronWebProps): React__default.ReactElement;
|
|
161
|
+
declare function NeuronWeb({ graphData, className, style, fullHeight, isFullScreen, isLoading, error, focusNodeSlug, onFocusConsumed, visibleNodeSlugs, renderEmptyState, renderLoadingState, ariaLabel, theme, layout, cameraFit, cardsMode, clickCard, clickZoom, studyPathRequest, onStudyPathComplete, renderNodeHover, renderNodeDetail, hoverCard, onNodeHover, onNodeClick, onNodeDoubleClick, onNodeFocused, onBackgroundClick, performanceMode, }: NeuronWebProps): React__default.ReactElement;
|
|
141
162
|
|
|
142
163
|
export { type CameraFitOptions as C, type HoverCardOptions as H, type NeuronWebTheme as N, type NeuronWebThemeOverride as a, type NeuronLayoutOptions as b, NeuronWeb as c, type NeuronWebProps as d, type NeuronLayoutMode as e, type ClickCardOptions as f, type ClickZoomOptions as g, type CardsMode as h };
|
|
@@ -1061,6 +1061,8 @@ function NeuronWeb({
|
|
|
1061
1061
|
cardsMode,
|
|
1062
1062
|
clickCard,
|
|
1063
1063
|
clickZoom,
|
|
1064
|
+
studyPathRequest,
|
|
1065
|
+
onStudyPathComplete,
|
|
1064
1066
|
renderNodeHover,
|
|
1065
1067
|
renderNodeDetail,
|
|
1066
1068
|
hoverCard,
|
|
@@ -1076,9 +1078,12 @@ function NeuronWeb({
|
|
|
1076
1078
|
const clickCardRef = react.useRef(null);
|
|
1077
1079
|
const [hoveredNodeId, setHoveredNodeId] = react.useState(null);
|
|
1078
1080
|
const [selectedNodeId, setSelectedNodeId] = react.useState(null);
|
|
1081
|
+
const [studyPathPlayer, setStudyPathPlayer] = react.useState(null);
|
|
1079
1082
|
const fitStateRef = react.useRef({ hasFit: false, signature: "" });
|
|
1080
1083
|
const firstFilterChangeRef = react.useRef(true);
|
|
1081
1084
|
const [filterTransitioning, setFilterTransitioning] = react.useState(false);
|
|
1085
|
+
const pathEdgeIdsRef = react.useRef([]);
|
|
1086
|
+
const focusEdgesRef = react.useRef(null);
|
|
1082
1087
|
const filteredGraphData = react.useMemo(() => {
|
|
1083
1088
|
if (visibleNodeSlugs === null || visibleNodeSlugs === void 0) {
|
|
1084
1089
|
return graphData;
|
|
@@ -1250,6 +1255,43 @@ function NeuronWeb({
|
|
|
1250
1255
|
});
|
|
1251
1256
|
return map;
|
|
1252
1257
|
}, [workingGraph.edges]);
|
|
1258
|
+
const applyFocusEdges = react.useCallback(
|
|
1259
|
+
(edgeIds) => {
|
|
1260
|
+
if (!edgeRenderer) return;
|
|
1261
|
+
focusEdgesRef.current = edgeIds;
|
|
1262
|
+
const merged = new Set(edgeIds ?? []);
|
|
1263
|
+
pathEdgeIdsRef.current.forEach((id) => merged.add(id));
|
|
1264
|
+
edgeRenderer.setFocusEdges(merged.size ? Array.from(merged) : null);
|
|
1265
|
+
},
|
|
1266
|
+
[edgeRenderer]
|
|
1267
|
+
);
|
|
1268
|
+
const resolvedStudyPathSteps = react.useMemo(() => {
|
|
1269
|
+
if (!studyPathRequest) return null;
|
|
1270
|
+
if (studyPathRequest.steps && studyPathRequest.steps.length) {
|
|
1271
|
+
return studyPathRequest.steps;
|
|
1272
|
+
}
|
|
1273
|
+
if (studyPathRequest.fromNodeId && studyPathRequest.toNodeId) {
|
|
1274
|
+
return [
|
|
1275
|
+
{ nodeId: studyPathRequest.fromNodeId },
|
|
1276
|
+
{ nodeId: studyPathRequest.toNodeId }
|
|
1277
|
+
];
|
|
1278
|
+
}
|
|
1279
|
+
return null;
|
|
1280
|
+
}, [studyPathRequest]);
|
|
1281
|
+
react.useEffect(() => {
|
|
1282
|
+
if (!resolvedStudyPathSteps || resolvedStudyPathSteps.length === 0) {
|
|
1283
|
+
setStudyPathPlayer(null);
|
|
1284
|
+
pathEdgeIdsRef.current = [];
|
|
1285
|
+
applyFocusEdges(focusEdgesRef.current);
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
setStudyPathPlayer({
|
|
1289
|
+
steps: resolvedStudyPathSteps,
|
|
1290
|
+
index: 0,
|
|
1291
|
+
playing: true,
|
|
1292
|
+
stepDurationMs: studyPathRequest?.stepDurationMs ?? 4200
|
|
1293
|
+
});
|
|
1294
|
+
}, [resolvedStudyPathSteps, studyPathRequest?.stepDurationMs, applyFocusEdges]);
|
|
1253
1295
|
const nodeByIdentifier = react.useMemo(() => {
|
|
1254
1296
|
const map = /* @__PURE__ */ new Map();
|
|
1255
1297
|
resolvedNodes.forEach((node) => {
|
|
@@ -1266,6 +1308,70 @@ function NeuronWeb({
|
|
|
1266
1308
|
const clickCardOffset = clickCard?.offset ?? [24, 24];
|
|
1267
1309
|
const clickCardWidth = clickCard?.width ?? 320;
|
|
1268
1310
|
const clickZoomEnabled = clickZoom?.enabled ?? true;
|
|
1311
|
+
react.useEffect(() => {
|
|
1312
|
+
if (!studyPathPlayer) return;
|
|
1313
|
+
const step = studyPathPlayer.steps[studyPathPlayer.index];
|
|
1314
|
+
const stepKey = step?.nodeSlug ?? step?.nodeId ?? null;
|
|
1315
|
+
const node = stepKey ? nodeByIdentifier.get(stepKey) ?? null : null;
|
|
1316
|
+
if (node && nodeRenderer && edgeRenderer) {
|
|
1317
|
+
setSelectedNodeId(node.id);
|
|
1318
|
+
nodeRenderer.setSelectedNode(node.id);
|
|
1319
|
+
nodeRenderer.pulseNode(node.id);
|
|
1320
|
+
if (clickZoomEnabled) {
|
|
1321
|
+
const nodePosition = nodeRenderer.getNodePosition(node.id);
|
|
1322
|
+
if (nodePosition) {
|
|
1323
|
+
animationController?.focusOnNode(nodePosition, () => {
|
|
1324
|
+
if (onNodeFocused) onNodeFocused(node);
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
const slug = node.slug;
|
|
1329
|
+
applyFocusEdges(slug ? edgesBySlug.get(slug) ?? [] : []);
|
|
1330
|
+
}
|
|
1331
|
+
const nextStep = studyPathPlayer.index < studyPathPlayer.steps.length - 1 ? studyPathPlayer.steps[studyPathPlayer.index + 1] : null;
|
|
1332
|
+
const currentSlug = node?.slug ?? (step?.nodeSlug ?? null);
|
|
1333
|
+
const nextKey = nextStep?.nodeSlug ?? nextStep?.nodeId ?? null;
|
|
1334
|
+
const nextNode = nextKey ? nodeByIdentifier.get(nextKey) ?? null : null;
|
|
1335
|
+
const nextSlug = nextNode?.slug ?? (nextStep?.nodeSlug ?? null);
|
|
1336
|
+
if (currentSlug && nextSlug) {
|
|
1337
|
+
pathEdgeIdsRef.current = workingGraph.edges.filter(
|
|
1338
|
+
(edge) => edge.from === currentSlug && edge.to === nextSlug || edge.to === currentSlug && edge.from === nextSlug
|
|
1339
|
+
).map((edge) => edge.id);
|
|
1340
|
+
} else {
|
|
1341
|
+
pathEdgeIdsRef.current = [];
|
|
1342
|
+
}
|
|
1343
|
+
applyFocusEdges(focusEdgesRef.current);
|
|
1344
|
+
}, [
|
|
1345
|
+
studyPathPlayer,
|
|
1346
|
+
nodeByIdentifier,
|
|
1347
|
+
nodeRenderer,
|
|
1348
|
+
edgeRenderer,
|
|
1349
|
+
edgesBySlug,
|
|
1350
|
+
workingGraph.edges,
|
|
1351
|
+
animationController,
|
|
1352
|
+
clickZoomEnabled,
|
|
1353
|
+
onNodeFocused,
|
|
1354
|
+
applyFocusEdges
|
|
1355
|
+
]);
|
|
1356
|
+
react.useEffect(() => {
|
|
1357
|
+
if (!studyPathPlayer || !studyPathPlayer.playing) return;
|
|
1358
|
+
const timer = window.setTimeout(() => {
|
|
1359
|
+
setStudyPathPlayer((prev) => {
|
|
1360
|
+
if (!prev) return prev;
|
|
1361
|
+
if (prev.index >= prev.steps.length - 1) {
|
|
1362
|
+
if (onStudyPathComplete) onStudyPathComplete();
|
|
1363
|
+
return { ...prev, playing: false };
|
|
1364
|
+
}
|
|
1365
|
+
return { ...prev, index: prev.index + 1 };
|
|
1366
|
+
});
|
|
1367
|
+
}, studyPathPlayer.stepDurationMs);
|
|
1368
|
+
return () => window.clearTimeout(timer);
|
|
1369
|
+
}, [studyPathPlayer, onStudyPathComplete]);
|
|
1370
|
+
react.useEffect(() => {
|
|
1371
|
+
if (!studyPathPlayer || studyPathPlayer.playing) return;
|
|
1372
|
+
pathEdgeIdsRef.current = [];
|
|
1373
|
+
applyFocusEdges(focusEdgesRef.current);
|
|
1374
|
+
}, [studyPathPlayer, applyFocusEdges]);
|
|
1269
1375
|
react.useEffect(() => {
|
|
1270
1376
|
if (!sceneManager || !nodeRenderer || !edgeRenderer) return;
|
|
1271
1377
|
nodeRenderer.renderNodes(resolvedNodes);
|
|
@@ -1280,6 +1386,55 @@ function NeuronWeb({
|
|
|
1280
1386
|
if (!sceneManager) return;
|
|
1281
1387
|
sceneManager.updateBackground(resolvedTheme.colors.background);
|
|
1282
1388
|
}, [sceneManager, resolvedTheme.colors.background]);
|
|
1389
|
+
react.useEffect(() => {
|
|
1390
|
+
if (!sceneManager || !nodeRenderer) return;
|
|
1391
|
+
const raycaster = new THREE__namespace.Raycaster();
|
|
1392
|
+
const pointer = new THREE__namespace.Vector2();
|
|
1393
|
+
const tempDir = new THREE__namespace.Vector3();
|
|
1394
|
+
const plane = new THREE__namespace.Plane();
|
|
1395
|
+
const intersection = new THREE__namespace.Vector3();
|
|
1396
|
+
const updatePointer = (event) => {
|
|
1397
|
+
const rect = sceneManager.renderer.domElement.getBoundingClientRect();
|
|
1398
|
+
pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1;
|
|
1399
|
+
pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
1400
|
+
};
|
|
1401
|
+
const onPointerDown = (event) => {
|
|
1402
|
+
updatePointer(event);
|
|
1403
|
+
raycaster.setFromCamera(pointer, sceneManager.camera);
|
|
1404
|
+
const nodes = nodeRenderer.getNodeObjects();
|
|
1405
|
+
if (nodes.length) {
|
|
1406
|
+
const intersects = raycaster.intersectObjects(nodes, true);
|
|
1407
|
+
if (intersects.length) {
|
|
1408
|
+
const hit = intersects[0].object;
|
|
1409
|
+
const nodeId = hit.userData?.nodeId;
|
|
1410
|
+
if (nodeId) {
|
|
1411
|
+
const nodePosition = nodeRenderer.getNodePosition(nodeId);
|
|
1412
|
+
if (nodePosition) {
|
|
1413
|
+
sceneManager.controls.target.copy(nodePosition);
|
|
1414
|
+
sceneManager.controls.update();
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (intersects[0].point) {
|
|
1419
|
+
sceneManager.controls.target.copy(intersects[0].point);
|
|
1420
|
+
sceneManager.controls.update();
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
sceneManager.camera.getWorldDirection(tempDir).normalize();
|
|
1426
|
+
plane.setFromNormalAndCoplanarPoint(tempDir, sceneManager.controls.target.clone());
|
|
1427
|
+
if (raycaster.ray.intersectPlane(plane, intersection)) {
|
|
1428
|
+
sceneManager.controls.target.copy(intersection);
|
|
1429
|
+
sceneManager.controls.update();
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
const dom = sceneManager.renderer.domElement;
|
|
1433
|
+
dom.addEventListener("pointerdown", onPointerDown);
|
|
1434
|
+
return () => {
|
|
1435
|
+
dom.removeEventListener("pointerdown", onPointerDown);
|
|
1436
|
+
};
|
|
1437
|
+
}, [sceneManager, nodeRenderer]);
|
|
1283
1438
|
const cameraFitSuspended = Boolean(selectedNodeId || focusNodeSlug);
|
|
1284
1439
|
react.useEffect(() => {
|
|
1285
1440
|
if (!sceneManager || !animationController || !resolvedCameraFit.enabled) return;
|
|
@@ -1393,9 +1548,9 @@ function NeuronWeb({
|
|
|
1393
1548
|
if (selectedNodeId && !nodeMap.has(selectedNodeId)) {
|
|
1394
1549
|
setSelectedNodeId(null);
|
|
1395
1550
|
nodeRenderer.setSelectedNode(null);
|
|
1396
|
-
|
|
1551
|
+
applyFocusEdges(null);
|
|
1397
1552
|
}
|
|
1398
|
-
}, [selectedNodeId, nodeMap, nodeRenderer, edgeRenderer]);
|
|
1553
|
+
}, [selectedNodeId, nodeMap, nodeRenderer, edgeRenderer, applyFocusEdges]);
|
|
1399
1554
|
react.useEffect(() => {
|
|
1400
1555
|
if (!focusNodeSlug || !nodeRenderer || !edgeRenderer) return;
|
|
1401
1556
|
const node = nodeByIdentifier.get(focusNodeSlug);
|
|
@@ -1406,7 +1561,7 @@ function NeuronWeb({
|
|
|
1406
1561
|
setSelectedNodeId(node.id);
|
|
1407
1562
|
nodeRenderer.setSelectedNode(node.id);
|
|
1408
1563
|
nodeRenderer.pulseNode(node.id);
|
|
1409
|
-
|
|
1564
|
+
applyFocusEdges(node.slug ? edgesBySlug.get(node.slug) ?? [] : []);
|
|
1410
1565
|
if (clickZoomEnabled) {
|
|
1411
1566
|
const nodePosition = nodeRenderer.getNodePosition(node.id);
|
|
1412
1567
|
if (nodePosition) {
|
|
@@ -1429,7 +1584,8 @@ function NeuronWeb({
|
|
|
1429
1584
|
clickZoomEnabled,
|
|
1430
1585
|
animationController,
|
|
1431
1586
|
onNodeFocused,
|
|
1432
|
-
onFocusConsumed
|
|
1587
|
+
onFocusConsumed,
|
|
1588
|
+
applyFocusEdges
|
|
1433
1589
|
]);
|
|
1434
1590
|
react.useEffect(() => {
|
|
1435
1591
|
if (!interactionManager || !nodeRenderer || !edgeRenderer) return;
|
|
@@ -1439,13 +1595,13 @@ function NeuronWeb({
|
|
|
1439
1595
|
nodeRenderer.setHoveredNode(nodeId);
|
|
1440
1596
|
if (nodeId) {
|
|
1441
1597
|
const slug = nodeSlugById.get(nodeId);
|
|
1442
|
-
|
|
1598
|
+
applyFocusEdges(slug ? edgesBySlug.get(slug) ?? [] : []);
|
|
1443
1599
|
} else {
|
|
1444
1600
|
const selectedSlug = selectedNodeId ? nodeSlugById.get(selectedNodeId) : null;
|
|
1445
1601
|
if (selectedSlug) {
|
|
1446
|
-
|
|
1602
|
+
applyFocusEdges(edgesBySlug.get(selectedSlug) ?? []);
|
|
1447
1603
|
} else {
|
|
1448
|
-
|
|
1604
|
+
applyFocusEdges(null);
|
|
1449
1605
|
}
|
|
1450
1606
|
}
|
|
1451
1607
|
if (onNodeHover) {
|
|
@@ -1465,7 +1621,7 @@ function NeuronWeb({
|
|
|
1465
1621
|
}
|
|
1466
1622
|
}
|
|
1467
1623
|
const slug = nodeSlugById.get(node.id);
|
|
1468
|
-
|
|
1624
|
+
applyFocusEdges(slug ? edgesBySlug.get(slug) ?? [] : []);
|
|
1469
1625
|
if (onNodeClick) {
|
|
1470
1626
|
onNodeClick(node);
|
|
1471
1627
|
}
|
|
@@ -1485,7 +1641,7 @@ function NeuronWeb({
|
|
|
1485
1641
|
setSelectedNodeId(null);
|
|
1486
1642
|
nodeRenderer.setSelectedNode(null);
|
|
1487
1643
|
if (!hoveredNodeId) {
|
|
1488
|
-
|
|
1644
|
+
applyFocusEdges(null);
|
|
1489
1645
|
}
|
|
1490
1646
|
if (onBackgroundClick) onBackgroundClick();
|
|
1491
1647
|
};
|
|
@@ -1503,7 +1659,8 @@ function NeuronWeb({
|
|
|
1503
1659
|
onNodeClick,
|
|
1504
1660
|
onNodeDoubleClick,
|
|
1505
1661
|
onNodeFocused,
|
|
1506
|
-
onBackgroundClick
|
|
1662
|
+
onBackgroundClick,
|
|
1663
|
+
applyFocusEdges
|
|
1507
1664
|
]);
|
|
1508
1665
|
react.useEffect(() => {
|
|
1509
1666
|
if (!sceneManager || !interactionManager) return;
|
|
@@ -1704,5 +1861,5 @@ exports.NeuronWeb = NeuronWeb;
|
|
|
1704
1861
|
exports.SceneManager = SceneManager;
|
|
1705
1862
|
exports.ThemeEngine = ThemeEngine;
|
|
1706
1863
|
exports.applyFuzzyLayout = applyFuzzyLayout;
|
|
1707
|
-
//# sourceMappingURL=chunk-
|
|
1708
|
-
//# sourceMappingURL=chunk-
|
|
1864
|
+
//# sourceMappingURL=chunk-OUO3CKBM.cjs.map
|
|
1865
|
+
//# sourceMappingURL=chunk-OUO3CKBM.cjs.map
|