feat: Add Expert Transformation Agent with multi-expert perspective system
- Backend: Add expert transformation router with 3-step SSE pipeline - Step 0: Generate diverse expert team (random domains) - Step 1: Each expert generates keywords for attributes - Step 2: Batch generate descriptions for expert keywords - Backend: Add simplified prompts for reliable JSON output - Frontend: Add TransformationPanel with React Flow visualization - Frontend: Add TransformationInputPanel for expert configuration - Expert count (2-8), keywords per expert (1-3) - Custom expert domains support - Frontend: Add expert keyword nodes with expert badges - Frontend: Improve description card layout (wider cards, more spacing) - Frontend: Add fallback for missing descriptions with visual indicators 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { memo, useState } from 'react';
|
||||
|
||||
interface ExpertKeywordNodeProps {
|
||||
data: {
|
||||
label: string;
|
||||
expertName: string;
|
||||
expertId: string;
|
||||
color: string;
|
||||
isDark: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const ExpertKeywordNode = memo(({ data }: ExpertKeywordNodeProps) => {
|
||||
const { label, expertName, color, isDark } = data;
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
padding: '6px 12px',
|
||||
paddingRight: '36px',
|
||||
marginTop: 18,
|
||||
borderRadius: 6,
|
||||
background: isDark
|
||||
? 'linear-gradient(135deg, rgba(24, 144, 255, 0.15) 0%, rgba(255, 255, 255, 0.1) 100%)'
|
||||
: 'linear-gradient(135deg, rgba(24, 144, 255, 0.1) 0%, rgba(0, 0, 0, 0.03) 100%)',
|
||||
border: `2px solid ${color}`,
|
||||
borderWidth: isHovered ? 3 : 2,
|
||||
color: isDark ? '#fff' : '#333',
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'none',
|
||||
filter: isHovered ? 'brightness(1.1)' : 'none',
|
||||
boxShadow: isDark
|
||||
? '0 2px 8px rgba(24, 144, 255, 0.2)'
|
||||
: '0 2px 8px rgba(24, 144, 255, 0.15)',
|
||||
}}
|
||||
>
|
||||
{/* Expert Badge - positioned above the node */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: -18,
|
||||
left: 0,
|
||||
padding: '2px 6px',
|
||||
borderRadius: 3,
|
||||
background: isDark
|
||||
? 'rgba(24, 144, 255, 0.8)'
|
||||
: 'rgba(24, 144, 255, 0.9)',
|
||||
color: '#fff',
|
||||
fontSize: '10px',
|
||||
fontWeight: 500,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{expertName}
|
||||
</div>
|
||||
|
||||
{/* Keyword Label */}
|
||||
<div>{label}</div>
|
||||
|
||||
{/* NEW Badge - positioned below the node */}
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: -14,
|
||||
right: 0,
|
||||
padding: '2px 5px',
|
||||
borderRadius: 3,
|
||||
background: 'linear-gradient(135deg, #52c41a 0%, #73d13d 100%)',
|
||||
color: '#fff',
|
||||
fontSize: '9px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
NEW
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
ExpertKeywordNode.displayName = 'ExpertKeywordNode';
|
||||
Reference in New Issue
Block a user