MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / SidePanel

Function SidePanel

dashboard/holographic/src/SemanticMap.tsx:828–1032  ·  view source on GitHub ↗
({
  placed,
  selection,
  selected,
  onSelect,
  onShowAllCategories,
}: {
  placed: PlacedPoint[];
  selection: ReadonlySet<number> | null;
  selected: MemoryProjectionPoint | null;
  onSelect: (point: MemoryProjectionPoint | null) => void;
  /** Present when legend filters hide every point; restores all categories. */
  onShowAllCategories?: () => void;
})

Source from the content-addressed store, hash-verified

826const ROW_HEIGHT = 46;
827
828function SidePanel({
829 placed,
830 selection,
831 selected,
832 onSelect,
833 onShowAllCategories,
834}: {
835 placed: PlacedPoint[];
836 selection: ReadonlySet<number> | null;
837 selected: MemoryProjectionPoint | null;
838 onSelect: (point: MemoryProjectionPoint | null) => void;
839 /** Present when legend filters hide every point; restores all categories. */
840 onShowAllCategories?: () => void;
841}) {
842 const { containerRef, onScroll, start, end, totalHeight, offsetTop } =
843 useVirtualList({ count: placed.length, rowHeight: ROW_HEIGHT });
844
845 // Projection payloads truncate content at 200 chars; when the pinned fact
846 // looks truncated, fetch the full row from the fact-detail endpoint.
847 const selectedId = selected?.fact_id;
848 const maybeTruncated = (selected?.content?.length ?? 0) >= 200;
849 const [detail, setDetail] = useState<{ id: number; content: string } | null>(
850 null,
851 );
852 const [detailFailedId, setDetailFailedId] = useState<number | null>(null);
853 useEffect(() => {
854 if (selectedId == null || !maybeTruncated) return;
855 let cancelled = false;
856 setDetailFailedId((prev) => (prev === selectedId ? null : prev));
857 api
858 .getMemoryFact(selectedId)
859 .then((resp) => {
860 if (!cancelled) setDetail({ id: selectedId, content: resp.fact.content });
861 })
862 .catch(() => {
863 if (!cancelled) setDetailFailedId(selectedId);
864 });
865 return () => {
866 cancelled = true;
867 };
868 }, [selectedId, maybeTruncated]);
869
870 const fullContent =
871 detail && detail.id === selectedId ? detail.content : null;
872 const detailFailed = detailFailedId === selectedId;
873 const detailLoading = maybeTruncated && !fullContent && !detailFailed;
874
875 // Single tab stop + arrow-key roving selection (the rows themselves stay
876 // buttons for pointer users, but are skipped in the tab order).
877 const activeIndex = selected
878 ? placed.findIndex((p) => p.point.fact_id === selected.fact_id)
879 : -1;
880
881 const moveTo = (index: number) => {
882 if (placed.length === 0) return;
883 const next = Math.max(0, Math.min(placed.length - 1, index));
884 onSelect(placed[next].point);
885 const node = containerRef.current;

Callers

nothing calls this directly

Calls 3

useVirtualListFunction · 0.90
useStateFunction · 0.85
useEffectFunction · 0.85

Tested by

no test coverage detected