import { useEffect, useRef, useState } from "react"; import { Treemap } from "recharts"; import { contextGroupFill, formatContextTokens } from "./format.js"; import type { ContextSegmentViewData, ContextSystemSectionViewData, } from "./types.js"; interface TreemapDatum { name: string; size: number; group: string; segmentId: string; } function TreemapContent(props: any) { const { x, y, width, height, name, group, size } = props; if (width < 8 || height < 8) return null; const showLabel = width > 90 && height > 42; return ( {showLabel ? (
{name}
{formatContextTokens(size)}
) : null}
); } export function ContextTreemapView({ segments, systemSections = [], onSelect, }: { segments: ContextSegmentViewData[]; systemSections?: ContextSystemSectionViewData[]; onSelect?: (segmentId: string) => void; }) { const containerRef = useRef(null); const [chartWidth, setChartWidth] = useState(0); const data: TreemapDatum[] = segments .filter((segment) => segment.status !== "evicted" && segment.tokenCount > 0) .map((segment) => ({ name: segment.label, size: segment.tokenCount, group: segment.group, segmentId: segment.segmentId, })); data.push( ...systemSections .filter((section) => section.tokenCount > 0) .map((section) => ({ name: section.label, size: section.tokenCount, group: `System ยท ${section.governance}`, segmentId: section.segmentId, })), ); useEffect(() => { const element = containerRef.current; if (!element) return; const updateWidth = () => setChartWidth(Math.max(0, Math.floor(element.clientWidth - 8))); updateWidth(); const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(updateWidth) : null; observer?.observe(element); window.addEventListener("resize", updateWidth); return () => { observer?.disconnect(); window.removeEventListener("resize", updateWidth); }; }, []); if (data.length === 0) { return (
No active segments
); } return (
{chartWidth > 0 ? ( } onClick={(datum: any) => { if (datum?.segmentId) onSelect?.(datum.segmentId); }} /> ) : null}
); }