Fix: Generate avatar; Add knowledge graph; Modify the style of the MultiSelect component (#8952)

### What problem does this PR solve?

Fix: Generate avatar; Add knowledge graph; Modify the style of the
multi-select component
[#3221](https://github.com/infiniflow/ragflow/issues/3221)

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-07-21 19:11:27 +08:00
committed by GitHub
parent b8891fdbeb
commit dbc267758e
12 changed files with 708 additions and 58 deletions

View File

@ -1,44 +1,116 @@
import { cn } from '@/lib/utils';
import * as AvatarPrimitive from '@radix-ui/react-avatar';
import { random } from 'lodash';
import { forwardRef } from 'react';
import { forwardRef, memo, useEffect, useRef, useState } from 'react';
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
const Colors = [
{ from: '#4F6DEE', to: '#67BDF9' },
{ from: '#38A04D', to: '#93DCA2' },
{ from: '#EDB395', to: '#C35F2B' },
{ from: '#633897', to: '#CBA1FF' },
];
const getStringHash = (str: string): number => {
const normalized = str.trim().toLowerCase();
let hash = 104729;
const seed = 0x9747b28c;
export const RAGFlowAvatar = forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & {
name?: string;
avatar?: string;
isPerson?: boolean;
for (let i = 0; i < normalized.length; i++) {
hash ^= seed ^ normalized.charCodeAt(i);
hash = (hash << 13) | (hash >>> 19);
hash = (hash * 5 + 0x52dce72d) | 0;
}
>(({ name, avatar, isPerson = false, className, ...props }, ref) => {
const index = random(0, 3);
console.log('🚀 ~ index:', index);
const value = Colors[index];
return (
<Avatar
ref={ref}
{...props}
className={cn(className, { 'rounded-md': !isPerson })}
>
<AvatarImage src={avatar} />
<AvatarFallback
className={cn(
`bg-gradient-to-b from-[${value.from}] to-[${value.to}]`,
{ 'rounded-md': !isPerson },
)}
return Math.abs(hash);
};
// Generate a hash function with a fixed color
const getColorForName = (name: string): { from: string; to: string } => {
const hash = getStringHash(name);
const hue = hash % 360;
return {
from: `hsl(${hue}, 70%, 80%)`,
to: `hsl(${hue}, 60%, 30%)`,
};
};
export const RAGFlowAvatar = memo(
forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root> & {
name?: string;
avatar?: string;
isPerson?: boolean;
}
>(({ name, avatar, isPerson = false, className, ...props }, ref) => {
// Generate initial letter logic
const getInitials = (name?: string) => {
if (!name) return '';
const parts = name.trim().split(/\s+/);
if (parts.length === 1) {
return parts[0][0].toUpperCase();
}
return parts[0][0].toUpperCase() + parts[1][0].toUpperCase();
};
const initials = getInitials(name);
const { from, to } = name
? getColorForName(name)
: { from: 'hsl(0, 0%, 80%)', to: 'hsl(0, 0%, 30%)' };
const fallbackRef = useRef<HTMLElement>(null);
const [fontSize, setFontSize] = useState('0.875rem');
// Calculate font size
const calculateFontSize = () => {
if (fallbackRef.current) {
const containerWidth = fallbackRef.current.offsetWidth;
const newSize = containerWidth * 0.6;
setFontSize(`${newSize}px`);
}
};
useEffect(() => {
calculateFontSize();
if (fallbackRef.current) {
const resizeObserver = new ResizeObserver(() => {
calculateFontSize();
});
resizeObserver.observe(fallbackRef.current);
return () => {
if (fallbackRef.current) {
resizeObserver.unobserve(fallbackRef.current);
}
resizeObserver.disconnect();
};
}
}, []);
return (
<Avatar
ref={ref}
{...props}
className={cn(className, { 'rounded-md': !isPerson })}
>
{name?.slice(0, 1)}
</AvatarFallback>
</Avatar>
);
});
<AvatarImage src={avatar} />
<AvatarFallback
ref={(node) => {
fallbackRef.current = node;
calculateFontSize();
}}
className={cn(
'bg-gradient-to-b',
`from-[${from}] to-[${to}]`,
'flex items-center justify-center',
'text-white font-bold',
{ 'rounded-md': !isPerson },
)}
style={{
backgroundImage: `linear-gradient(to bottom, ${from}, ${to})`,
fontSize: fontSize,
}}
>
{initials}
</AvatarFallback>
</Avatar>
);
}),
);
RAGFlowAvatar.displayName = 'RAGFlowAvatar';