Merge pull request #1282 from danielaskdd/main
Optimize node color by pre-set colors
This commit is contained in:
File diff suppressed because one or more lines are too long
2
lightrag/api/webui/index.html
generated
2
lightrag/api/webui/index.html
generated
@@ -8,7 +8,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Lightrag</title>
|
||||
<script type="module" crossorigin src="/webui/assets/index-C08hvvRm.js"></script>
|
||||
<script type="module" crossorigin src="/webui/assets/index-sAkGsjRM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/webui/assets/index-QU59h9JG.css">
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import Graph, { DirectedGraph } from 'graphology'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { randomColor, errorMessage } from '@/lib/utils'
|
||||
import { errorMessage } from '@/lib/utils'
|
||||
import * as Constants from '@/lib/constants'
|
||||
import { useGraphStore, RawGraph, RawNodeType, RawEdgeType } from '@/stores/graph'
|
||||
import { toast } from 'sonner'
|
||||
@@ -11,32 +11,115 @@ import { useSettingsStore } from '@/stores/settings'
|
||||
|
||||
import seedrandom from 'seedrandom'
|
||||
|
||||
// Helper function to generate a color based on type
|
||||
// Predefined node colors - Primary colors
|
||||
const NODE_COLORS = [
|
||||
'#fdd868', // Yellow - UNKNOWN
|
||||
'#e3493b', // Google Red - geo
|
||||
'#1212a1', // Deep Cyan - weapon
|
||||
'#0f705d', // Green - organization
|
||||
'#a64dff', // Purple - technology
|
||||
'#f46a9b', // Magenta
|
||||
'#00bfa0', // Turquoise
|
||||
'#fdcce5', // Light Pink
|
||||
'#0f558a', // Blue - location
|
||||
'#b2e061', // Yellow Green
|
||||
'#bd7ebe', // Light Violet - event
|
||||
'#439bd6', // Cyan - person
|
||||
'#094338', // Deep Green
|
||||
'#dc0ab4', // Pink Red
|
||||
'#fd7f6f', // Light Red - category
|
||||
'#b04238', // Brown
|
||||
];
|
||||
|
||||
// Extended colors - Used when node types exceed primary colors
|
||||
const EXTENDED_COLORS = [
|
||||
'#5ad45a', // Light Green
|
||||
'#5a2c6d', // Deep Violet
|
||||
'#6c1313', // Dark Red
|
||||
'#184868', // Dark Cyan
|
||||
'#996600', // Yellow Brown
|
||||
'#4421af', // Deep Purple
|
||||
'#E67E22', // Carrot
|
||||
'#e61919', // Light Red
|
||||
];
|
||||
|
||||
// All available colors combined
|
||||
const ALL_COLORS = [...NODE_COLORS, ...EXTENDED_COLORS];
|
||||
|
||||
// Helper function to get color based on node type
|
||||
const getNodeColorByType = (nodeType: string | undefined): string => {
|
||||
const defaultColor = '#CCCCCC'; // Default color for nodes without a type or undefined type
|
||||
const defaultColor = '#5D6D7E'; // Default color for nodes without a type or undefined type
|
||||
|
||||
// Return default color if node type is undefined
|
||||
if (!nodeType) {
|
||||
return defaultColor;
|
||||
}
|
||||
|
||||
// Get type color map from store
|
||||
const typeColorMap = useGraphStore.getState().typeColorMap;
|
||||
|
||||
if (!typeColorMap.has(nodeType)) {
|
||||
// Generate a color based on the type string itself for consistency
|
||||
// Seed the global random number generator based on the node type
|
||||
seedrandom(nodeType, { global: true });
|
||||
// Call randomColor without arguments; it will use the globally seeded Math.random()
|
||||
const newColor = randomColor();
|
||||
|
||||
const newMap = new Map(typeColorMap);
|
||||
newMap.set(nodeType, newColor);
|
||||
useGraphStore.setState({ typeColorMap: newMap });
|
||||
|
||||
return newColor;
|
||||
// If this type already has an assigned color, return it
|
||||
if (typeColorMap.has(nodeType)) {
|
||||
return typeColorMap.get(nodeType) || defaultColor;
|
||||
}
|
||||
|
||||
// Restore the default random seed if necessary, though usually not required for this use case
|
||||
// seedrandom(Date.now().toString(), { global: true });
|
||||
return typeColorMap.get(nodeType) || defaultColor; // Add fallback just in case
|
||||
// Get all currently used colors
|
||||
const usedColors = new Set<string>();
|
||||
typeColorMap.forEach(color => {
|
||||
usedColors.add(color);
|
||||
});
|
||||
|
||||
// Assign color for new node type
|
||||
// Use a simple hash function to map node type to color index
|
||||
const getColorIndex = (str: string): number => {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = ((hash << 5) - hash) + str.charCodeAt(i);
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
// Ensure result is positive and within NODE_COLORS range only
|
||||
return Math.abs(hash) % NODE_COLORS.length;
|
||||
};
|
||||
|
||||
// Get initial color index from hash
|
||||
const colorIndex = getColorIndex(nodeType);
|
||||
let newColor = NODE_COLORS[colorIndex];
|
||||
|
||||
// If the color is already used, find the next available color
|
||||
if (usedColors.has(newColor) && usedColors.size < ALL_COLORS.length) {
|
||||
// First try to find an unused color in NODE_COLORS
|
||||
let foundUnused = false;
|
||||
for (let i = 0; i < NODE_COLORS.length; i++) {
|
||||
const candidateColor = NODE_COLORS[i];
|
||||
if (!usedColors.has(candidateColor)) {
|
||||
newColor = candidateColor;
|
||||
foundUnused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all NODE_COLORS are used, then try EXTENDED_COLORS
|
||||
if (!foundUnused) {
|
||||
newColor = defaultColor;
|
||||
for (let i = 0; i < EXTENDED_COLORS.length; i++) {
|
||||
const candidateColor = EXTENDED_COLORS[i];
|
||||
if (!usedColors.has(candidateColor)) {
|
||||
newColor = candidateColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If all colors are used, we'll still use the hashed color
|
||||
// In a more advanced implementation, we could create color variants here
|
||||
|
||||
// Update color mapping
|
||||
const newMap = new Map(typeColorMap);
|
||||
newMap.set(nodeType, newColor);
|
||||
useGraphStore.setState({ typeColorMap: newMap });
|
||||
|
||||
return newColor;
|
||||
};
|
||||
|
||||
|
||||
@@ -408,7 +491,7 @@ const useLightrangeGraph = () => {
|
||||
// Add a single node with "Graph Is Empty" label
|
||||
emptyGraph.addNode('empty-graph-node', {
|
||||
label: t('graphPanel.emptyGraph'),
|
||||
color: '#cccccc', // gray color
|
||||
color: '#5D6D7E', // gray color
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
size: 15,
|
||||
@@ -836,25 +919,25 @@ const useLightrangeGraph = () => {
|
||||
try {
|
||||
const state = useGraphStore.getState();
|
||||
|
||||
// 1. 检查节点是否存在
|
||||
// 1. Check if node exists
|
||||
if (!sigmaGraph.hasNode(nodeId)) {
|
||||
console.error('Node not found:', nodeId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取要删除的节点
|
||||
// 2. Get nodes to delete
|
||||
const nodesToDelete = getNodesThatWillBeDeleted(nodeId, sigmaGraph);
|
||||
|
||||
// 3. 检查是否会删除所有节点
|
||||
// 3. Check if this would delete all nodes
|
||||
if (nodesToDelete.size === sigmaGraph.nodes().length) {
|
||||
toast.error(t('graphPanel.propertiesView.node.deleteAllNodesError'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 清除选中状态 - 这会导致PropertiesView立即关闭
|
||||
// 4. Clear selection - this will cause PropertiesView to close immediately
|
||||
state.clearSelection();
|
||||
|
||||
// 5. 删除节点和相关边
|
||||
// 5. Delete nodes and related edges
|
||||
for (const nodeToDelete of nodesToDelete) {
|
||||
// Remove the node from the sigma graph (this will also remove connected edges)
|
||||
sigmaGraph.dropNode(nodeToDelete);
|
||||
|
Reference in New Issue
Block a user