leankg 0.19.31

Lightweight Knowledge Graph for AI-Assisted Development
import { useMemo } from 'react';
import { CLUSTER_COLORS, clusterColorOf, clusterIdOf } from '../lib/color';
import type { GraphNode } from '../lib/types';

/** FR-E04 — legend of cluster colors derived from /api/graph/clusters nodes. */
export default function ClusterLegend({ nodes }: { nodes: GraphNode[] }) {
  const clusters = useMemo(() => {
    const byId = new Map<string, number>();
    for (const n of nodes) {
      // Backend cluster nodes: id = "cluster:<dir>", filePath = the dir itself.
      const id = n.id.startsWith('cluster:')
        ? n.id.slice('cluster:'.length)
        : clusterIdOf(n.properties.filePath);
      byId.set(id, (byId.get(id) ?? 0) + 1);
    }
    return [...byId.entries()]
      .sort((a, b) => b[1] - a[1])
      .map(([id, count]) => ({ id, count, color: clusterColorOf(id) }));
  }, [nodes]);

  if (clusters.length === 0) return null;
  return (
    <div className="cluster-legend" data-testid="cluster-legend">
      <span className="legend-title">Clusters</span>
      {clusters.map((c) => (
        <span key={c.id} className="legend-row">
          <span className="legend-swatch" style={{ background: c.color }} />
          <span className="legend-name">{c.id || 'root'}</span>
          <span className="legend-count">{c.count}</span>
        </span>
      ))}
      <span className="legend-total">{CLUSTER_COLORS.length} colors max</span>
    </div>
  );
}