2026-02-06 23:01:30 -05:00

66 lines
2.1 KiB
TypeScript

'use client';
import React, { memo, useState, useCallback } from 'react';
import { type NodeProps } from '@xyflow/react';
import { ChevronDown, ChevronRight, FolderOpen } from 'lucide-react';
import type { ToolGroup } from '@mcpengine/ai-pipeline/types';
export interface GroupNodeData extends Record<string, unknown> {
group: ToolGroup;
childCount: number;
}
export const GroupNode = memo(function GroupNode({ data }: NodeProps) {
const { group, childCount } = data as GroupNodeData;
const [collapsed, setCollapsed] = useState(false);
const toggleCollapse = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
setCollapsed((prev) => !prev);
}, []);
return (
<div
className={`
bg-gray-800/30 border border-dashed border-gray-600 rounded-xl p-2
transition-all duration-200
${collapsed ? 'min-h-[56px]' : 'min-h-[120px]'}
`}
style={{ minWidth: 320 }}
>
{/* Header */}
<div
className="flex items-center gap-2 px-2 py-1.5 cursor-pointer select-none"
onClick={toggleCollapse}
>
<FolderOpen className="w-3.5 h-3.5 text-gray-500" />
<span className="text-xs uppercase text-gray-400 tracking-wider font-medium flex-1">
{group.name}
</span>
<span className="text-[10px] bg-gray-700/60 text-gray-400 px-1.5 py-0.5 rounded-full">
{childCount} tool{childCount !== 1 ? 's' : ''}
</span>
{collapsed ? (
<ChevronRight className="w-3.5 h-3.5 text-gray-500" />
) : (
<ChevronDown className="w-3.5 h-3.5 text-gray-500" />
)}
</div>
{/* Description (when expanded) */}
{!collapsed && group.description && (
<p className="px-2 pb-2 text-[11px] text-gray-500 leading-relaxed">
{group.description}
</p>
)}
{/* Child area — React Flow handles nested node rendering externally,
this is the visual container. Children are positioned by parentId in RF. */}
{!collapsed && (
<div className="min-h-[60px] rounded-lg" />
)}
</div>
);
});