Files
codebase-memory-mcp/graph-ui/src/components/NodeTooltip.tsx
T
Martin VogelandAndy Zehady a27680c3a9 feat(graph-ui): dead-code filtering, node code preview, GitHub deep-links
Distilled from #789 (safe features only). Adds backend dead-code classification
(status + in_calls in the layout JSON), a GET /api/repo-info endpoint for GitHub
deep-links, and the frontend dead-code filters, node code preview, and deep-links.

The render-cap revert was dropped (kept 2000 for DEFAULT_MAX_NODES/HARD_MAX_NODES
and GRAPH_RENDER_NODE_LIMIT); the sidebar regex-search refactor was omitted.

Security fixes over the original: repo-info strips credentials from any returned
remote_url; the legitimate https blob-URL construction is allow-listed so the
static gate passes; libgit2 is not re-initialized/shutdown per request (reuses the
process-wide init from cbm_alloc_init); deep-link path segments are URL-encoded.

Co-authored-by: Andy Zehady <azehady@ciroos.ai>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-04 22:19:53 +02:00

57 lines
2.0 KiB
TypeScript

import { Html } from "@react-three/drei";
import type { GraphNode } from "../lib/types";
import { colorForLabel, colorForStatus } from "../lib/colors";
interface NodeTooltipProps {
node: GraphNode;
}
function lineRange(node: GraphNode): string | null {
if (!node.start_line) return null;
if (node.end_line && node.end_line !== node.start_line)
return `L${node.start_line}-${node.end_line}`;
return `L${node.start_line}`;
}
export function NodeTooltip({ node }: NodeTooltipProps) {
return (
<Html
position={[node.x, node.y + node.size * 0.7, node.z]}
center
style={{ pointerEvents: "none" }}
>
<div className="bg-[#1a1a2e]/95 backdrop-blur border border-white/10 rounded-lg px-3 py-2 text-xs whitespace-nowrap shadow-xl max-w-[350px]">
<div className="flex items-center gap-1.5 mb-1">
<span
className="w-2 h-2 rounded-full shrink-0"
style={{ backgroundColor: colorForLabel(node.label) }}
/>
<span className="text-white font-medium truncate">{node.name}</span>
<span className="text-white/30 ml-1 shrink-0">{node.label}</span>
</div>
{node.file_path && (
<p className="text-white/30 font-mono truncate">
{node.file_path}
{lineRange(node) && <span className="text-white/40"> · {lineRange(node)}</span>}
</p>
)}
{node.status && node.status !== "structural" && (
<div className="flex items-center gap-1.5 mt-1">
<span
className="w-1.5 h-1.5 rounded-full shrink-0"
style={{ backgroundColor: colorForStatus(node.status) }}
/>
<span className="text-white/45">{node.status}</span>
{node.in_calls !== undefined && (
<span className="text-white/25">
· {node.in_calls} caller{node.in_calls === 1 ? "" : "s"}
</span>
)}
</div>
)}
<p className="text-white/20 mt-1 text-[10px]">click for code →</p>
</div>
</Html>
);
}