yangdx
commited on
Commit
·
f245cd6
1
Parent(s):
dc36e22
Optimize node color by pre-set colors
Browse files
lightrag_webui/src/hooks/useLightragGraph.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
import Graph, { DirectedGraph } from 'graphology'
|
2 |
import { useCallback, useEffect, useRef } from 'react'
|
3 |
import { useTranslation } from 'react-i18next'
|
4 |
-
import {
|
5 |
import * as Constants from '@/lib/constants'
|
6 |
import { useGraphStore, RawGraph, RawNodeType, RawEdgeType } from '@/stores/graph'
|
7 |
import { toast } from 'sonner'
|
@@ -11,32 +11,115 @@ import { useSettingsStore } from '@/stores/settings'
|
|
11 |
|
12 |
import seedrandom from 'seedrandom'
|
13 |
|
14 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
const getNodeColorByType = (nodeType: string | undefined): string => {
|
16 |
-
const defaultColor = '#
|
|
|
|
|
17 |
if (!nodeType) {
|
18 |
return defaultColor;
|
19 |
}
|
20 |
|
|
|
21 |
const typeColorMap = useGraphStore.getState().typeColorMap;
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
// Call randomColor without arguments; it will use the globally seeded Math.random()
|
28 |
-
const newColor = randomColor();
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
}
|
36 |
|
37 |
-
//
|
38 |
-
//
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
};
|
41 |
|
42 |
|
@@ -408,7 +491,7 @@ const useLightrangeGraph = () => {
|
|
408 |
// Add a single node with "Graph Is Empty" label
|
409 |
emptyGraph.addNode('empty-graph-node', {
|
410 |
label: t('graphPanel.emptyGraph'),
|
411 |
-
color: '#
|
412 |
x: 0.5,
|
413 |
y: 0.5,
|
414 |
size: 15,
|
@@ -836,25 +919,25 @@ const useLightrangeGraph = () => {
|
|
836 |
try {
|
837 |
const state = useGraphStore.getState();
|
838 |
|
839 |
-
// 1.
|
840 |
if (!sigmaGraph.hasNode(nodeId)) {
|
841 |
console.error('Node not found:', nodeId);
|
842 |
return;
|
843 |
}
|
844 |
|
845 |
-
// 2.
|
846 |
const nodesToDelete = getNodesThatWillBeDeleted(nodeId, sigmaGraph);
|
847 |
|
848 |
-
// 3.
|
849 |
if (nodesToDelete.size === sigmaGraph.nodes().length) {
|
850 |
toast.error(t('graphPanel.propertiesView.node.deleteAllNodesError'));
|
851 |
return;
|
852 |
}
|
853 |
|
854 |
-
// 4.
|
855 |
state.clearSelection();
|
856 |
|
857 |
-
// 5.
|
858 |
for (const nodeToDelete of nodesToDelete) {
|
859 |
// Remove the node from the sigma graph (this will also remove connected edges)
|
860 |
sigmaGraph.dropNode(nodeToDelete);
|
|
|
1 |
import Graph, { DirectedGraph } from 'graphology'
|
2 |
import { useCallback, useEffect, useRef } from 'react'
|
3 |
import { useTranslation } from 'react-i18next'
|
4 |
+
import { errorMessage } from '@/lib/utils'
|
5 |
import * as Constants from '@/lib/constants'
|
6 |
import { useGraphStore, RawGraph, RawNodeType, RawEdgeType } from '@/stores/graph'
|
7 |
import { toast } from 'sonner'
|
|
|
11 |
|
12 |
import seedrandom from 'seedrandom'
|
13 |
|
14 |
+
// Predefined node colors - Primary colors
|
15 |
+
const NODE_COLORS = [
|
16 |
+
'#0f5870', // Deep Cyan
|
17 |
+
'#e3493b', // Google Red - geo
|
18 |
+
'#fdd868', // Yellow - UNKNOWN
|
19 |
+
'#34A853', // Google Green
|
20 |
+
'#a64dff', // Purple
|
21 |
+
'#F39C12', // Orange
|
22 |
+
'#1ABC9C', // Turquoise - organization
|
23 |
+
'#1f42ad', // Blue
|
24 |
+
'#ee8377', // Light Red
|
25 |
+
'#bf95d0', // Light Violet
|
26 |
+
'#99cc00', // Yellow Green - tecknology
|
27 |
+
'#0f705d', // Deep Turquoise
|
28 |
+
'#E67E22', // Carrot - category
|
29 |
+
'#568be1', // Light Blue - person
|
30 |
+
'#803300' // Deep Brown
|
31 |
+
];
|
32 |
+
|
33 |
+
// Extended colors - Used when node types exceed primary colors
|
34 |
+
const EXTENDED_COLORS = [
|
35 |
+
'#ff4da6', // Magenta
|
36 |
+
'#094338', // Deep Green
|
37 |
+
'#D35400', // Pumpkin
|
38 |
+
'#002699', // Deep Blue
|
39 |
+
'#5a2c6d', // Deep Violet
|
40 |
+
'#996600', // Brown
|
41 |
+
'#2574A9', // Steel Blue
|
42 |
+
'#912c21', // Deep Red
|
43 |
+
'#293618' // Dark Green
|
44 |
+
];
|
45 |
+
|
46 |
+
// All available colors combined
|
47 |
+
const ALL_COLORS = [...NODE_COLORS, ...EXTENDED_COLORS];
|
48 |
+
|
49 |
+
// Helper function to get color based on node type
|
50 |
const getNodeColorByType = (nodeType: string | undefined): string => {
|
51 |
+
const defaultColor = '#5D6D7E'; // Default color for nodes without a type or undefined type
|
52 |
+
|
53 |
+
// Return default color if node type is undefined
|
54 |
if (!nodeType) {
|
55 |
return defaultColor;
|
56 |
}
|
57 |
|
58 |
+
// Get type color map from store
|
59 |
const typeColorMap = useGraphStore.getState().typeColorMap;
|
60 |
|
61 |
+
// If this type already has an assigned color, return it
|
62 |
+
if (typeColorMap.has(nodeType)) {
|
63 |
+
return typeColorMap.get(nodeType) || defaultColor;
|
64 |
+
}
|
|
|
|
|
65 |
|
66 |
+
// Get all currently used colors
|
67 |
+
const usedColors = new Set<string>();
|
68 |
+
typeColorMap.forEach(color => {
|
69 |
+
usedColors.add(color);
|
70 |
+
});
|
71 |
+
|
72 |
+
// Assign color for new node type
|
73 |
+
// Use a simple hash function to map node type to color index
|
74 |
+
const getColorIndex = (str: string): number => {
|
75 |
+
let hash = 0;
|
76 |
+
for (let i = 0; i < str.length; i++) {
|
77 |
+
hash = ((hash << 5) - hash) + str.charCodeAt(i);
|
78 |
+
hash |= 0; // Convert to 32bit integer
|
79 |
+
}
|
80 |
+
// Ensure result is positive and within NODE_COLORS range only
|
81 |
+
return Math.abs(hash) % NODE_COLORS.length;
|
82 |
+
};
|
83 |
+
|
84 |
+
// Get initial color index from hash
|
85 |
+
const colorIndex = getColorIndex(nodeType);
|
86 |
+
let newColor = NODE_COLORS[colorIndex];
|
87 |
+
|
88 |
+
// If the color is already used, find the next available color
|
89 |
+
if (usedColors.has(newColor) && usedColors.size < ALL_COLORS.length) {
|
90 |
+
// First try to find an unused color in NODE_COLORS
|
91 |
+
let foundUnused = false;
|
92 |
+
for (let i = 0; i < NODE_COLORS.length; i++) {
|
93 |
+
const candidateColor = NODE_COLORS[i];
|
94 |
+
if (!usedColors.has(candidateColor)) {
|
95 |
+
newColor = candidateColor;
|
96 |
+
foundUnused = true;
|
97 |
+
break;
|
98 |
+
}
|
99 |
+
}
|
100 |
|
101 |
+
// If all NODE_COLORS are used, then try EXTENDED_COLORS
|
102 |
+
if (!foundUnused) {
|
103 |
+
newColor = defaultColor;
|
104 |
+
for (let i = 0; i < EXTENDED_COLORS.length; i++) {
|
105 |
+
const candidateColor = EXTENDED_COLORS[i];
|
106 |
+
if (!usedColors.has(candidateColor)) {
|
107 |
+
newColor = candidateColor;
|
108 |
+
break;
|
109 |
+
}
|
110 |
+
}
|
111 |
+
}
|
112 |
}
|
113 |
|
114 |
+
// If all colors are used, we'll still use the hashed color
|
115 |
+
// In a more advanced implementation, we could create color variants here
|
116 |
+
|
117 |
+
// Update color mapping
|
118 |
+
const newMap = new Map(typeColorMap);
|
119 |
+
newMap.set(nodeType, newColor);
|
120 |
+
useGraphStore.setState({ typeColorMap: newMap });
|
121 |
+
|
122 |
+
return newColor;
|
123 |
};
|
124 |
|
125 |
|
|
|
491 |
// Add a single node with "Graph Is Empty" label
|
492 |
emptyGraph.addNode('empty-graph-node', {
|
493 |
label: t('graphPanel.emptyGraph'),
|
494 |
+
color: '#5D6D7E', // gray color
|
495 |
x: 0.5,
|
496 |
y: 0.5,
|
497 |
size: 15,
|
|
|
919 |
try {
|
920 |
const state = useGraphStore.getState();
|
921 |
|
922 |
+
// 1. Check if node exists
|
923 |
if (!sigmaGraph.hasNode(nodeId)) {
|
924 |
console.error('Node not found:', nodeId);
|
925 |
return;
|
926 |
}
|
927 |
|
928 |
+
// 2. Get nodes to delete
|
929 |
const nodesToDelete = getNodesThatWillBeDeleted(nodeId, sigmaGraph);
|
930 |
|
931 |
+
// 3. Check if this would delete all nodes
|
932 |
if (nodesToDelete.size === sigmaGraph.nodes().length) {
|
933 |
toast.error(t('graphPanel.propertiesView.node.deleteAllNodesError'));
|
934 |
return;
|
935 |
}
|
936 |
|
937 |
+
// 4. Clear selection - this will cause PropertiesView to close immediately
|
938 |
state.clearSelection();
|
939 |
|
940 |
+
// 5. Delete nodes and related edges
|
941 |
for (const nodeToDelete of nodesToDelete) {
|
942 |
// Remove the node from the sigma graph (this will also remove connected edges)
|
943 |
sigmaGraph.dropNode(nodeToDelete);
|