disclosure-bureau/web/components/entity-graph-mini.tsx

87 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* EntityGraphMini — sidebar widget with co-mentioned entities (neighbors)
* and a sample of chunks where they co-occur. Lives next to the entity body.
*
* Gracefully degrades to empty state when entity_mentions is unpopulated
* or the DB is unreachable.
*/
import Link from "next/link";
import { findEntity, getNeighbors } from "@/lib/retrieval/graph";
const CLASS_COLOR: Record<string, string> = {
person: "text-[#ff6ec7] border-[#ff6ec7]",
organization: "text-[#ff8a4d] border-[#ff8a4d]",
location: "text-[#3fde6a] border-[#3fde6a]",
event: "text-[#ffa500] border-[#ffa500]",
uap_object: "text-[#ff3344] border-[#ff3344]",
vehicle: "text-[#5b9bd5] border-[#5b9bd5]",
operation: "text-[#9b5de5] border-[#9b5de5]",
concept: "text-[#06d6a0] border-[#06d6a0]",
};
const CLASS_FOLDER: Record<string, string> = {
person: "people",
organization: "organizations",
location: "locations",
event: "events",
uap_object: "uap-objects",
vehicle: "vehicles",
operation: "operations",
concept: "concepts",
};
export async function EntityGraphMini({
entityClassSingular,
entityId,
}: {
entityClassSingular: string;
entityId: string;
}) {
let neighbors: Awaited<ReturnType<typeof getNeighbors>> = [];
let totalMentions = 0;
try {
const ent = await findEntity(entityClassSingular, entityId);
if (ent) {
totalMentions = ent.total_mentions ?? 0;
neighbors = await getNeighbors(ent.entity_pk, { limit: 25 });
}
} catch {
return null;
}
if (neighbors.length === 0) {
return null;
}
return (
<section className="mt-6 border-t border-[rgba(0,255,156,0.12)] pt-4">
<h3 className="font-mono text-[10px] text-[#8896aa] uppercase tracking-widest mb-3">
🕸 co-mentioned · top {neighbors.length} via {totalMentions} mentions
</h3>
<ul className="space-y-1.5">
{neighbors.map((n) => {
const folder = CLASS_FOLDER[n.entity_class] ?? n.entity_class;
const color = CLASS_COLOR[n.entity_class] ?? "text-[#7fdbff] border-[#7fdbff]";
return (
<li key={n.entity_pk}>
<Link
href={`/e/${folder}/${n.entity_id}`}
className="group flex items-center gap-2 text-xs hover:bg-[rgba(0,255,156,0.04)] rounded px-2 py-1 -mx-2"
>
<span className={`font-mono text-[9px] px-1.5 py-0.5 border rounded ${color} opacity-70 group-hover:opacity-100`}>
{n.entity_class.slice(0, 3)}
</span>
<span className="flex-1 truncate text-[#c8d4e6] group-hover:text-[#00ff9c]">
{n.canonical_name}
</span>
<span className="font-mono text-[10px] text-[#5a6678]">
×{n.weight}
</span>
</Link>
</li>
);
})}
</ul>
</section>
);
}